先叠甲,本人机器人工程大四能力有限,写博客只为加深对dds,ros2,rtps的理解,部分地方认知浅陋,工程落地谨慎使用。欢迎大家指正!(求内推slam算法或者ros2应用开发,有智能车国二+人工智能算法精英赛国一+两段强相关机器人slam领域实习+院三好+校三等奖学金+省奖若干,主播想run),ok,也是废话不多说直接上源码:https://github.com/zuyichengkuaishisu/cyclonedds_ws.git

首先介绍一下ros2,dds,rtps的关系

先区分一下DDS和RTPS

特性

DDS (数据分发服务)

RTPS (实时发布-订阅协议)

定位

高层通信标准和模型

底层通信协议和实现

关注点

做什么 (What) - 定义API、QoS、数据模型

怎么做 (How) - 定义网络传输格式、发现机制

关系

规范者,提出需求

实现者,满足需求,是DDS的“有线协议”

这里的RTPS和嵌入式里的RTOS看样子很像但是完全不是一个东西哦,主播个人觉得要想淌具身智能这个风口还是有必要了解一下嵌入式的基本信息的。

维度

RTPS (Real-Time Publish-Subscribe)

RTOS (Real-Time Operating System)

本质定义

通信协议 (网络协议/线协议)

操作系统 (系统软件内核)

核心职责

负责数据的传输、序列化、发现和路由

负责硬件资源管理、任务调度、中断处理

所属层级

位于应用层与网络层之间 (中间件协议)

位于硬件层之上 (最底层的系统软件)

实时性含义

保证数据在网络传输中低延迟、不丢包

保证程序指令在CPU执行时的确定性响应

典型代表

Fast DDS 的实现协议、Cyclone DDS 底层

FreeRTOS, VxWorks, QNX, RT-Linux

下面介绍ROS2和DDS,RTPS以及跟下一层传输层的关系

层级

核心组件

理解修正/补充

ROS2应用层

用户代码

调用 API,定义数据结构 (IDL)。

Fast DDS 层

DomainParticipant

一个程序可以有多个 Participant。它是工厂,负责生产 Publisher/Subscriber。

Topic

它是连接 Pub 和 Sub 的纽带,必须名字和类型一致。

RTPS 层

RTPSWriter/Reader

负责把上层的数据“翻译”成网络协议包,处理“发现”和“可靠性”。

传输层

UDP / TCP / SHM

真正的物理传输。RTPS 会自动选择最佳路径(比如同机走 SHM,跨机走 UDP)。

看到这里有读者可能会疑惑为啥FAST DDS下一个节点可以有多个 Participant?但实际ROS2是在同一进程下只能有一个ROS_DOMAIN_ID,而且ROS2规定了一个节点只能对应一个Participant,而一个Participant只能进入一个ROS_DOMIAN,所以在不同进程下不同的ROS_DOMAIN不可以相互通信,所以我这里可以通过在不同终端设置不同的 export ROS_DOMAIN_ID,轻松实现同一台机器上运行多个隔离的 ROS 2 系统,来完成多机协同的控制。下面是不同的ROS_DOMAIN通信测试,验证ROS_DOMAIN的进程隔离。ROS2的这些封装极大的减少了学习成本。

开始实战

明确目标:要在虚拟机(10.0.0.115)看到狗子(10.0.0.114)的话题,实现内外网的隔离,而且原本的ssh操作还要保持正常。

先装网卡

由于go2 edu 默认只有一个eth0网卡,狗子内部通过网线连接jesion和下位机,jesion在eth0的ip:192.168.123.18,下位机:好像是192.168.123.161/162。宇树不提供下位机的ROS_DOMAIN服务接口所以如果所有狗子连接上192.168.123.xx网段的网,由于dds的发现机制会导致控制命令的混乱,这个是无法通过更改ROS_DOMAIN_ID解决的:在jesion改会导致无法与下位机通信,我觉得唯一好的方法是加装usb无线网卡分配非192.168.123.xx网段的网络,做内外网的隔离。这里博主选用了绿联的ac1300(v我50广告费),按照教程安装好ubuntu20.04的驱动。连接好10.0.0.xx网段,记得将优先级设高,不然系统不知道到底该在哪个网卡上网。

测试ssh

ok,大功告成,ssh unitree@10.0.0.114看看topic,都有,看样子成功了一半,在echo一下,完了

没有数据,而且cpu占用拉满,看看top,发现是ros2占用了7个核心,逆天,绝对是有问题。

尝试解决:宇树采用了cyclone DDS(不知道为啥不用fastDDS,fast各方面都要比cyclone好,而且天生适配ROS2),更改一下/home/unitree/cyclonedds_ws/cyclondds.xml

