简介:APM飞控源码(ArduCopter 3.2.1 版本)是一份经过精简裁剪的开源飞控固件,面向无人机控制开发者与爱好者,适合希望深入理解多旋翼姿态控制、高度保持、位置锁定与航点导航等核心算法的读者。该版本在稳定性、效率与功能集之间做了平衡,是学习飞行控制原理和进行二次开发的实用起点。压缩包为 zip 格式,约 24.8MB,上游暂未提供文件明细,故不列具体文件类型与数量。目前已有 804 人学习下载。源码内添加了中文注释,能帮助初学者快速理解函数作用、变量含义与关键算法逻辑。ArduCopter 目录下涵盖 PID 控制器、传感器数据读取、飞行模式切换、地面站通信等核心模块,串起了从姿态估计到位置控制、再到无线交互的完整技术链路。建议在 Visual Studio 环境下配合插件进行代码浏览与调试,便于高效定位问题。对想要掌握无人机底层控制技术或在此基础上贡献新功能的人来说,这份源码具备较高的实践与学习价值。
1. APM飞控源码:不是拿到代码就能飞,先想清楚你要拆哪里
一个做嵌入式开发的同事第一次把APM飞控源码当普通工程解压,直接打开IDE编译,结果报错刷屏,卡了一个下午。源码包拿到手,你会看到满目 .pde、.cpp、.h 混在一起,结构和常见的裸机工程完全不一样。APM飞控源码实际是 ArduPilot 飞控系统中的 Copter 实现,解决的是从传感器读取、姿态解算到电机输出的完整控制链路。你要做飞控二次开发、无人机课程设计,或者只是想搞明白“油门一推,飞机为什么不会原地翻身”,这份源码就是最直接的解剖样本。
2. 源码目录先读懂:ArduCopter 不是 Arduino 工程
2.1 顶层目录:哪里是应用,哪里是驱动
刚开始别一上来就钻进libraries,那里有几百个算法和驱动文件,容易迷路。我一般会先把目录树打出来,看三层以内结构,确认这次要动的是多旋翼还是固定翼。
tree -L 3 -d ardupilot重点看三个位置:ArduCopter/是多旋翼主程序目录,飞行模式、任务逻辑、安全校验都在里面;libraries/是底层算法库,包含传感器驱动、惯性导航、PID 控制器;Tools/是仿真脚本和辅助工具。这个结构更像分层操作系统,ArduCopter相当于应用层,libraries是系统层。
拿“解锁电机”来说,遥控信号先进libraries/RC_Channel做通道解析,再到ArduCopter/arming.cpp做安全校验,最后交给AP_Motors类输出 PWM。所以排查解锁失败要进arming.cpp,而不是去改底层电机代码。这个定位逻辑能省下大半天。
2.2 姿态控制链路:目标姿态怎么变成电机指令
控制代码集中在ArduCopter目录的mode_系列文件和老版本的control_*.pde文件里。新版本里模式调度大多收敛到mode.cpp,但数据流没有变。下面这段是姿态控制的入口示意,不同版本函数名略有差异,但链路一致。
// 姿态控制入口,常见于 mode.cpp 的 run() Quaternion target_att; mode->get_attitude_targets(target_att); // 当前飞行模式生成目标姿态 attitude_control->input_quaternion(target_att); // 姿态环 PID 解算 attitude_control->rate_controller_run(); // 角速度内环更新get_attitude_targets的实现取决于当前模式:定高模式会把气压计高度和油门杆量换算成垂直速度目标;自稳模式只把摇杆角度映射成目标姿态角。input_quaternion跑的是外环角度 PID,rate_controller_run跑的是内环角速度 PID。
实际调悬停晃动,先动角速度环比例,也就是ATC_RAT_RAT_PIT_P和ATC_RATE_YAW_P这类参数,别一上来就动角度环。外环比例调太大,飞机会出现高频抖动,肉眼看到的是机臂一直在抖,这时候多半是内环增益过高。
2.3 传感器融合与 EKF:姿态误差的最大黑匣子
姿态解算不是单纯用陀螺仪,还要融合加速度计和磁力计。现代 ArduCopter 默认使用 EKF3 做数据融合,源码在libraries/AP_NavEKF3/。
EKF 输出的不是原始传感器读数,而是位置、速度、角度的最优估计。上层控制器拿到的都是这个估计值,所以传感器标定不准确,EKF 输出就会跑偏,后端 PID 再稳也没用。
下面这段代码是在源码里读取 EKF 姿态估计的常见路径:
// 从 EKF3 读取机体四元数,并转成欧拉角 Quaternion att_quat; ahrs.get_quaternion_body_to_ned(att_quat); Vector3f eulers; ahrs.get_euler_angles(eulers);get_quaternion_body_to_ned返回的是机体到东北天坐标系的旋转四元数,get_euler_angles再把它转成欧拉角供日志使用。想让这个输出稳定,前提是完成加速度计和罗盘校准。很多新手直接跳过校准去看姿态数据,数据当然是乱的,这不是源码 bug,是你的传感器真值输入就错了。
2.4 电机混控:四个电机是怎么配合的
在libraries/AP_Motors/AP_MotorsMulticopter.cpp里,混控器把“想要的滚转、俯仰、偏航、油门”映射到四个电机的 PWM。这是另一个容易被忽略的源码阅读难点。
// 示意代码:简化的电机混控输出 motor_out[0] = throttle_pct + roll_pid * 0.25 - yaw_pid * 0.25; motor_out[1] = throttle_pct - roll_pid * 0.25 + yaw_pid * 0.25; motor_out[2] = throttle_pct + roll_pid * 0.25 + yaw_pid * 0.25; motor_out[3] = throttle_pct - roll_pid * 0.25 - yaw_pid * 0.25;这里有一个重点:油门基础量位于每个电机输出的核心,姿态环输出只是叠加修正量。也就是说,如果油门通道方向反了,或者电机布局映射错误,姿态环越调会越乱。我排过一台“解锁后电机转速乱跳”的机子,最后发现是FRAME_CLASS参数设成了四轴,但实际机架是六轴结构,混控输出索引错位导致两个电机始终不响应。源码里对应的是AP_MotorsMatrix类的init函数,它会根据机架类型初始化输出映射表。
3. 编译固件与参数配置:从 Ubuntu 到解锁
3.1 编译环境搭建:别再用 IDE 直接打开
APM 飞控源码的编译现在基本都用 waf,不需要你去建工程。常见做法是在 Ubuntu 22.04 上装好交叉编译链和 Python 依赖,然后直接在源码根目录编译。
sudo apt install git gcc-arm-none-eabi python3-pip pip3 install pyserial empy pexpect cd ardupilot git submodule update --init --recursivegcc-arm-none-eabi是 ARM Cortex-M 的交叉编译工具链,没有它编译不出能跑在飞控板上的固件。pyserial是串口烧录工具依赖,empy和pexpect是仿真和自动测试脚本依赖。git submodule update会把第三方库以子模块形式拉全,这一步漏掉,后面编译必然报缺头文件。
提示:首次编译前先执行
git submodule status,确认输出里没有-字符。有-说明子模块目录是空的。
3.2 编译仿真板和真实板:同一套源码,两种产物
对应不同硬件目标,waf 用--board参数区分。
./waf configure --board sitl ./waf copter--board sitl是软硬件在环仿真,生成的固件跑在 PC 上,适合验证逻辑和参数改动。等逻辑验证过了再编译真实固件,常见 STM32F405 飞控板对应板号是fmuv3。
./waf configure --board fmuv3 ./waf copter ./waf --upload copter--upload会先触发重新编译,再通过 USB DFU 把固件写入飞控板。上传前需要按住板上的 bootloader 按钮再插 USB,系统才能在烧录模式下枚举出设备。这个步骤不熟的话,失败是最常见的。
我自己的习惯是先configure --board sitl验证源码能编译,再切真实板子,而且每次换版本都重新 configure 一次,不能偷懒省这一步。
3.3 关键参数表:不调这些,飞机不敢解锁
下载源码包后,真正花时间的不只是代码,还有参数初始化。以下是我每次装机后必须检查的参数表:
| 参数名 | 作用 | 新手建议 |
|---|---|---|
ARMING_CHECK | 解锁前自检项目掩码 | 保持默认全检,别关 |
RC1_MIN/RC1_MAX | 遥控第一通道输入范围 | 校准遥控后自动写入 |
INS_ACC_OFFSETS | 加速度计零偏 | 通过校准生成,别手改 |
AHRS_EKF_TYPE | 姿态融合算法选择 | 保持 3,使用 EKF3 |
ATC_RAT_PIT_P | 俯仰角速度环比例增益 | 悬停晃动时降低 |
BATT_VOLT_PIN | 电池电压采集引脚 | 与硬件接线一致 |
FRAME_CLASS/FRAME_TYPE | 机架类型和布局 | 四轴、六轴、V 型等 |
需要强调一点:新手下源码后的第一个调参动作往往是关掉ARMING_CHECK跳过自检,这基本就是翻车起点。正确落地顺序是先校准遥控和加速度计,再检查RC1_MIN/RC1_MAX范围,然后做罗盘校准,最后才解锁电机。顺序反了,后面每一步都会觉得源码有 bug。
3.4 参数保存与时序:改完不写 Flash 等于白改
在仿真或真机上用 MAVProxy 调参,很多人会忽略保存动作。常见操作是:
param set ATC_RAT_PIT_P 0.150 param saveparam set只是临时写入内存,掉电即丢;param save才会固化到存储区。调试时如果只 set 不 save,重启固件后参数会回到默认值,然后你就会看到一次“玄学复位”。另外在源码里做硬阈值判断时,要注意参数读取的时序:主循环启动后第一次读取可能仍是旧值,需要等params类完成加载后再做判断。这在libraries/AP_Param/AP_Param.cpp里能看到完整生命周期的注释。
4. 避坑:编译与调参常见的五个翻车现场
4.1 现象:编译到一半报错“GIT checkout failed”
原因:源码包是压缩包解压的,没有初始化 git 仓库,子模块目录完全是空的;或者压缩包截断了子模块引用关系。
解决:在源码根目录执行:
git submodule update --init --recursive如果你的源码包本身不带.git目录,先git init,再重新 clone 完整仓库并切到对应版本。压缩包方式在 ArduPilot 这种强依赖子模块的项目里很容易出问题,我后来只按 git clone 方式拿源码,不再解压 zip。
4.2 现象:USB 连着地面站,但执行--upload一直卡在等待 bootloader
原因:飞控板没有进入引导模式。地面站能枚举 USB 不代表进入 DFU,很多时候只是串口虚拟设备。
解决:拔掉 USB,按住飞控板上的 boot 按钮,再插入 USB,等两秒钟后松开,然后执行上传命令。如果还是卡住,用dfu-util -l检查系统是否把板子识别成 DFU 设备。同时确认当前用户有/dev/ttyUSB*的读写权限,sudo usermod -aG dialout $USER是常见做法,做完重新登录一次。
4.3 现象:解锁自检报“PreArm: Compass not healthy”
原因:罗盘没有校准,或者硬件安装方向与参数设置不一致。
解决:先做六面罗盘校准,校准过程中别让电机通电,周围不要有大块金属。完成后检查COMPASS_ORIENT参数,保证与装机朝向一致:飞控板箭头朝机头,通常用COMPASS_ORIENT=0。在校准通过后,再看地面站里磁场模长是否稳定,如果数值跳变超过 20%,多半是电调电源线离罗盘太近产生干扰,这是结构布局问题,改源码救不了。
4.4 现象:解锁后一推油门,飞机直接侧翻
原因:电机转向、桨叶方向、遥控通道映射三者有一个错了。注意侧翻的方向很有规律:向左翻,通常是右侧电机转速不够。
解决:先拆掉桨叶,进入地面站电机测试页,逐个电机确认转向和序号对应关系。确认无误后装桨做小油门测试。如果是在源码层面做混控改动,还要检查FRAME_CLASS和FRAME_TYPE是否与实际机架一致。电机输出脚位映射在AP_MotorsMatrix里由setup_motors函数初始化,端子接错也会表现成完全一样的侧翻。
4.5 现象:SITL 仿真模式下 GPS 有数据,但航向角乱转
原因:仿真环境的地磁模型没使能,或者COMPASS_ENABLE参数在仿真里被改成 0。
解决:在仿真启动参数里强制设置:
param set COMPASS_ENABLE 1 param save然后重启仿真。SITL 的状态估计依赖传感器模型的一致性,GPS 正常但罗盘失效,会导致 EKF 的航向方差发散。这个现象在真机上对应的是罗盘硬件故障,在仿真里就是参数开关问题。
4.6 现象:日志里加速度计曲线剧烈跳动,但飞机实际很稳
原因:传感器采样频率和日志记录频率不匹配,或者 EKF 输出的数据经过了滤波,你取的是原始传感器输出。
解决:看日志时不要直接看ACCX/ACCY/ACCZ的原始值,优先看EKF输出项。原始加速度计数值会受到机架高频振动和噪声影响,用它判断控制品质会有误判。我在源码里加调试打印时,也会从ahrs对象读姿态,而不是直接从寄存器读陀螺仪数值。
5. SITL 仿真验证:改源码前先在地面飞一遍
5.1 一键启动仿真并连接任务控制
源码根目录自带仿真脚本,CTRL+Z 交互方式跑固定翼和多旋翼都方便。常用的启动命令:
cd ardupilot sim_vehicle.py -v ArduCopter -w --console --map-w参数会清空仿真参数并重启,--console打开控制台窗口,--map打开实时地图。首次启动会生成一个仿真飞机,默认停在 GPS 坐标原点上位。等到控制台输出Ready to drive后,就可以用 MAVProxy 发指令了。
5.2 模拟起飞和悬停,验证参数影响
在 MAVProxy 命令行里解锁并起飞:
STABILIZE arm throttle takeoff 50takeoff 50让仿真飞机爬升到 50 厘米高度并定高。这时你把ATC_RAT_PIT_P调大,可以在控制台看到姿态角开始震荡。这个过程中记录日志:
log start log list日志文件会写到logs/目录,里面的ATT、IMU、MOT数据可以直接用地面站回放。我一般会在仿真里先验证参数是否会导致姿态震荡,再决定要不要真机试。这个习惯帮我避免了好几次炸机。
5.3 在源码加调试输出,验证自定义逻辑
改源码最常见的需求是加一段自定义状态判断。比如我想在特定高度触发一个串口打印,可以直接在模式控制文件里加:
// 示意:在定高模式下输出当前高度给调试串口 if (mode_has_takeoff && current_loc.alt > 500) { hal.console->printf("alt>50m\n"); }这段代码放在Copter::update_altitude()相关调用路径里,逻辑是当高度大于 50 米时向控制台输出一次提示。编译 SITL 后跑一遍,就能立刻看到打印。不需要真机、不需要电池,就能验证你自己的控制逻辑分支是否按预期触发。
从那以后,我每次拿到一份新的 APM 飞控源码包,都会强制走一遍完整流程:先看目录结构,再编译 SITL,接着调参数,最后才动代码。这个流程帮我避开了大部分低级错误,也让源码阅读顺序变成可控的排查路径。希望帮到你。
本文还有配套的精品资源,点击获取