☰
PX4+Gazebo+ROS 2仿真环境搭建:XRCE-DDS通信与Offboard控制实战
2026/10/3 6:52:53 网站建设 项目流程

1. 从零搭建 PX4 仿真链路:为什么这套组合值得折腾

搞无人机飞控开发的人,绕不开 PX4 这套开源栈。但很多人卡在第一步——环境搭不起来。我见过太多人兴冲冲装了 PX4,编译报错、Gazebo 黑屏、QGC 连不上、Offboard 模式死活切不进去,折腾三天直接放弃。这不是个别现象,是这条工具链本身的复杂度决定的。

PX4 负责飞控核心逻辑,Gazebo 提供物理仿真环境,XRCE-DDS 打通 PX4 和 ROS 2 之间的数据通道,QGC 做地面站监控和指令下发。四个组件各司其职,但版本兼容性、网络配置、编译选项任何一个环节出问题,整条链路就断。这套组合的核心价值在于:你不需要真机就能验证飞控算法、测试 Offboard 控制逻辑、跑通 ROS 2 与飞控的通信。对于做无人机自主导航、集群控制、视觉抓取的人来说,这是最低成本的开发验证方案。

适合谁看?如果你正在做 PX4 二次开发、ROS 2 与飞控通信、Offboard 模式控制,或者单纯想把仿真环境跑起来做算法验证,这篇内容能帮你省掉大量试错时间。我下面会按实际搭建顺序,把每个环节的关键配置、踩坑点和排查方法讲清楚。

2. 环境准备与版本选型:别一上来就追新

2.1 操作系统与基础依赖

Ubuntu 22.04 是目前最稳的选择。Ubuntu 20.04 也能用,但 ROS 2 Humble 官方支持到 22.04,后续装 Gazebo 和 ros_gz 桥接会省很多事。我试过在 20.04 上硬装 Humble,依赖冲突搞得头皮发麻,最后重装系统。所以如果你还没装系统,直接上 22.04。

基础依赖一次性装齐:

sudo apt update sudo apt install -y git cmake build-essential ninja-build \ python3-pip python3-venv python3-dev \ libeigen3-dev libopencv-dev \ protobuf-compiler libprotobuf-dev \ libxml2-dev libtinyxml2-dev \ libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ lsb-release gnupg curl wget

这些包看着多,但缺一个后面编译就可能报奇怪的错。特别是protobuf-compiler和libeigen3-dev,PX4 编译和 Gazebo 插件都依赖它们。

2.2 PX4 版本选择:稳定优先

PX4 的版本迭代很快,main 分支经常有未测试的改动。我建议用稳定 release 分支,比如v1.14.x或v1.15.x。这两个版本对 XRCE-DDS 和 Gazebo 的支持都比较成熟。

git clone https://github.com/PX4/PX4-Autopilot.git --recursive cd PX4-Autopilot git checkout v1.14.3 git submodule update --init --recursive

--recursive一定要加,PX4 依赖大量子模块,漏了后面编译必挂。如果克隆过程中子模块下载失败,多试几次或者单独git submodule update --init --recursive。

2.3 Gazebo 版本:Garden 还是 Fortress

Gazebo 现在有两个主要版本线:Classic(gazebo11)和 Ignition/Gazebo Sim(Fortress、Garden)。PX4 v1.14 默认支持 Gazebo Classic 和 Gazebo Sim 两种。我实测下来,Gazebo Garden 配合 PX4 v1.14 的gz桥接更顺,但如果你要用 ros_gz 做 ROS 2 桥接,Fortress 的文档和社区案例更多。

装 Gazebo Garden:

sudo wget https://packages.osrfoundation.org/gazebo.gpg -O /usr/share/keyrings/pkgs-osrf-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] http://packages.osrfoundation.org/gazebo/ubuntu-stable $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/gazebo-stable.list > /dev/null sudo apt update sudo apt install -y gz-garden

装完后gz sim --version能输出版本号就说明 OK。如果报共享库缺失,大概率是libignition系列包没装全,sudo apt install -y libignition-*补一下。

2.4 XRCE-DDS Agent 安装

XRCE-DDS 是 PX4 和 ROS 2 之间的桥梁。PX4 端内置了 XRCE-DDS Client,你只需要在主机上跑 Agent。

git clone https://github.com/eProsima/Micro-XRCE-DDS-Agent.git cd Micro-XRCE-DDS-Agent mkdir build && cd build cmake .. make -j$(nproc) sudo make install sudo ldconfig /usr/local/lib/

