ROS2 Humble与PX4 v1.14.3完整对接指南:从MicroXRCEAgent到OFFBOARD控制
我们直接进入正题。这篇是写给真正想把ROS2和PX4跑通、而不是停留在PPT层面的开发者。我假设你已经接触过Ubuntu知道终端怎么开也大概知道PX4和ROS2分别是什么。如果你对ROS2的几个发行版还分不清没关系这一篇从选型讲起。1. Humble、Iron、Jazzy、Rolling到底选哪个我现在打开电脑Ubuntu 22.04装的是ROS2 Humble配的是PX4 v1.14.3的源码。实话说我推荐新手直接选这个组合不是因为Humble功能最强而是因为Humble是当前ROS2 LTS发行版里生态最成熟、教程最多、踩坑记录最全的版本。Iron是非LTS版Jazzy是2024年发布的新LTSRolling则是滚动版天天变。这几个版本的差异用一句话概括Humble保守但稳Iron激进但边缘Jazzy新特性多但生态还在长Rolling是给开发者的试验田。具体到PX4对接场景我的建议是这样的发行版支持周期PX4对接成熟度适用场景Humble2027年LTS很高文档和源码示例多绝大多数开发、课程、毕业设计、产品原型Iron已结束短支持一般可跑通但记录少特定依赖需求不推荐新人Jazzy2030年LTS逐步完善PX4官方还未完全默认支持追新想提前适配新APIRolling滚动更新不推荐想持续适配ROS2上游变化的开发者有一个关键点PX4的官方ROS2接口px4_ros_com、px4_msgs针对的是Humble。也就是说当你用Humble时px4_msgs、px4_ros_com这些包可以直接编译按官方README就能跑通。换到Jazzy之后我实际试验过需要手动处理一些依赖改名问题比如python3-vcstool的依赖链、rclpy接口的小变化虽然能修但是没必要在入门阶段给自己加这个难度。所以我这篇所有实操步骤都以Ubuntu 22.04 ROS2 Humble为例。如果你用的是Jazzy步骤里我会特别标注需要改动的地方但那样的话你就要同时调试PX4和ROS2两边的兼容问题我的建议是不要这么折磨自己。版本确定的另一个好处是PX4固件、QGroundControl、Gazebo仿真这几个配套工具之间的兼容关系也基本定下来了。比如说PX4 v1.14.3对ROS2的支持就很稳v1.15系列开始有较大重构但是相关的教程和工具链反而不够全。2. PX4和ROS2之间的本质关系很多教程上来就先跑MicroXRCEAgent然后ros2 topic list看到一堆话题就以为“对接成功”了。但如果你不理解底层原理后面遇到问题多半会懵。我花点篇幅讲清楚。PX4内部用的是uORB——一种轻量级的发布订阅机制。uORB消息在PX4固件内部流转比如vehicle_attitude、vehicle_local_position这些都是uORB主题。ROS2用的是DDS——分布式中件有自己的话题和服务机制。两者语言不同、协议不同、概念也不同。PX4的/fmu/out/vehicle_attitude话题和ROS2的/fmu/out/vehicle_attitude看似同名其实不是一个系统里的东西。把两者连起来的机制是XRCE-DDSX-Robotics Communication Extension旧称Micro XRCE-DDS。PX4里面跑了一个XRCE客户端microdds_clientPC端跑了一个MicroXRCEAgent这个Agent就是桥接的一端。PX4的uORB主题如果被配置为“通过RTPS桥接出去”那么数据就会从PX4的uORB封装成RTPS标准的DDS消息通过UDP或者串口发给Agent再由Agent注入到ROS2的DDS网络里。你可以把这个机制理解成一个翻译网关PX4说中文uORBROS2说英文DDSAgent就是那个同声传译。你不需要在两个系统里分别订阅、分别处理只需要在ROS2侧订阅从Agent翻译出来的英文话题就行。默认情况下PX4会通过配置文件决定哪些uORB主题被翻译出去。这个文件在PX4源码里的msg/tools/urtps_bridge_topics.yaml。打开看一下你就会明白为什么ROS2里只能看到固定那些话题而不是PX4内部所有uORB主题——因为默认配置文件限制了路由范围。我再补一个点ROS2侧不只是多了一个消息通道PX4还通过这个机制支持了服务调用和动作调用。比如OFFBOARD模式的切换是通过ROS2的服务或者动作消息实现的不是简单往某个话题里发数据就行。这一点在下一章会重点讲。3. 环境配置Ubuntu、ROS2 Humble、PX4源码一次到位这里我直接给出我现在机器上验证过的稳定方案。网上教程很多但有些讲的版本太旧有些没标注依赖版本有些直接把系统搞崩了。我的这台测试机是Ubuntu 22.04.3装了ROS2 Humble固件源码是PX4-Autopilot v1.14.3Gazebo用的是经典版不是Ignition。3.1 ROS2 Humble安装的关键动作ROS2 Humble官方的安装方式是加ros2apt源用apt装。我用的是ros-humble-desktop完整版因为包含了Rviz2、gazebo、demo等一整套工具。安装命令很常规但有两个细节容易出问题第一必须正确设置locale。官方文档里那串locale命令不是走形式是真实会影响编译和运行。我见过至少三个人跳过这步后面跑ros2命令报编码错误。第二source的顺序。source /opt/ros/humble/setup.bash之后再把PX4的、自己工作空间的setup.bash按顺序source。很多人把顺序搞反结果找包找不到或者找到了旧版本。3.2 PX4固件源码克隆与子模块处理PX4的源码不能直接git clone完就用它的子模块非常多。正确做法是git clone --recursive https://github.com/PX4/PX4-Autopilot.git cd PX4-Autopilot git checkout v1.14.3 git submodule update --init --recursive如果克隆中途失败最常见的原因是网络问题导致子模块拉取不完整。这时不要慌进入源码目录后重新执行git submodule update --init --recursive它会自动补拉缺失的子模块。但如果你用的是国内网络这一这步可能要等很久我劝你耐心点不要中途CtrlC。PX4编译依赖需要装一堆工具链bash ./PX4-Autopilot/Tools/setup/ubuntu.sh这个脚本会自动装好ARM交叉编译器、Python依赖、Gazebo等。我提醒一下运行这个脚本之前系统里最好已经装了ROS2 Humble否则后面仿真和通信会缺依赖。3.3 编译固件和启动仿真编译PX4的SITL软件在环固件cd PX4-Autopilot make px4_sitl gazebo-classic这一行的意思是用SITL模式编译用gazebo-classic作为仿真环境。编译至少要等10到20分钟取决于你的机器。我用的是一台8核16线程的机器第一次编译大概花了15分钟。如果中途报错先看是不是子模块没更新完其次看是不是系统的Python库版本不兼容。启动仿真make px4_sitl gazebo-classic这条命令会同时启动Gazebo和PX4。注意启动后你会看到一个PX4 shell。在这个shell里可以输入commander takeoff之类的命令测试PX4是否工作正常。到这里PX4的仿真端就算跑起来了。接下来要处理的是怎么让ROS2和它通信。4. MicroXRCEAgent让ROS2看见PX4的“翻译官”这是整个对接流程里非常关键的一步错过这一步就算PX4仿真在跑、ROS2也在跑两边依然谁也看不见谁。4.1 Agent的安装与运行方式MicroXRCEAgent的安装方式有两种二进制安装和源码编译。二进制安装很简单sudo apt install ros-humble-microxrcedds-agent ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888udp4表示用IPv4的UDP通道端口默认8888。PX4那边默认的RTPS端口也是8888所以这样就能接上。如果是源码编译需要单独克隆Micro-XRCE-DDS-Agent仓库然后用colcon编译。我的建议是直接用apt源装省时省力。但要注意有些老教程还在用micro-ros-agent这个包名在Humble上已经改名了你搜micro_ros_agent才对。因为之前的热搜词里有“unable to find image microros/micro-ros-agent:humble locally”这个报错我在后面专门写一段排查。4.2 跑通行测试的完整链路现在我把完整链路跑给你看。要开四个终端终端1启动Gazebo和PX4 SITLcd PX4-Autopilot make px4_sitl gazebo-classic终端2启动Agentros2 run micro_ros_agent micro_ros_agent udp4 --port 8888终端3启动px4_ros_com的飞行器控制接口我后面会细讲cd ~/ws_px4 # 你的ROS2工作空间 source install/setup.bash ros2 launch px4_ros_com sensor_combined_listener.launch.py这个launch文件会启动一个订阅气流高度等话题的节点。如果它正常输出数据说明链路已经通了。终端4查看话题列表ros2 topic list你应该能看到/fmu/out/vehicle_attitude、/fmu/out/vehicle_odometry、/fmu/out/vehicle_status等话题。如果你走到这一步恭喜这套环境基本搭成了。4.3 Agent未连接时的常见表现我有一个朋友第一次跑的时候终端2一点输出都没有他还以为Agent没启动成功。实际上Agent正常运行时也不会刷屏只有在收到PX4发来的数据时才打印日志。所以“没有输出”不等于“没接上”要判断是否接通最直接的方式是在终端3看有没有收到消息。如果终端3一直没有数据优先排查PX4是否成功启动了RTPS桥接对齐下两边的端口号检查一下防火墙。Gazebo环境下一般不会有真实网卡的问题但虚拟机里偶尔会碰到UDP被隔离的情况。5. 用px4_ros_com构建自己的节点实现OFFBOARD控制前面的技能都是热身现在开始写真正的逻辑。我以“读取PX4当前的位置信息并让飞机起飞悬停到指定点”为例带你走一遍完整的消息流。5.1 工作空间搭建与px4_msgs引入先建工作空间并拉取官方接口包mkdir -p ~/ws_px4/src cd ~/ws_px4/src git clone https://github.com/PX4/px4_msgs.git git clone https://github.com/PX4/px4_ros_com.git cd ~/ws_px4 colcon build这里有一个易错点px4_ros_com里带了px4_msgs的依赖如果你只clone了px4_ros_com而没clone px4_msgs编译就会报找不到消息包。两者都要。编译好后sourcesource install/setup.bash5.2 OFFBOARD模式的触发机制PX4的OFFBOARD模式是无人机接受外部控制的核心。在PX4 v1.14里切换OFFBOARD有两种方式通过MAVLink命令或者通过ROS2 Action。ROS2的Action是比“往话题里发指令”更可靠的方式。因为Action有反馈和结果你可以明确知道PX4有没有接受你的指令。如果只是单纯往/fmu/in/offboard_control_mode里发消息PX4到底有没有切过去你是不确定的。我写了一个最小节点发布OFFBOARD模式的指令和期望位置// offboard_control.cpp #include rclcpp/rclcpp.hpp #include px4_msgs/msg/offboard_control_mode.hpp #include px4_msgs/msg/trajectory_setpoint.hpp class OffboardControl : public rclcpp::Node { public: OffboardControl() : Node(offboard_control) { offboard_pub_ this-create_publisherpx4_msgs::msg::OffboardControlMode(/fmu/in/offboard_control_mode, 10); trajectory_pub_ this-create_publisherpx4_msgs::msg::TrajectorySetpoint(/fmu/in/trajectory_setpoint, 10); timer_ this-create_wall_timer(std::chrono::milliseconds(100), [this](){ publish(); }); } private: void publish() { auto offboard_msg px4_msgs::msg::OffboardControlMode(); offboard_msg.position true; offboard_pub_-publish(offboard_msg); auto setpoint_msg px4_msgs::msg::TrajectorySetpoint(); setpoint_msg.position {0.0, 0.0, -1.0}; setpoint_msg.yaw 0.0; trajectory_pub_-publish(setpoint_msg); } rclcpp::Publisherpx4_msgs::msg::OffboardControlMode::SharedPtr offboard_pub_; rclcpp::Publisherpx4_msgs::msg::TrajectorySetpoint::SharedPtr trajectory_pub_; rclcpp::TimerBase::SharedPtr timer_; }; int main(int argc, char** argv) { rclcpp::init(argc, argv); rclcpp::spin(std::make_sharedOffboardControl()); rclcpp::shutdown(); return 0; }发布频率很关键。PX4要求OFFBOARD指令至少在2Hz以上实际操作中我用的是10Hz也就是100ms定时器。如果低于2HzPX4会认为连接丢失自动退出OFFBOARD模式。很多人试了半天发现飞机不响应先检查一下发布频率这是最常见的隐藏问题。5.3 从“收到消息”到“飞机飞起来”光发OFFBOARD模式指令还不够PX4里有一个“安全开关”机制——需要先解锁arm然后切到OFFBOARD模式。在仿真环境里我习惯用QGroundControl来解锁因为可以看到飞机状态。如果要用命令行可以在PX4 shell里输入commander arm commander mode offboard这个顺序不能反。有一次我图省事直接在外部的PX4 shell里输入commander mode offboardPX4会返回错误因为没解锁之前它不接受OFFBOARD模式切换。整个起飞逻辑是启动起飞位置指令发布节点上面的代码在QGroundControl或命令行中解锁在QGroundControl或命令行中切换OFFBOARD飞机收到位置指令后自动起飞到(0,0,-1)并悬停注意上面位置里的z方向是负值因为PX4的坐标系里高度向上为负。第一次接触的人经常会在这里栽跟头我见过有人写了正的z值结果飞机一头往地面钻。5.4 QoS不匹配问题一对“冤家”PX4通过Agent发布的ROS2消息默认使用的是Best Effort可靠性策略而很多ROS2节点默认用的是Reliable。如果两边不匹配你会看到一个很诡异的现象ros2 topic list能看到话题但ros2 topic echo收不到任何数据或者时断时续。我记得第一次遇到这个问题时整整排查了一个下午最后发现是QoS不匹配。解决办法是订阅时指定匹配的QoSauto qos rclcpp::QoS(rclcpp::KeepLast(10)).best_effort().durability_volatile(); auto sub this-create_subscriptionpx4_msgs::msg::VehicleAttitude(/fmu/out/vehicle_attitude, qos, callback);用ros2 topic info /fmu/out/vehicle_attitude --verbose可以查看发布端的QoS策略照着配就不会错。ROS2在Humble版本里如果QoS不匹配甚至不会报错只会在底层悄悄把消息丢掉这是新手最容易掉进去的坑。6. 自定义uORB消息用RTPS把私有话题桥接出来官方提供的话题够用吗日常开发基本够用但如果你要做自定义功能比如发送一个自己定义的传感器数据、控制某路舵机就得会拓展。6.1 自定义消息的完整流程第一步在PX4源码里新增uORB消息定义在PX4-Autopilot/msg/下新建一个.msg文件格式类似uint64 timestamp float32 my_value第二步在CMakeLists.txt或构建配置里找到消息列表把新消息加进去。PX4 v1.14.3的构建体系会自动扫描msg目录所以如果你只是新增一个.msg文件通常不需要手动改CMakeLists。第三步把新消息加入RTPS桥接的YAML文件。打开msg/tools/urtps_bridge_topics.yaml在rtps列表里加上你的消息- msg: my_custom_msg receive: truereceive表示是否可以从ROS2侧发送到这个主题。设置为false表示只能从PX4侧发出。第四步重新编译固件。这个流程里最容易出错的是第三步忘了加YAML。很多人改了.msg重新编译后在ROS2侧一直看不到新话题白白浪费了半小时。6.2 自定义消息和px4_msgs的关系有个概念必须澄清PX4的uORB消息和ROS2的px4_msgs不是一套东西。即使你在PX4源码里新增了.msgpx4_msgs里的对应消息也不会自动出现。你需要再到px4_msgs/msg/里同样新建一个.msg保持字段一致重新编译colcon build。这两边消息名字一样、字段一样但本质上是两个不同系统里的定义。不理解这个对应关系你会在自定义通信上被卡住很久。7. 我踩过的几个坑顺手帮你填平了有些坑是我自己在踩有些是帮人远程调试时遇到的。写在这给你的排错路径省点时间。7.1 Agent镜像拉取失败的排查在Hot Search里你很可能看到过这个报错unable to find image microros/micro-ros-agent:humble locally。这通常是在用Docker方式运行Agent时出现的。原因是 Docker Hub 上的镜像标签不是所有版本都有或者在极少数网络环境下需要手动拉取。我给出两个解决方案方案一推荐直接用apt装sudo apt install ros-humble-microxrcedds-agent方案二如果一定要用Docker先手动拉取指定标签docker pull microros/micro-ros-agent:humble如果拉取失败换成docker pull microros/micro-ros-agent:latest然后在运行命令里改对应标签。不过实话说在本地开发环境里用Docker反而多此一举ros2 run的方式更顺手。7.2 “ros2: command not found”的根因这个问题的核心不是你没装ROS2而是shell没有source环境。常见原因有三个第一你忘了source /opt/ros/humble/setup.bash。解决办法很简单在~/.bashrc末尾加上这一行一劳永逸。第二你source了某个工作空间里的setup.bash但是这个工作空间是在ROS2环境下编译的如果你没source基础环境里面的setup.bash就会报错导致后续命令找不到。第三你用的是zsh但只把source写进了~/.bashrc。这种问题经常发生在Ubuntu默认shell改成zsh的用户身上。检查一下~/.zshrc里有没有相应的source。7.3 Gazebo里飞机不动、Agent无数据的排查这种情况十次有八次是PX4进程没有真正启动RTPS桥接。判断方法是看PX4 shell启动日志里是否有类似RTPS link started的字样。如果没看到就去urtps_bridge_topics.yaml检查一下看看你的自定义配置有没有语法错误。还有一次我把Agent的端口写成了8899而PX4默认是8888结果Agent一直在等PX4也一直在发双方隔着网络互相看不见。改回8888就好了。7.4 ROS2多机通信与网络发现进阶如果你是用无人机的机载电脑远程连地面站想让多个ROS2设备共享话题那就要注意DDS的网络发现机制。ROS2默认使用多播进行节点发现在复杂网络环境下可能不理想。可以改用ROS_DOMAIN_ID隔离通信环境也可以设置ROS_AUTOMATIC_DISCOVERY_RANGE来限制发现范围。PX4仿真在单机上跑这些问题不明显但真正上无人机平台之后就会遇到了。到时候先在松耦合网络里试通ROS2节点再连PX4不要一起上一锅乱炖。8. 从仿真到真机三个必须新增的防护仿真跑通只是第一步。等你把代码部署到真正的无人机上时有几个防护必须给代码加上我建议现在就写在代码里别等到炸机了再后悔。第一发布频率监测。如果指令发布频率掉到2Hz以下PX4会退出OFFBOARD。你应该在代码里加入发布频率统计低于阈值就主动触发着陆或切换到定高模式。第二指令超时保护。如果PX4在一定时间内收不到位置更新或者速度更新应该立刻切换到安全模式。在代码里可以做一个看门狗每次成功收到PX4的回推消息时重置计时器超时就执行紧急着陆。第三控制目标合理性检查。你向PX4发送的期望位置需要经过校验防止因为算法异常发出离谱的指令。我的习惯是加一个最大加速度和位置边界限制的校验函数先判断目标是否在可飞区域内再发送给PX4。代码上只是加几个if判断但在真机上能避免非常多问题。也许你看过一些视频或者教程在真机上直接通过/fmu/in/offboard_control_mode发指令然后发现飞机反应迟钝其实那多半是发布频率不够。PX4的OFFBOARD是一个高实时性通道不是随便发几条消息就行的。我个人的体会是仿真阶段一定要把通信链路、消息定义、控制逻辑全部调试稳定因为你真正到了外场没有那么多时间来排查ROS2和PX4的接口问题。外场时间应该花在调参和验证算法上而不是浪费在“为什么主题收不到数据”这种低级问题上。文章写到这里从环境配置到数据通信的路径基本已经捋清楚了。ROS2对接PX4这件事本身并不神秘先理解uORB和DDS的翻译机制再确认Agent通道是通的最后通过px4_ros_com构建自己的控制逻辑。剩下的事情就交给时间去踩坑吧。