尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ROS2国产化迁移实战:DDS适配、平台移植与QoS调优

ROS2国产化迁移实战:DDS适配、平台移植与QoS调优 1. 从一次国产化替代项目说起为什么ROS2生态值得重新审视去年下半年我参与了一个工业巡检机器人的国产化替代项目客户的要求很明确主控从原来的x86工控机换成国产处理器平台操作系统从Ubuntu换成国产Linux发行版但上层机器人软件栈要保持功能一致。接到这个需求的时候团队第一反应是把ROS2重新编译一遍不就行了结果真正动手才发现事情远没有想象中那么简单。ROS2和ROS1最大的区别在于ROS2从设计之初就把**DDSData Distribution Service**作为底层通信中间件而不是像ROS1那样自己造一套TCPROS/UDPROS的轮子。这个架构决策在国产化场景下既是福音也是挑战——福音在于DDS本身有多个实现可以选挑战在于不同DDS实现在国产平台上的适配成熟度差异巨大。这篇文章想聊的不是ROS2怎么装这种入门话题网上教程一抓一大把。我想聊的是当你真正要把ROS2落到国产化平台上时整个生态链条上哪些环节会出问题哪些选择会决定项目成败以及我自己踩过的那些坑。适合已经对ROS2有基本了解、正在做或准备做国产化迁移的工程师阅读也适合想理解ROS2生态全貌的技术决策者参考。2. ROS2的通信骨架DDS到底在中间做了什么2.1 为什么ROS2要抛弃ROS1的自研通信层ROS1时代的通信机制说白了就是一套基于Master节点的集中式架构。所有节点启动后都要向Master注册通信双方通过Master牵线搭桥然后建立TCP或UDP连接。这套机制在实验室环境里跑得挺好但一到生产环境就暴露问题Master是单点故障一旦挂了整个系统就瘫了跨网段通信配置复杂QoS策略几乎没有实时性无法保障。ROS2的决策层想得很清楚——通信这件事不应该由机器人框架自己来做应该交给专业的中间件。DDS在工业、军工、航空领域已经用了十几年有成熟的QoS体系、有去中心化的发现机制、有丰富的传输层适配。于是ROS2把DDS作为底层通信抽象层自己在上面封装了一层RMWROS Middleware Interface让用户可以灵活切换不同的DDS实现。这个架构带来的直接好处是通信的可靠性和实时性由DDS保证ROS2只需要关注机器人领域的抽象。但代价是ROS2的行为在很大程度上取决于你选了哪个DDS实现。2.2 主流DDS实现的横向对比目前在ROS2生态中最常用的DDS实现有这么几个我整理了一张对比表都是实际项目中验证过的感受DDS实现许可证国产平台适配资源占用QoS支持典型场景Fast DDSApache 2.0良好中等完整通用机器人、工业控制Cyclone DDSEclipse良好较低完整嵌入式、资源受限平台RTI Connext商业需商务合作较高最完整军工、航空、高安全场景GurumDDS商业一般中等完整韩国市场为主Fast DDS是ROS2的默认RMW实现eProsima维护社区活跃度高文档也相对完善。Cyclone DDS由Eclipse基金会维护在嵌入式场景下表现更好内存占用明显低于Fast DDS。RTI Connext是DDS领域的老牌商业产品QoS策略最丰富但授权费用不低。在国产化项目中我的建议是优先考虑Fast DDS和Cyclone DDS这两个开源实现。原因很简单源码可控遇到问题可以自己排查和修改不依赖国外厂商的技术支持响应。2.3 DDS的发现机制与国产网络环境的碰撞DDS的节点发现机制分为两种简单发现协议SDP和发现协议配置DPD。默认情况下DDS使用多播进行节点发现这在实验室的单一网段里没问题但国产化项目往往涉及复杂的网络拓扑——可能有多个网段、可能有防火墙策略、可能有多播被禁用的情况。我遇到过一个典型案例客户的国产化工控机部署在轨道交通的AFC系统中网络环境里多播被交换机策略禁掉了。ROS2节点启动后互相发现不了ros2 node list永远是空的。排查了半天才定位到是多播的问题。解决方案有两种一是配置DDS使用单播发现指定对方的IP地址和端口二是部署一个Discovery Server所有节点通过Server来交换发现信息。前者适合节点数量少、拓扑固定的场景后者适合节点多、动态变化的场景。以Fast DDS为例配置单播发现的XML片段大概长这样participant profile_nameunicast_participant rtps builtin discovery_config discoveryProtocolSIMPLE/discoveryProtocol initialPeersList locator udpv4 address192.168.1.100/address /udpv4 /locator /initialPeersList /discovery_config /builtin /rtps /participant然后在环境变量里指定这个配置文件export FASTRTPS_DEFAULT_PROFILES_FILE/path/to/fastdds_profile.xml注意单播发现需要每个节点都配置其他节点的地址节点数量多了之后维护成本很高。如果节点超过10个建议直接上Discovery Server方案。3. 国产化平台上的ROS2移植从编译到跑通3.1 国产处理器平台的现状与选择国产化替代绕不开处理器选型。目前主流的国产处理器平台包括龙芯、飞腾、鲲鹏、兆芯、海光等架构上分MIPS、ARM、x86几条路线。ROS2官方支持的是x86_64和aarch64其他架构需要自己移植。我实际接触过的是龙芯3A5000和飞腾FT-2000/4这两个平台。龙芯是LoongArch架构飞腾是ARMv8架构。飞腾的移植难度明显低于龙芯因为aarch64是ROS2官方支持的目标架构大部分软件包都有现成的二进制或者可以顺利从源码编译。龙芯的LoongArch架构则需要大量手动移植工作很多依赖库需要自己打补丁。这里有一个经验如果你的项目对实时性要求不是极端苛刻优先选ARM架构的国产平台。生态成熟度差距是实打实的不是靠技术能力能完全弥补的。3.2 源码编译ROS2的关键步骤与常见报错在国产平台上安装ROS2基本只有源码编译一条路。二进制包要么没有对应架构的要么版本太旧。源码编译ROS2的流程大致如下第一步是安装依赖。ROS2的依赖管理用rosdep但这个工具本身需要Python环境而且会从境外服务器拉取依赖列表。在国产化环境中通常需要提前把依赖列表缓存到本地或者手动安装依赖。# 初始化rosdep需要提前配置好本地源或代理 sudo rosdep init rosdep update # 安装依赖 rosdep install --from-paths src --ignore-src -y --skip-keys fastcdr rti-connext-dds-6.0.1 urdfdom_headers第二步是编译。ROS2的编译工具是colcon底层调用CMake。在国产平台上编译时最常见的报错集中在几个方面编译器版本不匹配ROS2 Humble要求GCC 9以上Jazzy要求GCC 11以上。国产Linux发行版自带的GCC版本往往偏低需要手动升级。Python版本问题ROS2大量使用Python脚本Python版本不对会导致各种奇怪的错误。建议用系统自带的Python3不要自己编译安装。第三方库缺失比如Fast-CDR、TinyXML2、console_bridge这些需要提前装好。编译命令本身不复杂colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPERelease但实际跑起来在龙芯平台上编译整个ROS2桌面版花了将近4个小时中间还断了好几次。建议用--packages-select参数分批编译先编译核心包确认没问题再编译其他包。3.3 国产Linux发行版上的环境配置陷阱国产Linux发行版如统信UOS、麒麟OS大多基于Debian或RHEL衍生但都有各自的定制。在配置ROS2环境时有几个坑是共通的locale设置问题。ROS2对locale有要求必须是UTF-8。国产系统默认的locale可能是zh_CN.GBK或者zh_CN.GB18030会导致ROS2启动时报编码错误。解决方法是在~/.bashrc里显式设置export LANGen_US.UTF-8 export LC_ALLen_US.UTF-8动态库路径问题。国产系统有时候会把一些库放在非标准路径下ROS2运行时找不到。需要在/etc/ld.so.conf.d/下添加对应的路径配置文件然后执行sudo ldconfig刷新缓存。防火墙与SELinux。部分国产系统默认开启了防火墙或SELinux会拦截DDS的通信端口。开发阶段建议先临时关闭确认ROS2能正常通信后再逐条添加规则。4. ros2_control国产化场景下的硬件抽象层适配4.1 ros2_control的架构逻辑ros2_control是ROS2中负责硬件抽象和控制的框架它的设计思路是把硬件驱动和控制器逻辑解耦。整个架构分三层Hardware Interface层负责和实际硬件通信读取关节状态、下发控制指令。这一层需要针对具体硬件写驱动。Controller Manager层管理控制器的生命周期负责在硬件和控制器之间转发数据。Controller层实现具体的控制算法比如关节轨迹控制、差分驱动控制等。在国产化项目中Controller Manager和Controller层通常不需要改动直接复用ROS2社区的实现。需要重点适配的是Hardware Interface层因为这一层直接和国产硬件打交道。4.2 写一个国产硬件接口的实操要点ros2_control的硬件接口用C编写继承hardware_interface::SystemInterface基类需要实现on_init、on_configure、on_activate、on_deactivate、read、write这几个方法。在国产平台上写硬件接口有几个实际经验通信方式的选择。国产工控硬件常用的通信方式有EtherCAT、CAN、串口、Modbus等。EtherCAT在国产平台上的主站实现是个难点建议优先考虑CAN或串口驱动成熟度更高。实时性的保障。read和write方法在实时循环中调用不能有阻塞操作。如果硬件通信有延迟需要在on_configure阶段做好缓冲在read/write中只做数据搬运。单位换算的处理。国产硬件的编码器分辨率、减速比等参数和进口硬件可能不同需要在硬件接口里做好单位换算保证上层控制器的输入输出单位一致。一个简化的硬件接口示例#include hardware_interface/system_interface.hpp class MyRobotHardware : public hardware_interface::SystemInterface { public: hardware_interface::CallbackReturn on_init(const hardware_interface::HardwareInfo info) override { // 初始化通信接口读取硬件参数 // 根据info中的joint数量分配状态和命令数组 return hardware_interface::CallbackReturn::SUCCESS; } hardware_interface::return_type read(const rclcpp::Time , const rclcpp::Duration ) override { // 从硬件读取关节位置、速度、力矩 // 注意这里不能有阻塞操作 return hardware_interface::return_type::OK; } hardware_interface::return_type write(const rclcpp::Time , const rclcpp::Duration ) override { // 向硬件下发控制指令 return hardware_interface::return_type::OK; } };4.3 控制器配置文件的国产化适配ros2_control的控制器通过YAML文件配置在国产化项目中这个配置文件需要根据实际硬件参数调整。常见的配置项包括joints关节名称列表必须和URDF中的定义一致command_interfaces命令接口类型如position、velocity、effortstate_interfaces状态接口类型pid参数如果使用PID控制器需要根据国产电机的特性重新整定我遇到过一个坑国产电机的编码器分辨率是2500线四倍频后是10000脉冲/转而原来的进口电机是17位绝对值编码器分辨率差了十几倍。如果不调整PID参数控制效果会非常差表现为低频振荡或者响应迟钝。5. 国产化迁移中的QoS策略与实时性调优5.1 ROS2 QoS策略的核心参数QoS是DDS提供的一套通信质量保障机制ROS2通过RMW层暴露了这些配置。核心参数包括ReliabilityRELIABLE保证消息不丢失BEST_EFFORT不保证。传感器数据通常用BEST_EFFORT控制指令用RELIABLE。DurabilityTRANSIENT_LOCAL让后加入的订阅者能收到历史消息VOLATILE只收新消息。地图、参数这类静态数据用TRANSIENT_LOCAL。HistoryKEEP_LAST只保留最近N条KEEP_ALL保留所有。根据内存情况选择。Depth配合KEEP_LAST使用指定保留的消息数量。在国产化项目中QoS配置不当是导致通信异常的常见原因。比如发布者用RELIABLE订阅者用BEST_EFFORT两者不兼容通信直接建立不起来。5.2 国产网络环境下的QoS调优实践国产化项目的网络环境往往比实验室复杂QoS调优需要结合实际网络状况。我的经验是先测网络延迟和丢包率。用ping和iperf测一下基础网络性能如果丢包率超过1%RELIABLE模式会导致大量重传反而降低有效吞吐量。根据数据特性选择QoS。激光雷达、摄像头这类高频大数据量的传感器用BEST_EFFORT加KEEP_LASTdepth设小一点比如5避免内存暴涨。控制指令用RELIABLE加KEEP_LASTdepth设1保证实时性。注意DDS的资源限制。Fast DDS默认的内存池大小可能不够用在国产平台上如果内存有限需要调整ResourceLimitsQosPolicy中的max_samples、max_instances等参数。5.3 实时性调优的系统级手段ROS2的实时性不仅取决于DDS还和操作系统调度、CPU亲和性、内存锁定等因素有关。在国产Linux平台上可以做这些优化设置CPU亲和性把关键节点绑定到特定CPU核心避免上下文切换开销。用taskset命令或者pthread_setaffinity_np接口。使用实时调度策略把关键线程的调度策略设为SCHED_FIFO优先级设高。需要root权限或者CAP_SYS_NICE能力。锁定内存用mlockall防止内存页被换出减少缺页中断。在国产平台上如果内存紧张这个操作要谨慎。调整内核参数比如net.core.rmem_max、net.core.wmem_max调大网络缓冲区vm.swappiness设低减少交换。这些优化手段在x86平台上效果明显在国产平台上效果因硬件而异。建议先用ros2 topic hz和ros2 topic delay测一下基线性能再做优化用数据说话。6. 生态工具链的国产化适配现状6.1 可视化与仿真工具的替代方案ROS2生态里的可视化工具主要是RViz2仿真工具主要是Gazebo。这两个工具在国产平台上的适配情况RViz2基于Qt和OGRE在国产Linux上编译难度不大但需要确保显卡驱动正常。国产平台的显卡驱动是个老大难问题尤其是集成显卡。如果RViz2跑不起来可以先用rqt系列工具做基础调试或者用Foxglove Studio通过网络远程可视化。Gazebo的适配难度更大它依赖的物理引擎、渲染引擎在国产平台上的性能表现参差不齐。如果Gazebo跑不动可以考虑用Webots或者Ignition Gazebo现在叫Gazebo Sim后者的架构更现代移植性更好。6.2 常用命令行工具的兼容性ROS2的命令行工具ros2 topic、ros2 node、ros2 service等基于Python和rclpy在国产平台上基本都能正常工作。但有几个细节需要注意ros2 bag录制和回放依赖SQLite3确保SQLite3版本不要太旧ros2 launch依赖Python的launch库如果Python版本不对可能会报语法错误ros2 doctor可以用来诊断环境问题国产化迁移初期建议经常跑一下6.3 国产化工具链的补充除了ROS2官方工具国产化项目中还需要一些辅助工具系统监控htop、iotop、nethogs这些在国产Linux上都有用来监控CPU、IO、网络性能分析perf、gprof、valgrind用来定位性能瓶颈日志分析ROS2的日志默认输出到~/.ros/log可以用rqt_console查看也可以用grep直接搜如果国产平台上缺少某个工具可以尝试从源码编译大部分开源工具在国产平台上的编译难度都不大。7. 踩坑实录那些让我加班到凌晨的问题7.1 动态库版本冲突导致的段错误这个问题发生在龙芯平台上。ROS2编译成功后运行ros2 topic list直接段错误。用gdb跟了半天发现是libfastrtps.so和系统自带的libboost版本不兼容。国产系统自带的Boost版本是1.67而ROS2 Humble要求1.71以上。系统里同时存在两个版本的Boost动态链接器加载了错误的版本。解决方法是在编译时显式指定Boost路径并且在运行时通过LD_LIBRARY_PATH确保加载正确版本。更彻底的方法是把ROS2依赖的Boost静态链接进去避免运行时冲突。7.2 多播禁用导致的节点发现失败前面提过这个案例这里补充一下完整的排查过程。现象是ros2 node list为空但ros2 topic list能看到话题。这说明节点本身启动了但发现机制有问题。排查步骤用ros2 doctor检查提示发现机制可能有问题用tcpdump抓包发现没有多播包发出检查网络配置发现交换机的多播策略被禁用改用单播发现问题解决这个问题的教训是国产化项目的网络环境一定要提前确认不要假设多播可用。7.3 实时线程优先级设置失败在国产Linux上设置SCHED_FIFO调度策略时pthread_setschedparam返回EPERM错误。原因是国产系统的默认ulimit -r是0不允许普通用户设置实时优先级。解决方法有两种一是用root运行二是修改/etc/security/limits.conf添加* soft rtprio 99 * hard rtprio 99然后重新登录生效。注意实时优先级设置过高可能导致系统无响应建议从低优先级开始测试。7.4 ros2_control控制器加载失败ros2_control的控制器通过插件机制加载在国产平台上经常遇到插件加载失败的问题。错误信息通常是Failed to load controller或者Could not find library。原因通常是插件的动态库路径没有正确设置。ros2_control通过pluginlib查找插件pluginlib依赖ament_index来定位资源。在国产平台上如果AMENT_PREFIX_PATH环境变量设置不对就会找不到插件。解决方法是确保source了ROS2的setup.bash并且检查AMENT_PREFIX_PATH是否包含了工作空间的install目录。8. 从项目实战中沉淀的几条经验做了几个国产化ROS2项目之后有些经验是反复验证过的这里分享几条第一不要追求最新版本。ROS2的版本迭代很快但国产平台的适配往往滞后。Humble是目前最成熟的选择Jazzy虽然新但在国产平台上的适配还在完善中。选一个稳定的版本把精力放在业务逻辑上。第二编译环境要隔离。国产平台上往往同时存在多个项目的依赖很容易冲突。建议用Docker或者chroot创建独立的编译环境避免污染系统环境。如果国产平台不支持Docker可以用debootstrap手动创建一个干净的根文件系统。第三性能测试要趁早。不要等到功能都开发完了才做性能测试。在项目初期就搭好测试环境测一下通信延迟、控制周期、CPU占用这些关键指标。如果发现性能不达标早期调整的代价远小于后期重构。第四文档和脚本要同步维护。国产化项目的环境配置往往很复杂涉及大量的环境变量、配置文件、编译参数。建议把所有的配置步骤写成脚本并且维护一份详细的文档。否则换个人来接手光是配环境就要花好几天。第五保持和社区的联系。虽然国产化项目有很多特殊性但大部分问题在ROS2社区里都能找到线索。遇到问题先搜一下Discourse和GitHub Issues往往能少走很多弯路。最后说一个我个人的判断ROS2在国产化平台上的生态成熟度大概相当于五年前ROS2在x86平台上的水平。核心功能可用但工具链和第三方包的适配还需要时间。如果你的项目周期比较紧建议在架构设计阶段就预留足够的适配时间不要低估国产化迁移的工作量。
返回列表