一、代码结构

整体框架:
image.png
APM学习笔记(一) - 图2
1.Vehicle specific flight code
包括不同航行器的代码:Plane、 Copter、APMrover2、AntennaTracker、Sub

2.Libraries目录中放的是不同航行器通用的设备/模块,包括:
(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:硬件抽象层库,提供一个飞控总的AP_HAL抽象,通过不同类型的飞控板去实现继承,这样就可以很好的兼容不同的板子,方便ardupilot可以支持不同的硬件。
AP_HAL_PX4:GPIO、I2C、UART、RCinput/output、scheduler、semaphores、storage。
(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,先进先出)缓冲区
AP_AccelCal、AP_Declination、AP_RCMapper、AP_RPM、AP_RSSI
AP_ADC:AD转换
APM_Control: pitch/roll/yaw controller
DataFlash:flash memory
GCS_Console/GCS_MAVLink:地面站通信、飞行日志子模块

在libraries目录下的各个设备中,有些会提供一个example目录,里边提供了一些针对该设备的测试文件,可以编译后单独测试该设备。同时也可以通过例程学习该设备相关函数和接口的使用方法。

3.其他的部分:external support code(也就是modules)、Tools

二、编译的背景知识(了解)

编译代码用的是gcc-arm-none-eabi-6-2017-q2-update-linux
APM学习笔记(一) - 图3
APM学习笔记(一) - 图4

采用waf编译

  1. 切换到./ardupilot目录
  2. 配置硬件信息:./waf configure --board=Pixhawk1
  3. 编译源代码:./waf copter //以copter为例
  4. 烧录源代码到板子中:./waf --targets bin/arducopter --upload

使用waf命令能够列出可以构建的例子

  1. 终端输入:./waf list | grep 'examples'

三、基础入门(学习库例程)

hal引用:

  1. const AP_HAL::HAL& hal = AP_HAL::get_HAL(); //声明

每个例程如果想使用 AP_HAL 功能的文件都需要声明 HAL 引用。可以访问 AP_HAL: HAL 对象, 它提供对所有硬件特定功能的访问, 包括将消息打印到控制台、休眠和与 I2C 和 SPI 总线通信。
**
最常用的 hal 函数有:

hal.console->printf() 打印字符串
AP_HAL::millis()和AP_HAL::micros() 获得启动后的时间
hal.scheduler->delay()和hal.scheduler->delay_microseconds() 延迟一段时间
hal.gpio->pinMode(), hal.gpio->read() 和hal.gpio->write() 用于访问 GPIO 引脚
hal.i2c 用于访问 I2C
hal.spi用于访问 SPI


需要注意的是,例程中的setup()和loop() 只是复杂的板子程序的冰山一角。它会使 ArduPilot 看起来像是单线程, 但实际应用会有大量的实时线程启动。

**

四、主函数入口

AP_HAL_MAIN(); 这是一个 hal 宏, 它生成必要的代码来声明 c++ 主函数,以及 hal 的任何板级初始化代码。

在AP_HAL_Main.h 文件中,有如下代码:

  1. #define AP_HAL_MAIN() \
  2. AP_HAL::HAL::FunCallbacks callbacks(setup, loop); \
  3. extern "C" { \
  4. int AP_MAIN(int argc, char* const argv[]); \
  5. int AP_MAIN(int argc, char* const argv[]) { \
  6. hal.run(argc, argv, &callbacks); \
  7. return 0; \
  8. } \
  9. }
  10. #define AP_HAL_MAIN_CALLBACKS(CALLBACKS) extern "C" { \
  11. int AP_MAIN(int argc, char* const argv[]); \
  12. int AP_MAIN(int argc, char* const argv[]) { \
  13. hal.run(argc, argv, CALLBACKS); \
  14. return 0; \
  15. } \
  16. }

而对于hal.run函数,我们会根据不同的飞控板层最终调用不同的设备run函数来实现。
下面以Chibios系统为例,直接定位AP_HAL_Chibios目录的HAL_ChibiOS_Class源文件即可找到相应的run()函数实现,其中会执行main_loop函数:

  1. …………
  2. g_callbacks = callbacks; //函数定义传递
  3. //接管执行main------------Takeover main
  4. main_loop();

