请教PIX拓攻m2飞控拆解 telem1 telem2 接口设置

豆丁微信公众号
君,已阅读到文档的结尾了呢~~
pixhawk源碼總結_圖文
扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
pixhawk源碼總結_圖文
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='http://www.docin.com/DocinViewer--144.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口Pixhawk 源码分析--- Sany_huang目录阿国总结整理---Pixhawk 源码一:APM 代码基本结构 Pixhawk 源码二:APM 线程 Pixhawk 源码三:串行接口 UART 和 Console Pixhawk 源码四:学习 RC Input and Output Pixhawk 源码五:存储与 EEPROM 管理 Pixhawk 源码六:源码预览与 APM 程序库 Pixhawk 源码七:姿态控制预览 Pixhawk 源码八:添加新的参数 Pixhawk 源码九:添加新的飞行模式 Pixhawk 源码十:代码调度,使之定时运行 Pixhawk 源码十一:增加新的 MAVLink 消息 Pixhawk 源码十二:采用 Visual Studio 编译 Mission Planner 方法与问题总结Pixhawk 源码笔记一:代码基本结构ArduPilot 代码分为 5 个主要部分,基本结构分类如下:(无人机控制系统 ArduPilotMega (APM) 是市面上最强大的基于惯性导航的开源自驾仪(并且是最便宜的之一!) 特性包括: 免费开源固件)? ? ? ? ?vehicle directories AP_HAL libraries tools directories external support code1、vehicle directories 模型类型 当前共有 4 种模型:ArduPlane, ArduCopter, APMrover2 and AntennaTracker 。都是.pde 文件,就是为了兼容 arduino 平台,以后可能会放弃。 ArduPlane:固件运行在 APM 自动驾驶仪上,可以给任何固定翼飞机全部自主飞行能力 ArduCopter 多旋翼机 arduino 平台一款便捷灵活、方便上手的开源电子原型平台,包含硬件(各种型号的 Arduino 板)和软件 (Arduino IDE) 2、AP_HAL 硬件抽象层 硬件抽象层,使得在不同硬件平台上的移植变得简单。 其中 AP_HAL 目录定义了一个通用的接口。其他的目录 AP_HAL_XXX 针对不同硬件平台进行 详细的定义。例如 AP_HAL_AVR 目录对于 AVR 平台,AP_HAL_PX4 对应 PX4 平台,AP_HAL_Linux 对应 Linux 平台。 3、tools directories 工具目录 主要提供支持。For examples, tools/autotest provides the autotest infrastructure behind theautotest.diydrones.com site and tools/Replay provides our log replay utility. 4、external support code 外部支持代码 对于其他平台,需要外部支持代码。例如 Pixhawk、PX4 的支持代码如下:? ? ? ?PX4NuttX C 板载实时系统。the core NuttX RTOS used on PX4 boards PX4Firmware C PX4 固件。the base PX4 middleware and drivers used on PX4 boards uavcan C 飞行器 CAN 通信协议。the uavcan CANBUS implementation used in ArduPilot mavlink C Mavlink 通信协议。the mavlink protocol and code generator5、系统编译 针对不同的硬件板,编译可以采用“make TARGET”的形式。? ? ? ?make apm1 C the APM1 board make apm2 C the APM2 board make px4-v1 C the PX4v1 make px4-v2 C the Pixhawk 如果要移植到新的硬件,可以在 mk/targets.mk 文件中添加。 比如: make apm2-octa -j8 或者: make px4-v2 -j8 采用 8 通道并行编译方式,针对 APM、Pixhawk 硬件板(AVR、STM32),编译八旋翼代码。第二部分: 学习 sketch 例程代码 http://dev.ardupilot.com/wiki/learning-ardupilot-the-example-sketches/ sketch,是指使用 .pde 文件编写的主程序。 开始之前,你可以试着阅读、编译并运行下面的 sketches? ? ? ? ?libraries/AP_GPS/examples/GPS_AUTO_test libraries/AP_InertialSensor/examples/INS_generic libraries/AP_Compass/examples/AP_Compass_test libraries/AP_Baro/examples/BARO_generic libraries/AP_AHRS/examples/AHRS_Test 例如,下面的编译方法,将在 Pixhawk 上安装 AP_GPS 例程 sketch。 cd libraries/AP_GPS/examples/GPS_AUTO_test make px4-clean make px4-v2 make px4-v2-upload 正确理解 sketch 例程代码,我们以 GPS_AUTO_test.pde 代码为例(目录ardupilot\libraries\AP_GPS\examples\GPS_AUTO_test),主要几个特点: 1、 pde 文件包含很多 includes; 2、 定义了 hal 引用声明; 3、 代码非常粗糙; 4、 setup() 和 loop()函数 1、include 文件 pde 文件转变为 C++文件后,提供必要的库引用支持。 2、hal 引用声明 定义如下: const AP_HAL::HAL& hal = AP_HAL_BOARD_DRIVER;// pixhawk 等价于 AP_HAL_PX4 该定义,方便访问硬件接口,比如 console 终端、定时器、I2C、SPI 接口等。 实际的定义是在 HAL_PX4_Class.cpp 中定义,如下: const HAL_PX4 AP_HAL_PX4; hal 是针对 AP_HAL_PX4 的引用。 经常使用的方法如下:? ? ? ? ? ?终端字符输出。 hal.console-&printf() and hal.console-&printf_P() to print strings (use the _P to use less memory on AVR) 获取当前运行时间。hal.scheduler-&millis() and hal.scheduler-&micros() to get the time since boot 延时。 hal.scheduler-&delay() and hal.scheduler-&delay_microseconds() to sleep for a short time IO 输入输出。hal.gpio-&pinMode(), hal.gpio-&read() and hal.gpio-&write() for accessing GPIO pins I2C 操作,hal.i2c SPI 操作,hal.spi3、setup()和 loop() 每个 sketch 都有一个 setup()和 loop()函数。板子启动时,setup()被调用。这些调用都 来自 HAL 代码中的 main()函数调用(HAL_PX4_Class.cpp 文件 main_loop() )。setup()函数只调用一次, 用于初始化所有 libraries。 Loop()循环被调用,执行主任务。 4、AP_HAL_MAIN()宏指令 每一个 sketch(.pde 文件)最底部,都有一个“AP_HAL_MAIN();”指令,它是一个 HAL 宏, 用于定义一个 C++ main 函数,整个程序的入口。它真正的定义在 AP_HAL_PX4_Main.h 中。 #define AP_HAL_MAIN() \ extern &C& __EXPORT int SKETCH_MAIN(int argc, char * const argv[]); \ int SKETCH_MAIN(int argc, char * const argv[]) { \ hal.init(argc, argv); \ return OK; \ } 作为程序的起点,在 AP_HAL_MAIN()里,就正式调用了 hal.init()初始化代码。 程序的执行过程就是:程序起点 AP_HAL_MAIN() ? hal.init() sketch 中的 setup()和 loop()。 ? hal.main_loop() ? Pixhawk 源码笔记二:APM 线程第三部分 APM 线程详细参考:http://dev.ardupilot.com/wiki/learning-ardupilot-threading/ 对于 APM1、APM2 硬件板,不支持多线程,所以只能通过简单的定时器加回调函数 来实现。类似 PX4 和 Linux 硬件板支持 Posix 标准的多线程。线程一般是指基于多任务操作 系统的并行任务,我们首先要明白的几个概念如下:1、 定时回调 2、 HAL 专属线程 3、 驱动专属线程 4、 APM 驱动与板级驱动 5、 板级专属线程、任务 6、 AP_Scheduler 任务调度系统 7、 信号灯(任务队列互锁用) 8、 lockless data structures 如果你对操作系统运行机制比较了解,那就很好理解了。1、定时回调 The timer callbacks每个飞控平台都提供一个 1kHz 的定时器(见 AP_HAL),通过“注册”一个定时器函数 来获取 1kHz 定时功能。所有注册的定时器将被顺序调用。调用形式如下:hal.scheduler-&register_timer_process(AP_HAL_MEMBERPROC(&AP_Baro_MS5611:: _update));定时器优先级为 181,高于主进程的 180。上面代码是以 MS5611 气压计驱动为例,其 中 AP_HAL_MEMBERPROC() 宏,主要作用是将一个 C++成员函数包装起来,作为一个 回调参数。其定义在 AP_HAL_Namespace.h 文件中,如下:// macro to hide the details of AP_HAL::MemberProc #define AP_HAL_MEMBERPROC(func) fastdelegate::MakeDelegate(this, func)使用 hal.scheduler-&millis() and hal.scheduler-&micros() 可以记录时间。 好了,你可以试着自己边一个简单的 sketch,在 setup()和 loop()函数中练习一下 1 秒钟 向 USB 终端输出一个时间或字符。2、HAL 专属线程以 PX4 为例,HAL 专属线程有: 1、 UART 线程,用于读、写串行接口数据(包括 USB); 2、 定时器线程,支持 1kHz 定时功能; 3、 IO 线程,支持写 microSD、EEPROM、FRAM 等。 对于 Pixhawk, 请准备一条调试电缆, 连接到 nsh console (serial 5 端口) , 波特率 57600。 如果已经连接,试下”ps”命令,你会得到如下信息: PID PRI SCHD TYPE NP STATE NAME 0 0 FIFO TASK READY Idle Task() 1 192 FIFO KTHREAD WAITSIG hpwork() 2 50 FIFO KTHREAD WAITSIG lpwork() 3 100 FIFO TASK RUNNING init() 37 180 FIFO TASK WAITSEM AHRS_Test() 38 181 FIFO PTHREAD WAITSEM ( 60 FIFO PTHREAD READY ( 59 FIFO PTHREAD WAITSEM ( 240 FIFO TASK WAITSEM px4io() 定时器线程 AHRS 线程UART 线程 IO 线程 13 100 FIFO TASK WAITSEM fmuservo() 30 240 FIFO TASK WAITSEM uavcan() 上面的线程为定时器线程(优先级 181),UART 线程(60), IO 线程(59),以及其他 线程诸如:px4io, fmuservo, uavcan, lpwork, hpwork and idle tasks 线程的主要目的是在不干扰主进程的情况下,在后台处理一些低优先级任务。例如 AP_Terrain library,需要向 microSD 卡写地形文件,它的实现方式如下:hal.scheduler-&register_io_process(AP_HAL_MEMBERPROC(&AP_Terrain::io_timer));注意:IO 线程优先级 59,相比定时器 181 优先级慢了很多。3、Driver 专属线程没什么好说的,请参考英文原版,需要提的一点是,我们可以利用 register_io_process() 和 register_timer_process()来处理驱动的访问。4、APM 驱动与板级(原生)驱动我们可以看到 MPU6000 驱动有两个版本:一个是 APM 版本,在 libraries/AP_InertalSensor/AP_InertialSensor_MPU6000.cpp,另一个为原生代码版本,在 PX4Firmware/src/drivers/mpu6000。 注意,对于 Pixhawk,APM 代码使用的是 Pixhawk 原生驱动,因为原生驱动已经做得 很好了。libraries/AP_InertialSensor/AP_InertialSensor_PX4.cpp 中可以查看详情。 在非 PX4 平台上,我们使用 AP_InertialSensor_MPU6000.cpp 驱动,在 PX4 平台上, 我们就用 PX4 原生驱动 AP_InertialSensor_PX4.cpp 5、板级专属线程、任务在上面第 2 节“HAL 专属线程”讲到”ps”命令显示的线程。很多都不是 AP_HAL_PX4 Schedule 启动的线程,这些线程列举如下:? idle task C called when there is nothing else to run ? init C used to start up the system ? px4io C handle the communication with the PX4IO co-processor ? hpwork C PX4 稍低优先级驱动线程。handle thread based PX4 drivers (mainly I2Cdrivers)? lpwork CPX4 非常低优先级驱动线程。handle thread based low priority work (eg. IO) ? fmuservo C AUX 输出。handle talking to the auxillary PWM outputs on the FMU ? uavcan C handle the uavcan CANBUS protocol这些任务的启动,由 rc.APM 脚本文件(ardupilot\mk\PX4\ROMFS\init.d\rc.APM)指定。 PX4 启动时,会读取该文件。rc.APM 属于 nsh 类型脚本。作为练习,你可以修改 rc.APM 脚本文件,增加一些 sleep 和 echo 命令,那么当 PX4 启动时,通过 debug console(也就是 serial 5)可以显示出来。 更多内容,可以参考英文原版。原生线程的启动代码如下:hrt_call_every(&_call, 1000, _call_interval, (hrt_callout)&MPU6000::measure_trampoline, this);等同于 AP_HAL 中的 hal.scheduler-&register_timer_process()。上述代码的意思是,HRT (high resolution timer)高精度定时器,以 1000 微妙的周期调用 MPU6000::measure_trampoline 函数。这些操作是禁止中断的,最多占用数十微妙的时间。 上面的优先级非常高。下面的方法,是稍低优先级。work_queue(HPWORK, &_work, (worker_t)&HMC5883::cycle_trampoline, this, 1);用于处理 I2C 设备。大概花几百微妙的操作时间。是可以被中断的任务。如果是最低 优先级,那么参数改为 LPWORK,这样的任务一般需要花费更长的时间。6、AP_Scheduler 任务调度系统用于飞行器主线程,提供了简单的机制控制每个操作花费了多少时间。例如:1、等待 一个新 IMU 采样;2、在每一个 IMU 采样周期之间调用一系列其他任务。 每一个飞行器都有一个 AP_Scheduler::Task table 任务列表,参考代码 (ardupilot\libraries\AP_Scheduler\ Scheduler_test.pde )类似如下: static const AP_Scheduler::Task scheduler_tasks[] PROGMEM = { { ins_update, 1, 1000 }, { one_hz_print, 50, 1000 }, { five_second_call, 250, 1800 }, }; 结构体第 1 列,循环调用的任务函数。第 2 列,调用频率(也叫 tick,一个 tick,就是 一个最小时间单元, pixhawk 为 2.5ms) 。 第 3 列为最大可能占用的操作时间, scheduler.run() 会传递当前可用的时间(微秒),如果时间不够,那么这个任务就 pass 掉了,不执行。 注意,AP_Scheduler::Task table 列表必须具备以下条件: 1、 他们不能被阻塞。 2、 在飞行时,他们不能调用 sleep function 3、 他们必须有可预估的最坏的运行时间。 你可以修改 Scheduler_test.pde, 加入自己的代码来读取气压计、 罗盘、 GPS、 更新 AHRS 输出 roll/pitch。7、信号灯有 3 种方法可以避免多线程访问冲突:1、信号灯;2、lockless data;3、PX4 ORB。 例如:I2C 驱动可以通过信号灯,确保同一时间,只有一个 I2C 设备被使用。可以查看 ardupilot\libraries\AP_Compass\AP_Compass_HMC5843.cpp 了解: 获得信号灯:_i2c_sem-&take(1); 释放信号灯:_i2c_sem-&give();8、Lockless Data StructuresLockless Data Structures 比信号灯要方便,例子见:? the _shared_data structure inlibraries/AP_InertialSensor/AP_InertialSensor_MPU9250.cpp? the ring buffers used in numerous places. A good example islibraries/DataFlash/DataFlash_File.cpp Go and have a look at these two examples, and prove to yourself that they are safe for concurrent access. For DataFlash_File look at the use of the _writebuf_head and _writebuf_tail variables.9、PX4 ORBORB(Object Request Broker)是 PX4 的互斥机制。 另外两种 PX4 驱动通信机制,列举如下:? ioctl calls (see the examples in AP_HAL_PX4/RCOutput.cpp) ? /dev/xxx read/write calls (see _timer_tick in AP_HAL_PX4/RCOutput.cpp)Pixhawk 源码笔记三:串行接口 UART 和 Console第四部分 串行接口 UART 和 Console详细参考:http://dev.ardupilot.com/wiki/learning-ardupilot-uarts-and-the-console/ UART 很重要,用于调试输出,数传、GPS 模块等。1、5 个 UART目前共定义了 5 个 UART,他们的用途分别是:?uartA C 串行终端,通常是 Micro USB 接口,运行 MAVLink 协议。 ? ? ? ?uartB C GPS1 模块。 uartC C 主数传接口,也就是 Pixhawk telem1 接口。 uartD C 次数传接口,也就是 telem2 接口。 uartE C GPS2 模块。有些 UART 具备双重角色,比如通过修改 SERIAL2_PROTOCOL 参数, 可以将 uartD 的 Mavlink telemetry 数传更改为 Frsky telemetry 数传(中国江苏产数传)。 测试 libraries/AP_HAL/examples/UART_test 目录下的 example sketch, 分别对 5 个 UART 都输出 hello 消息。使用 USB 转串口工具,可以测试。2、调试终端 Debug console作为 5 个 UART 的补充,有些平台额外的还有一个 debug console 调试终端。你可以通 过检查 HAL_OS_POSIX_IO 宏定义来判断,诸如: #if HAL_OS_POSIX_IO ::printf(&hello console\n&); #endif 如果定义了 HAL_OS_POSIX_IO,可以试着查看 AP_HAL/AP_HAL_Boards.h 代码。3、UART 函数每个 UART 都一系列基本操作函数,主要有: 1. 2. 3. 4. printf C formatted print printf_P C formatted print with progmem string (saves memory on AVR boards) println C print and line feed write C write a bunch of bytes 5. 6. 7. 8.read C read some bytes available C check if any bytes are waiting txspace C check how much outgoing buffer space is available get_flow_control C check if the UART has flow control capabilities可以到 AP_HAL 中查看他们的定义,并使用 UART_TEST 进行测试。4、UART 接口说明众多 UART 接口,众多名称,他们的对应关系,我总结如下,如有问题,欢迎发邮件 至
新浪@WalkAnt,欢迎指正。代码定义 PCB 电路表述 飞控板接口 Serial 标号 说明 APM 代码中的 电路板上的表 Pixhawk 外壳上 串口序号 表述 述 的标识 uartA Micro USB USB USB 接 USB,支持 MAVLink 协议 uartB UART4 GPS Serial 3 接 GPS 模块,另 CAN2 接口 uartC UART2 Telem1 Serial 1 接第 1 数传模块 uartD UART3 Telem2 Serial 2 接第 2 数传模块 uartE UART8 SERIAL4/5 Serial 4 一般接 GPS2 模块 / UART7 SERIAL4/5 Serial 5 Debug Console 用 于程序调试前面的文章:Pixhawk 源码笔记一:APM 代码基本结构: http://blog.sina.com.cn/s/blog_402c071e0102v59r.html Pixhawk 源码笔记二:APM 线程: http://blog.sina.com.cn/s/blog_402c071e0102v5br.html 经过对 UART 测试代码: libraries/AP_HAL/examples/UART_test)进行详细测试: void loop(void) { // Micro USB 口输出: Hello on UART uartA at 764.461 seconds test_uart(hal.uartA, &uartA&);// GPS 接口输出:Hello on UART uartB at 91.845 seconds test_uart(hal.uartB, &uartB&);// 数传 Telem1 输出:Hello on UART uartC at 24.693 seconds test_uart(hal.uartC, &uartC&);// 数传 Telem2 输出:Hello on UART uartD at 805.557 seconds test_uart(hal.uartD, &uartD&);// SEIRAL 4 口输出:Hello on UART uartE at 911.812 seconds test_uart(hal.uartE, &uartE&); // also do a raw printf() on some platforms, which prints to the // debug console #if HAL_OS_POSIX_IO // SEIRAL 5 口输出: Hello on debug console at 976.857 seconds ::printf(&Hello on debug console at %.3f seconds\n&, hal.scheduler-&millis()*0.001f); #endif hal.scheduler-&delay(1000); } SERIAL 5 作为重要的调试口。Pixhawk 启动时会通过该接口输出大量信息,可以用来对启 动过程进行监控。Pixhawk 源码笔记四:学习 RCInputandOutput第五部分 学习 RC Input and Output参考:http://dev.ardupilot.com/wiki/learning-ardupilot-rc-input-output/ RC Input,也就是遥控输入,用于控制飞行方向、改变飞行模式、控制摄像头等外围装 置。ArduPilot 支持集中不同 RC input(取决于具体的硬件飞控板): 1. PPMSum C on PX4, Pixhawk, Linux and APM2 2. SBUS C on PX4, Pixhawk and Linux 3. Spektrum/DSM C on PX4, Pixhawk and Linux 4. PWM C on APM1 and APM2 5. RC Override (MAVLink) C all boards 其中 SBUS and Spektrum/DSM 是串行协议,SBUS 为 100kbps 反 UART 协议, Spektrum/DSM 为 115200bps UART 协议。 对于 PX4, 这些协议是通过硬件 UARTs 实现的, 而有些 Linux 系统是通过软件 UARTs 实现的。(原文:Some boards implement these using hardware UARTs (such as on PX4) and some implement them as bit-banged software UARTs (on Linux).) RC Output,是指飞控接受到 RC 输入后,再将其处理后,输出到伺服和电机(电调) 上。RC Output 默认 50Hz PWM 信号。对于 ArduCopter 多轴飞行器和直升机,输出频率为 400Hz。1、RCInput 对象(AP_HAL)RCInput 对象声明: AP_HAL::RCInput* 相关例程: libraries/AP_HAL/examples/RCInput/RCInput.pde ,试着动动遥控器手柄, 看看输出是否符合预期。2、RCOutput 对象(AP_HAL)RCOutput 对象声明: AP_HAL::RCOutput* 不同的飞控,代码实现有所不同,可能包含了片上定时器、I2C、经由协处理器(PX4IO) 输出等程序。相关例程: libraries/AP_HAL/examples/RCOutput/RCOutput.pde 这段程序从 1 通道到 14 通道,控制电机从最小转速到最大转速逐级变化。3、RC_Channel 对象hal.rcin 和 hal.rcout 对象, 为低层次调用。 最常用的是使用更高级封装的 RC_Channel 对 象来实现 RC input 和 output。 它允许用户对参数进行配置, 例如每个通道 min/max/trim 值, 同时支持辅助 AUX 通道函数,还可对 input output 进行比例缩放处理等。 相关例程: libraries/RC_Channel/examples/RC_Channel/RC_Channel.pde 例程教你如何 setup、read、copy input to output。4、RC_Channel 奇怪的 input/output 设置看代码时,有些地方程序会让你感到奇怪,有一些是由于程序代码的不完善产生的, 有一些则不是。例如,很多变量作用在 input 和 output 上: radio_out = (pwm_out * _reverse) + radio_ 上述代码中的 radio_trim,是一个 trim 叠加,用来修正遥控器的值。 又例如, 对于固定翼飞行器, roll (横滚) 输入, 成为了 steer (转向 yaw) 。 对于 ArduCopter 中的多轴飞行器,在处于 Drift 模式(漂移模式)时,我们看到,pitch 用于前飞,roll 用于 转向 (而不是传统 yaw 用于转向) 。 以后, APM 团队会将其纠正过来, 将这两个概念分开。 大家知道这么回事就 OK 了。5、RC_Channel_aux 对象 另一个非常重要的类:RC_Channel_aux class,它是 RC_Channel 的子类。它有很多特 点可供用户使用。这个会有一点比较难以理解,举个例子:用户想要使用通道 6(Channel 6)对航拍设备的横向稳定进行控制,那么他可以将 FUNCTION 设置为 21,枚举变量类型为” k_rudder”(偏航,偏转,转向的意思)。如下: AP_GROUPINFO(&FUNCTION&, 1, RC_Channel_aux, function, 21), AP_GROUPEND 如果程序中调用此代码,RC_Channel_aux::set_servo_out(RC_Channel_aux::k_rudder, 4500);, 那么所有 FUNCTION 设为 21(k_rudder)的通道(channel)都将输出满偏(4500 就是满偏 最大值)。 在相应的 update_aux_servo_function()代码中, case RC_Channel_aux::k_rudder: _aux_channels[i]-&set_angle(4500);// 设置最大角度。 注意这是一对多的设置。就我的理解,其实也就是我们常说的混控输出。比如在辅助 通道 6 中,我们可以将其他通道设置为使用 function = 21。那么其他使用了 21 的通道,将 会被通道 6 混控。(这个很复杂,我也没太明白,对这个有更好理解的,请一定告诉我,相 互学习:。当然如果日后我能有更好的理解,我会更新本博客。) 下图,RC_Channel 共 4 个通道,RC_Channel_aux 共 10 个通道。 第一组:1、2、3、4、5、6、7、8、10、11(共 10 通道) 第二组:9、12 第三组:13、14 FUNCTION 参数如下: // FUNCTION 为 1-27,function 参数。 typedef enum { k_none = 0, ///& disabled k_manual = 1, ///& manual, just pass-thru the RC in signal k_flap = 2, ///& flap k_flap_auto = 3, ///& flap automated k_aileron = 4, ///& aileron k_unused1 = 5, ///& unused function k_mount_pan = 6, ///& mount yaw (pan) k_mount_tilt = 7, ///& mount pitch (tilt) k_mount_roll = 8, ///& mount roll k_mount_open = 9, ///& mount open (deploy) / close (retract) k_cam_trigger = 10, ///& camera trigger k_egg_drop = 11, ///& egg drop k_mount2_pan = 12, ///& mount2 yaw (pan) k_mount2_tilt = 13, ///& mount2 pitch (tilt) k_mount2_roll = 14, ///& mount2 roll k_mount2_open = 15, ///& mount2 open (deploy) / close (retract) k_dspoiler1 = 16, ///& differential spoiler 1 (left wing) k_dspoiler2 = 17, ///& differential spoiler 2 (right wing) k_aileron_with_input = 18, ///& aileron, with rc input k_elevator = 19, ///& elevator k_elevator_with_input = 20, ///& elevator, with rc input k_rudder = 21, ///& secondary rudder channel k_sprayer_pump = 22, ///& crop sprayer pump channel k_sprayer_spinner = 23, ///& crop sprayer spinner channel k_flaperon1 = 24, ///& flaperon, left wing k_flaperon2 = 25, ///& flaperon, right wing k_steering = 26, ///& ground steering, used to separate from rudder k_parachute_release = 27, ///& parachute release k_epm = 28, ///& epm gripper k_nr_aux_servo_functions ///& This must be the last enum value (only add new values _before_ this one) } Aux_servo_function_t; AP_Int8 ///& see Aux_servo_function_t enumPixhawk 源码笔记五:存储与 EEPROM 管理第六部分 存储与 EEPROM 管理 用户参数、航点、集结点、地图数据以及其他有用的信息需要存储。ArduPilot 提 供 4 个基本存储接口: 1、AP_HAL::Storage 对象:hal.storage; 2、StorageManager 库,是 hal.storage 更高级别的封装; 3、DataFlash 用于日志存储; 4、Posix IO 函数,是传统文件系统读写函数。 其他用于永久存储信息的函数库, 都是基于以上 4 种实现。 例如: AP_Param library (用 于处理用户可配置参数)是建立在 StorageManager 库之上的,而 StorageManager 库则是基 于 AP_HAL::Storage 之上。AP_Terrain library(用于处理地形数据)则是建立在 Posix IO functions 之上,用于操作地形数据库。 1、AP_HAL::Storage library AP_HAL::Storage 对象适用于所有 ArduPilot 硬件平台。 最小支持 4096 字节空间的存储, 一些类似 PX4v1 的板子有 8K EEPROM ,Pixhawk 有 16K FRAM 。所有这些都封装在 AP_HAL::Storage API 中。 hal.storage API,非常简单,仅 3 个函数: 1、init(),初始化存储系统; 2、read_block(),读块数据; 3、write_block(),写块数据。 之所以这么简单,是因为 APM 团队鼓励开发者使用 StorageManager API,而不是 hal.storage。只有在代码移植或调试时,使用 hal.storage 会比较方便(原文:You should only be delving into hal.storage when doing bringup of a new board, or when debugging.) 。 存储空间的大小, 在 AP_HAL/AP_HAL_Boards.h 文件中的 HAL_STORAGE_SIZE 宏中 定义,如下: #define CONFIG_HAL_BOARD_SUBTYPE HAL_BOARD_SUBTYPE_PX4_V2 #define HAL_STORAGE_SIZE 16384 // 存储空间 16KB #endif 也就是说,我们不支持动态存储空间的定义。如果希望使用动态存储空间,可以使用 Posix IO。 2、StorageManager library 在将 ArduPilot 代码移植到一个新的硬件板上时,hal.storage API 非常简单,但是在操 作存储区时就不那么好使了。我们会采用 StorageManager。StorageManager library 提供对存 储区域“伪连续块”(一般用作不同的功能和目的)的访问。正因此我们将存储区域分配了不 同的功能: 1、参数区; 2、飞行区域限制点数据区; 3、航点数据区; 4、集结点数据区。 参见: libraries/StorageManager/StorageManager.cpp,我们可以看到存储区域的划分: const StorageManager::StorageArea StorageManager::layout_copter[STORAGE_NUM_AREAS] PROGMEM = { // ------------------------ 0-4096 分配给了 AVR 版本的 APM { StorageParam, 0, 1536}, // 0x600 param bytes { StorageMission, }, { StorageRally, 3958, 90}, // 6 rally points { StorageFence, 4048, 48}, // 6 fence points #if STORAGE_NUM_AREAS &= 8 // ------------------------
分配给了 PX4 版本 { StorageParam, }, { StorageRally, }, { StorageFence, }, { StorageMission, }, // leave 128 byte gap for // expansion and PX4 sentinal #endif #if STORAGE_NUM_AREAS &= 12 // Pixhawk // ------------------------
分配给了 Pixhawk 版本 { StorageParam, }, // 类型 偏移量 长度 { StorageRally, }, { StorageFence, }, { StorageMission, 1}, // leave 128 byte gap for expansion #endif }; 对于上面的存储分布,我们可以观察到 AVR 版本用到存储地址是 0-4095,而 PX4 用 到地址是 ,Pixhawk 用到的地址是 。这样的结构,是为了更好的与之 前的版本兼容。这样一来,用户在更新最新的固件时,所有之前配置的参数将不会改变,将 继续起作用。 StorageManager API 也提供对类似整型数的读写访问, AP_Mission 中就会利用这个 API 来存储和恢复航点数据。 相关例程(libraries/StorageManager/examples/StorageTest.pde)对 StoageManager layer 和 AP_HAL::Storage object 进行了测试。它对随机的偏移量、随机的长度进行了随机的 IO 操作。 这也就意味可能会出现跨边界访问。 这个例程非常有用, 它用于对 StorageManager API 进行严苛测试,同样对于移植 ArduPilot 到新硬件平台也是极为有用的,因为它对 EEPROM 的访问函数进行了很严格的测试。 注意 StorageTest 是一个毁坏性的测试,它将会删除你之前存储的参数和航点。一定要 记得测试之前,备份你的配置。 存储对象的声明,一般如下: StorageAccess AP_Param::_storage(StorageManager::StorageParam); 又或者 StorageAccess AP_Rally::_storage(StorageManager::StorageRally); StorageAccess AP_Mission::_storage(StorageManager::StorageMission); StorageAccess AP_Limit_Geofence::_storage(StorageManager::StorageFence); 3、DataFlash library 另一类存储,就是飞行日志存储,这个基于 DataFlash library。这个库的名字看上去有 些怪怪的,实际上这个库最开始是为 APM1 的 DataFlash 芯片设计的,它原本是一个硬件驱 动库,后来慢慢演变为一个通用日志系统,这个可以在代码中找到蛛丝马迹(这些都是以前 的痕迹,不是最好的代码实现方式) 。 现在 DataFlash API 主要用于实现日志存储。它允许你自定义日志消息的数据结构。例 如 GPS 消息,用于记录 GPS 传感器的日志数据。它能够非常有效存储这些数据,它同时也 对其他库提供相应的 APIs,用来进行日志回传、下载。 LOG 数据结构是自定义的,其结构可以查看日志文件的 FMT 消息。FMT 消息地应以 的其他数据的存储格式。 相关例程 libraries/DataFlash/examples/DataFlash_test/DataFlash_test.pde。这里描述了数 据的存储结构和数据格式。简单列举如下: 第一点,在.log 文件中,我们可以看到如下格式的表达: FMT, 128, 89, FMT, BBnNZ, Type,Length,Name,Format,Columns FMT, 129, 23, PARM, Nf, Name,Value FMT, 130, 45, BIHBcLLeeEefI,Status,TimeMS,Week,NSats,HDop,Lat,Lng,RelAlt,Alt,Spd,GCrs,VZ,T FMT, 131, 31, IMU, Iffffff, TimeMS,GyrX,GyrY,GyrZ,AccX,AccY,AccZ FMT, 132, 67, MSG, Z, MessageGPS,第二点,上述格式,对应的代码(参见 DataFlash.h) : #define LOG_BASE_STRUCTURES \ { LOG_FORMAT_MSG, sizeof(log_Format), \ &FMT&, &BBnNZ&, &Type,Length,Name,Format,Columns& }, \ { LOG_PARAMETER_MSG, sizeof(log_Parameter), \ &PARM&, &Nf&, &Name,Value& }, \ { LOG_GPS_MSG, sizeof(log_GPS), \ &GPS&, &BIHBcLLeeEefI&, &Status,TimeMS,Week,NSats,HDop,Lat,Lng,RelAlt,Alt,Spd,GCrs,VZ,T& }, \ { LOG_IMU_MSG, sizeof(log_IMU), \ &IMU&, &Iffffff&, &TimeMS,GyrX,GyrY,GyrZ,AccX,AccY,AccZ& }, \ 上述结构,以 LOG_IMU_MSG 为例讲解: 信息类型 ID 数据大小 信 息 名 称 数 据 类 型 数据 1 数据 2 数据 3 数据 4 数据 5 数据 6 数据 7LOG_IMU_M sizeof(log_I IM Iffff Time SG MU) U ff MS 131 31(字节)GyrXGyrYGyrZAccXAccY AccZIM l: 整 整型 0.1 -0.9 0.7 U 型 ;
59 95 36 02 f: 浮 点第三点,日志文件(.log)的一条数据如下: IMU, 4703, -0.000190, -0.000359, -0..034236, -9.748702 第四点,消息类型的定义: // message types for common messages // 消息类型, , ,对应 FMT 中的消息类型, , ,见日志文件 .log 文件。 #define LOG_FORMAT_MSG 128 #define LOG_PARAMETER_MSG 129 #define LOG_GPS_MSG 130 #define LOG_IMU_MSG 131 #define LOG_MESSAGE_MSG 132 #define LOG_RCIN_MSG 133 #define LOG_RCOUT_MSG 134 #define LOG_IMU2_MSG 135 … 第五点, log_IMU 的结构,共占用 3 + 4 + 12 + 12 = 31 字节。 struct PACKED log_IMU { LOG_PACKET_HEADER; // 3 uint32_ // 4 float gyro_x, gyro_y, gyro_z; // 4*3 = 12 float accel_x, accel_y, accel_z; // 4*3 = 12 }; 第六点:如果要增加自定义的数据结构,那么可以像以下代码一样增加。 #define LOG_TEST_MSG 1 struct PACKED log_Test { LOG_PACKET_HEADER; uint16_t v1, v2, v3, v4; int32_t l1, l2; }; static const struct LogStructure log_structure[] PROGMEM = { LOG_COMMON_STRUCTURES, { LOG_TEST_MSG, sizeof(log_Test), // 增加自定义格式数据 &TEST&, &HHHHii&, &V1,V2,V3,V4,L1,L2& } // 增加自定义格式数据 }; 第七点:具体的数据结构操作 DataFlash.Init(log_structure, sizeof(log_structure)/sizeof(log_structure[0])); log_num = DataFlash.StartNewLog(); DataFlash.WriteBlock(&pkt, sizeof(pkt)); DataFlash API 隐藏了底层如何存储 log 文件的细节。另外,对于 Pixhawk or Linux 这样 的支持 Posix IO 的系统,日志文件是存储在 microSD 卡的“LOGS”目录中的。用户可以直接 抽出 SD 卡,直接拷贝到电脑中。 4、Posix IO 有些板子是带操作系统的,支持类似 Posix API,如 Linux 和 NuttX。AP_Terrain library 就是一个典型的例子。地形数据对于 EEPROM 是非常的大,经常要随机的存储。DataFlash API 就不够灵活了,同时又了 Posix IO 支持,也就没必要再用 DataFlash 了。 查看 AP_HAL_Boards.h 文件,确认 HAL_OS_POSIX_IO 宏已定义,如下: #define HAL_OS_POSIX_IO 1 IO// 带文件系统,has posix-like filesystem下面给出了 LOG 和 TERRAIN 文件存放路径: #define HAL_BOARD_LOG_DIRECTORY &/fs/microsd/APM/LOGS& // LOG 日志文件地址 #define HAL_BOARD_TERRAIN_DIRECTORY &/fs/microsd/APM/TERRAIN& // 地表、 地形文件地址 有上述信息,就表示支持 Posix IO 功能,另外需要说明的是: 1、Posix IO 函数,智能通过 IO timer 定时器,或者其他低优先级线程调用。IO 线程优 先级 59。 2、不要通过其他 API 直接调用,哪怕是简单 stat()函数,都不可以,除非你长得太帅。 3、尽量少存储,存储数据长度小,尽量少用 seek(搜寻)功能。 很简单,一个原则,不要太耗时,影响飞控代码执行。一个简单的针对 SD 卡的 IO 操 作有可能花上一秒钟, 这段时间足够让你的飞行器翻转, 垂直掉落, 直接炸鸡了。 Pixhawk SD 卡读写操作一般几毫秒,偶尔花费的时间会很长。现在在你知道这么做了? 相关例程 libraries/AP_Terrain/TerrainIO.cpp ,我们会发现处理 IO 的状态机都是通过 AP_Terrain::io_timer 调用的。Pixhawk 源码笔记六:源码预览与 APM:Copter 程序库第七部分 源代码预览与 APM:Copter 程序库APM::Copter 代码主要放在 ArduCopter 文件夹中,并且和 ArduPlane 和 ArduRover 使用同样的库文件。下面这张图展示了从飞行模式到电机输出的调用关系:
APM:Copter 程序库这些库文件也同样被 ArduPlane 和 ArduRover 所使用。 下面将列出一系列高层次的库的 说明和它们的函数说明。1 核心库? AP_AHRS:采用 DCM(方向余弦矩阵方法)或 EKF(扩展卡尔曼滤波方法)预估飞行器姿态。? AP_Common:所有执行文件(sketch 格式,arduino IDE 的文件)和其他库都需要的基础核心库。? AP_Math:包含了许多数学函数,特别对于矢量运算 ? AC_PID:PID 控制器库 ? AP_InertialNav:扩展带有 gps 和气压计数据的惯性导航库 ? AC_AttitudeControl:姿态控制相关库 ? AP_WPNav:航点相关的导航库 ? AP_Motors:多旋翼和传统直升机混合的电机库 ? RC_Channel:更多的关于从 APM_RC 的 PWM 输入/输出数据转换到内部通用单位的库,比如角度? AP_HAL,AP_HAL_AVR,AP_HAL_PX4:硬件抽象层库,提供给其他高级控制代码一致的接口,而不必担心底层不同的硬件。2 传感器相关库? AP_InertialSensor:读取陀螺仪和加速度计数据,并向主程序执行标准程序和提供标准单位数据(deg/s,m/s)。? AP_RangerFinder:声呐和红外测距传感器的交互库 ? AP_Baro:气压计相关库 ? AP_GPS:GPS 相关库 ? AP_Compass:三轴罗盘相关库 ? AP_OpticalFlow:光流传感器相关库3 其他库? AP_Mount,AP_Camera, AP_Relay:相机安装控制库,相机快门控制库 ? AP_Mission: 从 eeprom(电可擦只读存储器)存储/读取飞行指令相关库 ? AP_Buffer:惯性导航时所用到的一个简单的堆栈(FIFO,先进先出)缓冲区关于库的导航图,如下: Pixhawk 源码笔记七:姿态控制预览第八部分 姿态控制预览手动飞行模式,诸如自稳模式(Stabilize Mode)、特技模式(Acro Mode)、飘逸模式 (Drift Mode),其程序结构如下图:在主循环执行过程中 (比如 Pixhawk 的任务调度周期 2.5ms, 400Hz; APM2.x 为 10ms, 100Hz),每一个周期,程序会按下述步骤执行:? 首先,高层次文件 flight_mode.pde 中的 update_flight_mode()函数被调用。通过检查control_mode 变量,前飞行器的飞行模式(使用变量),然后执行相应飞行模式下 的_run()函数(如自稳模式的 stabilize_run,返航模式(RTL)的 rtl_run 等)。执 行_run 的结果是,系统将会找到与飞行模式相对应的命名为 control_.pde 飞行 控制文件(比如:control_stabilize.pde,control_rtl.pde 等)。 ? _run 函数负责将用户的输入(从 g.rc_1.control_in,g.rc_2.control_in 等读入)转换为此时飞行模式下的倾斜角(lean angle)、滚转速率(rotation rate)、爬升率(climb rate)等(也就是设置目标值 roll\pitch\yaw\throttle)。举个例子:AltHold(定高, altitude hold)模式中将用户的滚转和俯仰输入转换为倾斜角(单位:角度/° ),将 偏航输入转换为滚转速率 (单位: ° /s) , 将油门输入转换为爬升率 (单位: cm/s) 。? _run 函数最后还必须要完成的就是将预期角度、 速率等参数传送给姿态控制和/或方位控制库(它们都放在 AC_AttitiudeControl 文件夹内)。? AC_AttitiudeControl 库提供了 5 种可能的方法来调整飞行器的姿态, 下面来说明最通用的三种方法:1) angle_ef_roll_pitch_rate_ef_yaw():该函数需要一个地轴系坐标下滚转和偏航角度,一个 地轴系坐标下的偏航速率。 例如: 传递给该函数三个参数分别为, roll= -1000, pitch = -1500,yaw = 500 代表飞行器此时向左倾斜 10° ,低头 15° ,向右偏航速率为 5° /s。2) angle_ef_roll_pitch_yaw():该函数接受地轴系下的滚转、 俯仰和偏航角。 和上面的函数类似, 不过参数 yaw= 500 代表飞行器北偏东 5°3) rate_bf_roll_pitch_yaw():该函数接受一个体轴系下的滚转、俯仰和偏航角速率(° /s)。例 如:传递给该函数三个参数:roll= -1000, pitch = -1500, yaw = 500 代表飞行器此时左倾速率 10° /s,低头速率 15° /s,绕 Z 轴速率为 5° /s。? 当上述这些函数调用之后,就会接着调用AC_AttitudeControl::rate_controller_run()函数,将上面所列举的函数的输出转化为滚转、偏航和俯仰输入,并使用 set_roll,set_pitch,set_yaw 和 set_throttle 方法将这些输入发送给 AP_Motors 库。另外, ? AC_PosControl 库用来控制飞行器的 3D 方位。不过通常只用来调整比较简单的 Z轴方向(如姿态控制),这是因为许多需要复杂 3D 方位调整的飞行模式(例如悬 停 Loiter)使用的是“AC_WPNav 库”。总之,AC_PosControl 库中常用的方法有:1) set_alt_target_from_climb_rate():将爬升率(cm/s)作为参数,用来更新一 个需要调整的相对高度目标。2) set_pos_target():接受一个以系统中的 home 位置作为参考点的 3D 位置矢量(单位: cm)。? 如果调用了 AC_PosControl 中的任何一个方法,那么在该飞行模式下就必须调用函数 AC_PosControl::update_z_controller()。这样的话,就可以启用 Z 轴 的方位控制 PID 循环,并向 AP_Motors 库发送低级别的油门信息。同样,如果调 用了 xy 轴的函数,那就就必须调用AC_PosControl::update_xy_controller()函数。? AP_Motors 库含有“电机混合模式”代码。这些代码负责将从 AC_AttitudeControl和 AC_PosControl 库发送过来的滚转、俯仰、偏航角度和油门值信息转换为电机 的相对输出值 (例如: PWM 值) 。 因此, 这样高级别的库就必须要使用如下函数:1) set_roll(),set_pitch(),set_yaw(): 接受在[-]角度范围内的滚转、 俯仰和偏航角。 这些参数不是期望角度或者速率,更准确的讲,它仅仅是一个数值。例如,set_roll(-4500)将代表飞行器尽 可能快的向左滚转。2) set_throttle():接受一个范围在[0,1000]的相对油门值。 0 代表电机关闭, 1000 代表满油 门状态。? 虽然对于不同飞行器构型(如四旋翼,Y6,传统直升机等)的控制代码中有许多不同的类,但这些类中都有一个相同的函数 output_armed,负责将这些滚转、俯 仰、偏航和油门值转换为 PWM 类型输入值。这转换的过程中,会应用到 stability patch,用来控制由于飞行器构型限制所引起的轴系的优先级问题(例如四旋翼的 四个电机不可能在做最大速度滚转时四个电机的油门同时达到最大,因为必须一 部分电机输出小于另一部分才能引起滚转) 。 在执行函数 output_armed 的最后, 还将调用 hal.rcout-&write(),把期望 PWM 值传递给 AP_HAL 层。? AP_HAL 库(硬件抽象层)提供了针对所有飞控板统一的接口。实际控制中,hal.rc_out-&write()函数将接受到的来自于 AP_Motors 类中指定的 PWM 值,输出至飞控板对应的 PWM 端口(pin 端)。Pixhawk 源码笔记八:添加新的参数如 何 向 代 码 中 添 加 新 的 参 数 , 对 应 了 MissionPlanner 中 的 可 配 置 参 数 。第九部分 添加新的参数 1 在主执行代码中添加参数 第一步: Step #1: 在文件 Parameters.h 参数类中的枚举变量(enum)的合适位置,像下面代码块最后一 行一样添加你自己的新的参数。你需要注意下面这些事情: 尽量在执行类似功能的参数区域添加新的参数,或者最坏的情形下就是在 “Misc(混合)” 区域的末尾添加。 确保你添加的参数区域中还可以有编号添加新的参数。检查是否能继续添加参数的方法是: 检查参数的计数, 确保你所要添加的参数的上一个元素编号要小于你的下一部分代码的编号。 比如,Misc 部分的第一个参数起始于#20,。my_new_parameter 是#36。如果下一部分参数开 始于#36,那么我们就不能在这里添加这个新参数。 不要在一个代码块的中间添加新的参数,那样容易造成现存参数对应的信息的改变。 不要在参数旁边用“弃用(deprecated)”或“移除(remove)”做注解,这是因为一些使用者 将此注释用作在 eeprom 上的旧的参数的默认注解,如果你添加的新参数也是这样注解,那 么就让人就会看起来很奇怪和疑惑。 enum { // Misc // k_param_log_bitmask = 20, k_param_log_last_filenumber, // *** Deprecated - remove // with next eeprom number // change k_param_toy_yaw_rate, // THOR The memory // location for the // Yaw Rate 1 = fast, // 2 = med, 3 = slow k_param_crosstrack_min_distance, // deprecated - remove with next eeprom number change k_param_rssi_pin, k_param_throttle_accel_enabled, // deprecated - remove k_param_wp_yaw_behavior, k_param_acro_trainer, k_param_pilot_velocity_z_max, k_param_circle_rate, k_param_sonar_gain, k_param_ch8_option, k_param_arming_check_enabled, k_param_sprayer, k_param_angle_max, k_param_gps_hdop_good, // 35 k_param_my_new_parameter, // 36第二步: Step #2: 在枚举变量后面的参数 类中声明上面枚举变种提到的参数。可使用的类型包括 AP_Int8,AP_Int16,AP_Float,AP_Int32,AP_Vector3(目前还不支持 unsigned integer 无符号整型)。 新的枚举变量的名称应该保持一致,只是去掉了前缀 k_param_。 // 254,255: reserved }; AP_Int16 AP_Int8 format_ software_// Telemetry control // AP_Int16 AP_Int16 AP_Int8 AP_Int8 AP_Int16 AP_Int8 AP_Int8sysid_this_ sysid_my_ serial3_ telem_ rtl_ sonar_ sonar_ // 0 = XL, 1 = LV, // 2 = XLL (XL with 10m range) // 3 = HRLV sonar_ battery_ // 0=disabled, 3=voltage only, // 4=voltage and current volt_div_ curr_amp_per_ pack_ // Battery pack capacity less reserve failsafe_battery_ // battery failsafe enabled failsafe_gps_ // gps failsafe enabled failsafe_ // ground station failsafe behavior gps_hdop_ // GPS Hdop value below which represent a good my_new_ // my new parameter's description goes hereAP_Float AP_Int8 AP_Float AP_Float AP_Int16 AP_Int8 AP_Int8 AP_Int8 AP_Int16 position AP_Int16第三步: Step #3: 在 Parameters.pde 文件中向 var_info 表中添加变量的声明信息。 // @Param: MY_NEW_PARAMETER // @DisplayName: My New Parameter // @Description: A description of my new parameter goes here // @Range: - // @User: Advanced GSCALAR(my_new_parameter, &MY_NEW_PARAMETER&, MY_NEW_PARAMETER_DEFAULT), 地面站(如 Mission Planner)中将使用@Param ~ @User 的注释信息向使用者说明用户 所设置的变量的范围等。 第四步: Step #4: 在 config.h 中添加你的新参数。 #ifndef MY_NEW_PARAMETER_DEFAULT # define MY_NEW_PARAMETER_DEFAULT100// default value for my new parameter #endif 向主执行代码添加参数的工作就完成了! 添加到主代码中(并非库中)的参数就可以通过 诸如 g.my_new_parameter 这样来使用。 2 向库中添加参数 同样可以使用下列步骤向库中添加新的参数。以 AP_Compass 库为例: 第一步: Step #1: 首 先 在 库 代 码 的 .h 头 文 件 添 加 新 的 变 量 ( 如 Compass.h) 。 可 使 用 的 类 型 包 括 AP_Int8,AP_Int16,AP_Float,AP_Int32,AP_Vector3f。然后添加你的参数的默认值(我们将在 Step #2 中使用) 。 #define MY_NEW_PARAM_DEFAULT 100 class Compass { public: int16_t product_ int16_t mag_x; int16_t mag_y; int16_t mag_z; uint32_t last_ /// Constructor /// Compass(); protected: AP_Int8 _ AP_Vector3f _ AP_Float _ AP_Int8 _use_for_ AP_Int8 _auto_ AP_Int16 _my_new_lib_ };/// product id ///& magnetic field strength along the X axis ///& magnetic field strength along the Y axis ///& magnetic field strength along the Z axis ///& micros() time of last update ///& true if last read OK/// /// /// description of my new parameter第二步: Step #2: 然后在.cpp 文件 (如 Compass.cpp) 中添加变量包含有@Param ~ @Increment 的 var_info 表信息,以便允许 GCS 向用户显示来自地面站的关于该参数值的范围设定。当添加新参数 时应注意: 自己添加的代码编号(下面的编号 9)一定要比之前变量的大。 参数的名称(如 MY_NEW_P)包括对象自动添加的前缀要少于 16 个字符。比如罗盘对象 的前缀为“COMPASS_”。 const AP_Param::GroupInfo Compass::var_info[] PROGMEM = { // index 0 was used for the old orientation matrix // @Param: OFS_X // @DisplayName: Compass offsets on the X axis // @Description: Offset to be added to the compass x-axis values to compensate for metal in the frame // @Range: -400 400 // @Increment: 1// @Param: ORIENT // @DisplayName: Compass orientation // @Description: The orientation of the compass relative to the autopilot board. // @Values: 0:None,1:Yaw45,2:Yaw90,3:Yaw135,4:Yaw180,5:Yaw225,6:Yaw270,7:Yaw315,8:Roll180 AP_GROUPINFO(&ORIENT&, 8, Compass, _orientation, ROTATION_NONE), // @Param: MY_NEW_P // @DisplayName: My New Library Parameter // @Description: The new library parameter description goes here // @Range: - // @User: Advanced AP_GROUPINFO(&MY_NEW_P&, 9, Compass, MY_NEW_PARAM_DEFAULT), AP_GROUPEND }; 这样, 新添加的参数将以_my_new_lib_parameter 包含在库中。 需要指明的是: protected 保护类型的参数是不能够在类外被访问的。 如果我们将其变为 public 类型, 那么我们就可以 在主代码中使用 compass._my_new_lib_parameter 参数了。 第三步: Step #3: 前面提到的是在已经存在的类(比如 AP_Compass)中定义一个新的变量。如果你重新 定义了一个新类,在这个新类中添加参数。添加参数的方法如第二步。不过你还有一个工作 要做,就是将这个新类,添加到 Parameters.pde 文件的 var_info 数组列表中去。下面加粗的 代码就是一个示例。 const AP_Param::Info var_info[] PROGMEM = { // @Param: SYSID_SW_MREV // @DisplayName: Eeprom format version number_my_new_lib_parameter, // @Description: This value is incremented when changes are made to the eeprom format // @User: Advanced GSCALAR(format_version, &SYSID_SW_MREV&, 0), // @Group: COMPASS_ // @Path: ../libraries/AP_Compass/Compass.cpp GOBJECT(compass, &COMPASS_&, Compass), // @Group: INS_ // @Path: ../libraries/AP_InertialSensor/AP_InertialSensor.cpp GOBJECT(ins, &INS_&, AP_InertialSensor), AP_VAREND };Pixhawk 源码笔记九:添加新的飞行模式这一节讲述如何向 APM 代码中添加新的飞行模式。 通过这里我们可以对飞行模式相关 的几个文件有一个非常清晰的认识。添加新的飞行模式这部分将涵盖一些怎样创建一个新的高级别的飞行模式的基本操作步骤 (类似于自 稳,悬停等),这些新模式处于“the onion”(洋葱头工程)中的高级别代码控制部分,如之 前姿态控制页面描述的一样。 不过遗憾的是本页面并没有提供给你关于所创建的理想飞行模 式需要的所有信息,但是希望这将是一个好的开始。Step #1:在文件 defines.h 中用#define 定义你自己新的飞行模式,然后将飞行模式数量 NUM_MODES 加 1。// Auto Pilot modes// ---------------- #define STABILIZE 0 // hold level position#define ACRO 1 // rate control#define ALT_HOLD 2 // AUTO control#define AUTO 3 // AUTO control#define GUIDED 4 // AUTO control#define LOITER 5 // Hold a single location#define RTL 6 // AUTO control#define CIRCLE 7 // AUTO control#define LAND 9 // AUTO control#define OF_LOITER 10 // Hold a single location using optical flow sensor#define DRIFT 11 // DRIFT mode (Note: 12 is no longer used)#define SPORT 13 // earth frame rate control#define FLIP 14 // flip the vehicle on the roll axis#define AUTOTUNE 15 // autotune the vehicle's roll and pitch gains#define POSHOLD 16 // position hold with manual override#define NEWFLIGHTMODE 17// new flight mode description#define NUM_MODES 18 Step #2:类似于相似的飞行模式的 control_stabilize.pde 或者 control_loiter.pde 文件, 创建新的飞行模式的.pde 控制 sketch 文件。 该文件中必须包含一个_init()初始化函数和_run() 运行函数,类似于 static bool althold_init(bool ignore_checks)和 static void althold_run()/// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-// newflightmode_init - initialise flight modestatic bool newflightmode_init(bool ignore_checks){// do any required initialisation of the flight mode here// this code will be called whenever the operator switches into this mode// return true initialisation is successful, false if it fails// if false is returned here the vehicle will remain in the previous flight mode}// newflightmode_run - runs the main controller// will be called at 100hz or morestatic void newflightmode_run(){ // if not armed or throttle at zero, set throttle to zero and exit immediatelyif(!motors.armed() || g.rc_3.control_in &= 0) {attitude_control.relax_bf_rate_controller();attitude_control.set_yaw_target_to_current_heading();attitude_control.set_throttle_out(0, false);}// convert pilot input into desired vehicle angles or rotation rates//g.rc_1.control_in : pilots roll input in the range -4500 ~ 4500//g.rc_2.control_in : pilot pitch input in the range -4500 ~ 4500//g.rc_3.control_in : pilot's throttle input in the range 0 ~ 1000//g.rc_4.control_in : pilot's yaw input in the range -4500 ~ 4500// call one of attitude controller's attitude control functions like//attitude_control.angle_ef_roll_pitch_rate_yaw(roll angle, pitch angle, yaw rate);// call position controller's z-axis controller or simply pass through throttle//attitude_control.set_throttle_out(desired throttle, true); }Step #3:在文件 flight_mode.pde 文件的 set_mode()函数中增加一个新飞行模式的 case (C++中 switch..case 语法)选项,然后调用上面的_init()函数。// set_mode - change flight mode and perform any necessary initialisationstatic bool set_mode(uint8_t mode){// boolean to record if flight mode could be setbool success =bool ignore_checks = !motors.armed(); check to perform// allow switching to any mode if disarmed. We rely on the arming// return immediately if we are already in the desired modeif (mode == control_mode) {}switch(mode) {case ACRO:#if FRAME_CONFIG == HELI_FRAME success = heli_acro_init(ignore_checks);#elsesuccess = acro_init(ignore_checks);#endifcase NEWFLIGHTMODE:success = newflightmode_init(ignore_checks);}}Step #4:在文件 flight_mode.pde 文件的 update_flight_mode()函数中增加一个新飞行模 式的 case 选项,然后调用上面的_run()函数。// update_flight_mode - calls the appropriate attitude controllers based on flight mode// called at 100hz or morestatic void update_flight_mode(){switch (control_mode) {case ACRO: #if FRAME_CONFIG == HELI_FRAMEheli_acro_run();#elseacro_run();#endifcase NEWFLIGHTMODE:success = newflightmode_run();}}Step #5: 在文件 flight_mode.pde 文件的 print_flight_mode()函数中增加可以输出新飞 行模式字符串的 case 选项。static void print_flight_mode(AP_HAL::BetterStream *port, uint8_t mode){switch (mode) {case STABILIZE:port-&print_P(PSTR(&STABILIZE&)); case NEWFLIGHTMODE:port-&print_P(PSTR(&NEWFLIGHTMODE&));Step #6:在文件 Parameters.pde 中向 FLTMODE1 ~ FLTMODE6 参数中正确的增加 你的新飞行模式到@Values 列表中。// @Param: FLTMODE1// @DisplayName: Flight Mode 1// @Description: Flight mode when Channel 5 pwm is 1230, &= 1360// @Values: 0:Stabilize,1:Acro,2:AltHold,3:Auto,4:Guided,5:Loiter,6:RTL,7:Circle,8:Position,9:Land,10:OF_Loiter,11:ToyA, 12:ToyM,13:Sport,17:NewFlightMode// @User: StandardGSCALAR(flight_mode1, &FLTMODE1&,FLIGHT_MODE_1),// @Param: FLTMODE2// @DisplayName: Flight Mode 2// @Description: Flight mode when Channel 5 pwm is &1230, &= 1360// @Values: 0:Stabilize,1:Acro,2:AltHold,3:Auto,4:Guided,5:Loiter,6:RTL,7:Circle,8:Position,9:Land,10:OF_Loiter,11:ToyA, 12:ToyM,13:Sport,17:NewFlightMode // @User: StandardGSCALAR(flight_mode2, &FLTMODE2&,FLIGHT_MODE_2),Step #7: 如果你想让你的新飞行模式出现的 Mission Planner 的平视显示器 HUD 和飞行模式 组件中,你需要相应修改 Mission Planner 代码。关于 Mission Planner 如何编译的问题,请 参考我的另外一篇文章:http://blog.sina.com.cn/s/blog_402c071e0102v4kx.html。Pixhawk 源码笔记十:代码调度,使之定时运行( 11:15:24)转载 标签:pixhawk分类: 航模 源码分析 任务调度 scheduler ap_scheduler这一节将向你介绍如何规划你的新代码块,,使之可以按需运行。实际上在本节内容在 本博客《Pixhawk 源码笔记二:APM 线程》的第 6 节“AP_Scheduler 任务调度系统”中已有 讲述,这里再做进一步介绍。Pixhawk 源码笔记十:调用代码,使之定时运行1、用代码调度器(scheduler)运行你的代码在给定时间间隔内来运行你的代码的最灵活的方式就是使用调度器。这可以通过将你 的函数添加到文件 ArduCopter.pde 中的 scheduler_tasks 数组来完成。需要表明的是:实际 上该文件中有两个任务列表,上面的任务列表是针对高频 CPUs(如 Pixhawk),对应的调 度频率是 400Hz,下面的是针对低频 CPUs(如 APM2),对应的调度频率是 100Hz。 添加一个任务是相当的简单,你只要在列表添加新的一行代码就可以了(列表中位置 越靠前意味着拥有更高的级别)。任务项中的第一列代表了函数名,第二列是以 2.5ms 为单 位的数字 (或者 APM2 中以 10ms 为单位) 。 所以, 如果你想要你的函数执行频率为 400Hz, 那么该列就需要填写为“1”,如果想要 50Hz,那么就需要改为“8”。任务项的最后一列代表 该函数预计运行花费的微秒(百万分之一秒)时间。这可以帮助调度器来预估在下一个主循 环开始之前有否有足够的时间来运行你的函数。static const AP_Scheduler::Task scheduler_tasks[] PROGMEM = { { update_GPS, { update_nav_mode, { medium_loop, { update_altitude, { fifty_hz_loop, { run_nav_updates, { slow_loop, { gcs_check_input,2, 1, 2, 10, 2, 10, 10,900 }, 400 }, 700 }, 1000 }, 950 }, 800 }, 500 }, 2, 700 }, 700 }, 1500 }, 1200 }, 2, 700 }, 900 }, 1100 }, 200 }, 500 }{ gcs_send_heartbeat, 100, { gcs_data_stream_send, 2, { gcs_send_deferred, { compass_accumulate, 2,{ barometer_accumulate, 2, { super_slow_loop, { my_new_function, { perf_update, }; 100, 10,1000,2、作为循环的一部分运行你的代码 为了代替在代码调度器中加入一个新的函数入口,你还可以在现有的任何时间循环事 件中添加你的函数。除了在 fast-loop 循环中添加外,这种方法对比起上面的代码调度器方 法并没有什么实质性好处。但当你的代码添加到 fast-loop 循环中时,就意味着它将以最高 的优先级别来执行(它几乎能 100%达到所确保的 400hz 运行速度)。? fast_loop:APM2 上运行频率 100hz,Pixhawk 上 400Hz ? fifty_hz_loop:运行频率 50hz ? ten_hz_logging_loop:运行频率 10hz ? three_hz_loop:运行频率 3.3hz ? on_hz_loop:运行频率 1hz所以举个例子,如果你想让你的代码运行频率为 10hz,那么你就要将它添加到 ArduCopter.pde 文件的 ten_hz_logging_loop()函数声明中。 // ten_hz_logging_loop // should be run at 10hz static void ten_hz_logging_loop() { if (g.log_bitmask & MASK_LOG_ATTITUDE_MED) { Log_Write_Attitude(); } if (g.log_bitmask & MASK_LOG_RCIN) { DataFlash.Log_Write_RCIN(); } if (g.log_bitmask & MASK_LOG_RCOUT) { DataFlash.Log_Write_RCOUT(); } if ((g.log_bitmask & MASK_LOG_NTUN) && mode_requires_GPS(control_mode)) { Log_Write_Nav_Tuning(); }// your new function call here my_new_function(); } Pixhawk 源码笔记十一:增加新的 MAVLink 消息这一节将向你介绍如何增加新的 MAVLink 消息。 (上图以自动起飞为例,简单说明了 函数之间的调用关系)如有问题,第十二部分 增加新的 MAVLink 消息MavLink 协议:https://pixhawk.ethz.ch/mavlink/地面站之间的数据和指令通信都是通过串行接口使用 MAVLink 协议来传递的。本页面将提 供关于添加新的 MAVLink 信息的一些高级建议。 这些指令仅在 Liunx 上测试完成(准确的说,是在 Windows 上运行的 Ubuntu 虚拟机上测试 完成的)。关于设置虚拟机的方法在 SITL(软件层面仿真)页面有相关介绍。如果你要运行 SITL,你最好遵循下面的一些建议。这些指令不能直接在 Windows 或者 Mac 平台上本地运 行。Step #1:确保你已经安装了最新的 ardupilot 代码,同时也检查一下 mavproxy 是否是最新版 本。mavproxy 工具可以通过在终端窗口运行下面命令进行升级。 sudo pip install --upgrade mavproxyStep #2:先确定你所要添加的信息的类型,以及如何和已有的 MavLink 消息兼容。 比如:你可能会想要向飞行器发送一个新的导航指令,让它可以在任务中期(自动模式中) 模仿一个特技动作(比如翻筋斗)。在这个例子中,你需要一个类似于 MAV_CMD_NAV_WAYPOINT(可以在 MAVLink 消息页面搜索 MAV_CMD_NAV_WAYPOINT)一样的新的导航指令 MAV_CMD_NAV_TRICK。 又或者你想要从飞行器发送一个新的传感器数据类型到地面站,可能类似于 SCALED_PRESSURE 消息。Step #3:在 common.xml 和 ardupilotmega.xml 文件中添加你的信息的定义声明。 如果你希望将该指令添加到 MAVLink 协议中,那么你应该添加该指令 到../ardupilot/libraries/GCS_MAVLink/message_definitions/common.xml 文件中。如果你仅仅 个人使用或者仅仅和 ArduCopter,ArduPlane,ArduRover 搭配使用,那么它就应该被添加 到 ardupimega.xml 文件中。Step #4:重新生成你的所有 inlcude 文件,确保添加的信息在主代码中可以被识别。 首先将目录切换到 ardupilot 文件夹下,然后执行下面命令: ./libraries/GCS_MAVLink/generate.sh成功执行后,你应该看到下面这些文件都应经被更新。../libraries/GCS_MAVLink/include/mavlink/v1.0/ardupilotmega/ardupilotmega.h ../libraries/GCS_MAVLink/include/mavlink/v1.0/ardupilotmega/version.h ../libraries/GCS_MAVLink/include/mavlink/v1.0/common/version.h 文件 version.h 仅简单的更新了文件的日期和时间, 但是 ardupilotmega.h 文件已经应该有了 你的新消息的定义声明。Step #5:在飞行器主代码中添加函数方法用来控制向/从地面站发送/接收指令。 这些顶层代码指令绝大部分包含在飞行器的 GCS_MAVLink.pde 文件中或 在../libraries/GCS_MAVLink/GCS 类中。 在我们想要添加一个新的导航指令的例子中(比如执行特技动作),应该需要下面信息:? 扩展 AP_Mission 库中的 mission_cmd_to_mavlink()和 mavlink_to_mission_cmd()方法,将 mavproxy 的指定转换到一个 AP_Mission::Mission_Command 结构体中。// GCS 地面站。将 Mission_Command 对象 转换到mavlink 消息, 该消息能够被发送到bool AP_Mission::mission_cmd_to_mavlink(const AP_Mission::Mission_Command& cmd, mavlink_mission_item_t& packet) {… …} // 到 eeprom将 mavlink 消息 转换到Mission_Command 对象,该对象可以被存储bool AP_Mission::mavlink_to_mission_cmd(const mavlink_mission_item_t& packet, AP_Mission::Mission_Command& cmd) {… …}? 在飞行器的 commands_logic.pde 文件中分别添加 start_command()函数和verify_command()函数的一个 case 分支,用来校验新的消息指令 MAV_CMD_NAV_TRICK 是否接收到。这些需要你调用自己创建的两个新函数 do_trick()和 verify_trick()(具体参考下面)。? 创建两个新函数 do_trick()和 verify_trick(),用来控制飞行器如何执行特技动作(这可能需要调用 control_auto.pde 中的另一个函数来设置 auto_mode 变量, 然后调用 新方法 auto_trick_start())。当指令第一次被唤醒时将使用 do_trick()函数。 verify_trick()函数将会以 10hz 频率(或者更高)被重复调用直到特技动作完成,当 特技动作执行完毕之后 verify_trick()函数应该返回 True。Pixhawk 源码笔记十二:采用 译 Mission? ? ? ?Visual Studio编Planner方法与问题总结Mission Planner 编译。 如何编译 Mission Planner 呢? 我的步骤是: 1、使用 GitHub 下载 Mission Planner 源码。 ?2、 观察代码解决方案 ArdupilotMega.sln , 用记事本打开, 确定是 Visual Studio 版本。我下载的源码是 2013 版本。? ? ? ? ? ? ? ?3、下载并安装 Visual Studio 2013 4、用 VS 打开 Mission Planner 工程(ArdupilotMega.sln)。现在来一个首次编译――,你会发现有 100 多个错误。 不要害怕,其实,我们还有很多设置需要去处理。第一步:我们可能会看到 OpenTK 相关的错误 源码中,实际上已经有 OpenTK 工程了,在解决方案中,在 MissionPlanner\ExtLibs\GLControl 文件夹中,将他添加进来,就可以了。?值得注意的是,我们编译之前,要确认所有的 LIB 库都已经包含到 VS 工程中 来,列举如下:? ? ? ? ? ? ? ? ? ? ? ? ? ? ?AviFile BaseClasses BSE.Windows.Forms Core CsAssortedWidgets GMap.Net.Core GMap.Net.WindowsForms KMLib MetaDataExtractor OpenTK.GLControl SharpKml UPdater wix ZedGraph 等等,具体见下图。 ? ? ?第二步:在电脑上安装一个最新的 Mission Planner。(一个直接可以打开使用的 MissionPlanner)?我的 Mission Planner 安装在 C:\Program Files\Mission Planner。尽量确保安装的 Mission Planner 和你下的源码都是最新的,版本能够匹配得上。?官方参考:Before you attempt to build (compile) Mission Planner you must also have an installed version on your PC. That is where some of the files you will need to reference are located. This is because these files (.dll files, Etc.) are not included in the Git repository. Install MP the normal way or if installed, be sure you have the latest revision. The installed revision must match the download you just did.? ?官方:http://dev.ardupilot.com/wiki/buildin-mission-planner/ ? ? ?第三步:做一些必要的设置,来减少错误。 这些设置如下: 1、打开 VS Solution Explorer 解决方案资源管理器,右键点击 Mission Planner 工程(注意前面有 C#图标的工程,不是整个解决方案),点击【属性】、选择【引 用路径】、将已经安装的 Mission Planner 路径填进去,点击添加文件夹。我的是: C:\Program Files\Mission Planner。见下图? ?2、点击上图中的【生成事件】,移除所有“预先生成事件命令行“和”后期生成 事件命令行“的内容。? ?3、点击上图中的【生成】,将配置改为”Active(Debug)”。 4、按照这个方法,继续将其他工程,都一一进行设置。最后进行编译,错误会 少很多了。? ?第四步:可能还会存在这样的错误:未能找到类型或命名空间名称 log4net 是否缺 少 using 指令 或程序集引用。 ? ?实际上相关的工程都已经添加了 log4net 的引用,为什么还报错。我查看了 log4net 的版本是 .net framework 4.0 的。而工程用到仍然是 3.5,甚至 2.0 版本。于 是,都改成了 4.0,如下图所示。?? ?OK,再编译。所有问题都没有了。 运行界面如下: ? ??Building Mission Planner with Visual StudioEND.Pixhawk 源码分析总结完毕,欢迎吐糟?
赞助商链接

我要回帖

更多关于 飞控接口 的文章

 

随机推荐