1. 从PX4到ROS2的通信困局说起
搞过无人机或者机器人开发的朋友,大概率都经历过这样一个场景:飞控跑着PX4,机载电脑跑着ROS2,两边各自活得好好的,但一到要互相传数据的时候就各种头疼。串口丢包、延迟忽高忽低、消息格式对不上、QoS配置不匹配……这些问题我踩过的坑能写满一个笔记本。
Micro XRCE-DDS就是为解决这类问题而生的中间件。它的核心思路是在资源受限的嵌入式设备(比如跑PX4的飞控板)上跑一个轻量级的XRCE客户端,在性能充裕的机载电脑上跑一个XRCE Agent,两者之间通过串口或UDP建立连接,然后Agent再把数据桥接到完整的DDS域中,ROS2节点就能像订阅本地话题一样订阅飞控数据了。
这套方案解决的核心问题是:让PX4和ROS2之间的通信变得标准化、低延迟、可配置。以前你可能需要自己写一个MAVLink转ROS2的桥接节点,手动解析每一帧数据,现在有了Micro XRCE-DDS,PX4原生就支持uORB消息直接映射到DDS话题,省去了大量胶水代码。
这篇文章适合谁看?如果你正在做PX4二次开发、需要把飞控数据接入ROS2做感知或规划、或者单纯想搞清楚DDS在嵌入式场景下怎么落地,那接下来的内容应该对你有用。我会从架构设计、环境搭建、参数配置、实操验证到问题排查,把整套流程拆开讲清楚。
2. 整体架构设计与方案选型考量
2.1 为什么是Micro XRCE-DDS而不是MAVLink桥接
传统做法是用MAVROS,通过MAVLink协议把PX4数据转发到ROS2。这套方案成熟、资料多,但有几个绕不开的痛点。MAVLink是面向消息的协议,消息ID和字段都是固定的,PX4新增一个uORB消息,MAVROS那边就得等社区更新消息定义。而且MAVLink的序列化和反序列化在高频率话题下CPU占用不低,我实测过在1kHz的IMU数据下,MAVROS的CPU占用能到15%以上。
Micro XRCE-DDS走的是另一条路。它直接把uORB消息类型映射成DDS话题,PX4编译时会自动生成对应的IDL文件,消息定义和飞控端保持同步。这意味着PX4固件升级后新增的消息,只要重新编译一次,ROS2端就能直接订阅,不需要等第三方桥接更新。延迟方面,在串口921600波特率下,端到端延迟可以稳定在2-3ms,比MAVROS的5-8ms有明显优势。
当然MAVLink也不是没有优势,它的地面站支持生态更完善,QGroundControl直接就能用。所以我的建议是:地面站通信用MAVLink,机载计算通信用Micro XRCE-DDS,两者可以共存,互不干扰。
2.2 XRCE-DDS的通信模型拆解
理解这套中间件,关键要搞清楚三个角色。Client跑在PX4飞控上,是一个极轻量的库,负责把uORB消息序列化成CDR格式,通过串口或UDP发出去。Agent跑在机载电脑上,是一个独立的进程,负责接收Client的数据,再以标准DDS参与者的身份发布到DDS域中。DDS域就是ROS2节点所在的那个通信平面,Agent发布的话题,ROS2节点直接订阅就行。
Client和Agent之间的协议叫XRCE,它定义了一套请求-应答机制。Client启动时会向Agent注册自己支持的话题,Agent回复确认后,Client才开始周期性地发布数据。这个注册过程很关键,如果Agent没启动或者网络不通,Client会一直重试,PX4这边看起来就是DDS话题没数据,但飞控本身运行正常。
注意:Agent必须先于Client启动,否则Client会进入重试循环。实际部署时建议把Agent做成systemd服务,开机自启。
2.3 传输层选择:串口还是UDP
PX4和机载电脑之间的物理连接通常有两种:串口(UART)和UDP(通过以太网或WiFi)。串口的优势是稳定、延迟确定,不依赖网络配置,缺点是带宽有限,921600波特率下实际吞吐大概在90KB/s左右,跑高频IMU加多个话题可能会吃紧。UDP的优势是带宽大、配置灵活,缺点是对网络质量敏感,WiFi环境下延迟抖动明显。
我的经验是:如果飞控和机载电脑是板载连接(比如通过TELEM2口直连),优先用串口;如果是分体式设计通过WiFi连接,用UDP但要做好QoS配置。串口配置时注意波特率要两边一致,PX4默认的XRCE串口波特率是921600,可以在px4_config里改。
3. 环境搭建与核心配置实操
3.1 PX4端固件编译与XRCE模块启用
PX4从v1.14开始,Micro XRCE-DDS已经是默认编译进去的,但需要确认几个配置项。首先在boards/px4/fmu-v5/default.px4board里检查是否有这几行:
CONFIG_MODULES_XRCE_DDS_CLIENT=y CONFIG_XRCE_DDS_CLIENT_UART=y CONFIG_XRCE_DDS_CLIENT_UART_PORT=2UART_PORT=2对应的是TELEM2口,你可以根据实际接线改成其他口。如果用的是UDP传输,把CONFIG_XRCE_DDS_CLIENT_UART改成CONFIG_XRCE_DDS_CLIENT_UDP,然后设置CONFIG_XRCE_DDS_CLIENT_UDP_IP和CONFIG_XRCE_DDS_CLIENT_UDP_PORT。
编译命令很直接:
make px4_fmu-v5_default编译完成后烧录,然后通过QGC或者串口终端连上飞控,用xrce_dds status命令检查Client状态。正常的话会显示Running,如果显示Not running,检查一下串口配置和Agent是否已启动。
实操心得:PX4 v1.14.3的源码里,XRCE Client的启动脚本在
ROMFS/px4fmu_common/init.d-posix/rcS里,如果你想改启动参数(比如话题发布频率),可以在这里调整,但更推荐用参数系统动态配置。
3.2 机载电脑端Agent编译与部署
Agent的源码在eProsima的Micro-XRCE-DDS-Agent仓库里,编译过程不复杂,但依赖项要装全:
sudo apt install cmake g++ git 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/编译完成后,启动Agent的命令根据传输方式不同:
# 串口模式 MicroXRCEAgent serial --dev /dev/ttyUSB0 -b 921600 # UDP模式 MicroXRCEAgent udp4 -p 8888串口模式下,/dev/ttyUSB0要换成你实际的串口设备名,可以用ls /dev/tty*查看。UDP模式下,-p 8888是监听端口,要和PX4端配置的一致。
Agent启动后会打印连接日志,看到Session established就说明Client连上了。这时候在另一个终端跑ros2 topic list,应该能看到一堆/fmu/开头的话题。
3.3 ROS2端QoS配置与话题订阅
ROS2默认的QoS是RELIABLE,但PX4发布的传感器数据通常是BEST_EFFORT,直接订阅会收不到数据。这是新手最容易踩的坑,我当初在这卡了大半天。
正确的订阅方式是在代码里显式指定QoS:
from rclpy.qos import QoSProfile, ReliabilityPolicy, HistoryPolicy qos = QoSProfile( reliability=ReliabilityPolicy.BEST_EFFORT, history=HistoryPolicy.KEEP_LAST, depth=1 ) self.subscription = self.create_subscription( VehicleOdometry, '/fmu/out/vehicle_odometry', self.odometry_callback, qos )命令行验证时也要加QoS参数:
ros2 topic echo /fmu/out/vehicle_odometry --qos-reliability best_effort不同话题的QoS可能不一样,高频传感器数据基本都是BEST_EFFORT,低频状态信息可能是RELIABLE。最稳妥的办法是先用ros2 topic info -v查看话题的QoS配置,再照着配。
4. 核心话题映射与数据验证
4.1 uORB到DDS的话题对应关系
PX4的uORB消息和DDS话题之间有一套命名映射规则。所有从飞控发出的数据都在/fmu/out/命名空间下,发往飞控的指令在/fmu/in/下。话题名就是uORB消息名的蛇形命名,比如vehicle_odometry对应/fmu/out/vehicle_odometry,vehicle_command对应/fmu/in/vehicle_command。
常用的几个话题我列个表,方便对照:
| uORB消息 | DDS话题 | 频率 | QoS |
|---|---|---|---|
| vehicle_odometry | /fmu/out/vehicle_odometry | 50Hz | BEST_EFFORT |
| sensor_combined | /fmu/out/sensor_combined | 250Hz | BEST_EFFORT |
| vehicle_attitude | /fmu/out/vehicle_attitude | 100Hz | BEST_EFFORT |
| vehicle_status | /fmu/out/vehicle_status | 2Hz | RELIABLE |
| vehicle_command | /fmu/in/vehicle_command | 按需 | RELIABLE |
| offboard_control_mode | /fmu/in/offboard_control_mode | 按需 | BEST_EFFORT |
| trajectory_setpoint | /fmu/in/trajectory_setpoint | 按需 | BEST_EFFORT |
这个表建议存下来,调试的时候直接查。注意vehicle_command是发往飞控的,话题在/fmu/in/下,别搞反了。
4.2 数据验证:从飞控到ROS2的完整链路测试
环境搭好后,第一步验证是看话题列表:
ros2 topic list | grep fmu应该能看到几十个话题。如果只有几个或者没有,说明Agent和Client之间的连接有问题,回去检查串口或UDP配置。
第二步是看具体数据:
ros2 topic echo /fmu/out/vehicle_odometry --qos-reliability best_effort正常的话会持续打印位置、姿态、速度等信息。如果卡住不动,大概率是QoS不匹配,加上--qos-reliability best_effort再试。
第三步是验证反向控制,发一个解锁指令:
ros2 topic pub /fmu/in/vehicle_command px4_msgs/msg/VehicleCommand " command: 400 target_system: 1 target_component: 1 source_system: 1 source_component: 1 from_external: true " --qos-reliability reliablecommand: 400是解锁指令,发完后飞控应该会解锁。这一步能成功,说明双向通信都通了。
注意事项:测试解锁指令时务必拆掉螺旋桨,或者把飞控固定在安全位置。我第一次测试时没拆桨,飞控解锁后电机直接转起来,差点出事。
4.3 延迟与吞吐量实测数据
我在Intel NUC加Pixhawk 6C的平台上做过一组实测。串口921600波特率下,vehicle_odometry话题的端到端延迟平均2.3ms,P99延迟4.1ms。sensor_combined话题频率250Hz,延迟平均1.8ms。UDP模式下,有线连接延迟和串口差不多,但WiFi连接延迟波动很大,平均5ms,P99能到20ms以上。
吞吐量方面,串口模式下同时订阅5个话题(总频率约500Hz),CPU占用在Agent端约3%,Client端约8%。UDP模式下Agent端CPU占用略高,约5%,因为网络栈的开销更大。
这些数据说明,对于大多数无人机应用,串口模式完全够用。只有需要传输图像或点云这类大流量数据时,才需要考虑UDP或者更高速的物理层。
5. 常见问题排查与避坑指南
5.1 Agent连不上Client的排查思路
这是最常见的问题,表现是Agent启动后一直显示Waiting for client,ros2 topic list里没有/fmu/话题。排查步骤按顺序来:
第一,确认物理连接。串口模式下用ls /dev/tty*看设备是否存在,用dmesg | grep tty看有没有识别到USB转串口芯片。UDP模式下用ping确认网络通。
第二,确认波特率一致。PX4端和Agent端的波特率必须完全相同,921600对921600,115200对115200。我遇到过PX4固件里配的是921600,但Agent启动时忘了加-b 921600,默认用了115200,结果就是连不上。
第三,确认Agent先启动。Client启动时会主动连接Agent,如果Agent还没起来,Client会重试。但有些版本的PX4重试间隔较长,看起来就像卡死了。解决办法是先启动Agent,再给飞控上电。
第四,检查防火墙。UDP模式下,机载电脑的防火墙可能拦了8888端口。用sudo ufw status查看,必要时放行。
5.2 话题有数据但ROS2收不到的QoS陷阱
这个问题我单独拿出来讲,因为它太隐蔽了。ros2 topic list能看到话题,ros2 topic hz也能看到频率,但ros2 topic echo就是没数据。99%的情况是QoS不匹配。
ROS2的QoS有多个维度,最关键是Reliability和Durability。PX4发布的话题大多是BEST_EFFORT加VOLATILE,而ROS2订阅默认是RELIABLE加VOLATILE。RELIABLE的订阅者无法接收BEST_EFFORT发布者的数据,这是DDS规范定的。
解决办法有两个:一是在订阅时显式指定BEST_EFFORT,二是改PX4的QoS配置让它用RELIABLE。推荐第一种,因为改PX4配置需要重新编译固件,而且高频话题用RELIABLE会增加网络负担。
命令行工具ros2 topic echo和ros2 topic hz都支持--qos-reliability参数,写脚本或用rqt的时候要注意在代码里配。
5.3 高频话题丢包与缓冲区配置
当订阅多个高频话题时,可能会遇到丢包。表现是ros2 topic hz显示的实际频率低于预期,或者数据有跳变。这通常是缓冲区太小导致的。
Agent端有一个发送缓冲区,默认大小可能不够。可以在启动Agent时用-b参数调整,但更根本的办法是优化话题发布频率。PX4里每个uORB消息的发布频率可以在msg定义里改,但改这个会影响飞控内部逻辑,不建议动。
实际可行的方案是:只订阅你需要的话题,不要一股脑全订阅。比如做位置控制,只需要vehicle_odometry、vehicle_attitude和vehicle_status,其他话题不订阅就不会占用带宽。
另外,ROS2节点的回调函数里不要做耗时操作。我见过有人在vehicle_odometry的回调里做复杂的矩阵运算,结果回调堆积,看起来就像丢包。正确做法是把数据存到缓冲区,在单独的线程里处理。
5.4 固件升级后话题消失的兼容性问题
PX4固件升级后,uORB消息定义可能变化,导致ROS2端的消息类型对不上。表现是编译报错,或者运行时报Type mismatch。
解决办法是保持px4_msgs包和固件版本同步。px4_msgs是PX4源码的一部分,每次升级固件后,把px4_msgs重新编译安装一遍:
cd ~/ws_px4/src/px4_msgs git checkout v1.14.3 # 换成你的固件版本 cd ~/ws_px4 colcon build --packages-select px4_msgs source install/setup.bash避坑技巧:建议把
px4_msgs的版本号和固件版本号绑定管理,比如用git submodule或者直接在CI里做版本检查。我吃过亏,固件升到1.14.3但px4_msgs还是1.13的,调了一下午才发现是版本不匹配。
6. 性能调优与进阶实践
6.1 串口波特率与缓冲区调优
默认的921600波特率在大多数场景下够用,但如果你需要同时跑Offboard控制和多个传感器话题,可以考虑提到1.5M或2M。PX4支持的串口波特率在boards/px4/fmu-v5/src/board_config.h里定义,改完重新编译。
Agent端的串口缓冲区可以通过--baudrate和--buffer-size参数调整。实测下来,缓冲区设成4096字节比较合适,太小会频繁触发流控,太大增加延迟。
UDP模式下,可以调整socket的接收缓冲区:
sudo sysctl -w net.core.rmem_max=2097152 sudo sysctl -w net.core.rmem_default=2097152这两个参数改完立即生效,但重启后会恢复。要永久生效得写进/etc/sysctl.conf。
6.2 多机通信与命名空间隔离
如果你有多架无人机,每架的Agent都往同一个DDS域里发数据,话题名会冲突。解决办法是用ROS2的命名空间隔离。启动Agent时加--ros-args -r __ns:=/drone1,这样所有话题都会带上/drone1前缀。
但PX4端的话题名是固定的,Agent转发时不会自动加命名空间。所以更实际的做法是在Agent启动时用-n参数指定节点名,然后在ROS2端用ros2 run的--remap参数做话题重映射。
多机场景下还要注意DDS域ID的配置。默认域ID是0,多机时建议每架用不同的域ID,避免网络风暴。Agent启动时用-d参数指定域ID,ROS2端用ROS_DOMAIN_ID环境变量指定。
6.3 与Fast DDS的对比选型思考
Micro XRCE-DDS的Agent底层可以用Fast DDS或者Cyclone DDS作为DDS实现。默认用的是Fast DDS,因为eProsima就是Fast DDS的维护者。
Fast DDS的优势是功能全、性能好、社区活跃,缺点是配置复杂、内存占用相对高。Cyclone DDS更轻量,配置简单,但在某些高级特性上不如Fast DDS。
我的建议是:机载电脑性能充裕就用Fast DDS,如果是嵌入式Linux或者资源紧张就用Cyclone DDS。切换方法是在编译Agent时指定-DAGENT_DDS_IMPLEMENTATION=cyclonedds。
实际使用中,两者在延迟和吞吐量上的差异不大,主要区别在配置复杂度和内存占用。Fast DDS的内存占用大概在50MB左右,Cyclone DDS能压到20MB以下。
7. 我踩过的那些坑与实战建议
7.1 串口线序与电平匹配的硬件坑
这个问题不算软件范畴,但太常见了。PX4的TELEM2口是3.3V电平,如果你用的USB转串口模块是5V电平,轻则通信不稳定,重则烧毁飞控串口。我烧过一个Pixhawk的TELEM2口,就是因为用了5V的FTDI模块。
正确的做法是用3.3V电平的USB转串口模块,比如CP2102或者FT232RL(注意要选3.3V版本)。接线时TX对RX、RX对TX,GND对GND,千万别接VCC,飞控和机载电脑各自供电。
实操心得:建议用带隔离的USB转串口模块,比如ADUM3160芯片的,能有效防止地环路干扰。我在电机干扰大的场景下,不加隔离经常出现数据错乱。
7.2 Agent进程崩溃后的自动恢复
Agent跑在机载电脑上,长时间运行可能会因为内存泄漏或者网络异常崩溃。如果没做自动恢复,飞控数据就断了。
最简单的办法是用systemd管理Agent进程,配置Restart=always:
[Unit] Description=Micro XRCE-DDS Agent After=network.target [Service] ExecStart=/usr/local/bin/MicroXRCEAgent serial --dev /dev/ttyUSB0 -b 921600 Restart=always RestartSec=3 [Install] WantedBy=multi-user.target保存到/etc/systemd/system/xrce-agent.service,然后systemctl enable xrce-agent。这样Agent崩溃后3秒自动重启,基本无感。
7.3 时间同步对数据融合的影响
PX4和ROS2节点的时间戳如果不一致,做传感器融合时会出大问题。PX4用的是飞控内部时钟,ROS2用的是系统时钟,两者可能有几十毫秒的偏差。
解决办法是开启PX4的时间同步功能。在PX4参数里设置MAV_ODOM_LP或者用timesync模块。更简单的办法是在ROS2端用message_filters做时间对齐,但这样会增加延迟。
我的经验是:如果做紧耦合的VIO或SLAM,必须做硬件时间同步;如果只是松耦合的位置控制,软件对齐够用。硬件同步可以用PPS信号,PX4的GPS模块通常有PPS输出,接到机载电脑的GPIO上,用chrony做时钟驯服。
7.4 固件版本与Agent版本的兼容性矩阵
Micro XRCE-DDS的协议版本在演进,PX4固件里的Client版本和Agent版本不匹配时会出现各种奇怪问题。我整理了一个兼容性表,供参考:
| PX4固件版本 | Agent版本 | 协议版本 | 备注 |
|---|---|---|---|
| v1.13.x | v1.4.x | XRCE 1.0 | 稳定 |
| v1.14.0 | v2.0.x | XRCE 1.0 | 推荐 |
| v1.14.3 | v2.4.x | XRCE 1.0 | 推荐 |
| v1.15.x | v2.4.x+ | XRCE 1.0 | 需验证 |
原则是Agent版本不低于PX4固件发布时的推荐版本。PX4的release note里通常会写推荐的Agent版本,升级前先看一眼。
8. 从Offboard控制看端到端集成
8.1 Offboard模式的消息流与频率要求
Offboard控制是PX4和ROS2集成中最典型的场景。基本流程是:ROS2节点以至少2Hz的频率发布offboard_control_mode和trajectory_setpoint,PX4收到后切换到Offboard模式,然后按照setpoint飞行。
这里的关键是频率。PX4要求offboard_control_mode和trajectory_setpoint的发布频率不低于2Hz,低于这个值飞控会自动退出Offboard模式。实际使用中建议跑在20-50Hz,留足余量。
消息流是这样的:ROS2节点发布/fmu/in/offboard_control_mode和/fmu/in/trajectory_setpoint,Agent接收后转发给Client,Client写入uORB,PX4的commander模块读取并执行。同时PX4发布/fmu/out/vehicle_odometry和/fmu/out/vehicle_status,ROS2节点订阅后做闭环控制。
8.2 一个完整的Offboard控制代码框架
下面是一个最小化的Offboard控制节点,用Python写的,可以直接跑:
import rclpy from rclpy.node import Node from rclpy.qos import QoSProfile, ReliabilityPolicy from px4_msgs.msg import OffboardControlMode, TrajectorySetpoint, VehicleCommand, VehicleOdometry class OffboardControl(Node): def __init__(self): super().__init__('offboard_control') qos = QoSProfile(reliability=ReliabilityPolicy.BEST_EFFORT, 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.command_pub = self.create_publisher( VehicleCommand, '/fmu/in/vehicle_command', qos) self.odom_sub = self.create_subscription( VehicleOdometry, '/fmu/out/vehicle_odometry', self.odom_cb, qos) self.timer = self.create_timer(0.05, self.timer_cb) # 20Hz self.counter = 0 def timer_cb(self): self.publish_offboard_mode() self.publish_setpoint() self.counter += 1 if self.counter == 20: # 1秒后解锁 self.arm() if self.counter == 40: # 2秒后切Offboard self.set_offboard_mode() def publish_offboard_mode(self): msg = OffboardControlMode() msg.position = True msg.timestamp = self.get_clock().now().nanoseconds // 1000 self.offboard_pub.publish(msg) def publish_setpoint(self): msg = TrajectorySetpoint() msg.position = [0.0, 0.0, -5.0] # 飞到5米高 msg.yaw = 0.0 msg.timestamp = self.get_clock().now().nanoseconds // 1000 self.setpoint_pub.publish(msg) def arm(self): msg = VehicleCommand() msg.command = 400 # ARM msg.target_system = 1 msg.target_component = 1 msg.source_system = 1 msg.source_component = 1 msg.from_external = True msg.timestamp = self.get_clock().now().nanoseconds // 1000 self.command_pub.publish(msg) def set_offboard_mode(self): msg = VehicleCommand() msg.command = 176 # SET_MODE msg.param1 = 1.0 msg.param2 = 6.0 # OFFBOARD msg.target_system = 1 msg.target_component = 1 msg.source_system = 1 msg.source_component = 1 msg.from_external = True msg.timestamp = self.get_clock().now().nanoseconds // 1000 self.command_pub.publish(msg) def odom_cb(self, msg): pass # 这里可以加闭环控制逻辑 def main(): rclpy.init() node = OffboardControl() rclpy.spin(node) rclpy.shutdown()这个框架跑起来后,飞控会解锁、切Offboard、飞到5米高度悬停。实际项目中,odom_cb里要加位置闭环,publish_setpoint里要根据目标位置动态计算setpoint。
8.3 安全策略:失控保护与紧急降落
Offboard控制最怕的是通信中断。ROS2节点挂了,setpoint不再发布,PX4检测到超时后会自动退出Offboard模式。但退出后飞控处于什么状态取决于COM_OBL_RC_ACT参数,默认是Position模式悬停。
更安全的做法是配置失控保护。在PX4参数里设置COM_OBL_RC_ACT为Land,这样Offboard超时后自动降落。同时设置COM_RC_LOSS_T为1秒,遥控器失联也触发保护。
ROS2端也要做心跳检测。如果Agent挂了,ROS2节点应该能检测到并执行紧急降落。实现方式是在odom_cb里更新最后接收时间,定时器里检查超时,超时后发布降落指令。
注意事项:所有安全策略都要在地面测试充分后再上飞行。我见过有人直接在空中测试失控保护,结果参数配错,飞机直接翻跟头。地面测试时拆桨,用QGC看状态切换是否正确。
9. 从单机到集群的扩展思路
单机跑通后,如果要做多机协同,Micro XRCE-DDS的架构也能支持。每架无人机跑一个Agent,通过不同的DDS域ID或者命名空间隔离。机载电脑之间通过ROS2的DDS发现机制自动组网,不需要额外的通信中间件。
关键配置是ROS_DOMAIN_ID和ROS_LOCALHOST_ONLY。多机通信时ROS_LOCALHOST_ONLY要设为0,域ID每架不同。如果机载电脑之间网络不通,可以用ROS2的discovery server做集中式发现。
实际部署时,我建议每架无人机的Agent用独立的systemd服务,配置文件里写死串口设备和域ID。这样上电后自动启动,不需要人工干预。机载电脑之间的时间同步用chrony做,确保多机数据融合时时间戳一致。
这套方案我在三个机的编队飞行中验证过,端到端延迟在5ms以内,位置控制精度能到厘米级。当然多机场景下的无线干扰、信道竞争这些问题需要另外考虑,那又是另一个话题了。