装完后MicroXRCEAgent --help能输出帮助信息就对了。注意sudo ldconfig这步别漏,否则运行时找不到动态库。

2.5 QGC 安装

QGC 直接下 AppImage 最省事:

wget https://d176tv9ibo4jno.cloudfront.net/latest/QGroundControl.AppImage chmod +x QGroundControl.AppImage ./QGroundControl.AppImage

如果 AppImage 跑不起来,检查 FUSE 是否安装:sudo apt install -y libfuse2。Ubuntu 22.04 默认没有 libfuse2,这个坑很常见。

3. 编译与首次仿真启动:把飞机飞起来

3.1 PX4 编译配置

进入 PX4 目录,先跑一次环境检查:

cd PX4-Autopilot make px4_sitl gazebo-classic

如果你用的是 Gazebo Garden,命令是:

make px4_sitl gz_x500

第一次编译会下载大量依赖,时间取决于网速,半小时到两小时都正常。编译过程中如果卡在某个子模块下载,可以手动git submodule update --init --recursive后重试。

编译成功后会自动启动 Gazebo 和 PX4 SITL,你能看到一架 X500 无人机停在跑道上。这时候 QGC 应该能自动识别到 UDP 连接,显示飞行器已连接。

3.2 QGC 连接配置

QGC 默认监听 UDP 14550 端口。PX4 SITL 启动后会自动往这个端口发心跳。如果 QGC 没反应,检查:

  • QGC 的 Application Settings -> Comm Links 里是否有 UDP 监听
  • 防火墙是否拦了 14550
  • PX4 启动日志里是否有INFO [mavlink] mode: Normal, data rate: 1000000 B/s on udp port 14550

手动添加连接:Comm Links -> Add -> UDP -> 端口 14550 -> 确定。

3.3 首次解锁与起飞

在 QGC 里点 "Takeoff" 之前,先确认 GPS 状态。仿真环境里 GPS 是模拟的,一般几秒就能定位。如果显示 "No GPS",检查 Gazebo 里是否加载了 GPS 插件。

解锁流程:QGC 左侧工具栏 -> 安全 -> 滑动解锁。或者用遥控器内八解锁。内八解锁需要设置参数COM_RC_IN_MODE为 1 或 2,具体看你的遥控器配置。仿真里一般用 QGC 直接解锁就行。

起飞后切 Offboard 模式需要先发 setpoint,否则会自动切回 Position 模式。这是 PX4 的安全机制,后面讲 Offboard 时会详细说。

4. XRCE-DDS 打通 ROS 2 通信:让飞控和 ROS 说上话

4.1 启动 XRCE-DDS Agent

PX4 SITL 启动后,XRCE-DDS Client 会尝试连接主机的 8888 端口。你先启动 Agent:

MicroXRCEAgent udp4 -p 8888

看到INFO日志里出现create_client和session established就说明 PX4 和 Agent 连上了。

4.2 验证 ROS 2 话题

确保 ROS 2 Humble 已安装并 source:

source /opt/ros/humble/setup.bash ros2 topic list

你应该能看到/fmu/out/vehicle_odometry、/fmu/out/vehicle_status、/fmu/in/offboard_control_mode等话题。如果看不到,检查:

  • Agent 是否在跑
  • PX4 启动日志里是否有INFO [uxrce_dds_client]相关输出
  • ROS_DOMAIN_ID 是否一致(默认 0)

4.3 px4_msgs 编译

要在 ROS 2 里发 PX4 消息,需要编译px4_msgs:

mkdir -p ~/ws_px4/src cd ~/ws_px4/src git clone https://github.com/PX4/px4_msgs.git cd .. colcon build source install/setup.bash

px4_msgs的版本要和 PX4 固件版本匹配。v1.14 的 PX4 对应px4_msgs的release/1.14分支。版本不匹配会导致消息定义对不上,编译报错。

4.4 Offboard 控制示例

写一个最简单的 Offboard 控制节点:

