本文面向有嵌入式 C/RTOS 背景、首次接触 ROS2 的工程师,系统讲解 ROS2 的版本、架构、源码获取与构建、开发模型。写作时已核实版本信息:截至 2026-08,最新 LTS 为Lyrical Luth(2026-05 发布)。一、ROS2 是什么ROS2(Robot Operating System 2)不是操作系统,而是一套构建机器人应用的中间件 工具链 生态:中间件层:基于 DDS(Data Distribution Service)或 Zenoh 的发布/订阅消息总线,提供跨进程、跨机器的节点通信工具链:构建系统(colcon/ament)、包管理(rosdep)、可视化(rviz2)、录制(ros2 bag)、launch 编排生态:数千个功能包(导航 Nav2、感知、运动规划、SLAM、驱动…)与 ROS1 的核心区别:维度ROS1ROS2中间件自研 TCPROS/UDPROS,中心化 masterDDS/Zenoh,去中心化,无 master实时性无保证支持 RTOS、实时调度、QoS 策略多机需配置天然支持(DDS 发现)安全无支持 DDS Security(SLS/TLS)平台Linux 为主Linux/Windows/macOS/嵌入式生命周期无受控生命周期节点对嵌入式背景的你要点:ROS2 节点跑在 Linux/POSIX 上(不是 MCU),用 C(rclcpp)或 Python(rclpy)。MCU 上跑的是 micro-ROS(ROS2 的精简分支,通过 DDS-XRCE 桥接)。GNSS 驱动这类跑在主机/树莓派/工控机上、读串口、发消息的场景,正是 ROS2 的典型用法。动手实操:ROS2 本质、colcon 编译、从零开发一个包、工具链使用见 [[ROS2实操指南-是什么怎么编译开发使用]]。二、版本全景(已核实至 2026-08)ROS2 版本按字母顺序命名(考古字母表),每年 5 月发布一个新版,LTS 版本支持 5 年。2.1 活跃版本版本代号发布类型EOL主要平台LyricalLyrical Luth(lyrical)2026-05LTS2031-05Ubuntu 26.04 / RHEL 10 / WindowsKiltedKilted Kaiju(kilted)2025-05普通2026-11Ubuntu 24.04 / RHEL 9 / WindowsJazzyJazzy Jalisco(jazzy)2024-05LTS2029-05Ubuntu 24.04 / RHEL 9 / WindowsHumbleHumble Hawksbill(humble)2022-05LTS2027-05Ubuntu 22.04 / RHEL 8 / WindowsRollingRolling Ridley(rolling)滚动开发分支—永远最新(不稳定)2.2 已 EOL 版本版本代号发布类型EOL平台FoxyFoxy Fitzroy(foxy)2020-06LTS2023-06(已 EOL)Ubuntu 20.04IronIron Irwini(iron)2023-05普通2024-11Ubuntu 22.04GalacticGalactic Geochelone2021-05普通2022-12Ubuntu 20.04更早Eloquent/Dashing/Crystal/Bouncy/Ardent2017-2019—已 EOL—⚠️ 关于 Foxy:Foxy 已于 2023-06 停止官方维护,不再收到安全/bug 修复,Ubuntu 20.04 也已于 2025-04 EOL。除非客户目标机是已有 Ubuntu 20.04 旧系统、无法升级且不关心长期维护,否则不建议新项目选 Foxy。若被迫用 Foxy,需在方案中明确标注 EOL 风险,并争取迁移到 Humble。2.3 开发中Makoa Mata-mata(makoa):计划 2027-05 发布,当前在 Rolling 上开发2.4 选版建议现状:方案待定,版本候选包括Foxy / Humble / Jazzy / Lyrical。下面给出各候选的取舍,供调研评估。候选Ubuntu维护至评估意见Foxy20.04❌ 已 EOL(2023-06)不推荐新选。仅当客户旧机无法升级时考虑,须标注 EOL 风险Humble22.042027-05短期可接受,生态最成熟;但 Ubuntu 22.04 已 EOL(2025-04),剩约 1 年支持,有过渡压力Jazzy(LTS)24.042029-05推荐。长期支持,生态齐全,UM982 社区驱动已验证可跑Lyrical(LTS)26.042031-05最新 LTS,长期最优;但要求 Ubuntu 26.04,需确认目标机能装关键约束:ROS2 版本与 Ubuntu 版本强绑定(Foxy→20.04,Humble→22.04,Jazzy→24.04,Lyrical→26.04)。调研第一步:摸清客户/目标机的 Ubuntu 版本——这直接决定 ROS2 distro 可选范围。GNSS 驱动作为功能包发布时,也要注明支持哪个 distro。三、整体架构┌───────────────────────────────────────────────────────────┐ │ 应用层:你的节点(GNSS 驱动节点、导航、感知…) │ ├───────────────────────────────────────────────────────────┤ │ 客户端库: rclcpp(C) rclpy(Python) rclrs(Rust) │ ├───────────────────────────────────────────────────────────┤ │ rcl(Ros Client Library,C 接口,与语言无关的核心逻辑) │ ├───────────────────────────────────────────────────────────┤ │ rmw(RMW,中间件抽象接口 —— 切换 DDS 实现的统一抽象) │ ├───────────────────────────────────────────────────────────┤ │ DDS 实现: Fast DDS / Cyclone DDS / Zenoh / Connext │ │ (rmw_fastrtps / rmw_cyclonedds / rmw_zenoh …) │ ├───────────────────────────────────────────────────────────┤ │ OS: Linux / Windows / macOS │ └───────────────────────────────────────────────────────────┘3.1 分层职责DDS / Zenoh(底层通信):负责序列化、传输、服务发现、QoS。ROS2 不自己造轮子,直接用工业级 DDS。rmw(RMW Interface):中间件抽象层。屏蔽不同 DDS 实现差异,代码可跨 DDS 移植。切换 DDS 只需改环境变量,无需重编译(大部分情况)。rcl:C 语言写的核心库,封装 rmw,提供节点、话题、服务、执行器等原语。所有语言客户端都基于 rcl。rclcpp / rclpy:面向 C/Python 的客户端库,你日常用它们写节点。3.2 三种 RMW 实现RMW实现特点rmw_fastrtps_cppeProsima Fast DDS历史默认,功能全,配置项多rmw_cyclonedds_cppEclipse Cyclone DDS性能稳定,配置简单,社区活跃rmw_zenohEclipse Zenoh轻量、低开销、跨网络友好,新版重点方向切换方式(无需重编译):exportRMW_IMPLEMENTATIONrmw_cyclonedds_cpp# 或 rmw_fastrtps_cpp / rmw_zenoh对嵌入式背景:rmw 抽象相当于 HAL 层——你换 DDS 实现就像换底层驱动,上层 rclcpp 代码不动。GNSS 驱动开发通常用默认 DDS 即可,默认配置足够。四、核心概念4.1 节点(Node)节点是 ROS2 的基本执行单元,一个进程可含多个节点。每个节点:有名字和命名空间(/gnss_driver)可发布/订阅话题、提供/调用服务、提供动作可有参数(运行时可改的配置)#includerclcpp/rclcpp.hppclassGnssNode:publicrclcpp::Node{public:GnssNode():Node(gnss_driver){// 声明参数、创建发布者、定时器…}};4.2 三种通信原语原语模式典型用途GNSS 场景Topic(话题)发布/订阅,异步,多对多持续数据流发布位置/原始观测量(高频)Service(服务)请求/响应,同步,一对一触发式查询/命令查询固件版本、触发冷启动Action(动作)请求/响应 反馈 可取消,长时间任务导航到点、机械臂运动GNSS 较少用,可选:RTK 基站对齐4.3 消息(Message)接口消息用.msg文件定义,跨语言。ROS2 内置丰富消息类型,位于sensor_msgs、std_msgs、geometry_msgs等包:# sensor_msgs/msg/NavSatFix —— 位置信息标准消息 std_msgs/Header header uint8 status # 状态(无定位/单点/RTK…) float64 latitude # 纬度 float64 longitude # 经度 float64 altitude # 高度客户需求对应 ROS2 标准消息所在包位置信息sensor_msgs/msg/NavSatFixsensor_msgs定向信息(航向)geometry_msgs/msg/Quaternion(姿态)或自定义geometry_msgs原始观测量无标准,通常自定义.msg(RINEX/RTCM 片段)自定义debug 信息std_msgs/msg/String(文本)或自定义结构std_msgs速度geometry_msgs/msg/TwistStampedgeometry_msgs卫星状态sensor_msgs/msg/NavSatStatussensor_msgs重要:能用标准消息就别自定义,这样你的 GNSS 驱动能直接被 Nav2、robot_localization 等下游消费。位置信息务必用NavSatFix,这是 ROS2 生态约定。4.4 QoS(服务质量)ROS2 的 QoS 是相比 ROS1 的重大升级,决定消息传递的可靠性、历史、 deadline:QoS 策略选项GNSS 建议值ReliabilityRELIABLE / BEST_EFFORT位置:RELIABLE;原始观测量高频可用 BEST_EFFORTHistoryKEEP_LAST(N) / KEEP_ALLKEEP_LAST(1~10),丢旧保新DurabilityVOLATILE / TRANSIENT_LOCAL一般 VOLATILE;配置参数用 TRANSIENT_LOCALDeadline时间按定位频率(如 100ms for 10Hz)对传感器数据,BEST_EFFORT KEEP_LAST(1)很常见——丢几帧无所谓,要最新值。这与 ROS1 默认 RELIABLE 不同,是 ROS2 新手常踩坑点(订阅者 QoS 与发布者不匹配会收不到数据)。深入:QoS 的七大策略、匹配规则、事件回调、GNSS 选型见 [[ROS2-QoS详解]]。4.5 参数(Parameter)节点运行时可配置的变量,类型化(int/double/string/bool/array)。可从 launch 文件、YAML、命令行ros2 param set设置。// 声明参数(带默认值)this-declare_parameterstd::string(serial_port,/dev/ttyUSB0);this-declare_parameterint(baudrate,115200);std::string portthis-get_parameter(serial_port).as_string();4.6 生命周期节点(Lifecycle Node)受控启动的节点,有明确状态机:unconfigured → inactive → active → inactive → finalized ↑________↓ (configure/activate/deactivate/cleanup)适合需要有序启停的硬件驱动:配置阶段打开串口,activate 开始发数据,deactivate 停止,cleanup 关串口4.7 执行器(Executor)执行器把回调(订阅、定时器、服务)分发到线程。ROS2 提供:SingleThreadedExecutor:单线程,简单MultiThreadedExecutor:多线程,回调可并发(注意线程安全)StaticSingleThreadedExecutor:实体固定时更高效rclcpp::executors::MultiThreadedExecutor exec;exec.add_node(node);exec.spin();4.8 TF 坐标变换tf2维护一棵坐标系树,广播坐标变换(如base_link → gps),下游用它把 GPS 位置转到车体坐标。GNSS 驱动通常需广播一个静态 TF:gps_link(天线位置)相对base_link(车体原点)用tf2_ros::StaticTransformBroadcaster4.9 Launch 系统用 Python/XML/YAML 描述多节点启动、参数加载、命名空间、重映射。一个 GNSS robot_localization 的 launch 例子:# launch/gnss_bringup.launch.pyfromlaunchimportLaunchDescriptionfromlaunch.actionsimportDeclareLaunchArgumentfromlaunch.substitutionsimportLaunchConfigurationfromlaunch_ros.actionsimportNodedefgenerate_launch_description():returnLaunchDescription([Node(packagemy_gnss_driver,executablegnss_node,namegnss_driver,parameters[{serial_port:/dev/ttyUSB0,baudrate:460800,frame_id:gps_link,publish_rate:10.0,}],remappings[(fix,/gnss/fix)],),])五、源码获取与构建5.1 方式一:apt 二进制安装(推荐入门)最快上手,无需编译 ROS2 本体。# 1. 添加 ROS2 apt 源(Lyrical / Ubuntu 26.04)sudoaptupdatesudoaptinstallsoftware-properties-commoncurl-ysudocurl-sSLhttps://raw.githubusercontent.com/ros/rosdistro/master/ros.key\-o/usr/share/keyrings/ros-archive-keyring.gpgechodeb [arch$(dpkg --print-architecture)signed-by/usr/share/keyrings/ros-archive-keyring.gpg] \ http://packages.ros.org/ros2/ubuntu$(./etc/os-releaseecho$UBUNTU_CODENAME)main\|sudotee/etc/apt/sources.list.d/ros2.list/dev/null# 2. 安装桌面完整版(含 rviz2、demo)sudoaptupdatesudoaptinstallros-lyrical-desktop# 3. Jazzy 用户把上面的 lyrical 换成 jazzy、desktop 同理# 4. source 环境(写入 ~/.bashrc)source/opt/ros/lyrical/setup.bashJazzy:ros-jazzy-desktop,Ubuntu 24.04。Humble:ros-humble-desktop,Ubuntu 22.04。5.2 方式二:源码编译(定制/贡献/学习)当你需要改 ROS2 本体、或想学习源码时用此方式。官方一键脚本(ros2/ros2 仓库):# 1. 准备目录mkdir-p~/ros2_rolling/srccd~/ros2_rolling# 2. 下载 ros2.repos 清单(描述所有子仓库地址)vcsimport--inputhttps://raw.githubusercontent.com/ros2/ros2/rolling/ros2.repos src# 3. 安装依赖sudorosdep init rosdep update rosdepinstall--from-paths src --ignore-src-y--rosdistrorolling# 4. 编译(colcon)colcon build --symlink-install关键仓库:仓库内容ros2/ros2顶层清单仓库,ros2.repos 汇总所有子模块ros2/rclcppC 客户端库源码ros2/rclC 核心库源码ros2/rmwRMW 接口定义ament/ament_cmake构建系统ros2/demos示例节点ros2/sensor_msgs等标准消息定义vcs(vcstool)是 ROS2 用来管理多仓库的工具,类似 git submodule 但更灵活。ros2.repos是个 YAML,列出每个子仓库的 url 和 version。5.3 方式三:rosdep 包依赖管理# 安装功能包的所有依赖(读 package.xml)rosdepinstall--from-paths src --ignore-src-y--rosdistrolyrical5.4 colcon 构建系统colcon 是 ROS2 的构建工具(替代 ROS1 的 catkin),底层调 CMake/ament。colcon build# 编译整个工作区colcon build --packages-select my_gnss_driver# 只编某个包colcon build --symlink-install# 软链接安装,改 Python/launch 不用重编colcon build --cmake-args-DCMAKE_BUILD_TYPERelease# Release 模式sourceinstall/setup.bash# source 工作区(在编译后)工作区结构:~/ros2_ws/ ├── src/ ← 你的源码包(功能包) │ └── my_gnss_driver/ │ ├── package.xml │ ├── CMakeLists.txt │ ├── src/ │ ├── include/ │ ├── msg/ ← 自定义消息 │ └── launch/ ├── build/ ← 编译中间产物(可删) ├── install/ ← 安装产物(source 它) └── log/六、开发模型(C 为主)GNSS 驱动建议用C(rclcpp),因为:性能好、串口/二进制解析方便、嵌入式背景熟悉、可与其他 C 定位融合包无缝集成。6.1 一个功能包的最小结构my_gnss_driver/ ├── package.xml # 包元数据(依赖、作者、license) ├── CMakeLists.txt # 构建配置 ├── src/ │ ├── gnss_node.cpp # 节点 main │ └── nmea_parser.cpp # 协议解析 ├── include/my_gnss_driver/ │ └── nmea_parser.hpp ├── msg/ │ └── RawObservation.msg # 自定义原始观测量消息 └── launch/ └── gnss_bringup.launch.py6.2 package.xml(关键依赖)packageformat3namemy_gnss_driver/nameversion0.1.0/versiondescriptionGNSS 模组 ROS2 驱动/descriptionmaintaineremailyouexample.com赵宇涵/maintainerlicenseApache-2.0/licensebuildtool_dependament_cmake/buildtool_dependdependrclcpp/dependdependsensor_msgs/dependdependgeometry_msgs/dependdependstd_msgs/dependdependtf2_ros/dependtest_dependament_lint_auto/test_dependexportbuild_typeament_cmake/build_type/export/package6.3 CMakeLists.txt(关键片段)cmake_minimum_required(VERSION 3.8) project(my_gnss_driver) find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) find_package(sensor_msgs REQUIRED) find_package(geometry_msgs REQUIRED) find_package(std_msgs REQUIRED) find_package(tf2_ros REQUIRED) find_package(rosidl_default_generators REQUIRED) # 自定义消息 rosidl_generate_interfaces(${PROJECT_NAME} msg/RawObservation.msg ) add_executable(gnss_node src/gnss_node.cpp src/nmea_parser.cpp) ament_target_dependencies(gnss_node rclcpp sensor_msgs geometry_msgs std_msgs tf2_ros) install(TARGETS gnss_node DESTINATION lib/${PROJECT_NAME}) install(DIRECTORY launch DESTINATION share/${PROJECT_NAME}) ament_package()6.4 节点骨架(生命周期节点 串口 多话题)#includerclcpp/rclcpp.hpp#includerclcpp_lifecycle/lifecycle_node.hpp#includesensor_msgs/msg/nav_sat_fix.hpp#includesensor_msgs/msg/nav_sat_status.hpp#includestd_msgs/msg/string.hpp#includemy_gnss_driver/msg/raw_observation.hppclassGnssDriver:publicrclcpp_lifecycle::LifecycleNode{public:GnssDriver():rclcpp_lifecycle::LifecycleNode(gnss_driver){declare_parameterstd::string(serial_port,/dev/ttyUSB0);declare_parameterint(baudrate,460800);declare_parameterstd::string(frame_id,gps_link);}// 配置回调:打开串口、创建发布者rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturnon_configure(constrclcpp_lifecycle::State){port_get_parameter(serial_port).as_string();// serial_.open(port_, baudrate) ...fix_pub_create_publishersensor_msgs::msg::NavSatFix(/gnss/fix,10);obs_pub_create_publishermy_gnss_driver::msg::RawObservation(/gnss/raw_obs,10);debug_pub_create_publisherstd_msgs::msg::String(/gnss/debug,10);RCLCPP_INFO(get_logger(),configured, port%s,port_.c_str());returnrclcpp_lifecycle::CallbackReturn::SUCCESS;}// 激活回调:启动定时读串口rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturnon_activate(constrclcpp_lifecycle::State){timer_create_wall_timer(std::chrono::milliseconds(10),std::bind(GnssDriver::read_loop,this));fix_pub_-on_activate();obs_pub_-on_activate();debug_pub_-on_activate();returnrclcpp_lifecycle::CallbackReturn::SUCCESS;}rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturnon_deactivate(constrclcpp_lifecycle::State){timer_.reset();returnrclcpp_lifecycle::CallbackReturn::SUCCESS;}rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturnon_cleanup(constrclcpp_lifecycle::State){// serial_.close() ...fix_pub_.reset();obs_pub_.reset();debug_pub_.reset();returnrclcpp_lifecycle::CallbackReturn::SUCCESS;}private:voidread_loop(){// 1. 从串口读一行/一帧// 2. 解析(NMEA/二进制)// 3. 按类型发布sensor_msgs::msg::NavSatFix fix;fix.header.stampnow();fix.header.frame_idframe_id_;fix.latitudelat;fix.longitudelon;fix.altitudealt;fix_pub_-publish(fix);}std::string port_,frame_id_;rclcpp::TimerBase::SharedPtr timer_;rclcpp_lifecycle::Publishersensor_msgs::msg::NavSatFix::SharedPtr fix_pub_;rclcpp_lifecycle::Publishermy_gnss_driver::msg::RawObservation::SharedPtr obs_pub_;rclcpp_lifecycle::Publisherstd_msgs::msg::String::SharedPtr debug_pub_;};intmain(intargc,char**argv){rclcpp::init(argc,argv);rclcpp::executors::SingleThreadedExecutor exec;autonodestd::make_sharedGnssDriver();exec.add_node(node-get_node_base_interface());exec.spin();rclcpp::shutdown();return0;}七、常用 CLI 命令# 节点/话题ros2nodelist# 列节点ros2 topic list# 列话题ros2 topicecho/gnss/fix# 看消息ros2 topic hz /gnss/fix# 看频率ros2 topic info /gnss/fix# 看发布者/订阅者# 服务/动作ros2servicelist ros2servicecall /gnss/cold_start std_srvs/srv/Trigger# 参数ros2 param list /gnss_driver ros2 paramset/gnss_driver baudrate921600# 录制回放ros2 bag record-a# 录全部ros2 bag record /gnss/fix /gnss/raw_obs ros2 bag play rosbag2_2026xxx# 回放# 包ros2 pkg create --build-type ament_cmake --node-name gnss_node my_gnss_driver ros2 run my_gnss_driver gnss_node# 运行节点ros2 launch my_gnss_driver gnss_bringup.launch.pyros2 bag是调试利器:现场录制 GNSS 原始数据,回实验室反复回放调试解析逻辑,不用反复跑现场。八、官方资料资料链接官方文档(主入口)https://docs.ros.org/en/rolling/教程(Tutorials)https://docs.ros.org/en/rolling/Tutorials.htmlLyrical 版本文档https://docs.ros.org/en/lyrical/ros2/ros2 源码仓库https://github.com/ros2/ros2包索引(找包)https://index.ros.org/Answers(问答)https://answers.ros.org/Discourse(讨论)https://discourse.ros.org/九、常见坑(嵌入式背景易踩)QoS 不匹配收不到数据:订阅者与发布者 QoS 不兼容(尤其 Reliability),消息静默丢弃。传感器常用 BEST_EFFORT,调试时先确认双方 QoS。忘了 source 环境:每开新终端要source /opt/ros/lyrical/setup.bash和工作区install/setup.bash,否则ros2命令找不到。建议写进~/.bashrc。节点名/话题名/命名空间混淆:launch 里可重映射,别在代码里写死话题名,用参数或 remapping。阻塞回调:串口读写在回调里阻塞会拖死整个执行器(单线程时)。用单独线程读串口 无锁队列传递,或用异步 IO。时间戳:NavSatFix 的header.stamp要用 GNSS 时间(若可得)或node-now(),别用 0,否则 robot_localization 等下游会拒收。frame_id 漏设:所有消息要带frame_id(如gps_link),否则 TF 查找失败。C 版本:ROS2 Lyrical 用 C17/20,注意编译标准对齐(CMAKE_CXX_STANDARD 17)。十、速记卡最新 LTS:Lyrical Luth(2026-05,Ubuntu 26.04);Jazzy(2024 LTS,Ubuntu 24.04)仍活跃版本与 Ubuntu 强绑定:先定 Ubuntu,再定 ROS2 distro分层:应用 → rclcpp/rclpy → rcl → rmw → DDS/Zenoh → OSrmw:切换 DDS 实现的抽象层,改环境变量即可,不动代码三种通信:Topic(流)/ Service(查询)/ Action(长任务)QoS:传感器常用 BEST_EFFORT KEEP_LAST(1),与 ROS1 默认不同生命周期节点:GNSS 驱动推荐,有序管理串口开关构建:colcon ament_cmake;源码用 vcstool 按 ros2.repos 拉取调试三件套:ros2 bag(录回放)、rviz2(可视化)、plotjuggler(画曲线)Foxy 已 EOL(2023-06):除非旧机无法升级,否则新项目排除