1. 这次为什么又折腾了一遍PX4仿真环境先说结论这不是我第一次搭PX4仿真环境但确实是踩坑踩得最值的一次。第一次搭建的时候我照着网上的教程一步步来结果卡在编译环节整整两天最后勉强跑起来一个不带相机、不带里程计的裸机仿真连offboard模式都没测利索。这次重新搭一方面是换了新电脑想从零走一遍完整流程另一方面也是因为之前对PX4仿真的理解太浅——只会敲make px4_sitl gazebo这种命令根本不知道背后发生了什么更不知道怎么改参数、怎么加传感器模型。第二次搭建最大的不同在于我不再满足于“能跑起来”而是想真正搞懂这套仿真链路是怎么串起来的。如果你正准备接触PX4仿真或者已经装了半截发现跑不通这篇记录应该能帮你省下大量试错时间。文章会按照实际搭建顺序展开从环境准备、源码编译、固件构建到Gazebo仿真启动、QGroundControl地面站连接再到MAVROS与ROS2的桥接整个过程全部记录下来。里面会涉及一些命令行操作和配置文件修改但不会堆砌术语每一步我都会解释清楚为什么这么做、不这么做会有什么后果。先说一个重要判断PX4仿真环境的搭建难度80%集中在环境依赖的处理上而不是PX4代码本身。PX4的源码编译本身并不复杂真正折磨人的是Ubuntu版本、Gazebo版本、ROS版本、显卡驱动、CMake缓存这几样东西之间的隐性冲突。如果你之前在任何教程里看到过“我按照这篇文章一步步做就成功了”那大概率是运气好——因为不同机器上预装的库版本差异会导致完全不同的报错内容。所以这篇记录会特别强调“排查思路”而不是“标准答案”你要学的不是某一条命令而是当命令报错时知道去哪里看日志、改什么参数、查什么依赖。2. 搭建前的思路梳理与整体规划2.1 先明确PX4仿真的完整链路很多人第一次搭PX4仿真时会直接搜索“PX4仿真环境搭建”然后照着教程复制粘贴命令跑起来一个窗口就觉得自己完成了。但如果你不知道这个窗口里运行的到底是什么后面改需求时会寸步难行。完整的PX4 SITLSoftware In The Loop软件在环仿真链路是这样的PX4飞控程序本身也就是固件是在你电脑上以进程方式运行的它并不知道自己是在真实硬件上还是在模拟环境里。飞控的传感器数据IMU、气压计、GPS、磁力计等来自Gazebo仿真器里模拟出来的世界。Gazebo负责物理引擎计算和渲染把虚拟世界里的无人机状态反馈给PX4。两者之间通过MAVLink协议通信通常是UDP端口。地面站QGroundControl也通过MAVLink连接PX4进程用于显示状态、发送指令。这条链路里有三个独立的大件PX4固件、Gazebo仿真器、QGroundControl地面站。它们各自有各自的依赖和启动方式但只要理清了“谁给谁发数据、谁依赖谁启动”这个关系后续的故障排查就变得非常简单。2.2 版本选型是成败的关键第二次搭建时我在版本选择上比第一次谨慎得多。原因很直接PX4的master分支一直在更新Gazebo的版本也从老版的Gazebo 9/11过渡到了Gazebo Classic和新的GazeboIgnitionROS这边还有ROS1 Noetic和ROS2 Humble/Iron等版本。如果你不刻意锁定版本直接git clone最新代码很可能碰到今天能编译、下周就被某个依赖更新搞挂的情况。我的建议是不要追求最新版本。PX4官方文档会告诉你不同Ubuntu版本对应哪些Gazebo版本但你真正要关注的是“你打算用哪个版本长期开发”。如果你只是练习仿真建议选择LTS长期支持组合Ubuntu 20.04 ROS Noetic Gazebo 11 PX4 v1.13或v1.14。如果你需要ROS2那就选Ubuntu 22.04 ROS2 Humble Gazebo Classic默认随PX4附带 PX4 v1.14或以上。我在这次搭建中选择的是Ubuntu 22.04 ROS2 Humble PX4 v1.14.3稳定分支Gazebo用的是PX4自带工具链安装的Gazebo Classic 11。为什么要选稳定分支而不是main因为main分支的开发进度快但偶尔会引入需要额外安装的依赖对新手不友好。稳定分支可能在某些特性上落后几个月但胜在“社区验证过的人多坑基本都被踩平了”。2.3 磁盘空间与硬件配置的提前规划这是很多人忽略但影响巨大的点。PX4源码加上Gazebo模型库、编译中间文件、ROS2环境全部加起来至少需要30GB左右的磁盘空间。而且编译过程中会生成大量小文件机械硬盘的编译速度会比固态硬盘慢好几倍。如果你是用虚拟机装的Ubuntu还要注意给虚拟机分配的内存不能太少——PX4编译时建议至少4GB内存Gazebo运行时又需要额外的内存总共8GB是底线16GB才够宽敞。我在这次搭建前先检查了磁盘剩余空间、CPU核数和内存大小。编译时可以开多线程加速用make -j$(nproc)但如果你内存不够开太多线程会导致内存溢出直接编译失败。我的机器是8核16线程、32GB内存编译时用make -j12整个过程大约15分钟完成。如果你只有4核8GB内存建议用make -j4编译时间会长一些但至少不会崩。3. 核心依赖与前置知识准备3.1 Ubuntu系统的初始配置与换源拿到一台新装的Ubuntu 22.04第一件事不是装PX4而是先把系统的基础环境配置好。这里我强烈建议先更换apt软件源因为默认源在国内的下载速度实在感人尤其是安装一些基础依赖包时动辄几百MB的下载量慢的话能等半小时。换源的操作很简单编辑/etc/apt/sources.list把archive.ubuntu.com替换为国内镜像源地址比如清华源或阿里源。然后执行sudo apt update和sudo apt upgrade把系统更新到最新状态。这一步之所以放在最前面是因为后面安装的所有依赖都依赖apt源如果源本身有问题后面每装一个包都会卡壳。基础工具链也需要提前装好build-essential编译工具、git、cmake、python3-pip、ninja-build等。这些是PX4编译的必要条件但不同版本的PX4对cmake版本有要求——PX4 v1.14要求cmake 3.10.2以上Ubuntu 22.04自带的cmake版本是3.22满足要求。如果你用的是Ubuntu 20.04自带cmake是3.16也满足要求。这里不用特意升级cmake系统自带版本就能用。3.2 自动脚本与手动安装的选择PX4官方文档提供了一个自动安装脚本ubuntu_sim_common_deps.sh可以帮你装好Gazebo、ROS、MAVROS等一系列依赖。第一次搭建时我就直接跑了这个脚本结果出了不少问题——脚本里有些依赖包的版本和我系统里已有的包冲突而且脚本运行完之后你根本不知道它装了哪些东西、改了什么配置。出了问题时很难排查。第二次搭建时我改用“半自动”方式官方脚本照跑但每执行一步都记录下这一步装了什么、输出了什么警告。如果脚本中途报错就去查对应包的安装方法而不是盲目重跑。这样做的好处是如果最终编译失败我能清楚地知道是哪一步引入的问题而不是对着一个黑洞一样的“一键安装”脚本束手无策。如果你是新手我建议这样操作先跑官方脚本如果成功恭喜你可以直接跳到编译环节如果失败再回到我这篇文章的排查部分按图索骥。不要死磕脚本——脚本失败不等于环境有问题很多时候只是某个包的下载源临时不可达。3.3 认识目录结构PX4源码在电脑上是怎么组织的PX4源码克隆下来后你会看到一个名为PX4-Autopilot的目录这个目录就是整个飞控固件的家。里面有几个子目录特别重要src/存放着飞控的核心代码包括模块、驱动等Tools/存放仿真相关工具比如sitl_gazebo子模块就是Gazebo仿真器的桥接插件build/目录是编译输出目录编译产生的所有二进制文件都在这里ROMFS/存放文件系统镜像里面包含参数定义、启动脚本等。新建终端进入PX4源码目录后你需要先运行一条命令来加载环境变量source Tools/setup_gazebo.bash $(pwd) $(pwd)/build/px4_sitl_default。这条命令会把Gazebo插件路径、模型路径等环境变量注入当前终端这样Gazebo启动时才能找到PX4提供的传感器模型和世界文件。这也是新手最容易忽略的一步如果你没有source这个脚本Gazebo也能启动但你会发现无人机模型加载不出来或者加载出来了但没有传感器数据。因为Gazebo不知道去哪里找那些模型插件。每次新开终端都需要重新source一次这个问题我会在后面反复强调。4. 实操全过程从源码拉取到仿真跑通4.1 第一步拉取PX4源码并切换到稳定分支打开终端进入你准备存放源码的目录我建议放在主目录下路径中不要有中文和空格执行git clone https://github.com/PX4/PX4-Autopilot.git --recursive这个--recursive参数非常重要它会把PX4依赖的子模块一并拉取下来。如果你忘了加这个参数后续编译时会提示缺少子模块此时需要进入目录执行git submodule update --init --recursive来补拉。但这里有个坑PX4的子模块数量非常多而且有些子模块在国外的服务器上下载速度极慢甚至经常失败。如果你网络状况不好建议直接使用国内镜像源加速git clone https://ghproxy.com/https://github.com/PX4/PX4-Autopilot.git --recursive或者先用git clone拉取主仓库再手动配置子模块的URL为镜像地址。但这个操作比较复杂不太适合新手。我的建议是直接用官方源拉取如果子模块下载失败就反复执行git submodule update --init --recursive不要因为一次失败就放弃——多试几次失败的子模块会被逐个补齐。源码拉取完成后立即切换分支。执行git branch -a查看所有分支找到v1.14.3或你选择的其他稳定版本标签执行git checkout v1.14.3 git submodule update --init --recursive切换到稳定分支后也要再更新一次子模块因为不同分支对应的子模块版本可能不同。这一步完成后你的源码就处于一个“固定版本”的状态后续不会因为远端更新而变动。4.2 第二步安装系统依赖与GazeboPX4官方提供了一个安装脚本PX4-Autopilot/Tools/setup/ubuntu.sh直接执行bash ./PX4-Autopilot/Tools/setup/ubuntu.sh这个脚本会安装PX4编译所需的所有依赖包括Gazebo、ROS如果你选择ROS版本、MAVROS、Python包等。但脚本运行时间很长而且中途大概率会卡在某一步。我的经验是分步骤执行先安装基础依赖sudo apt-get install -y \ build-essential \ cmake \ git \ curl \ wget \ python3-pip \ python3-venv \ ninja-build \ autoconf \ automake \ libtool \ pkg-config然后再单独安装Gazebo。Ubuntu 22.04上PX4默认使用Gazebo Classic安装命令是sudo apt-get install -y gazebo libgazebo11-devGazebo安装完成后执行gazebo --version如果能看到版本号说明安装成功。这里注意Ubuntu 22.04的默认Gazebo版本是11如果你在终端输入gazebo启动的却是新版的GazeboIgnition说明你的环境里同时装了新旧两个版本输入gazebo命令会启动旧版输入ign gazebo才会启动新版。PX4的仿真脚本默认调用的是gazebo命令所以旧版的存在是必要的。接下来不要急着跑PX4编译先测试一下Gazebo本身能不能正常启动。在终端输入gazebo等待几秒如果出现一个3D模拟窗口说明Gazebo没问题。如果启动失败大概率是显卡驱动问题——我在下面问题排查部分会专门讲。4.3 第三步编译PX4固件进入源码目录执行cd PX4-Autopilot make px4_sitl gazebo-classic这条命令做了三件事编译PX4飞控固件SITL版本、编译Gazebo的PX4插件、启动Gazebo仿真。第一次编译时系统会先下载一些模型文件然后开始漫长的编译过程。编译时你会看到大量的[ xx%]进度提示这个过程可能持续10-30分钟具体取决于你机器性能。编译过程中屏幕上可能会出现一些warning警告不用慌张只要不是error错误就不会影响最终结果。有一个常见问题是编译过程中内存耗尽。如果你发现编译中途进程被系统杀掉或者报出internal compiler error多半是内存不够。解决办法是在make时限制并行编译线程数make px4_sitl gazebo-classic -j4-j后面的数字就是你希望使用的线程数4表示同时编译4个文件。线程数少一些编译慢一些但不容易崩。编译完成后如果一切顺利终端会停留在PX4 shell提示符下类似pxh同时Gazebo窗口会打开里面有一架多旋翼无人机。这说明你的仿真环境已经跑起来了。在PX4 shell里输入commander takeoff无人机就会起飞——当然前提是所有传感器都正常工作。4.4 第四步启动QGroundControl地面站单独的PX4仿真窗口只能看到无人机在Gazebo里飞行但无法直观地看到飞控状态、参数、日志等信息。这时候需要启动QGroundControl地面站。去QGroundControl官网下载对应Ubuntu版本的AppImage文件。下载完成后赋予执行权限chmod x QGroundControl.AppImage ./QGroundControl.AppImage启动地面站后它会自动通过UDP端口14550连接PX4仿真进程。如果连接成功你会看到地面站界面右上角出现一个绿色的连接指示灯同时能看到无人机当前的姿态、高度、GPS状态等信息。这里注意QGroundControl连接的是PX4 SITL进程而不是Gazebo——PX4进程和Gazebo插件之间通过另一个UDP端口14557通信地面站和PX4通过14550端口通信。有时候地面站启动后无法连接仿真最常见的原因是Gazebo还没启动完成PX4进程还没进入等待连接的状态。解决办法是等Gazebo完全启动、PX4 shell出现后再打开QGroundControl。4.5 第五步验证offboard模式与MAVROS桥接纯PX4仿真的关键验证点是能否通过外部指令控制无人机——也就是offboard模式。在PX4仿真中最常见的控制方式是通过MAVROS包将ROS消息发送给PX4。安装MAVROS的方法在PX4官方文档里有详细教程核心是添加ROS源后用apt安装。安装完成后需要配置PX4飞控的仿真参数在QGroundControl的连接页面中确保MAVLink参数里的MAV_PROTO_VER设置为2.0默认就是2.0。然后在终端启动MAVROSroslaunch mavros px4.launch fcu_url:udp://:14540127.0.0.1:14557这条命令会让MAVROS作为MAVLink客户端连接本机的14557端口也就是PX4 SITL监听的端口。连接成功后你可以用rostopic echo /mavros/state查看飞控状态其中connected字段应该为true。然后就可以写一个简单的offboard控制脚本了。这里我提供一个最简单的Python示例先切换offboard模式再发送位置指令#!/usr/bin/env python3 import rospy from geometry_msgs.msg import PoseStamped from mavros_msgs.msg import State from mavros_msgs.srv import CommandBool, SetMode rospy.init_node(offboard_test) state State() def state_cb(msg): global state state msg rospy.Subscriber(/mavros/state, State, state_cb) pose_pub rospy.Publisher(/mavros/setpoint_position/local, PoseStamped, queue_size10) arming_client rospy.ServiceProxy(/mavros/cmd/arming, CommandBool) set_mode_client rospy.ServiceProxy(/mavros/set_mode, SetMode) # 先发送一些期望位置让飞控有初步的设定点 pose PoseStamped() pose.pose.position.x 1.0 pose.pose.position.y 1.0 pose.pose.position.z 2.0 rate rospy.Rate(10) # 10Hz for _ in range(100): pose_pub.publish(pose) rate.sleep() while not rospy.is_shutdown(): if not state.armed: arming_client(True) if state.mode ! OFFBOARD: set_mode_client(0, OFFBOARD) pose_pub.publish(pose) rate.sleep()运行这个脚本后如果一切正常无人机会起飞飞到(1, 1, 2)的位置。这是PX4仿真环境搭建成功的最有力证明。5. 常见问题与排查技巧实录5.1 编译报错排查从日志中找到关键信息PX4编译失败时终端会打印一大段错误信息新手往往看得一头雾水。我的经验是先找error:关键词它会直接告诉你哪个文件哪一行出了问题。最常见的编译错误有几类缺依赖提示找不到某个头文件或库文件比如fatal error: xxx.h: No such file or directory。这种问题最简单去网上搜索这个头文件属于哪个包然后apt install即可。比如rapidjson相关的头文件缺失就执行sudo apt install rapidjson-dev。CMake配置错误提示CMake Error说找不到某个包。这就需要检查你是否漏装了对应的开发包。例如提示找不到GStreamer就安装libgstreamer1.0-dev和libgstreamer-plugins-base1.0-dev。编译缓存冲突如果你之前用旧版本的PX4编译过再次编译新版本时可能因为build目录下的缓存文件导致错误。解决办法是删除build/px4_sitl_default目录重新编译。我把这几个问题整理成一个速查表方便你对照排查。报错关键词可能的根因解决办法No such file or directory缺少某个头文件使用apt-file search或搜索工具查找所属包并安装Could not find a package configuration fileCMake无法找到依赖包检查该包的-dev版本是否安装undefined reference库文件未链接检查CMakeLists.txt中是否有对应的target_link_librariesinternal compiler error内存不足或编译线程过多减少-j参数或增加系统内存还有一个很隐蔽的问题如果你在编译过程中切换了PX4的git分支但没有清理之前的编译缓存偶尔会出现“修改不生效”的诡异问题。解决方法永远是那三板斧删掉build目录、重新source环境变量、重新编译。5.2 Gazebo启动失败与模型加载不出来的处理Gazebo启动异常是仿真环境搭建中最常见的问题。正常情况下PX4启动后Gazebo会自动打开里面有一架无人机模型盘旋也就是在地面上微微震动。如果你看到Gazebo窗口打开了但里面是空白世界没有无人机模型或者模型显示为灰色/半透明这通常是模型加载路径有问题。检查方法单独开一个终端手动执行模型加载命令source Tools/setup_gazebo.bash $(pwd) $(pwd)/build/px4_sitl_default echo $GAZEBO_MODEL_PATH如果$GAZEBO_MODEL_PATH为空说明环境变量没有生效。你需要确认启动PX4的那个终端是否执行过source命令。记住每次新开终端都要先source环境变量再启动仿真否则Gazebo不会加载模型。还有一种情况是Gazebo启动后卡死窗口不刷新无人机模型没出现。如果是虚拟机这个问题尤其常见——Gazebo依赖OpenGL渲染虚拟机默认的显卡驱动性能极差。如果你用的是VMware或VirtualBox建议在虚拟机设置里启用3D加速或者安装VMware Tools/增强功能。如果还是卡死可以在Gazebo启动前设置环境变量禁用GPU渲染export LIBGL_ALWAYS_SOFTWARE1这会强制使用软件渲染画面会卡一些但至少不会崩溃。5.3 QGroundControl连接不上仿真的排查思路QGroundControl无法连接仿真进程的表现是地面站右上角一直显示“未连接”没有无人机状态信息。这种情况通常有以下几个原因。第一PX4 SITL进程没启动成功。检查PX4源码目录的终端看看是否有pxh提示符。如果PX4进程根本没启动QGroundControl自然连不上。第二PX4进程和QGroundControl的通信端口被防火墙拦截。Ubuntu的ufw防火墙默认不拦截UDP广播但如果你手动配置过防火墙规则可能会屏蔽14550端口。执行sudo ufw disable暂时关闭防火墙试试。第三QGroundControl版本太新对MAVLink协议版本的要求与PX4固件不兼容。建议下载QGroundControl的稳定版不要用最新的Daily版本。如果以上都排除了可以在终端里看一下PX4启动时是否有MAVLink相关的输出。正常情况下启动仿真后终端会显示类似MAVLink: creating UDP server的日志后面会带上端口号。如果你看到端口号不是14550说明PX4配置不同QGroundControl连接时也要调整端口。5.4 MAVROS连不上飞控的检查清单MAVROS连接不上时先运行rostopic echo /mavros/state看看数据有没有进来。如果这个topic没有输出说明MAVROS到PX4的链路不通。这时候按照这个顺序排查第一步确认PX4仿真进程在运行。第二步确认MAVROS启动命令中fcu_url的端口号和PX4监听的端口号一致。第三步确认MAVROS的UDP端口没有被占用netstat -ulnp | grep 14540能看到MAVROS进程监听在14540端口。第四步检查ROS主节点是否启动——如果你是用roscore方式启动的MAVROS要先执行roscore再启动MAVROS节点。如果你是用roslaunch启动的它会自动启动ROS Master不用手动执行roscore。这里有个容易忽略的坑MAVROS启动时对PX4的MAVLink协议版本有要求。PX4 v1.14默认使用MAVLink 2.0而MAVROS也同时支持1.0和2.0正常情况下两者能自动协商。但如果你在源码中手动改过MAV_PROTO_VER参数可能会造成版本不匹配导致MAVROS能连接到端口但收不到有效数据。检查QGroundControl中该参数的值确保是2。5.5 我踩过的一个隐蔽的坑编译子模块版本不匹配最后分享一个我在第二次搭建时踩过的坑这个坑在网上的教程里几乎没人提到。PX4源码使用git子模块来管理Gazebo相关插件但子模块的版本可能和主仓库存放的版本不一致。如果你直接克隆的是v1.14.3标签然后执行子模块更新此时子模块的版本会自动切换到与该标签匹配的版本一般不会出问题。但如果你之前在main分支上更新过子模块再切换回稳定分支子模块并不会自动变回旧版本仍然是main分支对应版本。这时候编译期间可能出现莫名其妙的功能变化比如某个传感器模型的参数不对或者Gazebo启动时加载了一个新版本插件。解决办法很简单切换分支后强制更新子模块到分支对应的版本git submodule sync --recursive git submodule update --init --recursive --force--force会强制覆盖本地子模块的修改确保和远端版本完全一致。这个坑的排查方法也很简单进入Tools/sitl_gazebo目录执行git status如果显示有修改或不在预期的分支说明子模块版本有问题强制更新即可。6. 一点真实感受第二次搭建带来的认知提升第二次搭建PX4仿真环境明显比第一次从容了很多。这不仅仅是技术熟练度的问题更关键的是对整套系统有了更清晰的认识——我知道Gazebo挂了应该去哪里查模型路径知道MAVROS连不上应该先看端口而不是重装系统知道编译失败时最该做的是看日志关键词而不是满屏复制给搜索引擎。这种“知道去哪里找问题”的掌控感才是搭建过程中最宝贵的收获。如果让我给正准备入门的读者一个建议我想说的是不要害怕报错。PX4仿真环境的报错信息虽然多但绝大多数错误都是可以追根溯源的。碰到问题时先打开一个新的终端敲几下echo $环境变量看看路径对不对或者去PX4源码目录的build目录下找CMakeCache.txt看看有没有残留配置。这种“自己动手查一层”的习惯比任何现成教程都能让你更快地成长。最后分享一个小技巧在你的Ubuntu主目录下创建一个别名文件把常用的source和启动命令写成一条自定义命令比如alias px4_sitlcd ~/PX4-Autopilot source Tools/setup_gazebo.bash $(pwd) $(pwd)/build/px4_sitl_default make px4_sitl gazebo-classic这样每次打开新终端只需输入px4_sitl就能一键启动仿真环境不再需要忍受一长串命令和反复记忆路径。这个小改动能让你把更多精力放在飞控逻辑本身而不是环境搭建上。