import rclpy from rclpy.node import Node from px4_msgs.msg import OffboardControlMode, TrajectorySetpoint, VehicleCommand, VehicleStatus from rclpy.qos import QoSProfile, ReliabilityPolicy, HistoryPolicy class OffboardControl(Node): def __init__(self): super().__init__('offboard_control') qos = QoSProfile( reliability=ReliabilityPolicy.BEST_EFFORT, history=HistoryPolicy.KEEP_LAST, depth=1 ) self.offboard_pub = self.create_publisher( OffboardControlMode, '/fmu/in/offboard_control_mode', qos) self.setpoint_pub = self.create_publisher( TrajectorySetpoint, '/fmu/in/trajectory_setpoint', qos) self.cmd_pub = self.create_publisher( VehicleCommand, '/fmu/in/vehicle_command', qos) self.status_sub = self.create_subscription( VehicleStatus, '/fmu/out/vehicle_status', self.status_cb, qos) self.timer = self.create_timer(0.1, self.timer_cb) self.counter = 0 self.nav_state = 0 def status_cb(self, msg): self.nav_state = msg.nav_state def timer_cb(self): self.counter += 1 # 发 OffboardControlMode ocm = OffboardControlMode() ocm.position = True ocm.timestamp = self.get_clock().now().nanoseconds // 1000 self.offboard_pub.publish(ocm) # 发位置 setpoint sp = TrajectorySetpoint() sp.position = [0.0, 0.0, -5.0] # NED 坐标,-5 米高度 sp.yaw = 0.0 sp.timestamp = self.get_clock().now().nanoseconds // 1000 self.setpoint_pub.publish(sp) # 超过 10 次后切 Offboard 并解锁 if self.counter == 10: self.send_command(VehicleCommand.VEHICLE_CMD_DO_SET_MODE, 1, 6) self.send_command(VehicleCommand.VEHICLE_CMD_COMPONENT_ARM_DISARM, 1, 0) def send_command(self, command, param1, param2): cmd = VehicleCommand() cmd.command = command cmd.param1 = float(param1) cmd.param2 = float(param2) cmd.target_system = 1 cmd.target_component = 1 cmd.source_system = 1 cmd.source_component = 1 cmd.from_external = True cmd.timestamp = self.get_clock().now().nanoseconds // 1000 self.cmd_pub.publish(cmd) def main(): rclpy.init() node = OffboardControl() rclpy.spin(node) rclpy.shutdown() if __name__ == '__main__': main()

这个节点做了三件事:持续发 OffboardControlMode 和 TrajectorySetpoint,10 个周期后切 Offboard 模式并解锁。注意timestamp单位是微秒,用nanoseconds // 1000转换。

4.5 关键参数与注意事项

Offboard 模式有几个参数必须注意:

参数默认值说明
COM_OBL_RC_ACT0Offboard 丢失时的动作,0 是 Position 模式
COM_RC_IN_MODE0遥控器输入模式,仿真里可以设 1 禁用 RC
NAV_RCL_ACT2RC 丢失动作,2 是 Land
COM_OF_LOSS_T1.0Offboard 丢失超时时间,秒

注意:Offboard 模式下如果 setpoint 发送频率低于 2Hz,PX4 会触发 failsafe 切回 Position 模式。所以定时器周期要小于 0.5 秒,我一般用 0.1 秒。

5. Gazebo 仿真环境深度配置:模型、传感器与场景

5.1 加载自定义模型

PX4 自带的 X500 模型在Tools/sitl_gazebo/models下。要加自定义模型,把模型文件夹放到~/.gazebo/models或者 PX4 的模型路径下。

模型文件夹结构:

my_drone/ ├── model.config ├── model.sdf └── meshes/ └── body.stl

model.config定义模型元信息,model.sdf定义物理属性和插件。传感器插件在 SDF 里配置,比如加一个深度相机:

<sensor name="depth_camera" type="depth"> <update_rate>30</update_rate> <camera> <horizontal_fov>1.047</horizontal_fov> <image> <width>640</width> <height>480</height> </image> <clip> <near>0.1</near> <far>10.0</far> </clip> </camera> <plugin name="depth_camera_controller" filename="libgazebo_ros_camera.so"> <ros> <namespace>/camera</namespace> </ros> <frame_name>depth_camera_link</frame_name> </plugin> </sensor>

5.2 场景搭建与保存

Gazebo 里可以直接拖拽模型搭场景,但保存时经常卡死。我踩过几次坑,总结下来:

  • 保存前先暂停仿真(点暂停按钮)
  • 场景文件不要太大,模型数量控制在 50 个以内
  • 用gz model --save命令行保存比 GUI 稳
  • 保存路径不要有中文和空格

场景文件默认在~/.gazebo/worlds下。PX4 启动时用PX4_SITL_WORLD环境变量指定自定义世界:

export PX4_SITL_WORLD=/path/to/my.world make px4_sitl gz_x500

5.3 多机仿真配置

