MicoAir H743配置uXRCE-DDS:PX4飞控数据直连ROS2实战
2026/9/9 11:55:14 网站建设 项目流程

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 上哪些口能用来跑 DDS

2.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.px4boardsrc/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 接机载电脑串口的 RX,RX 接 TX,GND 接 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_DDS

grep -r "UXRCE_DDS" boards/

如果是基于px4_fmu-v5x或者durandal改的,通常已经把CONFIG_UXRCE_DDS=y写进默认配置了。如果没找到,就在default.px4board里加一行。另外,要确认你的目标板上CONFIG_BOARD_HAS_UXRCE_DDS=y之类的板级支持宏,每个硬件的外设支持情况不同,这个宏决定了模块能不能正确初始化硬件。

编译命令按标准流程走:

cd ~/PX4-Autopilot make micoair_h743_default

如果你的板名不是这个,改成你自己的 board 路径名。编译产出固件后,用 QGroundControl 刷进去。

3.2 机载参数设置:UXRCE_DDS_CFG 与波特率

固件刷好、确认能正常启动之后,去 QGroundControl 的参数界面搜索UXRCE,会看到下面这些关键参数:

参数名作用我的设置
UXRCE_DDS_CFG选择 uXRCE-DDS Client 走哪个口TELEM2
UXRCE_DDS_PRT当 CFG 选择 UART 时的具体端口号视情况
UXRCE_DDS_BAUD串口波特率921600
UXRCE_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 配置:让飞控连上 ROS2

4.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-devlibtinyxml2-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 2019

PX4 固件默认的 uXRCE-DDS 网络端口是 2019,Agent 也要用 2019。

还有一个容易忽略的参数是 Domain ID。PX4 侧默认是 0,ROS2 的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 时遇到的问题很典型,日志里反复出现timeoutreset 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_ID=1,飞控端默认 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_attitudevehicle_local_positionoffboard_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_TYPE=Release选项,通常能把 CPU 占用降下来不少。

另外,机载电脑和飞控共地这个问题再强调一次。有些飞控串口的信号地跟机载电脑的电源地不是同一个回路,直接用不同的电源适配器供电时,两端地电位有差异,轻则通信偶发错误,重则烧接口。我在实验室里用 Jetson 和飞控分别供电,第一次没共地,数据偶尔会错乱,后来在同一个排针上把两边 GND 接在一起,问题立刻消失。所以不管你是临时调试还是装机飞行,GND 一定要连。

配置 uXRCE-DDS 这件事,原理并不复杂,真正花时间的其实是对齐各种细节和排查链路问题。把这套笔记里的步骤走一遍,从硬件接线、固件配置、Agent 启动到 ROS2 话题验证,整个流程下来一两个小时就能跑通。之后你再做机载视觉、自主飞行或者集群实验,就会发现飞控数据的获取变得简单直接,不再需要跟各种中间层较劲了。

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

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

立即咨询