<General>
  <Interfaces>
    <NetworkInterface name="eth0" priority="default" multicast="default" />
    <NetworkInterface name="wlan0" priority="default" multicast="default" />
  </Interfaces>
  <AllowMulticast>spdp</AllowMulticast>
</General>

export一下,环境变量建议使用 **`CYCLONEDDS_URI=file:///绝对路径/cyclonedds.xml`**,避免 `~` 未展开,再echo看看效果:

逆天,只有一条数据,cpu占用依旧拉满,再看top,还是ros2的锅。这里忘了截图了。

不过好歹是有一条数据了,说明方向是对了,那是cycloneDDS的问题吗?

分析一下,dds在reliable模式在接受方会在接受到一条消息后返回一条ack应答给发送方,所以是ack应答没有收到导致的bug:只有一条消息。

简单来说,问题出在 “多网卡冗余” 遇上了 “Reliable(可靠)” 传输策略。

核心冲突:多路径传输 vs. 单路径确认 (ACK)

DDS 的 Reliable 模式要求接收方必须向发送方发送确认(ACK),发送方才能继续发送后续数据。当配置了双网卡且开启冗余时,问题就来了:

  • 发送路径不一致:由于开启了 RedundantNetworking 或多播接收,CycloneDDS 可能会利用两张网卡同时发送数据包(或者根据路由表随机选择)。
  • 回包路径迷失
    • 场景 A(本机双网卡):如果发送方和接收方在同一台机器(或同一局域网),接收方收到数据包后,操作系统需要决定通过哪张网卡回传 ACK。
    • 场景 B(跨设备):如果接收方在另一台设备,它收到包后,会根据自己的路由表回包。如果路由表优先走的是 wlan0但是又必须优先走wlan0(因为我们想无线访问狗子),而发送方的 CycloneDDS 正在 eth0 上等待 ACK,发送方就会认为“包丢了”,从而停止发送(断流)。
  • 结果:发送方因为没收到 ACK 而卡住,接收方因为没收到后续数据而“只显示一条”或断流。

多播 (Multicast) 的陷阱

  • 多播的特性:多播包会发送到局域网内的所有接口。
  • 接收端的困惑:如果一张网卡(如 eth0)连接的是有线网络(宇树机器人),另一张(wlan0)连接的是无线网络(你的路由器),接收端可能会从两个接口同时收到同一个多播包。
  • 资源竞争:DDS 实体需要处理这些重复的数据包,这会导致内部状态机混乱或 CPU 占用飙升,尤其是在处理高频数据(如点云)时。

解决问题(矛盾)

看样子不能配置双网卡,在.bashrc改一下,我们为每一个网卡写一个xml,并在打开终端时进行配置

source ~/cyclonedds_ws/install/setup.bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
# CycloneDDS:交互选择 1=有线(eth0) / 2=无线(wlan0)。已预先 export DDS_NET_MODE 则不再询问。
if [[ $- == *i* ]] && [[ -z "${DDS_NET_MODE:-}" ]]; then
  echo "CycloneDDS 网卡: 1=有线(eth0)  2=无线(wlan0)  [回车默认 1]"
  read -r _dds_pick
  case "${_dds_pick:-1}" in
    2) export DDS_NET_MODE=wireless ;;
    *) export DDS_NET_MODE=wired ;;
  esac
  unset _dds_pick
fi
export DDS_NET_MODE="${DDS_NET_MODE:-wired}"
_CYC_WS="/home/unitree/cyclonedds_ws"
case "${DDS_NET_MODE,,}" in
  wireless|wifi|wlan|2)
    export CYCLONEDDS_URI="file://${_CYC_WS}/cyclonedds_wireless.xml"
    ;;
  wired|eth|ethernet|1|"")
    export CYCLONEDDS_URI="file://${_CYC_WS}/cyclonedds_wired.xml"
    ;;
  *)
    export CYCLONEDDS_URI="file://${_CYC_WS}/cyclonedds_wired.xml"
    ;;
esac
unset _CYC_WS

下面按照名称添加相应的xml,不想贴上来了,详情见源码。

试验一下:

topic list可以看到,但是hz没有,而且cpu占用也下来了,看样子之前分析的没错。

后面再选用eth0也成功看到下位机传来的数据。

现在我们可以远程看到在jesion启动的节点的数据,

也可以使用ROS_DOMIAN_ID来进行多机的隔离,而且运动控制(宇树运动控制go2和b2竟然是一样的!同事控制b2经常会到我的go2上)也不会混乱了。

未完待续。(后面主播将仔细研究一下单播定向机制,这个问题只能说是半解决)

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