多机仿真需要改 PX4 启动脚本。在ROMFS/px4fmu_common/init.d-posix/下复制rcS文件,修改PX4_SIM_MODEL和MAV_SYS_ID:

# 第一架 PX4_SYS_AUTOSTART=4001 PX4_SIM_MODEL=gz_x500 ./build/px4_sitl_default/bin/px4 -i 0 # 第二架 PX4_SYS_AUTOSTART=4001 PX4_SIM_MODEL=gz_x500 ./build/px4_sitl_default/bin/px4 -i 1

每架飞机的 XRCE-DDS 端口要错开,Agent 也要起多个实例或者用不同端口。QGC 里手动添加多个 UDP 连接,端口分别对应。

6. 常见问题排查与避坑指南

6.1 编译类问题

问题:make px4_sitl报ninja: build stopped: subcommand failed

先看具体报错行。常见原因是子模块没更新全,跑git submodule update --init --recursive。如果是 Python 包缺失,pip3 install -r Tools/setup/requirements.txt。

问题:Gazebo 启动黑屏或闪退

检查显卡驱动。NVIDIA 显卡建议装官方驱动,nvidia-smi能正常输出。如果是虚拟机,开 3D 加速。另外export LIBGL_ALWAYS_SOFTWARE=1可以强制软件渲染,但性能会差很多。

6.2 通信类问题

问题:QGC 连不上 PX4

按顺序检查:PX4 是否在跑 -> 14550 端口是否监听 -> 防火墙 -> QGC 的 Comm Links 配置。netstat -ulnp | grep 14550能看到监听就说明 PX4 端没问题。

问题:ROS 2 看不到/fmu/话题

检查 Agent 是否在跑,ros2 topic list前先source /opt/ros/humble/setup.bash。如果 Agent 日志里有session established但 ROS 2 没话题,可能是px4_msgs没 source,或者 ROS_DOMAIN_ID 不一致。

6.3 Offboard 类问题

问题:切 Offboard 后飞机不动

先确认 setpoint 在发。ros2 topic echo /fmu/in/trajectory_setpoint看有没有数据。如果数据在发但飞机不动,检查 OffboardControlMode 的position字段是否设为 True。另外解锁命令要在切 Offboard 之后发,顺序反了会被拒绝。

问题:Offboard 模式自动退出

大概率是 setpoint 频率不够。PX4 要求 Offboard 模式下 setpoint 更新频率大于 2Hz。把定时器周期改成 0.1 秒试试。另外检查COM_OF_LOSS_T参数,默认 1 秒,可以适当调大。

6.4 常见问题速查表

现象可能原因排查方法
QGC 无连接端口未监听netstat -ulnp | grep 14550
ROS 2 无话题Agent 未启动检查MicroXRCEAgent进程
Offboard 切不进setpoint 未发ros2 topic echo确认
Gazebo 黑屏显卡驱动问题nvidia-smi检查
编译失败子模块缺失git submodule update
多机冲突端口重复每架飞机用不同-i编号

提示:所有排查都遵循"从底层往上"的顺序——先确认进程在跑,再确认端口通,最后确认数据格式对。跳步排查容易绕弯路。

7. 从仿真到真机的过渡经验

仿真跑通不代表真机就能飞。我踩过的坑里,最典型的是仿真里 Offboard 切得好好的,真机上一切 Offboard 就触发 failsafe。原因是真机的 RC 信号和仿真不一样,COM_RC_IN_MODE参数要重新调。仿真里可以设 1 禁用 RC,真机上必须保留 RC 作为紧急接管手段。

另一个坑是坐标系。Gazebo 用 ENU,PX4 内部用 NED,ROS 2 默认也是 ENU。px4_msgs里的消息用的是 NED,所以你在 ROS 2 里发 setpoint 时要注意 z 轴方向。我一开始没注意,发了个正的 z 值,飞机直接往地下钻。

真机测试前,建议先在仿真里把所有 Offboard 逻辑跑至少 20 遍,确认没有偶发的模式切换失败。真机上第一次切 Offboard 一定要在开阔场地,手放在遥控器上随时准备接管。

这套环境搭一次大概需要半天到一天,主要时间花在编译和下载依赖上。搭好之后建议做个系统镜像或者记录完整的安装命令,换机器时能快速复现。我现在用的是 Ubuntu 22.04 + PX4 v1.14.3 + Gazebo Garden + ROS 2 Humble + XRCE-DDS Agent 这套组合,跑了几个月没出过大问题,推荐给同样在做飞控开发的朋友。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询