再来看main_loop函数:

  1. static void main_loop()
  2. {
  3. daemon_task = chThdGetSelfX();
  4. /*
  5. switch to high priority for main loop
  6. */
  7. chThdSetPriority(APM_MAIN_PRIORITY);
  8. #ifdef HAL_I2C_CLEAR_BUS
  9. // Clear all I2C Buses. This can be needed on some boards which
  10. // can get a stuck I2C peripheral on boot
  11. ChibiOS::I2CBus::clear_all();
  12. #endif
  13. ChibiOS::Shared_DMA::init();
  14. peripheral_power_enable();
  15. hal.uartA->begin(115200);
  16. #ifdef HAL_SPI_CHECK_CLOCK_FREQ
  17. // optional test of SPI clock frequencies
  18. ChibiOS::SPIDevice::test_clock_freq();
  19. #endif
  20. hal.uartB->begin(38400);
  21. hal.uartC->begin(57600);
  22. hal.analogin->init();
  23. hal.scheduler->init(); //初始化任务init线程
  24. /*
  25. run setup() at low priority to ensure CLI doesn't hang the
  26. system, and to allow initial sensor read loops to run
  27. */
  28. hal_chibios_set_priority(APM_STARTUP_PRIORITY);
  29. if (stm32_was_watchdog_reset()) {
  30. // load saved watchdog data
  31. stm32_watchdog_load((uint32_t *)&utilInstance.persistent_data, (sizeof(utilInstance.persistent_data)+3)/4);
  32. last_persistent_data = utilInstance.persistent_data;
  33. }
  34. schedulerInstance.hal_initialized(); //系统初始化
  35. g_callbacks->setup(); //调用应用层的setup()函数
  36. #ifdef IOMCU_FW
  37. stm32_watchdog_init();
  38. #elif !defined(HAL_BOOTLOADER_BUILD)
  39. // setup watchdog to reset if main loop stops
  40. if (AP_BoardConfig::watchdog_enabled()) {
  41. stm32_watchdog_init();
  42. }
  43. #ifndef HAL_NO_LOGGING
  44. if (hal.util->was_watchdog_reset()) {
  45. AP::internalerror().error(AP_InternalError::error_t::watchdog_reset);
  46. const AP_HAL::Util::PersistentData &pd = last_persistent_data;
  47. AP::logger().WriteCritical("WDOG", "TimeUS,Task,IErr,IErrCnt,MavMsg,MavCmd,SemLine,FL,FT,FA,FP,ICSR", "QbIIHHHHHIBI",
  48. AP_HAL::micros64(),
  49. pd.scheduler_task,
  50. pd.internal_errors,
  51. pd.internal_error_count,
  52. pd.last_mavlink_msgid,
  53. pd.last_mavlink_cmd,
  54. pd.semaphore_line,
  55. pd.fault_line,
  56. pd.fault_type,
  57. pd.fault_addr,
  58. pd.fault_thd_prio,
  59. pd.fault_icsr);
  60. }
  61. #endif // HAL_NO_LOGGING
  62. #endif // IOMCU_FW
  63. schedulerInstance.watchdog_pat();
  64. hal.scheduler->system_initialized(); //系统初始化
  65. thread_running = true;
  66. chRegSetThreadName(SKETCHNAME);
  67. /*
  68. switch to high priority for main loop
  69. */
  70. chThdSetPriority(APM_MAIN_PRIORITY);
  71. while (true) {
  72. g_callbacks->loop(); //调用应用层的loop线程
  73. /*
  74. give up 50 microseconds of time if the INS loop hasn't
  75. called delay_microseconds_boost(), to ensure low priority
  76. drivers get a chance to run. Calling
  77. delay_microseconds_boost() means we have already given up
  78. time from the main loop, so we don't need to do it again
  79. here
  80. */
  81. #ifndef HAL_DISABLE_LOOP_DELAY
  82. if (!schedulerInstance.check_called_boost()) {
  83. hal.scheduler->delay_microseconds(50);
  84. }
  85. #endif
  86. schedulerInstance.watchdog_pat();
  87. }
  88. thread_running = false;
  89. }

在Rover.cpp的最后几行:

  1. Rover rover;
  2. AP_HAL_MAIN_CALLBACKS(&rover);

因此对于我们用的rover而言,执行 g_callbacks->setup()会运行下面函数

  1. void Rover::setup()
  2. {
  3. // load the default values of variables listed in var_info[]
  4. AP_Param::setup_sketch_defaults();
  5. init_ardupilot(); //传感器初始化,注册
  6. //初始化整个主loop任务调度
  7. scheduler.init(&scheduler_tasks[0], ARRAY_SIZE(scheduler_tasks), MASK_LOG_PM);
  8. }

其中:scheduler_tasks[]数组就是我们的任务列表

APM学习笔记(一) - 图5

执行 g_callbacks->loop()会运行下面函数

  1. void Rover::loop()
  2. {
  3. scheduler.loop();
  4. G_Dt = scheduler.get_last_loop_time_s();
  5. }