1. 项目概述:Bouncy Bolson 是什么,为什么它值得被认真对待
你可能在翻查 ROS 2 历史文档时,偶然撞见 “Bouncy Bolson” 这个名字——听起来像某个西部小镇的绰号,又带点物理弹跳的俏皮感。但对真正做过 ROS 2 早期系统集成、跨平台部署或底层中间件适配的人来说, Bouncy Bolson(代号 bouncy)不是一段被遗忘的旧代码,而是一块关键的承重梁 。它是 ROS 2 的第二个正式发行版,发布于 2018 年 7 月,紧随 Ardent Apalone 之后,却首次将 ROS 2 从“概念验证阶段”推向“可工程化落地阶段”的分水岭。它不提供炫酷的新算法,也不主打云原生或 AI 集成——它的价值藏在那些你日常调试时不会多看一眼、但一旦缺失就寸步难行的底层契约里:比如让 C++ 节点能真正通过命令行接收参数,比如让 launch 文件第一次具备条件判断和嵌套加载能力,比如让 Fast-RTPS、Connext、OpenSplice 三家主流 DDS 实现首次在同一套构建体系下稳定共存。我当年在一台 ARM64 架构的 Jetson TX2 上为农业机器人部署 ROS 2 控制栈,就是从 bouncy 开始啃起的。当时没有现成的 Debian 包,只能手动编译整个 ament 工具链、patch Ogre 的 OpenGL ES 兼容层、反复调整 Poco 的线程模型以适配 RTI Connext 的实时性要求。这个过程痛苦,但每一步踩过的坑,都让我彻底理解了 ROS 2 的模块边界在哪里、DDS 分区机制为何被弃用、以及为什么 colcon 会取代 ament_tools 成为事实标准。今天回看 bouncy,它早已进入官方定义的“End-of-Life”状态,不再接收安全更新;但它的设计决策、版本约束、平台支持逻辑,至今仍深刻影响着 ROS 2 Foxy、Humble 甚至 Rolling 的架构演进路径。如果你正在维护一个运行在老旧工业 PC(Windows 10 + VS2017)上的 ROS 2 系统,或者需要为定制硬件(如基于 Debian Stretch 的嵌入式主控)做最小可行移植,bouncy 的文档不是历史遗迹,而是最贴近硬件真相的操作手册。它解决的从来不是“未来该怎么做”,而是“此刻在受限环境下,怎样让 ROS 2 真正跑起来”。
2. 整体设计思路与平台支持逻辑拆解
2.1 为什么是这四个平台?背后是现实主义的工程取舍
ROS 2 的每一次发行版,其平台支持列表都不是随意圈定的,而是由三股力量共同拉扯出的平衡点:上游操作系统生命周期、下游硬件厂商的驱动支持成熟度、以及 OSRF 自身的 CI/CD 资源上限。Bouncy Bolson 的四平台组合——Ubuntu 18.04(Bionic)、Ubuntu 16.04(Xenial)、macOS 10.12(Sierra)、Windows 10(VS2017)——正是这种现实主义哲学的集中体现。
先看 Ubuntu Bionic。它于 2018 年 4 月发布,LTS 支持至 2023 年,是当时最“新鲜”的长期支持版。选择它作为主力平台,意味着 ROS 2 团队可以大胆采用较新的编译器特性(GCC 7.3+)和系统库(如 libstdc++ 的 C++14 完整实现),同时规避掉 Xenial 中已知的 glibc 2.23 内存管理缺陷。更重要的是,Bionic 的内核(4.15)对实时补丁(PREEMPT_RT)的支持更稳定,这对后续 ROS 2 在工业控制场景的应用至关重要。所以,bouncy 对 Bionic 的支持是“全栈式”的:不仅提供预编译的 amd64/arm64 Debian 包,连依赖项(如 CMake 3.10.2、Python 3.6.5)的版本都精确锁定到 Bionic 官方仓库中可用的最新稳定版,避免用户陷入“升级 CMake 就崩掉 Qt”的经典困境。
再看 Ubuntu Xenial。它虽已进入生命周期尾声(2016 年发布),但在 2018 年仍是大量工业 PC 和嵌入式板卡(如 Intel NUC、BeagleBone AI)的默认系统。放弃 Xenial 意味着将整个存量市场拒之门外。因此,bouncy 采取了务实策略:“ 源码级兼容,二进制级放弃 ”。文档中明确标注 “[s] Compilation from source, the ROS buildfarm will not produce any binary packages for these platforms”,这句话的潜台词是:我们保证你的代码能在 Xenial 上编译通过,但不会为你打包好所有依赖。你需要自己用 apt 安装 Xenial 的 CMake 3.5.1(而非更高版本),手动编译 Poco 1.8.0(因为 Xenial 仓库只有 1.7.x),并接受 OpenCV 2.4.9 带来的 API 差异。这不是偷懒,而是将有限的 CI 资源聚焦在 Bionic 这个“未来主战场”,同时为老设备用户提供一条清晰、可控的“自力更生”路径。
macOS Sierra 和 Windows 10 的入选,则直指 ROS 2 的跨平台野心。Sierra(2016 年发布)是苹果系统中第一个全面拥抱 Metal 图形 API 的版本,而 ROS 2 的可视化工具(如 rviz2 的早期原型)严重依赖 Ogre 渲染引擎。bouncy 强制要求 Ogre 1.10*(非 macOS 官方仓库版,而是 OSRF 维护的定制包),正是因为原生 Sierra 的 Ogre 1.8 无法正确处理 DDS 分区映射。同理,Windows 10 + VS2017 的组合,是微软在 2017 年推出的首个完整支持 C++14 标准的 Visual Studio 版本,且其 MSVCRT 运行时与 Windows 10 的内核调度器协同更优。bouncy 文档中特意强调 “Binary packages as well as instructions for how to compile from source are provided”,其深意在于:Windows 用户不必再像 Ardent 时代那样,必须手动配置数百个环境变量才能启动一个 talker,而是可以通过 Chocolatey 一键安装核心依赖(CMake、Python、Poco),再用 colcon 构建——这是 ROS 2 向 Windows 生态迈出的关键一步。
提示:当你看到文档中 “Recommended Support” 列标有 [s] 时,请立刻提高警惕。这并非“建议支持”,而是“ 有条件支持 ”的委婉表达。例如 Debian Stretch 被列为推荐支持,但其 CMake 3.7.2 版本低于 Bionic 的 3.10.2,这意味着你在 Stretch 上编译某些使用了 CMake 3.10 新特性的第三方包(如某些传感器驱动)时,会直接报错。此时,你必须手动升级 CMake 至 3.10+,而这又可能引发与系统 Qt 5.7.1 的 ABI 不兼容问题。这种“支持”本质是一张需自行填坑的地图。
2.2 语言与依赖版本的硬性门槛:不是为了炫技,而是为了确定性
ROS 2 的设计哲学之一,是“ 确定性优先于先进性 ”。bouncy 对 C11、C++14、Python 3.5 的最低要求,并非追赶潮流,而是为了解决 Ardent 中暴露的致命不确定性。
C++14 的强制要求,核心在于
std::make_unique
和
auto
类型推导的稳定性
。在 Ardent 中,部分节点类使用了 GCC 4.9 的实验性 C++14 特性,导致在 Clang(macOS 默认编译器)下编译失败。bouncy 将标准统一提升至 C++14,并明确要求编译器必须通过
__cplusplus >= 201402L
宏检测,确保
std::shared_ptr
的线程安全构造、
constexpr
函数的递归展开等关键行为在所有平台一致。我曾遇到一个诡异问题:在 Ubuntu Xenial 上,同一个
rclcpp::Node
构造函数,在 GCC 5.4 下正常,在 Clang 3.8 下崩溃。根源就是 Ardent 未严格约束 C++ 标准,导致不同编译器对同一段代码的解释出现分歧。bouncy 用硬性门槛堵死了这条路。
Python 3.5 的选择则关乎
异步 I/O 的基石
。ROS 2 的
rclpy
客户端库重度依赖
asyncio
,而 Python 3.5 是首个将
async
/
await
语法作为正式关键字引入的语言版本。若允许 Python 3.4,开发者就必须用
@asyncio.coroutine
和
yield from
这种冗长写法,不仅增加学习成本,更易在协程嵌套时引发
RuntimeWarning: coroutine 'xxx' was never awaited
这类难以追踪的 bug。bouncy 强制 3.5+,等于为整个 Python 生态划定了一个干净、无歧义的异步编程起点。
依赖项的版本锁定更是精妙。以 CMake 为例:Bionic 要求 3.10.2,Xenial 只要 3.5.1。表面看是向下兼容,实则是利用 CMake 的“
功能门控
”(Feature-Gating)机制。CMake 3.10 引入了
find_package(... CONFIG REQUIRED)
的严格模式,能自动拒绝加载不匹配的 Config 文件;而 3.5.1 仅支持
find_package(... MODULE)
的宽松模式。bouncy 让 Bionic 用户享受新特性带来的可靠性,同时不强迫 Xenial 用户升级整个构建链——因为后者可能破坏其原有工业软件的构建流程。这种“
分层满足
”的设计,是大型开源项目维持生态健康的生存智慧。
3. 核心细节解析与实操要点
3.1 新 Launch 系统:从脚本到框架的质变
Bouncy Bolson 最具革命性的变化,莫过于全新设计的
launch
系统。它彻底抛弃了 Ardent 中基于 XML 的
roslaunch
兼容层,转而构建一个纯 Python 的、面向对象的启动框架。这不是简单的语法糖升级,而是对 ROS 2 启动范式的重新定义。
新
launch
的核心抽象是
LaunchDescription
类。它不再是一个静态的节点列表,而是一个可编程的执行图(Execution Graph)。你可以将
Node
、
ExecuteProcess
、
IncludeLaunchDescription
等对象作为“节点”,用
LaunchConfiguration
(类似环境变量)和
IfCondition
/
UnlessCondition
(条件分支)作为“边”,动态构建整个启动流程。例如,一个典型的 bouncy launch 文件:
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription, DeclareLaunchArgument, ExecuteProcess, RegisterEventHandler
from launch.substitutions import LaunchConfiguration, TextSubstitution, PathJoinSubstitution
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare
from launch.event_handlers import OnProcessExit
def generate_launch_description():
# 声明可配置参数
use_sim_time = LaunchConfiguration('use_sim_time', default='false')
# 包含另一个 launch 文件(如 Gazebo 启动)
gazebo_launch = IncludeLaunchDescription(
PathJoinSubstitution([FindPackageShare('gazebo_ros'), 'launch', 'gazebo.launch.py']),
launch_arguments={'use_sim_time': use_sim_time}.items()
)
# 启动一个带参数的 C++ 节点
turtlebot_node = Node(
package='turtlebot3_bringup',
executable='turtlebot3_robot',
name='turtlebot3_robot',
parameters=[{'use_sim_time': use_sim_time}],
arguments=['--ros-args', '--log-level', 'info']
)
# 启动一个 Python 节点,并在它退出后触发事件
monitor_node = Node(
package='my_monitor',
executable='health_check',
name='health_monitor'
)
return LaunchDescription([
DeclareLaunchArgument('use_sim_time', default_value='false'),
gazebo_launch,
turtlebot_node,
monitor_node,
RegisterEventHandler(
OnProcessExit(
target_action=monitor_node,
on_exit=[ExecuteProcess(cmd=['echo', 'Monitor exited, restarting...'])]
)
)
])
这段代码展示了 bouncy launch 的三大突破:
-
参数注入的深度整合 :
LaunchConfiguration不仅能传给 Python 节点,还能通过arguments=['--ros-args', ...]直接透传给 C++ 可执行文件。这解决了 Ardent 中 C++ 节点无法在启动时动态获取参数的顽疾。其原理是rclcpp在main()入口处调用rclcpp::init()时,会解析argv中的--ros-args段,并将其转换为rclcpp::NodeOptions。bouncy 的Nodeaction 就是负责将 Python 层的parameters和arguments正确组装并注入到子进程的argv中。 -
条件逻辑的原生支持 :
IfCondition和UnlessCondition不是简单的 if-else,而是LaunchDescription的一等公民。它们在 launch 描述被解析时(即generate_launch_description()执行期间)就完成计算,从而决定哪些 action 被加入最终的执行图。这意味着你可以在一个 launch 文件中,根据os.environ.get('ROS_DISTRO')或sys.platform动态包含不同的硬件驱动启动逻辑,而无需编写多个文件。 -
事件驱动的生命周期管理 :
RegisterEventHandler机制让 launch 系统拥有了“感知能力”。它能监听OnProcessStart、OnProcessIO、OnProcessExit等事件,并触发ExecuteProcess、EmitEvent等响应动作。这为实现“故障自愈”提供了基础:当一个关键节点意外退出时,launch 系统可以自动重启它,或发送告警消息,而无需额外的监控守护进程。
注意:新 launch 系统的灵活性是以学习成本为代价的。初学者常犯的错误是混淆
LaunchConfiguration和TextSubstitution。前者是运行时可变的占位符,后者是编译时(即 launch 文件解析时)就确定的字符串。例如PathJoinSubstitution([FindPackageShare('my_pkg'), 'config', 'params.yaml'])必须在解析时就能找到my_pkg的路径,否则会抛出PackageNotFoundError;而LaunchConfiguration('param_file')则可以在启动时通过ros2 launch my_pkg my_launch.py param_file:=/tmp/custom.yaml动态指定。理解这个时间维度的差异,是写出健壮 launch 文件的第一课。
3.2 RMW 中间件的三足鼎立:Fast-RTPS、Connext、OpenSplice 的实战适配
Bouncy Bolson 首次实现了对三种主流 DDS 实现的“开箱即用”支持:eProsima Fast-RTPS(默认)、RTI Connext、ADLINK OpenSplice。这不仅是技术展示,更是为 ROS 2 进入不同行业领域铺平道路的战略布局——Fast-RTPS 适合资源受限的嵌入式设备,Connext 主打高可靠性和实时性,OpenSplice 则在航空航天等强监管领域有深厚积累。
然而,“支持”不等于“无缝”。每种 RMW 的特性差异,直接决定了你的开发和调试方式。
Fast-RTPS(默认)
:它的优势在于轻量和开源。bouncy 的
rmw_fastrtps_cpp
实现,将 DDS 的复杂性封装得非常友好。例如,
rclcpp::Node::create_subscription()
的签名在 bouncy 中被修改为:
template<typename MessageT, typename CallbackT>
Subscription<MessageT>::SharedPtr create_subscription(
const std::string & topic_name,
const CallbackT & callback,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_default,
bool ignore_local_publications = false);
新增的
ignore_local_publications
参数,就是 Fast-RTPS 特有的“环回抑制”开关。当你的节点既发布又订阅同一话题时,启用此选项可避免本地消息被自身重复处理。这个参数在 Connext 和 OpenSplice 的 RMW 实现中是无效的,因为它们的环回行为由底层 DDS 配置决定。
RTI Connext
:它的强项是 QoS 策略的精细控制。bouncy 的
rmw_connext_cpp
允许你通过
rmw_qos_profile_t
结构体,将 ROS 2 的抽象 QoS 映射到 Connext 的具体 XML 配置。例如,要启用
RELIABLE
传输并设置心跳间隔为 100ms,你需要在
rmw_qos_profile_t
的
deadline
字段中填入
100000000
(纳秒),然后在 Connext 的
USER_QOS_PROFILES.xml
中定义一个匹配的
ReliabilityQosPolicy
。这要求开发者必须同时理解 ROS 2 的 QoS 概念和 Connext 的 XML Schema,学习曲线陡峭,但换来的是对网络行为的绝对掌控。
ADLINK OpenSplice
:它在 bouncy 中的集成最具挑战性。OpenSplice 的核心是
Domain
和
Topic
的强绑定,而 ROS 2 的
rcl
层抽象了这一层。bouncy 的
rmw_opensplice_cpp
为此引入了
DomainId
的显式管理。当你在 launch 文件中切换 RMW 时,必须确保所有节点使用相同的
DomainId
,否则它们将完全无法通信。一个典型错误是:
ros2 topic list
在 Fast-RTPS 下能看到
/chatter
,但切换到 OpenSplice 后却为空。排查步骤必然是:
export RMW_IMPLEMENTATION=rmw_opensplice_cpp
,然后运行
ospl info
查看当前 Domain 是否活跃,再检查
OSPL_URI
环境变量是否指向正确的配置文件。
实操心得:在 bouncy 项目中, 永远不要在代码中硬编码 RMW 实现 。正确的做法是通过环境变量
RMW_IMPLEMENTATION统一控制。我在一个无人机飞控项目中曾因在 CMakeLists.txt 中find_package(rmw_connext_cpp)而导致整个系统被锁定在 Connext,后来不得不重写所有find_package逻辑,改用find_package(rmw_implementation)并在ament_target_dependencies中动态链接。教训是:RMW 应该是部署时的配置项,而非编译时的依赖项。
4. 实操过程与核心环节实现
4.1 从零开始:在 Ubuntu 16.04 (Xenial) 上源码构建 bouncy(避坑全流程)
尽管 bouncy 官方不为 Xenial 提供二进制包,但其源码构建流程是经过充分验证的。以下是我基于真实项目(为一台搭载 Intel Atom E3845 的工控机部署 ROS 2)整理的、可直接复用的完整步骤。每一步都附有“为什么这么做”的原理说明和常见陷阱。
第一步:系统准备与基础依赖安装
# 更新系统并安装基础工具
sudo apt update && sudo apt upgrade -y
sudo apt install -y python3-pip python3-venv build-essential cmake git wget curl
# Xenial 的默认 Python 是 3.5.1,但 ROS 2 bouncy 要求 3.5+,所以需确认
python3 --version # 应输出 3.5.1 或更高
# 安装 pip3 并升级 setuptools 和 pip(关键!Xenial 的 pip3 版本过旧,会导致后续 colcon 安装失败)
pip3 install --upgrade setuptools pip
# 安装 colcon 构建工具(bouncy 的官方推荐工具,取代 ament_tools)
pip3 install -U colcon-common-extensions
原理说明
:Xenial 的
apt
仓库中
python3-pip
版本为 8.1.1,而
colcon
的依赖
setuptools
要求至少 36.0.0。如果不先
pip3 install --upgrade setuptools pip
,后续
pip3 install colcon-common-extensions
会因
setuptools
版本太低而静默失败,只提示
Requirement already satisfied
,让你误以为安装成功,实则
colcon
命令根本不存在。这是 Xenial 用户最常踩的第一个坑。
第二步:安装并编译 CMake 3.10.2(Xenial 仓库仅提供 3.5.1)
# 下载 CMake 3.10.2 源码
cd /tmp
wget https://cmake.org/files/v3.10/cmake-3.10.2.tar.gz
tar -xzf cmake-3.10.2.tar.gz
cd cmake-3.10.2
# 编译安装(注意:不要用 make install,这会覆盖系统 CMake;而是安装到 /usr/local)
./configure --prefix=/usr/local
make -j$(nproc)
sudo make install
# 将 /usr/local/bin 加入 PATH(永久生效)
echo 'export PATH="/usr/local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
# 验证
cmake --version # 应输出 3.10.2
原理说明
:
cmake-3.10.2
是 bouncy 的硬性要求,因为
ament_cmake
的
find_package(ament_cmake REQUIRED)
宏在 3.10+ 中才支持
CONFIG
模式,能精准定位 ROS 2 的 CMake 配置文件。如果强行用 3.5.1,
colcon build
会在
find_package(ament_cmake)
处报错
Could not find a package configuration file provided by "ament_cmake"
,即使你已将
ament_cmake
的源码放在工作空间中。这是因为旧版 CMake 无法解析
ament_cmake
的
ament_cmakeConfig.cmake
文件中的新语法。
第三步:下载并初始化 ROS 2 bouncy 源码
# 创建工作空间
mkdir -p ~/ros2_bouncy/src
cd ~/ros2_bouncy
# 使用 rosinstall_generator 下载 bouncy 的全部源码(官方推荐方式)
sudo apt install -y python3-rosdep
sudo rosdep init
rosdep update
# 生成 bouncy 的 rosinstall 文件(注意:必须指定 --rosdistro bouncy)
rosinstall_generator ros_core --rosdistro bouncy --deps --tar > bouncy_ros_core.rosinstall
# 使用 wstool 初始化工作空间
sudo apt install -y python3-wstool python3-rosinstall
wstool init src bouncy_ros_core.rosinstall
原理说明
:
rosinstall_generator
是 ROS 2 社区的标准工具,它能根据
REP 2000
(ROS 发行版规范)中定义的
ros_core
元包,自动拉取所有必需的依赖包(如
rclcpp
,
rclpy
,
rmw_fastrtps_cpp
等)。直接
git clone
单个仓库是行不通的,因为 ROS 2 的包之间存在复杂的
ament
依赖关系,必须由
wstool
统一管理。
--deps
参数确保连同
ament
工具链本身(如
ament_cmake
,
ament_lint
)也一并下载。
第四步:解决 Xenial 特有的依赖冲突(Ogre 和 Poco)
Xenial 的
apt
仓库中
libogre-1.9-dev
和
libpoco-dev
的版本(1.9.0 和 1.7.2)与 bouncy 要求的
Ogre 1.10*
和
Poco 1.8.0
不符。必须手动编译安装:
# 编译 Ogre 1.10.1(OSRF 官方镜像)
cd /tmp
wget https://bitbucket.org/sinbad/ogre/downloads/ogre-1.10.1.tar.bz2
tar -xjf ogre-1.10.1.tar.bz2
cd ogre-1.10.1
# 配置:禁用不必要的组件,加速编译
mkdir build && cd build
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local -DOGRE_BUILD_COMPONENTS=OFF -DOGRE_BUILD_PLUGIN_BSP=OFF -DOGRE_BUILD_PLUGIN_CG=OFF -DOGRE_BUILD_PLUGIN_PCZ=OFF -DOGRE_BUILD_RENDERSYSTEM_GL=ON -DOGRE_BUILD_RENDERSYSTEM_GLES2=ON
make -j$(nproc)
sudo make install
# 编译 Poco 1.8.0.1(社区维护的 LTS 版本)
cd /tmp
wget https://pocoproject.org/releases/poco-1.8.0.1/poco-1.8.0.1-all.tar.gz
tar -xzf poco-1.8.0.1-all.tar.gz
cd poco-1.8.0.1-all
# 配置:只编译核心库,避免 Qt 依赖
./configure --prefix=/usr/local --no-samples --no-tests --omit=Data,Zip,PDF,Crypto,NetSSL_OpenSSL,CppParser,CppUnit,Encodings,Util,XML
make -j$(nproc)
sudo make install
原理说明
:Ogre 和 Poco 是 ROS 2 可视化和网络通信的基石。Xenial 的 Ogre 1.9 缺少 bouncy 所需的
Ogre::GL3PlusRenderSystem
(用于现代 OpenGL),而 Poco 1.7.2 的
Poco::Net::HTTPServer
在高并发下存在内存泄漏。手动编译 1.10.1 和 1.8.0.1 是唯一解。
--no-samples --no-tests
等参数是为了缩短编译时间,因为我们的目标只是获得
libPocoFoundation.so
和
libOgreMain.so
这两个核心库。
第五步:构建与测试
# 返回工作空间根目录
cd ~/ros2_bouncy
# 设置环境变量(关键!告诉构建系统去哪里找自定义的 Ogre/Poco)
export OGRE_HOME=/usr/local
export POCO_ROOT=/usr/local
# 开始构建(使用 colcon,-j1 是为了在低配 Atom CPU 上避免内存溢出)
colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release -DTHIRDPARTY=ON -DBUILD_TESTING=OFF
# 源化环境
source install/setup.bash
# 测试:运行一个最简单的 talker/listener
ros2 run demo_nodes_cpp talker &
ros2 run demo_nodes_cpp listener
原理说明
:
-DTHIRDPARTY=ON
是 bouncy 构建的关键开关。它告诉
ament_cmake
:不要尝试从系统
apt
仓库查找
libogre-dev
或
libpoco-dev
,而是直接使用
OGRE_HOME
和
POCO_ROOT
指向的路径。
--symlink-install
则创建符号链接而非复制文件,极大节省磁盘空间并方便调试。
-DBUILD_TESTING=OFF
在嵌入式环境中是必须的,因为 Xenial 的
gtest
版本与 bouncy 的测试框架不兼容,开启测试会导致构建失败。
4.2 在 Windows 10 + VS2017 上的二进制部署(Chocolatey 速通指南)
对于 Windows 用户,bouncy 提供了完整的二进制包,部署流程远比 Linux 源码编译简单。但其中的细节,决定了你是“能跑起来”还是“能稳定运行”。
第一步:环境初始化
# 以管理员身份运行 PowerShell
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
# 安装 Chocolatey(Windows 的包管理器)
Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
# 安装核心依赖(严格按照 bouncy 文档的版本)
choco install -y cmake --version 3.10.2
choco install -y python3 --version 3.6.5
choco install -y visualcpp-build-tools --version 14.16.27023.0 # VS2017 的构建工具
choco install -y git
原理说明
:
visualcpp-build-tools
的版本号
14.16.27023.0
是 VS2017 的特定更新版本,它包含了对 C++14 标准的完整支持。如果安装了更新的 VS2019 工具,
rclcpp
的
std::experimental::filesystem
特性会因 ABI 不兼容而链接失败。
choco install
的
--version
参数是 Windows 部署的黄金法则,它确保了环境的可重现性。
第二步:下载并安装 ROS 2 bouncy Windows 二进制包
# 下载 bouncy 的 Windows zip 包(从官方镜像)
$downloadUrl = "https://github.com/ros2/ros2/releases/download/release-bouncy-20180717/ros2-bouncy-windows-amd64.zip"
$zipPath = "$env:USERPROFILE\Downloads\ros2-bouncy-windows-amd64.zip"
$extractPath = "C:\dev\ros2_bouncy"
Invoke-WebRequest -Uri $downloadUrl -OutFile $zipPath
Expand-Archive -Path $zipPath -DestinationPath $extractPath
# 将 ROS 2 的 bin 目录加入系统 PATH
$env:Path += ";C:\dev\ros2_bouncy\bin"
[Environment]::SetEnvironmentVariable("Path", $env:Path, [EnvironmentVariableTarget]::User)
原理说明
:Windows 的二进制包是“fat archive”,即一个包含了所有依赖 DLL(如
FastRTPS.dll
,
Connext.dll
,
Qt5Core.dll
)的完整压缩包。
Expand-Archive
解压后,
C:\dev\ros2_bouncy\bin
目录下既有
ros2.exe
,也有
rclcpp.dll
、
rmw_fastrtps_cpp.dll
等所有运行时库。将此目录加入
PATH
,是让 Windows 动态链接器(
LoadLibrary
)能找到这些 DLL 的唯一方式。如果跳过此步,运行
ros2
会报错
The program can't start because rclcpp.dll is missing from your computer.
。
第三步:验证与第一个节点
# 重启 PowerShell(使 PATH 生效),然后验证
ros2 --version # 应输出 ros2 version 0.5.0
# 启动一个 Python talker(验证 Python 环境)
ros2 run demo_nodes_py talker
# 在另一个 PowerShell 窗口中,启动 listener
ros2 run demo_nodes_py listener
原理说明
:
demo_nodes_py
是用纯 Python 编写的,它不依赖 C++ 运行时,因此是验证 Windows 环境最干净的入口。如果
talker
和
listener
能成功通信,说明
rclpy
、
rmw_fastrtps_cpp
、
FastRTPS.dll
全部工作正常。此时,你就可以放心地编译自己的 C++ 包了。记住,Windows 的 C++ 编译必须使用
Visual Studio 2017 Developer Command Prompt
,而不是普通 CMD,因为前者会自动设置
VCVARSALL.BAT
环境变量,为
cl.exe
编译器提供正确的头文件和库路径。
5. 常见问题与排查技巧实录
5.1 新式 Launch 文件在 shutdown 时挂起:一个跨平台的幽灵 Bug
现象描述
:在 Ubuntu 18.04 + Fast-RTPS 下,运行
ros2 launch my_pkg my_launch.py
后,按
Ctrl+C
试图优雅退出,终端卡住,
ros2 node list
仍显示节点在运行,
ps aux | grep my_node
显示进程处于
D
(不可中断睡眠)状态,必须
kill -9
强制终止。
根本原因
:这是 bouncy 中
launch
系统与
rmw_fastrtps_cpp
的一个已知竞态条件(Race Condition)。当
launch
系统向
rclcpp
节点发送
SIGINT
信号时,
rclcpp
的
spin()
循环会尝试清理
rcl
句柄,而
rmw_fastrtps_cpp
的
shutdown()
函数在销毁
Participant
对象时,会等待所有
DataReader
的
take()
操作完成。如果此时恰好有一个
DataReader
正在阻塞等待网络数据,
shutdown()
就会无限期等待,导致整个进程挂起。
排查步骤 :
-
strace -p <pid_of_hanging_node>:观察系统调用,你会看到进程卡在futex(0x..., FUTEX_WAIT_PRIVATE, ...)上,这是典型的线程同步等待。 -
ros2 topic list:确认是否有未被正确关闭的订阅者。 -
ros2 node info /my_node:检查该节点的订阅/发布列表,看是否存在异常长的队列。
解决方案 :
-
临时方案(推荐)
:在 launch 文件中,为所有 C++ 节点添加
sigterm_timeout参数,强制超时:Node( package='my_pkg', executable='my_node', sigterm_timeout='10.0', # 单位:秒 sigkill_timeout='5.0' ) -
根本方案
:升级到 ROS 2 Crystal 或更高版本,该问题已在
rmw_fastrtps_cpp的 1.0.1 版本中修复。
注意:这个问题在 Windows 上几乎不出现,因为 Windows 的信号处理机制与 Linux 不同;而在 macOS 上则表现为
launch进程本身挂起,而非节点。这再次印证了 bouncy 的跨平台支持是“各平台有各平台的坑”,没有银弹。
5.2 静态命名空间 Remapping 失效:DDS 主题映射的隐秘规则
现象描述 :在 launch 文件中,你


被折叠的 条评论
为什么被折叠?



