
MicoAir H743 这块板子在我手上吃灰了两个月直到最近做六旋翼机载视觉实验需要把飞控的姿态、位置、速度这些数据实时送进 ROS2 里的规划节点才重新翻出来折腾。最初我走的是数传 MAVROS 的老路但始终觉得多了一层转发延迟和丢包都不太可控。后来把目光转向 uXRCE-DDS 客户端发现这条路才是让 PX4 生态直接接进 DDS/ROS2 网络的正解。这篇笔记不打算写成官方文档的复读主要记录我在 MicoAir H743 上配置 uXRCE-DDS 客户端时踩过的坑、验证过的命令、以及整套链路真正跑通之后的实际效果。不管你是要给无人机加机载电脑做自主飞行还是单纯想把飞控内部 uORB 消息拉到上位机做记录分析这篇笔记应该都能帮你少走几步弯路。1. 为什么要折腾 uXRCE-DDS飞控数据进 ROS2 的最短路径1.1 无人机开发里的数据孤岛问题飞控本质上是一个独立的实时系统PX4 内部所有数据都跑在 uORB 消息总线上姿态估计、位置估计、传感器原始数据、执行机构输出全都在这个总线上流转。但我们做机载视觉、路径规划、集群控制的时候算力需求远超飞控单片机能承受的范围必须在树莓派、Jetson 或者工控机上跑 ROS2 节点。问题就出在这两套系统之间。ROS2 使用 DDS 作为通信中间件节点之间通过话题和服务通信飞控内部是 uORB 消息总线。要把这两套机制打通传统方案是加一个 MAVROS 节点把 MAVLink 协议翻译成 ROS 话题。这个方案最大的问题是链路太长飞控 uORB 先序列化成 MAVLink经过串口或者 UDP 到 MAVROS再反序列化转成 ROS 消息中间任何一环抖动都会反映到最终数据质量上。uXRCE-DDS 的思路则完全不同。它把 PX4 飞控当作 DDS 网络中的一个 XRCE 客户端通过一套轻量级的序列化协议直接把 uORB 消息桥接到机载电脑上运行的 MicroXRCEAgent由 Agent 再以原生 DDS 写者的身份把数据发布到 ROS2 网络里。省掉了 MAVLink 翻译层数据结构从 uORB 到 DDS 几乎是点对点映射。1.2 uXRCE-DDS 的架构客户端-代理-主题要理解怎么配置先要把架构看清楚。uXRCE-DDS 这套机制在 PX4 里由三部分组成飞控端的 uXRCE-DDS Client 模块负责从 uORB 总线订阅消息通过串口或者 UDP 发送给 Agent。它不需要跑完整的 DDS 协议栈占用的 Flash 和 RAM 都很小这是它能在单片机上运行的前提。机载电脑端的 MicroXRCEAgent这是一个独立进程负责接收 Client 传来的数据并且在 DDS 域中代表 Client 完成主题的发布和订阅。Agent 可以随时启停飞控端的 Client 会自动重连。DDS 网络与 ROS2 节点Agent 把数据桥接进 ROS2 后任何 ROS2 节点都可以像订阅普通话题一样订阅飞控数据也可以向飞控发指令。这套架构有个特别好的特性飞控端完全不知道自己在对谁说话它只负责把 uORB 消息打包发出去Agent 在不在、ROS2 网络里有哪些节点飞控一概不管。这意味着你可以随时启动或者杀掉 Agent飞控不会受影响连接断了会自动重试。明白了这个架构配置目标就很清晰了第一确保飞控固件里编译了 uXRCE-DDS Client 模块第二给 Client 配置一个可用的串口和波特率第三在机载电脑上把 Agent 跑起来并且让两边的参数对齐。2. 选串口与接线MicoAir H743 上哪些口能用来跑 DDS2.1 先摸清板子上的串口资源MicoAir H743 这块板子用的是 STM32H743 主控接口相当丰富。我手里这块是带 OSD 的版本板载了两个 IMU、一个气压计、16MB 黑匣子存储整体布局比较接近 Holybro Durandal 那一类 H743 板子。串口资源从丝印上看有 TELEM1、TELEM2、TELEM3、GPS1、GPS2加上 UART4、UART7 之类扩展口。问题在于PX4 官方 board 列表里对 MicoAir 的支持并不像 ArduPilot 那边那么完善很多用户都是刷厂商给的 PX4 固件或者自己基于相近的 H743 板型做适配。这就导致一个很现实的情况你没法想当然地认为 TELEM1 一定对应某个固定的 /dev/ttyS 节点必须自己确认。怎么确认两个方法。第一个方法是看你手里固件的 board 文件PX4 源码里boards/厂商/板型目录下的default.px4board和src/drivers/uart相关配置会标注每个 UART 对外口的映射关系。第二个方法更直接把板子通过 USB 连上 QGroundControl打开 MAVLink Console执行ls /dev看串口设备再执行param show看当前串口相关配置。常见 H743 板型的映射规律大概是这样对外接口通常对应的设备节点用途TELEM1/dev/ttyS1数传、MAVLink 常用TELEM2/dev/ttyS2数传备用、CAN 扩展等GPS1/dev/ttyS4GPS 模块连接GPS2/dev/ttyS5备用 GPS 接口UART4/dev/ttyS6调试、外设这个表仅供参考不同板子差异很大务必以实际固件识别到的设备为准。我最终选了 TELEM2 作为 uXRCE-DDS 连接口把 TELEM1 留给数传这样调试的时候还能在 QGroundControl 里看到飞行数据不至于盲调。2.2 我的接线方案与电平注意点硬件连接其实很基础飞控 TELEM2 口的 TX 接机载电脑串口的 RXRX 接 TXGND 接 GND。如果你是接树莓派的 GPIO UART注意树莓派默认串口是 3.3V TTL跟飞控电平匹配不用转接。如果是 Jeston Nano 这类带 3.3V UART 的开发板同样可以直接接。这里必须强调一个很多人栽过的坑飞控串口是 3.3V 电平不要直接接 5V 的 USB-TTL 模块。我一开始图省事随手拿了个 CP2102 模块接到笔记本上调串口那个模块输出电平是 3.3V 的倒还好但如果你手里是老的 FT232RL 5V 版本直接怼上去运气好能用运气不好就把飞控 UART 引脚烧了。最好是使用明确标注 3.3V 的 USB-TTL 模块或者直接接机载电脑的原生 UART。另外一个建议是接线尽量短。uXRCE-DDS 跑 921600 波特率的时候信号沿很陡长线容易引入干扰。我在 Jetson 上跑的时候用杜邦线接了大概 10 厘米没出问题。如果你想远程连接把 Agent 跑在另一台机器上那就别用串口了直接用 PX4 的 UDP 模式配合 Wi-Fi 或者以太网后面我会讲。3. 固件层配置从编译到参数设置3.1 固件准备官方固件与自定义编译的选择uXRCE-DDS Client 模块在 PX4 v1.14 之后已经是默认固件的一部分理论上只要你刷的不是远古版本模块都是编译进去的。但 MicoAir H743 的问题在于厂商给的预编译固件有时用的是简化配置不一定把 uXRCE-DDS 相关功能完整编译进去。最稳的做法是自己编译一版固件。如果你拿到的板子在 PX4 源码里没有直接对应的 board 目录需要在源码的boards目录下新建一个板子定义。这个工作量说大不大说小不小。我提供一个快速路径找一个硬件规格最接近的现成板子做模板。H743 的板子可以参照boards/holybro/durandal或者boards/px4/fmu-v5x复制整个目录改成你自己的板名然后对照原理图微调 UART 映射、传感器驱动和电源配置。编译固件之前要确认默认配置里有没有启用 uXRCE-DDS Client。在 PX4 源码里搜CONFIG_UXRCE_DDSgrep -r UXRCE_DDS boards/如果是基于px4_fmu-v5x或者durandal改的通常已经把CONFIG_UXRCE_DDSy写进默认配置了。如果没找到就在default.px4board里加一行。另外要确认你的目标板上CONFIG_BOARD_HAS_UXRCE_DDSy之类的板级支持宏每个硬件的外设支持情况不同这个宏决定了模块能不能正确初始化硬件。编译命令按标准流程走cd ~/PX4-Autopilot make micoair_h743_default如果你的板名不是这个改成你自己的 board 路径名。编译产出固件后用 QGroundControl 刷进去。3.2 机载参数设置UXRCE_DDS_CFG 与波特率固件刷好、确认能正常启动之后去 QGroundControl 的参数界面搜索UXRCE会看到下面这些关键参数参数名作用我的设置UXRCE_DDS_CFG选择 uXRCE-DDS Client 走哪个口TELEM2UXRCE_DDS_PRT当 CFG 选择 UART 时的具体端口号视情况UXRCE_DDS_BAUD串口波特率921600UXRCE_DDS_DOM_IDDDS Domain ID0最关键的逻辑在这里UXRCE_DDS_CFG决定 Client 挂到哪个对外串口上而对应那个串口的波特率是由这个串口自己的参数决定的比如 TELEM2 对应的就是SER_TEL2_BAUD。如果你把 UXRCE_DDS_CFG 设为 TELEM2那么 921600 这个波特率要同时体现在 UXRCE_DDS_BAUD 和 SER_TEL2_BAUD 上Agent 那边的启动参数也要用 921600三边必须一致。这里解释一下为什么 TELEM 口的波特率要单独设。TELEM 口的默认波特率通常是 57600这是给 MAVLink 数传用的标准速度。但 uXRCE-DDS 的消息密度远比 MAVLink 高姿态、位置、传感器数据都是高频率发布的57600 波特率根本跑不动。实测中我用 57600 跑过Client 启动没问题但消息在 Agent 端大量积压、丢失系统负载还特别高。921600 算是性价比不错的选择H743 的 UART 完全能稳定支持。3.3 验证客户端是否在跑参数设好、板子重启之后怎么确认 Client 真的在跑用 QGroundControl 的 MAVLink Console 连上飞控执行uxrce_dds_client status如果模块已经在运行会返回类似下面的信息uxrce_dds_client running - mode: serial - device: /dev/ttyS2 - baudrate: 921600 - session established: no看到session established: no是正常的说明 Client 已经启动但还没连上 Agent。如果你现在就把机载电脑端的 Agent 跑起来过几秒再执行一次uxrce_dds_client status会发现这里变成了session established: yes。这个状态字段是你排查链路问题时最重要的信息之一。如果你执行uxrce_dds_client status提示命令不存在说明固件里没有把模块跑起来需要手动启动uxrce_dds_client start -t serial -d /dev/ttyS2 -b 921600-t serial表示用串口模式-d指定设备节点-b指定波特率。如果你确定模块已经编译进固件只是没自动启动可以在/etc/rc.txt启动脚本里加这行命令实现上电自动连接。4. 机载电脑端 Agent 配置让飞控连上 ROS24.1 安装 MicroXRCEAgent机载电脑端需要的是 eProsima 的 Micro-XRCE-DDS-Agent。我用的 Jetson 上装的是 Ubuntu 20.04 ROS2 Foxy当时直接源码编译的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编译依赖主要是 CMake、Fast DDS 以及其依赖库。如果你在 Ubuntu 上编译提前把libasio-dev、libtinyxml2-dev这些装好免得卡在 cmake 检查依赖那一步。装完之后终端里就能直接调用MicroXRCEAgent命令了。如果是 ROS2 Humble 之后的版本也可以考虑直接用apt安装micro-xrce-dds-agent这个包省去编译时间。但我个人习惯还是源码编译因为方便在编译选项里调整一些调试输出出了问题也更容易定位。4.2 启动命令与参数对齐Agent 启动命令看起来很简单但参数必须和飞控端严格对齐。串口模式下MicroXRCEAgent serial --dev /dev/ttyUSB0 -b 921600注意这里的设备文件名要换成机载电脑实际识别到的串口。用ls /dev/tty*查看拔插一次线缆对比变化就能确定。如果走 UDP飞控端要把 UXRCE_DDS_CFG 设置为网络模式然后 Agent 启动为MicroXRCEAgent udp4 -p 2019PX4 固件默认的 uXRCE-DDS 网络端口是 2019Agent 也要用 2019。还有一个容易忽略的参数是 Domain ID。PX4 侧默认是 0ROS2 的ROS_DOMAIN_ID默认也是 0所以大多数情况下不用特意配。但如果你的 ROS2 环境里有人改过ROS_DOMAIN_ID环境变量两边的 Domain 对不上Agent 日志里看起来是连接成功的但 ROS2 话题就是看不到这个坑我后面细说。Agent 启动之后如果飞控端 Client 正常终端里会刷出这样的日志[1646414403.123456] [INFO] Server session established [1646414403.123456] [INFO] Stream 0 open [1646414403.123456] [INFO] Stream 1 open [1646414403.123456] [INFO] Client 0x00016B2F created看到Server session established这一条恭喜你底层链路已经通了。4.3 用 ros2 topic echo 验证数据流链路通了之后进入 ROS2 侧验证。首先确认你的环境里装了px4_msgs因为 PX4 的 uORB 消息经过 Agent 进入 DDS 网络时用的消息类型名就是px4_msgs/msg/XXX。装px4_msgs的方法是从 PX4/px4_msgs 仓库 clone 下来放到你的 ROS2 workspace 里用colcon build编译。消息类型装好后先看看话题列表source /opt/ros/humble/setup.bash source install/setup.bash ros2 topic list正常情况下会看到一堆/fmu/out/开头的话题比如/fmu/out/vehicle_attitude、/fmu/out/vehicle_odometry、/fmu/out/sensor_combined。这些就是飞控 uORB 主题桥接过来的 DDS 主题。随便挑一个验证ros2 topic echo /fmu/out/vehicle_attitude终端会以接近实时的频率刷出姿态四元数数据--- timestamp: 1646414420123456789 timestamp_sample: 1646414419123456789 q: [0.999, 0.001, 0.002, 0.003] delta_q_reset: [1.0, 0.0, 0.0, 0.0] ...到这里飞控到 ROS2 的整条数据通路就算正式打通了。你可以在 ROS2 节点里直接订阅这些话题做姿态控制、状态估计或者数据记录都行。5. 实测中遇到的坑与完整排查链路5.1 Agent 反复 timeout波特率与串口占用排查我第一次启动 Agent 时遇到的问题很典型日志里反复出现timeout、reset session然后 Server 不断重新建立连接又不断超时死循环一样。这个坑的排查链路我完整梳理一下基本适用于所有串口接不通的情况第一步确认串口设备名对不对。先跑ls /dev/tty*记住当前的设备列表然后拔掉串口线再插上看多了哪个设备。这一步看着蠢但非常有效。我之前遇到过 Jetson 上 USB 转串口模块被识别成/dev/ttyUSB0重启后又变成/dev/ttyACM0的情况不确认设备名后面全是白忙。第二步确认串口没被别的进程占用。跑sudo lsof /dev/ttyUSB0如果有输出说明有进程占着这个串口。常见的是之前跑过的MicroXRCEAgent进程没杀干净或者是ModemManager在搞鬼。Ubuntu 桌面版自带 ModemManager 会自动探测 USB 串口并尝试发 AT 指令这种行为经常把飞控串口搞挂。直接禁用sudo systemctl stop ModemManager sudo systemctl disable ModemManager第三步飞控端确认 Client 有没有真的在往这个口发数据。在 MAVLink Console 里看uxrce_dds_client status如果显示session established: no且 Agent 那边一直在 timeout大概率是波特率不匹配。把飞控端UXRCE_DDS_BAUD、对应 TELEM 口的SER_TEL2_BAUD、Agent 的-b三处数值摆在一起核一遍必须完全一样。第四步如果以上都没问题用示波器或者逻辑分析仪看飞控 TX 引脚有没有波形输出。这一步对没有示波器的朋友不太友好但我那次排查发现其实是自己的 USB-TTL 模块坏了信号根本没有送进电脑。换了一个模块立刻就好。所以当你所有软件配置看起来都对但就是不通的时候也该怀疑一下硬件。5.2 连上了但收不到消息Topic 名字和命名空间底层链路通了Server session established也出现在日志里了但ros2 topic list里一个/fmu/out/话题都看不到。这种情况我遇到过两次根因不一样但都非常有代表性。第一次是 Agent 和飞控的 Domain ID 不一致。机载电脑上之前有人设了export ROS_DOMAIN_ID1飞控端默认 Domain ID 还是 0两边物理链路是通的但 DDS 层根本不处于同一个域里自然互相看不见。排查方法是先看自己终端的环境变量echo $ROS_DOMAIN_ID如果输出不是 0要么把 Agent 启动时加上-d 0要么把环境变量改回来。PX4 飞控端的UXRCE_DDS_DOM_ID也可以改但建议两边都用 0省得给自己挖坑。第二次是px4_msgs消息类型版本不匹配。飞控端固件是 v1.14机载电脑上的px4_msgs是从老版本仓库 clone 的部分消息的字段定义和固件端 uORB 不一致。Agent 在 DDS 层做了类型匹配之后发现 type mismatch干脆不发布。这个问题最隐蔽因为 Agent 日志里不报错只有开启 verbose 日志才能看到 type mismatch 的记录。解决办法是把px4_msgs升级到跟固件匹配的版本重新编译重建 workspace。5.3 TELEM1 被 MAVLink 占用引发的矛盾最后这个坑属于资源规划问题。一开始我把 uXRCE-DDS Client 配在了 TELEM1 上想着这个口最常用、最稳定结果发现 QGroundControl 的 MAVLink 连接时断时续。原因很简单TELEM1 默认承载 MAVLink 数传我把 uXRCE-DDS 的 Client 也挂上去之后两个模块同时使用同一个串口都往里面写数据时序就乱了。排查链路走到这一步你的选择是二选一。要么让 TELEM1 继续承担 MAVLink 数传职责把 uXRCE-DDS 挪到 TELEM2 或者 GPS2 这样的空闲口上要么直接砍掉 TELEM1 的 MAVLink 输出完全交给 uXRCE-DDS。我当时为了保留 QGroundControl 的实时查看能力选择了前者。如果你也遇到串口资源不够的情况可以考虑用 PX4 的 UDP 模式机载电脑和飞控通过以太网互联或者通过 Wi-Fi 连接同一个局域网飞控端 Client 配成 UDP走 2019 端口。这样释放了所有 UART 口带宽也比串口大得多。代价是延迟取决于网络质量不如 UART 直连稳定。6. 进阶用法与性能注意事项6.1 调整消息发布频率与 QoS链路通了只是起点真正用好 uXRCE-DDS 还需要了解它的性能行为。PX4 的 uORB 消息发布频率不同姿态控制相关的消息可能 250Hz 甚至更高传感器原始数据可能 800Hz 以上这些数据全部通过同一个串口桥接到 Agent串口带宽就成了瓶颈。921600 波特率的理论吞吐量大约是 92KB/s扣除协议开销后实际可用数据带宽大概在 60-70KB/s。如果你同时订阅十几个高频话题很容易把带宽打满表现为 Agent 端出现消息丢弃、飞控端 CPU 负载升高。这时候需要做的是按需订阅。PX4 的 uXRCE-DDS Client 提供了配置机制可以指定要让哪些 uORB 主题通过 DDS 桥接出去而不是默认全量转发。在 PX4 的uXRCE-DDS Client模块参数里可以配置UXRCE_DDS_AGENT_MSG之类的过滤项具体配置方法不同版本差异较大建议直接看固件版本对应的uxrce_dds_client命令行帮助。实践经验是先明确你的控制算法需要哪些消息只桥接必须的那些。比如做机载视觉落地的姿态控制通常只需要vehicle_attitude、vehicle_local_position、offboard_control_mode这三个话题其他的全都不要开。这能极大降低串口压力和系统复杂度。6.2 反向控制与多机扩展uXRCE-DDS 不只是单向把飞控数据送到 ROS2也可以从 ROS2 侧向飞控发指令。PX4 的/fmu/in/话题就是干这个的比如/fmu/in/offboard_control_mode配合/fmu/in/trajectory_setpoint可以完成 Offboard 模式下的指令发送。这意味着你可以完全抛开 MAVLink用 ROS2 节点直接写控制指令给飞控。我自己验证过一轮在 ROS2 的 rviz 里手动发布一个轨迹目标点飞控收到后立即切入 Offboard 模式跟随飞行。整个过程没有经过 QGroundControl纯 DDS 链路响应延迟体感上比 MAVROS 方案低很多。当然Offboard 模式下安全冗余要靠飞控内部的 failsafe 逻辑保证你必须在参数里把掉线保护时间设好避免 ROS2 节点挂了之后飞控失控。多机扩展也是这套方案的天然优势。每台飞控通过独立的串口连接到不同的机载电脑或者一台机载电脑接多个 UART每个 Agent 进程对应一个 Client。只要 DDS Domain 一致所有飞控数据都汇入同一个 ROS2 网络做集群协同的时候非常方便。我见过有人直接用这套架构带了三台四旋翼做蜂群编队每台飞控跑一个 Agent 实例话题通过namespace区分机号。关于多机的命名空间处理我的建议是在 Agent 启动时用-n参数指定命名空间前缀这样每台飞控的话题都能自动带上/uav1、/uav2这样的前缀不会互相冲突。具体命名方式没有强制要求但一定要在早期就定好规则不然等集群规模大了再改命名方案代价很高。6.3 一点经验补充最后补一个我实际工作中发现的小技巧。uXRCE-DDS 链路跑起来之后用top看一下机载电脑上 Agent 进程的 CPU 占用率如果长期居高不下除了消息量太大之外还有一种可能是 Agent 编译时默认开了调试日志输出。重新编译 Agent 时加上-DCMAKE_BUILD_TYPERelease选项通常能把 CPU 占用降下来不少。另外机载电脑和飞控共地这个问题再强调一次。有些飞控串口的信号地跟机载电脑的电源地不是同一个回路直接用不同的电源适配器供电时两端地电位有差异轻则通信偶发错误重则烧接口。我在实验室里用 Jetson 和飞控分别供电第一次没共地数据偶尔会错乱后来在同一个排针上把两边 GND 接在一起问题立刻消失。所以不管你是临时调试还是装机飞行GND 一定要连。配置 uXRCE-DDS 这件事原理并不复杂真正花时间的其实是对齐各种细节和排查链路问题。把这套笔记里的步骤走一遍从硬件接线、固件配置、Agent 启动到 ROS2 话题验证整个流程下来一两个小时就能跑通。之后你再做机载视觉、自主飞行或者集群实验就会发现飞控数据的获取变得简单直接不再需要跟各种中间层较劲了。