资讯动态

Ardupilot固件自编译入门:交叉编译与waf构建系统实战

发布时间:2026/10/14 12:56:40 来源:尧图企业网站定制
1. 为什么必须自己编译Ardupilot固件它解决的是开发路线的开头开源飞控领域Ardupilot的覆盖面确实广多旋翼、固定翼、直升机、无人车、无人船都可以用它来驱动。对刚入手的朋友来说最快的方法就是下载别人编译好的固件用地面站刷进飞控板再校准一下就能起飞。但如果你打算长期折腾飞控或者本身就是写代码的人就会发现这种“拿来主义”很快会碰到天花板。Ardupilot项目编译和构建是你从使用者切换到开发者必须跨过的第一道坎。编译Ardupilot固件到底是在做什么简单说就是把成千上万行C源码经过预处理、编译、汇编、链接变成一块飞控板能直接运行的二进制文件。这个过程和你在Linux下编译一个开源软件没有本质区别只不过因为要交叉编译到ARM处理器工具链和参数会复杂一点。你最终得到一个.apj或者.px4文件把它烧写进飞控板板子跑的就是你的代码而不是别人预编译好的标准固件。用别人的固件相当于在餐厅点外卖。厨师的刀工、火候、调料比例你都看不到出了问题只能瞎猜。自己编译相当于去菜市场买食材回厨房做饭每个环节都可控食材哪里买的、油盐放了多少、火候怎么调的出了问题也更容易定位。对飞控这种涉及传感器融合和电机控制的项目来说这种可控性特别重要。这篇内容我是按“Ardupilot part.1”来写的所以默认你手里还没有可用的编译产物。我会把整个过程拆成环境准备、构建系统、实际操作、问题排查四个模块并把我自己踩过的坑一并写进去。如果你是第一次接触这个项目哪怕完全不懂交叉编译按照步骤来也能把固件跑出来。如果你已经能刷官方固件但对“自己构建”还没有完整概念这篇文章同样能帮你把底层逻辑补齐。1.1 编译工作流里到底发生了什么Ardupilot不是一个单一程序而是一整套框架。它包含多个载具类型比如多旋翼、固定翼、地面车、水下设备等每个载具类型对应一个顶层目录但又共用一套底层库。这些底层库里有硬件驱动、姿态解算、导航算法、无线通信协议等代码。你编译的时候实际上是在告诉构建系统我需要哪个载具类型、目标跑在哪块硬件上。整个编译过程会先收集所有依赖的头文件然后把每个.cpp文件编译成目标文件最后把所有目标文件链接成一个可执行固件。听起来很常规但因为Ardupilot代码量非常庞大依赖的第三方库也多所以构建系统必须能处理并行编译、增量编译、交叉编译这些需求。这也是为什么Ardupilot没有用简单的Makefile而是选择了一套更灵活的构建系统。我在刚开始接触这个项目的时候总以为编译就是把整个仓库里的代码全部编译一遍。实际上不是Ardupilot的源码组织方式决定了你可以指定板卡和载具。比如你只需要多旋翼固件那就进到对应的载具目录配置目标板后只编译这一个目标。这种方式省时间也让项目结构更清晰。1.2 自编译能带来哪些官方固件给不了的能力官方发布的固件是面向大多数用户的标准版本它的配置偏保守功能是“全而稳”。但当你碰到下面这些场景官方固件就会显得不够用第一功能定制。有些项目根本不需要那么多协议支持默认为全开会导致固件体积膨胀、启动时间变长甚至在某些老硬件上不够用。自己编译时你可以通过配置和裁剪选项只保留需要的功能模块整个系统会更精简。第二硬件适配。市面上飞控板非常多虽然主流板子都有官方支持但一些非通用板卡或自定义硬件需要自己添加驱动、调整传感器驱动映射。这些改动不可能在别人已经编译好的二进制文件里实现必须重新编译。第三代码学习和调试。当你开始读Ardupilot源码想在某个模块里加日志、改控制参数或者把EKF输出的中间变量打印出来观察最简单的方式就是在源码里修改然后重新编译烧录。官方固件不可能给你这些调试入口。第四二次开发与算法研究。很多做无人机应用的人会把自己的导航算法、避障模块接入Ardupilot。接入之后必须重新构建整个系统确保新代码和原有框架正确链接。这一步做不扎实后面在实际飞行中的问题会非常难排查。所以自编译不是“工作多此一举”而是从“用户”走向“开发者”的必经之路。它不但影响你后面能做什么功能也影响你遇到问题时的排查效率。2. 编译环境准备Pixhawk交叉编译的依赖清单和工具链版本很多人刚开始编译Ardupilot第一个倒下的地方不是代码而是编译环境。Ardupilot是嵌入式项目需要在PC上安装一套完整的交叉编译工具链同时还要装一堆依赖库。如果你用的是Windows系统又没有虚拟机或Linux子系统那前期的环境问题就可能让你怀疑人生。我在实际部署中发现Ubuntu系统是最省心的选择。佳选是Ubuntu 20.04或22.04 LTS版本。内存最好有16GB磁盘空余空间至少15GB因为代码仓库、子模块和编译中间产物加起来会超过好几GB。CPU核心数越多越好因为编译时可以开多线程。2.1 操作系统与硬件选型Ardupilot官方的文档里Linux是首选的开发环境。原因很直接很多依赖包在Linux下可以用包管理器一行命令装好工具链和构建系统的兼容性也最稳。Windows用户也不是完全没法用但要么装WSL要么用虚拟机甚至有些人在Windows原生环境里折腾出了流程只是遇到问题的概率会高很多。即使你用的是Linux内存大小也直接影响编译体验。我试过用4GB内存的旧机器编译Pixhawk固件开了4线程编译过程中系统直接卡死。后来加了swap才勉强跑完但耗时比8GB内存差了将近一倍。建议至少8GB内存起步16GB以上你会舒心很多。磁盘方面要确认是SSD因为大量小文件读取才是编译的一大瓶颈。你还需要注意自己的用户目录权限。Ardupilot编译过程会在仓库和build目录里生成大量文件也可能往~/.config和~/.cache里写东西。最好用普通用户操作不要整个文件夹丢到root目录下也不要动不动就用sudo否则后期清理和权限管理会非常难受。2.2 依赖安装不能只装编译器和PythonArdupilot的构建系统基于Python编译却是C。你在安装依赖的时候这两样都不能漏。最基础的命令是sudo apt update sudo apt install -y git python3 python3-pip build-essential libtool autoconf automake此外还需要一些库文件比如XML解析、JSON解析、串口和网络相关的开发包。不同Ubuntu版本里的包名可能略有差异但大致包括libxml2-dev、libxslt1-dev、libjson-c-dev、libzmq3-dev。安装完这些之后还需要让pip进入可用状态python3 -m pip install --upgrade pip有个容易忽略的点Ardupilot的子模块中有一些需要用到Python的工具和库如果你系统的Python环境不干净后面在configuration阶段会报一些莫名其妙的错误。我的习惯是装完系统依赖之后单独在仓库根目录跑一遍官方自带的环境安装脚本。Ardupilot仓库里有这个脚本路径是Tools/environment_install/install-prereqs-ubuntu.sh但如果你不想执行仓库里的脚本手动把基础依赖装齐也能用。如果你在WSL环境中操作还需要额外注意USB设备透传。WSL2对USB的支持和原生Linux不一样编译本身没问题但后面如果要连接地面站进行固件烧录会多一层配置。我的建议是第一次编译用WSL没问题但真机烧录时最好回到原生Linux或专门的虚拟机环境。2.3 工具链版本版本不对编译结果就是一堆看不懂的报错交叉编译需要专用的ARM工具链也就是arm-none-eabi-gcc。Ardupilot对工具链版本很敏感官方推荐的版本通常不是系统包管理器里自带的那个。我见过很多新手用sudo apt install gcc-arm-none-eabi装完编译时立刻报内部编译器错误其实不是代码问题就是版本不匹配。最稳妥的方式是使用仓库里提供的工具链安装脚本。它会下载指定版本的arm-none-eabi-gcc放到当前用户目录下避免和系统工具链互相污染。安装完成之后需要把工具链的bin目录加到PATH里。比如export PATH$HOME/opt/gcc-arm-none-eabi-9-2019-q4-major/bin:$PATH验证版本arm-none-eabi-gcc --version这里有个细节我要特别强调export PATH最好写进你的~/.bashrc或者~/.zshrc。如果你只是临时在当前终端窗口执行那下次打开新的终端系统又会找不到编译器。另外当你执行which arm-none-eabi-gcc发现指向了系统自带路径而该路径下的版本不是Ardupilot要的那就说明PATH顺序也需要调整。Windows下用WSL也建议采取同样逻辑把工具链放在Linux文件系统里而不是放在Windows侧再引用。跨文件系统访问会带来权限和路径问题虽然不是不能解决但很容易干扰你对编译错误的判断。3. 核心构建系统解析waf的配置逻辑与编译参数拆解Ardupilot没有用传统的Makefile或CMake而是用一个叫waf的构建工具。很多第一次接触的人会问既然有make为什么还要用waf这个问题的答案恰恰是理解Ardupilot编译流程的关键。waf是一个基于Python的构建系统。它的核心特点是配置和构建分开你不需要写大量复杂的构建规则只需要通过命令行参数告诉它目标硬件和需要编译的载具类型。z构系统会根据这些信息自动处理依赖、编译选项和链接参数。对于Ardupilot这种跨平台、多板卡、多载具类型的项目waf的灵活性和可扩展性都比传统构建系统更合适。3.1 waf和make、CMake有什么区别make是最经典的构建工具靠Makefile描述文件之间的依赖关系规则相对底层。CMake则是在make之上做了更友好的配置层通过CMakeLists.txt生成构建脚本。waf是另一种思路它用一个Python脚本来处理全部配置、构建、安装过程不需要额外生成Makefile也可以直接进行交叉编译配置。你可以把waf理解成一个项目管家。它知道项目里有哪些目录、哪些文件需要一起编译、不同板卡需要哪些编译选项。你只需要告诉它“我要给这块板子编译多旋翼固件”它就会自己组织编译流程。这个过程对新手来说像黑盒但其实每个阶段都能看到输出日志不会让你完全不知道发生了什么。第一次运行waf时它会先进入configuration阶段生成一个build目录把检测到的工具链信息、编译参数、目标板配置都写进去。之后就是重复的build阶段waf会检查哪些文件发生了变化只重新编译有变动的部分。这种增量编译能力在多轮修改代码时特别重要。如果你改了一个库文件waf会重新编译这个库以及依赖这个库的模块而不是把整个项目全部推倒重来。3.2 configure阶段决定目标板Ardupilot编译的第一步是配置。你需要在仓库根目录或者对应载具目录执行类似下面的命令./waf configure --boardPixhawk1这条命令里的--board参数非常重要。它决定了整个编译过程会使用哪个目标硬件配置包括处理器的体系结构、引脚的映射、外设的驱动、启动文件的链接地址等。Ardupilot支持非常多板卡Pixhawk1只是其中最常见的之一。其他常见板卡还有Pixhawk4、CubeBlack、SITL等。如果你是在Linux主机上编译多旋翼固件理论上在ArduCopter目录下执行waf命令也可以但要注意相对路径。更多时候大家习惯在仓库根目录使用./waf configure然后统一管理构建产物。configure阶段不会编译只会检测环境并生成配置信息。如果这个阶段报错下面可能的错误是找不到工具链、缺少Python模块、子模块不完整等。configure的输出内容很多不要只看最开始的几行。真正有用的信息通常在最后比如告诉你使用的编译器版本、目标板名称、是否启用了某个功能。如果你看到ERROR或者FATAL字样就说明环境还有问题。我到后来形成了一种习惯每次配置结束直接搜索日志里的error关键字而不是逐行读。3.3 build与clean并行编译、局部编译和重来配置完成之后就可以开始正式编译了。最基础的命令是./waf build -j4其中的-j4表示用4个线程并行编译。这个值一般不要大于CPU逻辑核心数否则会无谓地增加调度开销。8核16线程机器用-j8会很稳但如果你内存只有8GB建议还是-j4因为每个并行编译任务都会占用一定内存。如果你只修改了某一块代码比如改了某个传感器驱动还指望整个项目重新编译那就太傻了。waf支持只编译特定目标./waf build --targetbin/arducopter这条命令只构建对应的可执行目标速度会快很多。对于日常开发调试我基本都是用这个方式完整编译只会在刚拉完代码或切换分支后进行。如果想要清理编译产物可以执行./waf clean它会把build目录下生成的中间文件和目标文件清掉但保留configure生成的配置。如果想要彻底回到初始状态用distclean。我第一次切换板卡时忘了清配置结果新旧配置混在了一起编译出来的固件反复出问题。后来每次更换--board之前都先执行clean再重新configure才算把这个坑填平。4. 实操全流程从Git克隆到生成可烧写固件环境搞定构建系统也理解了现在可以走一遍完整流程。很多人对着文档敲命令都会出问题不是因为命令不对而是我不知道每一步检查什么、等待什么、用什么判断结果。下面我就按实际操作的顺序把每个环节的关键节点和验证方法写清楚。实操之前先给你一个全貌整体流程是“获取源码 - 更新子模块 - 进入载具目录 - 配置目标板 - 编译 - 找到固件产物”。看起来简单但每一步都有小坑尤其是子模块和配置参数。4.1 获取源码与分支选择Ardupilot源码托管在公开代码仓库你只需要用git克隆即可。关键点是克隆时最好连同子模块一起拉下来否则后面会不断遇到文件缺失的问题。命令可以这样写git clone --recurse-submodules Ardupilot仓库地址如果你已经克隆完成但忘了加--recurse-submodules可以进入仓库目录后手动更新git submodule update --init --recursive这一步我建议多看几遍输出如果网络中途断了子模块可能只拉下来一部分编译时就会出现“找不到某个头文件”的错误。不要怕重复执行子模块更新命令它不会对仓库造成破坏缺多少补多少。分支选择也是很多人纠结的地方。Ardupilot的master分支是开发主线功能最新但稳定性没有保证。我的建议是如果你是刚开始搞先选一个已经发布的稳定分支比如某个载具的命名分支这样环境依赖和文档匹配度都很高。如果你想尝鲜新功能再切到master但要做好随时遇到编译问题的心理准备。切换分支后记得再次更新子模块git checkout 稳定分支 git submodule update --init --recursive4.2 配置目标板与载具类型在仓库根目录下能看到多个载具目录比如多旋翼对应的是ArduCopter固定翼对应的是ArduPlane还有车辆、水下设备等。你编译哪个载具就要进到对应的目录里操作。拿多旋翼举例cd ArduCopter然后配置目标板../../waf configure --boardPixhawk1这里的../../waf是因为当前在ArduCopter子目录下需要回到上一层目录找到waf入口。有些朋友喜欢直接在仓库根目录运行./waf configure也可以但要注意后面编译命令的路径也要同步调整。配置完以后会生成一个build目录。在ArduCopter里执行时构建产物会放在上一级目录下的build/Pixhawk1里。你可以在配置输出的末尾看到这些信息。选板子一定要仔细。Pixhawk系列看着长得差不多但不同板卡的MCU型号、传感器接口、PWM输出通道都可能不同。如果板子选错编译能通过但刷进去之后外设可能不工作这个问题非常隐蔽。如果你不确定自己手中板子的具体型号先找到板子上的丝印再对照Ardupilot的板卡列表确认。如果暂时没有硬件也可以配置SITL仿真板../../waf configure --boardSITLSITL板不需要交叉编译工具链编译产物是Linux下的可执行文件用来在电脑上仿真跑飞控逻辑。这个方式特别适合验证代码逻辑也是后续做无硬件调试的好帮手。4.3 执行构建并定位固件产物配置成功后直接执行编译../../waf build -j4第一次编译会花很长时间因为需要把所有依赖库都编译一遍。我在一台8核16线程、16GB内存的机器上编译Pixhawk固件大概需要十到二十分钟。如果你用-j4时间会更长。中间会有大量编译日志滚动不要因为看到警告就慌只要没有ERROR基本就是正常的。编译完成之后构建产物在build/Pixhawk1/bin/具体到多旋翼固件会有一个类似arducopter.apj的文件。.apj是Ardupilot特有的一种打包格式里面包含二进制固件和加载信息大多数地面站软件都支持直接烧写这个文件。你可以用命令看一下生成文件的信息ls -lh build/Pixhawk1/bin/ file build/Pixhawk1/bin/arducopter.apj看到文件大小和格式信息就可以确认固件已经生成。下一步就是通过地面站把它烧录到飞控板上。烧录本身虽然不是编译流程的一部分但却是验证编译结果最直接的方式。烧录完进行基本传感器校准如果一切正常说明你的自编译流程已经跑通。5. 编译过程常见问题与排查技巧实录代码编译这条路看似每一步都有文档但真正执行时会碰到很多文档没有细说的坑。下面我把高频问题集中列出来并给出对应的排查思路和操作建议。这些内容不是理论推演都是我实际操作中踩过的。5.1 子模块缺失最隐蔽的开局大坑现象是configure阶段报错提示找不到某个模块比如mavlink、gbenchmark等。但代码仓库明明刚拉下来怎么会缺原因大多数是你克隆时没有带--recurse-submodules或者子模块更新没有完整执行。解决办法是反复执行git submodule update --init --recursive执行完以后再看modules目录下的内容是否完整。可以检查某一个关键子模块目录里是否有文件如果目录是空的说明还没拉到。不要急着编译先确保子模块都到位。还有一种情况是切换分支后子模块的指向发生了变化。这时候同样需要重新更新一次子模块。我自己的经验是每次git checkout之后都主动跑一次子模块更新宁可白白浪费时间也不要让编译出了问题再来反复排查。5.2 工具链不匹配报错像天书其实是编译器问题有时候编译会报一些看起来像是代码层面的错误比如“internal compiler error”或“option not supported”。这种情况很大概率是交叉编译器版本太新或者太旧与环境里的某些编译选项不兼容。排查方法如下先查看当前工具链位置which arm-none-eabi-gcc再看版本arm-none-eabi-gcc --version如果发现版本不是Ardupilot推荐的版本就需要卸载或避开当前的编译器让Ardupilot指定的工具链生效。我之前遇到过PATH里同时存在系统自带工具链和Ardupilot推荐工具链的情况系统自带的优先级排在了前面导致每次都用了错误的编译器。后来把系统自带工具链移出了PATH问题才彻底解决。如果你实在不确定该用哪个版本可以用仓库里的工具链安装脚本重新装一次。装上之后确认PATH已经指向新工具链的目录再重新打开终端或者执行source ~/.bashrc。5.3 配置阶段报错不要慌configure的报错信息往往很长但真正关键的行通常出现在最后。常见的有找不到c编译器说明build-essential没装。找不到Python头文件说明python3-dev没有安装。提示某个库文件缺失说明对应的libxxx-dev包没有装。遇到这种报错直接根据提示安装对应包即可sudo apt install 缺少的包安装完以后重新执行configure。大部分依赖问题都能在这一步解决。如果configure里报的错误和“module”相关大概率是子模块问题。不要尝试手动去创建缺失文件直接从子模块更新的角度入手。5.4 build卡死或内存不足并行编译虽然快但也容易把内存塞满。尤其是你同时开着浏览器、编辑器、容器等程序16GB内存也可能告急。现象是编译进行到一半某条命令被系统杀掉进程消失或者系统整体卡顿。可以在编译前用free -h看内存余量。如果剩余量很少就减少并行线程数比如从-j8降到-j4甚至降到-j2。必要时可以加swap空间作为兜底。磁盘空间不足也会导致编译中断。Ardupilot的build目录会保留大量目标文件多次编译之后体积会明显膨胀。建议每隔一段时间执行./waf clean把生成产物清理一遍。尤其是在切换板卡之前clean一下能避免很多奇怪问题。我个人在实际操作中的体会是Ardupilot编译并不需要你有多少“天才”般的技巧它更考验的是你有没有耐心把环境理顺。第一步把工具链装对第二步把子模块更新完整第三步配置与清理的纪律保持好后面基本就是等待编译完成的事情。如果你已经把这一套流程跑通后续再编译SITL仿真、调试特定传感器、增加自己的代码模块都会顺畅许多。这篇文章只是Ardupilot系列的开头真正的玩法和坑还在后面慢慢等你探索。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