搞无人机仿真开发最难的不是某一个软件装不上而是把PX4、Gazebo、XRCE-DDS、QGC这一整套链路串起来那一刻的各种坑。我前前后后折腾了小一个月从环境搭建到联调完毕踩过的坑比飞过的航线都多。这篇文章就是把这套完整流程从头到尾捋一遍把我在实际配置中验证过的步骤、参数和排查思路全部写出来希望对正在搭环境或者卡在通信问题的朋友有直接帮助。先说清楚这套工具链是干什么的。PX4是开源飞控固件Gazebo是3D物理仿真环境XRCE-DDS是PX4和外部DDS通信网络的桥梁QGC是跨平台地面站。四者组合在一起就是一套完整的仿真飞机通信中间件地面控制闭环你在Gazebo里看到一架多旋翼模型它身上跑的是真正的PX4代码QGC能看到它的状态、发指令外部ROS2节点也能通过DDS获取数据。这套环境特别适合做四件事飞控算法验证、视觉导航开发、集群编队预研、以及新手的飞行安全练习——毕竟在仿真里炸机不花钱。要说这套组合的优点用一个字概括就是真。PX4 SITLSoftware In The Loop跑的是实机固件编译出来的Gazebo相关的东西不是简化模型XRCE-DDS又是工业级通信协议QGC更是PX4官方钦定的地面站。整条链路都贴近真实系统你在这套环境里跑通的参数和逻辑迁移到真机上不会有太大偏差。这也是我推荐新手直接从这套组合入手的原因——与其在单纯的理论仿真里自娱自乐不如一步到位接触真实工具的交互逻辑。1. 一套工具链的架构拆解谁在说话谁在听1.1 四个组件的角色分配很多朋友一开始容易犯迷糊PX4和Gazebo之间到底谁控制谁XRCE-DDS又是插在哪里的我打个比方你就懂了。PX4是大脑负责飞控解算、姿态控制、导航逻辑它输出的是控制指令和状态量。Gazebo是身体加感官负责模拟物理世界重力、风阻、碰撞、GPS信号、IMU读数它把传感器数据喂给大脑同时接收大脑的控制指令来更新飞机的位姿。QGC是操作台通过MAVLink协议和PX4对话给你看遥测数据、发解锁指令、规划任务。XRCE-DDS则是翻译员把PX4内部数据转换成标准DDS话题让ROS2节点能订阅和发布——毕竟现代无人机开发基本脱离不了ROS生态。在这个架构里数据流是这样走的Gazebo里的传感器仿真模块把IMU、GPS、气压计等数据通过共享内存或UDP发给PX4 SITL进程PX4跑完控制循环把PWM实际是电机转速指令发回Gazebo更新物理模型同时PX4通过MAVLink的UDP 14550端口把遥测推给QGC如果你启用了XRCE-DDS客户端PX4还会把RTPS消息通过UDP 2019端口发给Micro XRCE-DDS AgentAgent再转成DDS话题供ROS2使用。1.2 为什么选择XRCE-DDS而不是直接TCP/UDP这里有个关键问题PX4内部用的是uORB消息总线这是它的原生IPC机制性能很好但只在本进程内有效。ROS2用的是DDS数据分发服务一套完整的发布订阅中间件规范。两者要打通有两个方案一是直接写网桥把uORB的消息转成DDS话题二是采用XRCE-DDSX-Robotics Communication Environment这种轻量级协议在资源受限的飞控上运行客户端在上位机运行Agent天然适配这种嵌入式设备桌面系统的组合。我倾向用XRCE-DDS理由有三第一它是PX4官方主推的DDS桥接方案源码里自带client模块改个参数就能启动不需要自己维护网桥代码第二它的通信开销极低适合飞控这种CPU和内存都紧张的环境第三PX4提供的px4_ros_com库就是基于XRCE-DDS的用ROS2写PX4的外部控制节点时接口是现成的开发效率高很多。2. 环境搭建Ubuntu 22.04 ROS2 Humble 的完整准备工作2.1 基础环境与依赖安装我建议直接用Ubuntu 22.04 LTS配ROS2 Humble这是目前兼容性最稳的组合。别去追新用Ubuntu 24.04PX4编译链和Gazebo插件在那上面的兼容问题会让你怀疑人生也别守着Ubuntu 20.04ROS2 Foxy开始逐渐过时很多新功能包不再维护。先把系统基础包装齐注意这些依赖一个都不能少缺了后面编译必报错sudo apt update sudo apt upgrade -y sudo apt install -y \ git zip cmake build-essential genromfs ninja-build exiftool \ python3-pip python3-dev python3-venv \ protobuf-compiler libeigen3-dev libopencv-dev \ libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ libjson-c-dev libwebsockets-dev libtinyxml2-dev \ libgazebo11-dev sudo apt install -y \ ros-humble-desktop \ ros-humble-rmw-fastrtps-cpp装完记得初始化ROS2环境在~/.bashrc里加上source /opt/ros/humble/setup.bash另外强烈建议把tini装上PX4官方脚本会用它是做进程保活的缺了的话运行make px4_sitl可能会报exec format error之类的诡异错误sudo apt install -y tiniGazebo这边PX4现在默认用Gazebo Classic以前叫Gazebo 11虽然新版的Gazebo Ignition也支持PX4但社区生态和插件兼容性我更信任Classic。Ubuntu 22.04上装Gazebo 11很简单用官方源sudo apt install -y gazebo11 libgazebo11-dev装完验证一下gazebo --version能看到版本号就行。打不开图形界面的话检查一下~/.bashrc里有没有export LIBGL_ALWAYS_SOFTWARE1这种显卡兼容设置在虚拟机里常有这个需求。2.2 编译PX4 AutoPilot源码源码一定要放到~/PX4-Autopilot这个固定路径因为很多脚本和工具链写死了相对路径挪位置会出事cd ~ git clone https://github.com/PX4/PX4-Autopilot.git --recursive cd PX4-Autopilot git checkout v1.14.3我强烈建议你checkout到一个明确的release版本不要用默认的main分支。因为main分支一直在变今天的代码和明天的API可能就不一样教程里写的参数不一定对得上。v1.14.3是我试过最稳的版本文档全、教程多、ROS2接口完整。然后运行PX4官方提供的子模块初始化脚本make init_submodules这步会把所有依赖的子模块拉下来包括src/modules/mavlink、Tools/simulation/gazebo-classic等耗时比较长耐心等。接着安装Python依赖pip3 install --user -r Tools/setup/requirements.txt最后编译SITL不带硬件相关的固件目标make px4_sitl gazebo-classic第一次编译时间大约20-40分钟取决于机器配置。编译完成后你会看到终端停在PX4 shell的提示符上类似pxh。如果你的机器内存只有8G建议关掉其它应用再编译否则容易OOM。16G或以上就随便折腾了。2.3 安装QGC地面站并解决连接依赖QGC从官网下载Linux版AppImage就行了但有两个细节必须注意。第一AppImage需要给它执行权限chmod x QGroundControl.AppImage第二QGC需要配置串口访问权限和USB规则否则它识别不到设备。虽然仿真场景我们用不着真实USB但QGC和我们要用的MAVLink UDP通信依赖系统网络环境也一样得把用户加入dialout组防止权限错乱sudo usermod -a -G dialout $USER然后写一个默认启动配置把MAVLink的UDP端口固定下来。我通常用14550作为QGC接收遥测的端口PX4 SITL默认也是往这个端口发不需要额外配置。启动QGC./QGroundControl.AppImage首次启动会自动检测MAVLink设备包括UDP 14550。如果你看到一个正在连接的提示说明QGC已经嗅探到PX4 SITL发来的心跳包了。3. XRCE-DDS的通信原理从PX4到ROS2的一生3.1 先搞明白RTPS是什么再谈配置XRCE-DDS本质上实现了RTPSReal-Time Publish-Subscribe协议的子集。RTPS是DDS的底层线缆协议定义了数据怎么在网络上传输、怎么发现彼此、怎么保持可靠性。PX4端跑了一个Micro XRCE-DDS Client这个客户端资源占用极小通过UDP把序列化后的uORB消息发送给Agent。Agent这边Micro XRCE-DDS Agent是一个运行在桌面系统通常是ROS2机器上的独立进程它接收客户端的消息然后转换成真正的DDS-RTPS消息发布到ROS2的DDS域里。反向也一样ROS2节点发布的DDS消息Agent收到后转成XRCE协议发给PX4 ClientClient再反序列化成uORB消息喂给飞控模块。这个过程里PX4开发团队提供了一份UORB到ROS2消息的映射表比如vehicle_attitude对应px4_msgs/msg/VehicleAttitudevehicle_local_position对应px4_msgs/msg/VehicleLocalPosition。你在ROS2端订阅话题时话题名通常带有/fmu/out/前缀比如/fmu/out/vehicle_attitude。想要反向控制飞控往/fmu/in/offboard_controlmode这类话题发消息就行。3.2 安装并启动Micro XRCE-DDS AgentAgent的安装有两种方式源码编译或者用PX4官方做的静态链接二进制。我建议直接用源码编译因为版本匹配问题少出了问题也容易排查cd ~ git clone https://github.com/eProsima/Micro-XRCE-DDS-Agent.git cd Micro-XRCE-DDS-Agent mkdir build cd build cmake .. make sudo make install编译完成后Agent可执行文件会安装在/usr/local/bin下。启动Agent监听UDP端口2019MicroXRCEAgent udp4 -p 2019看到类似Serial: connection ... open和UDP: connection ... open的输出就正常了。注意这个终端窗口要保持开着一旦关了AgentPX4的RTPS桥接就失效了。3.3 在PX4端启用XRCE-DDS客户端PX4源码里已经集成了XRCE-DDS客户端的模块不用单独编译。只需要在PX4 shell里设置几个参数。如果你在编译完make px4_sitl gazebo-classic后已经进入了pxhshell直接敲param set xrce_dds_udp_port 2019 param set xrce_dds_udp_remote_ip 127.0.0.1 param set xrce_dds_udp_local_ip 127.0.0.1然后重启XRCE-DDS模块xrce_dds start或者干脆重启整个SITL让参数生效。这里最坑的是IP地址的选择如果是本机联调全都用127.0.0.1没问题如果你把Gazebo跑在台式机上Agent跑在笔记本上或者ROS2机器和PX4不在同一台机那xrce_dds_udp_remote_ip必须填Agent所在主机的IP不能想当然用回环地址。验证方法也简单。运行Agent的终端里如果刷出类似[Agent] Client connected ...的日志就说明PX4的Client已经和Agent握手成功了。此时你再用ros2 topic list就能看到一大票/fmu/out/的话题冒出来。4. 联调实操把PX4 SITL、Gazebo、QGC、DDS串成闭环4.1 启动顺序与准备工作这套系统对启动顺序其实没那么苛刻几个进程同时拉起也能跑通但为了排查问题方便我习惯按依赖关系依次启动先启动Micro XRCE-DDS Agent监听UDP 2019再启动QGC让它监听UDP 14550然后启动PX4 SITL加Gazebo仿真这是整个系统的心脏最后用ros2 topic list验证DDS链路是否通畅启动PX4 SITL时直接跑cd ~/PX4-Autopilot make px4_sitl gazebo-classic终端会出现pxh提示符同时会弹出一个Gazebo窗口里面就是那架经典的IRIS四旋翼。这时候PX4会自己往QGC发心跳包几秒后QGC界面右上角应该显示一条绿色的MAVLink连接飞机状态从未连接变成等待起飞。4.2 QGC里需要调整的关键参数连接上QGC之后先别急着解锁有四个参数必须改。我踩过坑的教训不改这些参数你的仿真飞机会出现各种匪夷所思的行为。第一个是MAV_1_CONFIG把它设置为-1禁用。这个参数默认是-1没问题但有些人会误配成别的值导致QGC收到的遥测数据被二次转发丢包严重。第二个是SYS_MC_EST_GROUP设置成1EKF2。PX4默认就是EKF2不必改动但如果你之前折腾过其它估计器记得复位。第三个是CBRK_IO_CHK如果不是想跳过安全开关保持默认0就行。很多教程让你直接设成162128绕过IO检查我建议你别这么做在仿真里没有Pixhawk硬件IO检查本来就跳过设了反而掩盖问题。第四个是COM_RC_IN_MODE如果你的遥控器没插好或者压根没有RC发射机设置成0RC disabled可以避免PX4因为没收到RC信号而拒绝解锁。在纯仿真场景下我通常用QGC的虚拟摇杆来代替实体遥控器所以这个参数我保持默认然后在QGC里启用虚拟摇杆界面。注意QGC的虚拟摇杆默认是隐藏的需要在设置里的通用选项卡打开虚拟摇杆选项。否则你连解锁按钮都点不动。4.3 解锁方式的坑PX4到底怎么解锁我在前言里提到内八解锁这其实是很多从APM/Pixhawk旧固件转过来的玩家带来的惯性思维。PX4固件没有内八解锁这个说法——它默认依赖安全开关加遥控器解锁通道。在仿真环境里没有安全开关SITL直接跳过IO遥控器又未必接得上所以解锁就变成了一件需要正确配置的事情。最稳的解锁方式是在QGC里直接用虚拟摇杆把油门打到最低然后点起飞按钮。但前提是QGC检测到了RC输入或虚拟摇杆并且PX4认为遥控器已在解锁位置。有些时候你点了解锁状态栏会提示Remote controller not available这说明PX4没收到RC信号。把COM_RC_IN_MODE改成1就允许在无RC信号状态下解锁仅仿真用真机慎改。另外有个参数叫COM_ARMOD控制解锁的方式组合。默认情况下它允许遥控器或地面站解锁无需额外改动。真正会让解锁失败的隐藏坑是NAV_RCL_ACT——如果PX4丢失RC信号后会执行安全动作但RC信号本来就缺失时它可能认为系统处于异常状态拒绝解锁。把COM_RC_IN_MODE调成1后这个隐患就消失了。实际飞行的解锁流程我记得特别清楚先在QGC右上角点点击Arm弹窗确认后等状态栏变为绿色已解锁再推油门。如果你用的是make px4_sitl gazebo-classic启动的IRIS模型它带有起落架解锁后你会看到Gazebo里的旋翼转速变化然后轻推油门飞机就起来了。4.4 验证DDS链路和ROS2节点QGC能控制飞机只说明MAVLink链路是通的XRCE-DDS链路还得单独验证。开一个新终端先确认Agent还在运行然后source /opt/ros/humble/setup.bash ros2 topic list | grep /fmu/out/正常会输出/fmu/out/vehicle_attitude、/fmu/out/vehicle_local_position、/fmu/out/vehicle_status等一大串话题。再订阅一个看看频率ros2 topic hz /fmu/out/vehicle_attitude你应该能看到输出稳定在10Hz左右。这个频率值很关键——如果远低于10Hz说明UDP传输有丢包检查是不是有防火墙在拦截2019端口如果压根没有输出第一嫌疑就是PX4的xrce_dds start没有真正起来或者Agent和Client的端口不匹配。订阅具体内容验证ros2 topic echo /fmu/out/vehicle_attitude --once能看到四元数数组就是完全正常了。这一步通过后你的仿真环境就等于同时具备了MAVLink和DDS双通道后续接px4_ros_com写外部控制、接MoveIt做规划都是水到渠成的事。5. 实战中必然遇到的坑问题排查与解决方案实录5.1 Gazebo窗口黑屏或启动卡死Gazebo启动卡死是最高频问题之一。如果你看到窗口弹出来但画面全黑飞机模型没加载出来十有八九是模型文件下载问题。PX4的Gazebo插件会在首次启动时从模型库下载模型网络不好时就会卡住。解决办法是手动提前下载模型到本地cd ~/.gazebo git clone https://github.com/osrf/gazebo_models.git models然后把~/.gazebo/models路径加到GAZEBO_MODEL_PATH环境变量里export GAZEBO_MODEL_PATH~/.gazebo/models加进~/.bashrc让它永久生效。这样Gazebo就不会在线下载模型了加载速度能快好几倍。如果黑屏外加终端报[Err] [RenderingAPI.cc] Could not create render context这是因为你的显卡OpenGL版本不够或者虚拟机的3D加速没开。在虚拟机上尝试安装mesa-utils然后运行glxinfo确认OpenGL至少是3.3。环境不行的把Gazebo窗口切到Software渲染export LIBGL_ALWAYS_SOFTWARE15.2 QGC连不上仿真飞机QGC右上角一直灰的或者提示No MAVLink device found别急着重装按这个顺序自查。第一步确认PX4 SITL是否真的在跑。看终端是否停在pxh提示符如果退出了MAVLink数据自然没人发。第二步确认端口。QGC监听UDP 14550PX4 SITL默认往127.0.0.1:14550发遥测。但你跑make px4_sitl gazebo-classic时仿真脚本可能自动选了另一个端口。用netstat -ulpn | grep 14550看看有没有进程在收发UDP。第三步用mavlink status命令在pxh shell里查看MAVLink链路状态mavlink status正常会输出UDP 14550 connected之类的信息。如果显示no data说明QGC没在监听这个端口或者QGC和PX4绑定到了不同的网卡上。我还遇到过一个特别隐蔽的问题QGC的AppImage默认会打开系统代理导致UDP数据被劫持到代理端口上。在QGC设置里把网络代理关掉或者在~/.bashrc里导出no_proxy127.0.0.1就能解决。5.3 XRCE-DDS Client连接断开或频率极低DDS链路不稳定的现象通常是ros2 topic hz只输出一两次就停了或者Agent那边报Client Disconnected。这种问题先看日志。Agent终端如果反复出现[Agent] Client disconnected大概率是Agent和Client之间的UDP连接被网络栈断开了。原因可能有三种UDP端口被防火墙屏蔽Agent和PX4绑定的网卡不在同一网段或者是PX4的xrce_dds_udp_local_ip配置错误。还有个常见误解很多人以为xrce_dds_udp_remote_ip填0.0.0.0就行其实这个参数必须填实际IP。本机跑就填127.0.0.1跨机跑就填Agent所在机器的局域网IP。另一种情况是Agent活着但话题频率低只有0.5Hz。我遇到过原因是PX4的RTPS配置中RTPS_DOMAIN_ID不匹配。PX4默认配置的Domain ID是0但如果你在~/.bashrc里设置了ROS_DOMAIN_ID为别的值ROS2节点的DDS域和PX4 Agent发布的域可能不一致导致话题发现异常。排查方法把ROS_DOMAIN_ID设成0重启所有节点再看。5.4 编译PX4时常见的第三方依赖错误编译这事最容易翻车的是geographiclib和pyulog这两个依赖。geographiclib的报错通常是脚本去下载一个Geoids数据包失败。解决办法是手动下载wget https://sourceforge.net/projects/geographiclib/files/distrib/GeographicLib-1.52.tar.gz然后按PX4官方文档里的说明安装到/usr/share/geographiclib。不过现在这样手动装的老版本可能和源码要求的版本不一致我后来是直接装新版本然后改PX4的CMake变量才搞定的。pyulog的报错比较简单直接重装pip3 install --user --upgrade pyulog还有一类报错是gcc版本过新。如果你在Ubuntu 22.04上用了系统默认的GCC 11编译部分老版本PX4会报编译错误。解决办法是安装GCC 10并降低优先级sudo apt install gcc-10 g-10 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 100 sudo update-alternatives --install /usr/bin/g g /usr/bin/g-10 100这个坑在编译v1.13以下版本时尤其常见。6. 扩展玩法与个人经验补充6.1 从单机仿真到集群仿真这套环境跑通之后扩展性是很好的。想要多机仿真PX4官方提供了make px4_sitl gazebo-classic_iris_2这种多机启动方式它会同时拉两个Gazebo模型和两个PX4 SITL实例QGC会同时看到两架飞机。XRCE-DDS在这里有个优势每架飞机的Client都有独立的xrce_dds_udp_port你可以给飞机A用2019飞机B用2020然后分别启动对应的Agent互不干扰。集群编队开发通常就是这么个套路PX4负责每架飞机底层的姿态控制上层编队算法跑在ROS2里靠订阅各机的/fmu/out/vehicle_local_position做队形保持。这套组合可以无缝支撑10架以内的仿真集群。6.2 我个人强烈推荐的几个小技巧最后分享几个我这段时间攒下的心得。技巧一改PX4源码后无需重新编译整个固件。如果你只改了Gazebo里的模型文件或者只改QGC里的参数根本不用跑make px4_sitl改完参数在pxh shell里直接重启对应的模块就行param set COM_ARMOD 0 param reboot技巧二把ros2 launch用起来。手动开一堆终端不是长久之计写一个Python的launch文件同时启动Agent、PX4 SITL、Gazebo和QGC点一次启动全部。PX4官方提供了px4_ros_com的launch模板直接在上面改最省事。技巧三备份Gazebo的~/.gazebo目录。装模型和插件很耗时备份整个目录换机器时直接恢复省得重新下载一堆模型。技巧四用tmux管理终端会话。开六个终端窗口的酸爽你体会过就知道用tmux分屏每个面板里跑一个组件日志不串、关终端不杀进程排查问题效率高一个档次。这套环境我前前后后帮五六个朋友搭过每次都会遇到新的幺蛾子但核心逻辑和排查路径是一样的先确认MAVLink链路再确认平台网络层最后才查DDS中继。只要主机网络不出问题这套组合的稳定性其实比想象中高很多——真正高频率的从入门到放弃大部分发生在安装阶段而非运行阶段。希望这篇教程能帮你少走弯路把精力省下来放在飞行算法与功能开发上。