micro_ros_setup
/---· 最后更新于 Nov 03, 2026
「This ROS 2 package is the entry point for building micro-ROS apps for different embedded platforms.」
这是「一天一个开源项目」系列今天的项目:micro-ROS Setup(简称 micro_ros_setup)。
想让一块 STM32 或者 ESP32 变成 ROS 2 节点的人,基本都会在同一个地方卡住。不是写代码卡住——rclc 发一个话题就十几行;是编译卡住。rosidl 那套消息生成器是跑在 x86 上的程序,必须先在你的 Ubuntu 上执行完,把生成的 .c/.h 交给 arm-none-eabi-gcc;rcl 默认要文件系统、要动态分配、要 64 位原子指令,而这块板子三样都没有;ros2/ros2 的官方仓库清单有上百个仓库,一个 MCU 客户端只需要其中十七个,但没人告诉你是哪十七个。等你把这些都试出来,一天过去了,而且换一块板子要再试一遍。
micro_ros_setup 就是把这四件事依次固化成文件:一份仓库白名单、几行 CMake 开关、两个工作区、一条四步命令。它自己不含一行 C/C++——整个仓库的语言构成是 Shell 66.6 KB、Python 4.8 KB、CMake 1.5 KB。它不实现 micro-ROS,只管一件事:让那半个 ROS 2 能被编译出来、并且被复现出来。
项目速览
⭐ 528 Stars | 🍴 187 Forks | 📄 Apache-2.0 协议 | 🔧 377 次提交 | 🐛 58 个 Open Issues(另有 10 个待合并 PR)
最有意思的一个数据是提交节奏:主流程两条脚本 scripts/create_firmware_ws.sh 和 scripts/build_firmware.sh,在 kilted 分支上分别只有 18 次和 6 次提交,最后一次改动停在 2020 年 8 月和 2020 年 11 月。五年多没动过的 212 行 shell,撑起了 14 个平台的构建入口。这个数字既是接口设计成功的证据,也藏着这套约定的漏洞——「深度设计剖析」会把它拆开。
阅读价值与前置说明
你将学到什么
- 一套「目录即插件」的平台约定:为什么新增一块开发板等于新增一个文件夹,这条约定为什么只对一半的 RTOS 成立,以及同一个流水线里为什么会并存两种脚本调用协议;
- 两段式构建的真实取舍:先把中间件编成一个
libmicroros.a、再让应用去链它,省下的是编译时间,付出的是状态耦合; - 交叉编译里最容易被忽略的一步:把宿主机的 ROS 2 环境从
PATH和CMAKE_PREFIX_PATH里摘出去,否则你链接到的是 x86 的库,错误要到烧进板子之后才发作; - MCU 上的 ROS 2 到底被砍到什么程度:17 个仓库、一份
colcon.meta里 5 个关键的 CMake 开关,换来一个没有文件系统、没有动态分配、没有发现协议的运行时。
前置知识
- 用过 CMake,知道交叉编译和 toolchain file 大概在做什么;
- 知道 ROS 2 里
rcl/rmw/rosidl的分工——不熟也能读,「核心特性拆解」「深度设计剖析」会现场解释; - 可选:上手过 FreeRTOS 或 Zephyr,被 STM32CubeMX 的图形化配置折腾过,会对「深度设计剖析」的取舍更有共鸣。
项目背景与完整档案
项目简介
README 第一句就是定位:「This ROS 2 package is the entry point for building micro-ROS apps for different embedded platforms.」 面向嵌入式平台构建 micro-ROS 应用的入口。官方文档站说得更明确——「a standalone build system in the form of a ROS 2 package」;而在 micro-ROS 官网首页上,它被称作整个项目的「primary repository」(据 micro-ROS 官网与文档站,2026-11-03 读取)。
要说清它的位置,得先分清三样东西。micro-ROS 本身是让 ROS 2 跑在微控制器上的整套方案:跑在 Linux 上的 agent 负责在 ROS 2 网络和 MCU 之间做桥,跑在 MCU 上的 client 库(Micro XRCE-DDS Client + rmw_microxrcedds + 裁剪过的 rcl/rclc)负责在板子上发收消息。而 micro_ros_setup 是这套东西的构建入口:你在 Ubuntu 上 colcon build 它,得到的东西不是一个库,而是 10 个可执行脚本(装在 lib/micro_ros_setup 下,用 ros2 run micro_ros_setup <脚本名> 调用);你再用这些脚本去生成另一个工作区,那个工作区里才有真正要烧进板子的固件。
它的归属没有悬念。package.xml 里两位 maintainer 的邮箱都在 eprosima.com 域名下(eProsima 是 DDS 厂商,也是 micro-ROS 的主要推动方),README 引用的官方文献是 2023 年 Springer《ROS: The Complete Reference (Volume 7)》里的 Micro-ROS 章节——14 位作者、53 页,作者单位横跨 Bosch、eProsima、PIAP、FIWARE、Bosch Rexroth,资助来自 EU Horizon 2020 的 OFERA 项目。它是 micro-ROS 项目本体的一部分,不是第三方复刻。
有个细节能说明它的定位:ROS 2 的 REP-2005「Common Packages」里,micro-ROS 分组收录了 micro_ros_arduino、micro-ROS-Agent、rmw_microxrcedds、rosidl_typesupport_microxrcedds 四个仓库,唯独没有 micro_ros_setup(据 ros-infrastructure/rep 仓库,2026-11-03 读取)。原因不难猜:它不是一个要进 ROS 2 发行版的运行时依赖,而是一个开发期工具。
顺带更新一条容易踩的链接:micro-ROS 的文档站已经迁到 eProsima 域名,micro-ros.github.io 现在 301 跳到 micro.vulcanexus.org,micro.ros.org 只留一句「micro-ROS has moved」。下文引用的官方文档链接都以迁移后的域名为准。
基础信息
| 项 | 值 |
|---|---|
| 仓库 | micro-ROS/micro_ros_setup |
| 包名 / 类型 | micro_ros_setup,ROS 2 ament_cmake 包 |
| 开源协议 | Apache-2.0 |
| 当前版本 | 6.1.0(2026-08-11) |
| 建仓时间 | 2019-04-17 |
| 默认分支 | kilted(一条 ROS 2 发行版一条分支) |
| 运行环境 | Ubuntu 宿主机 + ROS 2 |
| 目标平台 | FreeRTOS(6 块板)、Zephyr(5 项)、Linux host、Android、Raspbian、静态库生成 |
| 安装方式 | 源码 colcon build;官方另有 Docker 镜像 |
CHANGELOG.rst 里还能看到一段身世:4.x 时代的 issue 链接指向 micro-ROS/micro-ros-build,说明这个仓库是从 micro-ros-build 改名演化来的;3.0.0(2022-03-28)被标为「Initial release」,之后才按 ROS 2 发行版重新排版本号。
项目数据
| 指标 | 数值 |
|---|---|
| Stars | 528 |
| Forks | 187 |
| Watchers | 20 |
| 提交(默认分支可达历史,含合并提交) | 377 |
| 贡献者 | 21 |
| Open Issues | 58 |
| 待合并 PR | 10 |
| tag / 分支 | 23 / 27 |
| 最近一次推送 | 2026-10-09 |
数据截至 2026-11-03,取自 GitHub API。首日、首周的 star 增长没有可核实的公开报道,这里不写;能核实的是节奏——近 52 周只有 7 次提交,2026 年至今 6 次。这不是一个还在快速长大的项目,是一个已经稳定、按 ROS 2 发行版节拍维护的基础设施。
分支列表本身就是一份档案:crystal dashing foxy galactic humble iron jazzy kilted lyrical rolling,一条发行版一条。README 的依赖表把状态标得很坦白:Humble / Jazzy / Kilted / Lyrical / Rolling 是 Supported,其余 EOL。你在安装时敲的那句 git clone -b $ROS_DISTRO,就是在这里对上的——选错分支,后面 ros2 pkg prefix 拿到的配置目录就是另一套。
它解决什么问题
解决什么问题
给 MCU 写 ROS 2 节点,难的不是 API,是那条从源码到固件的流水线。四个卡点,每个都能单独耗掉一天:
- 两个架构要同时在场。 消息生成器必须在宿主机上跑,生成的代码必须在目标机上编。这意味着一次构建里既有 x86 的可执行程序,又有 ARM 的静态库,而它们必须指向同一套源码版本。
- 被砍掉的假设全是 Linux 假设。
rcl/rcutils默认假设有文件系统、可以动态分配、CPU 支持 64 位原子操作。MCU 一样都没有。你得知道该关哪个 CMake 选项——关错了不一定编译报错,可能是链接错误,甚至运行时崩溃。 - 官方仓库清单没有「最小集」。 完整的
ros2.repos是给整机 ROS 2 用的。一个 MCU 客户端要哪十几个仓库,只能顺着依赖图自己爬。 - 改一行应用代码,要不要重编中间件?
rcl+rclc+rmw_microxrcedds+microxrcedds_client加起来是整棵依赖树,而中间件那部分并不会因为你改了业务逻辑就发生变化。
micro_ros_setup 的解法是把这四件事依次变成文件约定,一条命令对应一步:
- 用两个工作区分工:
dev_ws在宿主机原生编译、提供构建工具,mcu_ws交叉编译、产出目标代码; - 用
colcon.meta把 MCU 的约束写成每个包各自的-D开关,一次提交进仓库,不再口口相传; - 用
yaml_filter.py(20 行 Python)把官方ros2.repos按白名单裁成 17 个仓库,再vcs import; - 用
make libmicroros把中间件打成一个静态库,应用只链它——改业务代码不再重编中间件。
对应四个动作:create_firmware_ws.sh → configure_firmware.sh → build_firmware.sh → flash_firmware.sh。四个脚本都通过 ros2 run micro_ros_setup 暴露,参数各自独立,中间状态落在当前目录的 ./firmware/ 里。官方文档站对这套流程的总结只有一句:编译、生成镜像、烧到板子上,「just with four ROS 2 commands」。
使用场景
- 教学 / 实验:给一块 Nucleo 板加一个 ROS 2 话题。 手上是 STM32,想让它发一条
std_msgs/msg/Int32。create_firmware_ws.sh freertos olimex-stm32-e407之后按四步走,不用自己拼 CMake、不用自己下工具链。 - 先在宿主机验证逻辑,再上板。 平台表里的
host一行不是摆设:同样的应用代码可以在 Linux 上原生编译(Zephyr 侧对应native_posix板),把通信逻辑跑通再换成交叉编译。对一个调试手段只有串口 print 的环境,这一步很值钱。 - 给已有构建系统喂库。
generate_lib平台输出的是静态库.a+ 头文件,交给 STM32CubeMX、PlatformIO 或自己的 Makefile。README 平台表右侧那列「Recommended Alternative」就是指向这条路径——真要深度集成,用平台自己的组件,而不是这套脚本。 - 顺手把 agent 编出来。
create_agent_ws.sh+build_agent.sh在同一个包里,端和桥一起解决,不用再单独 clone 一个仓库。
快速上手
前提:一台 Ubuntu,装好 ROS 2(Humble / Jazzy / Kilted / Lyrical / Rolling 之一),再补一个依赖:
sudo apt install python3-rosdep不需要预装 ARM 工具链——create.sh 会自己下载(FreeRTOS 下的是 gcc-arm-none-eabi-8-2019-q3-update,Zephyr 下的是对应版本的 Zephyr SDK)。
三步把工具装上(命令取自 README 的 Building 一节):
source /opt/ros/$ROS_DISTRO/setup.bash
mkdir uros_ws && cd uros_ws
git clone -b $ROS_DISTRO https://github.com/micro-ROS/micro_ros_setup.git src/micro_ros_setup
rosdep update && rosdep install --from-paths src --ignore-src -y
colcon build
source install/local_setup.bash然后四步出一个固件,以 FreeRTOS + Olimex STM32-E407 为例:
ros2 run micro_ros_setup create_firmware_ws.sh freertos olimex-stm32-e407
ros2 run micro_ros_setup configure_firmware.sh [应用名] -t udp -i <agent IP> -p 8888
ros2 run micro_ros_setup build_firmware.sh
ros2 run micro_ros_setup flash_firmware.sh几个容易踩的点:
configure_firmware.sh不带参数跑,会列出该 RTOS 支持的所有 demo 应用;-t udp/-t serial选传输方式,-i/-p只有网络类传输才用得上。build_firmware.sh -f是快速构建(fast build),跳过中间件重编,只在改应用代码时用。不加-f会重建整个mcu_ws。create_firmware_ws.sh会在当前目录建./firmware;如果它已经存在,脚本直接退出,要求你自己删掉。不存在「增量续跑」。- 不想配环境的话,README 的 Quick start 指向 eProsima 下载页的 micro-ROS Docker 镜像,client 和 agent 都预装好了。
核心特性拆解
1. 四步流水线:create / configure / build / flash。 把平台相关的一切切进四个阶段,每个阶段对应一个独立脚本。create 拉代码、装工具链、生成两个工作区;configure 选应用和传输方式;build 编库编固件;flash 烧写。价值:出错时你知道该排查哪一步,而不是在一整条黑盒命令里找。注意 create 是一次性的(firmware/ 存在就拒绝重跑),后半段的可重跑性比前半段好。
2. 平台配置目录:一块板子对应一个文件夹。 config/<RTOS>/<PLATFORM>/ 下最多四个脚本——create.sh(必需)、configure.sh(可选)、build.sh、flash.sh。README 的 Contributing 一节把这条约定写成了唯一的扩展方式。价值:对 FreeRTOS 这类按板子分目录的 RTOS,贡献一块新板子只需在自己目录里写 shell,不用碰 120 行的主流程;至于为什么对另一半 RTOS 不成立,「深度设计剖析」会讲。
3. 14 个平台条目开箱可用。 README 的平台表一共 14 行:FreeRTOS 6 行(Nucleo F446RE / F446ZE / F746ZG / F767ZI、ESP32、Crazyflie 2.1)、Zephyr 5 行(含 native_posix 模拟器)、Linux host、Android AOSP、静态库生成。价值:拿到板子先跑通,再决定要不要深入平台自己的构建系统。真正撑住这 14 行表格的不是文档,是 CI。ci.yml 里的 micro_ros_client 是一个 16 个组合的矩阵,每个组合都完整跑一遍 create_firmware_ws.sh → configure_firmware.sh → build_firmware.sh,再断言产物存在:generate_lib 断言 firmware/build/libmicroros.a,其余断言具体的二进制。覆盖 host、generate_lib、FreeRTOS 七块板、Zephyr 五块板,还有两个 README 表格里根本没写的 raspbian 组合;FreeRTOS 侧还多测了 olimex-stm32-e407——README 的示例命令用的正是这个组合,但表格里 Olimex 只出现在 Zephyr 那几行。换句话说,「平台支持」这件事是被每次 PR 真编一遍验证的,不是写在 README 里就算数。
4. 宿主 / 目标双工作区。 dev_ws 用 8 个 ament 包(ament_cmake、ament_index、ament_package、ament_lint、googletest、osrf_pycommon、uncrustify_vendor、ament_cmake_ros)在宿主机原生编译;mcu_ws 才按目标平台交叉编译。价值:把「必须跑在 x86 上的构建工具」和「必须编成 ARM 的库」分开,这是交叉编译 ROS 2 能成立的前提。
5. 中间件与应用分开编。 make libmicroros 产出静态库,make UROS_APP_FOLDER=... 只编应用。价值:这是「深度设计剖析」第一个设计亮点的核心,也是 generate_lib 平台和各类预编译库的基础。
6. component 命令:10 个独立组件的入口。 ros2 run micro_ros_setup component <名字>,把 ESP-IDF、Zephyr、Mbed、NuttX、Azure RTOS、Tiva C、STM32CubeMX、PlatformIO、Arduino、Pico SDK 这 10 个独立组件的仓库地址固化下来。实现方式非常直白:scripts/component 是一个 104 行的 Python 脚本,COMPONENTS 是一个字典列表,选中后 git clone(第 97 行)。价值不在于代码,在于它承认了「这套脚本只负责上手」——每个平台旁边都写了更专业的替代方案。
7. 按发行版分支持续跟进。 10 条 distro 分支上维护同一套逻辑,靠 Mergify 机器人做自动 backport:mergify[bot] 以作者身份留下了 35 个提交,提交标题直接标着来源,最近一条是 2026-10-09 的「Add Lyrical to CI (backport #838)」;分支列表里还躺着 mergify/bp/jazzy/pr-781、mergify/bp/kilted/pr-795 这类临时分支,命名规则就是「目标发行版 + 源 PR 号」。Mergify 的规则文件不在仓库里(应该配在它的 SaaS 后台),但行为证据足够清楚。价值:ROS 2 每年发两版,这套脚本必须跟上,但又不能让人工在 10 条分支上重复劳动。
深度设计剖析
DeepWiki 上这个仓库的架构导读(自动生成,第三方内容,事实仍以源码为准)把它称作一个「build system orchestrator」,章节分成 System Architecture、Main Workflow、Platform Support Matrix、Package Structure 几块。这个切法和下面三个点基本重合——「Main Workflow」就是四步流水线,「Platform Support Matrix」就是 config/ 的目录约定。我把重心放在三件事上:为什么中间件要单独编成一个库、为什么平台支持靠目录而不是代码、以及那半个 ROS 2 是怎么被挑出来的。
设计亮点一:两段式构建——先把整棵中间件压成一个 .a
常规方案: 把应用和中间件放进同一个 CMake / colcon 工程,一次编到底。改一行业务代码,整棵依赖树跟着重编。在 x86 上这最多是「慢」,在交叉编译环境里是「用不了」。
micro_ros_setup 的做法: config/freertos/olimex-stm32-e407/build.sh 只有 31 行,核心是两条命令:
make libmicroros # 先把整个 mcu_ws 压成一个 libmicroros.a
make -j$(nproc) UROS_APP_FOLDER=$UROS_APP_FOLDER # 再编应用,链接上一步的库真正实现这两个 target 的 Makefile 不在这个仓库里,而在 board.repos 拉下来的 micro-ROS/freertos_apps 里。它做的事值得说清楚:先 colcon build 把 mcu_ws 里整棵依赖树交叉编译一遍(--packages-ignore-regex=.*_cpp、带 ARM 的 toolchain 文件),然后把 install/lib 下所有 *.a 用 ar x 逐个拆开、重命名、再用 ar rc 合并成单个 libmicroros.a,最后 app 的 Makefile 里一行 OBJECTS += $(BUILD_DIR)/libmicroros.a 把它链进去。所谓「一个库」,物理上是几十个静态库被 ar 拼起来的。
启用快速构建时,这两条被一个条件包起来:if [ "$UROS_FAST_BUILD" = "off" ] || [ ! -d "build" ](build.sh:18)——不传 -f,或者根本没有 build 目录,才清空 mcu_ws/build、mcu_ws/install、mcu_ws/log 重新编库。
Zephyr 走的是另一条路。config/zephyr/generic/build.sh 里没有一条 make libmicroros,它拼的是一条 west build -b $BOARD ... -- -DMICRO_ROS_FIRMWARE_DIR=$FW_TARGETDIR -DMICRO_ROS_TRANSPORT=$TRANSPORT(第 76–86 行),把整个 firmware/ 目录的路径交给 Zephyr 的 micro-ROS 模块,mcu_ws 什么时候编、怎么编,由模块自己决定。同一个「分成两层」的目标,FreeRTOS 用两个显式的 make target 实现,Zephyr 用一次构建加一个目录参数实现。
为什么这么做: 两个理由。一是变更频率不同——中间件在一次实验里只会变一次(换 transport、换 rmw 配置才需要重编),应用代码一天改二十遍。二是可分发:一旦中间件是一个 .a,它就能脱离这套脚本被别处使用。
这不是推测,是官方自己的用法。micro_ros_arduino 仓库里那个生成 Arduino 预编译库的脚本,第一件事就是调 ros2 run micro_ros_setup create_firmware_ws.sh generate_lib,后面接着十几次 build_firmware.sh <toolchain>.cmake <colcon.meta>——不同 MCU 家族各编一份 libmicroros.a(据 micro_ros_arduino 的 extras/library_generation/library_generation.sh,2026-11-03 读取)。generate_lib 这个平台不只是「顺便提供的功能」,它是官方分发链路的上游。先有 .a 这个产物边界,才可能有 Arduino、Pico SDK 这些预编译库。
工程价值与代价: 省下的是每次迭代重走整棵依赖树的时间;付出的是状态耦合。libmicroros.a 是用特定 transport、特定 rmw 配置、特定消息集编出来的,应用必须与之一致。这正是 create_firmware_ws.sh 第 49–52 行要在 ./firmware 已存在时直接退出的原因——它宁可让你重建整个工作区,也不敢猜你的库还是不是配套的。同理,configure_firmware.sh 改过 transport 之后,下一次 build 不能带 -f。
设计亮点二:目录即插件——以及这条约定只对一半的 RTOS 成立
常规方案: 在一个 build.sh 里塞 case "$PLATFORM" in;或者给每个平台配一个 toolchain file,再叠一层「板子 × RTOS × transport」的开关矩阵。两种做法都会让主流程随着平台数量线性膨胀。
micro_ros_setup 的做法: 四步之间的全部持久状态就是几个纯文本文件。create_firmware_ws.sh 建好 ./firmware 之后,往 firmware/PLATFORM 写两行——RTOS 名、板子名(第 67–68 行);平台自己的 configure.sh 再往 firmware/APP 写选中的应用(一行),Zephyr 那版还往 firmware/TRANSPORT 写传输方式。之后每个步骤的入口都是同一段代码:读那两行,拼出 $PREFIX/config/$RTOS/$PLATFORM/ 或 $PREFIX/config/$RTOS/generic/,然后执行那个目录里的脚本。整个 create_firmware_ws.sh 120 行、build_firmware.sh 92 行,没有一处 case 分支列举平台。
这里有个容易被忽略的设计细节:四个步骤用了两种不同的调用协议。
create、build、flash用source(点号)加载平台脚本,平台脚本和主脚本跑在同一个 shell 进程里。所以build_firmware.sh从firmware/TRANSPORT读出来的TRANSPORT变量根本没有export,Zephyr 的build.sh照样能用它决定host-udp.conf还是别的配置文件。configure用exec,换一个进程。参数不靠位置参数传:主脚本先把-t/-d/-i/-p全部消费掉(shift干净),再export成UROS_TRANSPORT、UROS_AGENT_IP这些环境变量;平台configure.sh执行时,$@已经是空的。
也就是说,同一个「插件接口」里,一半传的是整个 shell 状态,另一半只传导出的环境变量。这个差别没有任何文档写过,是每个平台脚本自己「知道」的。
三层兜底让约定足够松:缺 configure.sh 时 configure_firmware.sh 第 20–30 行打印「No configuration step needed」并 exit 0(host、android、generate_lib 三个平台确实没有这个文件);缺 flash.sh 时同样打印一句就放过;config/<RTOS>/generic/ 存在时,整套实现由它接管,板子名只作为参数传下去。还有一处真正的「声明式」设计——每个平台目录里那个 package.xml:它的包名统一叫 firmware,用 build_depend 列出这块板子需要的宿主侧 apt 包(olimex 只要一个 openocd,Zephyr 有 9 个),然后交给 rosdep 去装。这是整条流水线里唯一一个「用元数据描述需求、让工具去解析」的地方。
为什么这么做: 平台的差异形态就是「四个阶段各自不同」,那就按阶段切,而不是按平台切。新增平台的作者只写 shell,主流程的读者只需要理解一遍。
工程价值与代价: 代价比想象中大,值得一条条列出来。
- 「加一个目录就支持一块新板子」只对一半 RTOS 成立。 只要
config/<RTOS>/generic/存在,configure/build/flash就永远不会去看config/<RTOS>/<板子>/——Zephyr 就是这种情况。想在 Zephyr 上加一块板,实际动作是去config/zephyr/generic/build.sh第 40–55 行加一条elif,把PLATFORM翻译成 Zephyr 的BOARD名(这 5 个板名加host → native_posix全是硬编码的 if/elif)。约定的漂亮说法,在执行层面退化成了一段字符串映射表。 - 接口是隐式的。 没有 schema、没有类型检查,变量靠
source共享,写错一个名字没有任何提示,报错在半小时之后。 - 没有抽象,所以只能复制粘贴。 9 行
COLCON_IGNORE补丁在 6 个create.sh里几乎逐字重复;fast build的判断依据已经分叉——olimex 看扩展目录下的build,crazyflie 看mcu_ws/build;连client-colcon.meta里tracetools这个键都出现了两次(第 3–8 行和第 28–33 行,JSON 解析取后者,功能等价,纯复制粘贴残留)。 - 主流程改动几乎为零,但这不等于没有债。
create_firmware_ws.sh的最后 3 次提交全在 2020 年 8 月,build_firmware.sh只有 6 次提交、最后一次 2020 年 11 月——变化被成功关进了config/,债也被一起关了进去。
设计亮点三:裁剪与隔离——MCU 上那半个 ROS 2 是怎么被挑出来的
常规方案: 全量拉取 ros2.repos,再用 colcon build --packages-select 挑着编。问题是依赖会自己找上门:你选了 rcl,rcl 会拉进 rcutils、rmw、rosidl 一整串,最后还是得手写一份「实际需要的清单」。
micro_ros_setup 的做法: 分三步,每一层都写死在配置文件里。
第一步,裁仓库清单。 create_ws.sh 从 raw.githubusercontent.com/ros2/ros2/kilted/ros2.repos 拉官方清单,通过管道喂给 yaml_filter.py 的 stdin(第 43–44 行);后者按 config/client_ros2_packages.txt 里的 keep: 白名单过滤,只留下 17 个仓库——ros2/rcl、ros2/rmw、ros2/rosidl、ros2/rosidl_defaults、ros2/common_interfaces、ros2/rcl_interfaces、ros2/rcpputils……然后 vcs import。同一个脚本还服务另外三种场景,各自用另一份白名单:dev_ws 用 dev_ros2_packages.txt(8 个 ament 包)、agent 工作区用 agent_ros2_packages.txt(内容是 keep: none,一个 ROS 2 源码仓库都不拉,直接用装好的 ROS 2)、host 平台用 client_host_packages.repos(直接指向 eProsima 的 Micro-CDR 和 Micro-XRCE-DDS-Client 仓库)。
第二步,裁能力。 config/<RTOS>/<板>/client-colcon.meta 通过 colcon 的 --metas 机制,把 MCU 的约束写成每个包各自的 CMake 开关:
"rcutils": { "cmake-args": [
"-DENABLE_TESTING=OFF",
"-DRCUTILS_NO_FILESYSTEM=ON",
"-DRCUTILS_AVOID_DYNAMIC_ALLOCATION=ON",
"-DRCUTILS_NO_64_ATOMIC=ON" ] },
"microxrcedds_client": { "cmake-args": [
"-DUCLIENT_PIC=OFF", "-DUCLIENT_PROFILE_DISCOVERY=OFF" ] },
"rmw_microxrcedds": { "cmake-args": ["-DRMW_UXRCE_TRANSPORT=custom"] }这五个开关是「MCU 上的 ROS 2 长什么样」的完整答案:没有文件系统、不做动态分配、不用 64 位原子指令、不开 DDS 发现(发现由宿主机上的 agent 代做)、transport 换成自定义(UART / UDP / 自定义链路)。configure_firmware.sh 传下去的 -t/--transport,最终就是被 config/utils.sh 里那两个小函数改写进这个 JSON 的——用一条 python3 -c 把 -D 参数增删进 colcon.meta,再 mv 覆盖回去。
第三步,隔离宿主机环境。 这一步藏在 clean_env.sh 里,也是最容易被忽略的:交叉编译前,它把 AMENT_PREFIX_PATH 里的每一条路径从 PATH、LD_LIBRARY_PATH、CMAKE_PREFIX_PATH、PYTHONPATH 中逐段删掉,最后 unset AMENT_PREFIX_PATH 和 COLCON_PREFIX_PATH。不这么做的后果是交叉编译的经典陷阱:CMake 顺着 CMAKE_PREFIX_PATH 先找到 x86 的 librcl,configure 阶段一切正常,链接到一个目标机根本跑不了的库,错误要等烧进板子才发作。
为什么这么做: 前两步决定「编多少、砍多少」,第三步决定「别让宿主漏进来」。三者是同一个目标:让目标机看到的 ROS 2,是一个被显式声明过的子集,而不是「碰巧编过的那些」。
工程价值与代价: 代价是白名单会腐坏。官方 ros2.repos 新增一个必需依赖,或者某个包改了依赖关系,这三份清单不会自己跟上——CHANGELOG.rst 里那一串「Fix rolling」「Fix RCLC rolling」「Fix Agent and host Rolling CI」「Remove Iron from nightly」,就是在还这笔债。好消息是还债成本可控:改的是一个配置文件,不是主流程。
项目细节补充
维护真相。 21 位贡献者,前两位(iluetkeb 145 次、pablogs9 113 次)合计 258 次,占 377 次提交的 68%。第三名是 mergify[bot],35 次——那不是人,是自动 backport 机器人:一条改动进 rolling,Mergify 自动开 PR 抄到各条 distro 分支,分支列表里那些 mergify/bp/kilted/pr-795 就是它留下的痕迹。10 条分支的矩阵靠机器人维持一致性,而对外沟通基本停了:官方博客最后一篇停在 2023-02-06(发布那本书章),此后只有 GitHub 提交和每日 04:00 UTC 的 nightly CI 在说话(据官方站点 sitemap,2026-11-03 核对)。这和「近 52 周 7 次提交」是同一件事的两面——项目没死,只是进入了静默维护。
版本口径有两处对不上。 一是 pin 的不对称:所有 vcs 引用都跟分支走(kilted、ros2,agent 侧的 drive_base 甚至是 master),没有 commit pin;被精确钉死的只有外部工具链——ARM GCC 2019 q3、Zephyr v2.6.0 加 SDK 0.12.4、ESP-IDF v4.1、Android NDK r23。理由不难想:交叉编译里工具链一动就可能全线崩,宁可牺牲新特性换可复现。代价是这套脚本今天还停在 2019 年的 ARM GCC 和 2021 年的 Zephyr 上。二是 Release 与 CHANGELOG 不对齐:GitHub 上只有 3 个 tag——3.1.0(「micro-ROS Humble」,2022-05-26)、4.1.0(「micro-ROS Iron」,2023-06-22)、6.1.0(2026-08-11),前两个是整个 micro-ROS 栈的发版、CHANGELOG 里没有对应条目;反过来 CHANGELOG 里的 5.0.0 和 6.0.0 在 GitHub 上没有 Release。所以本文统一写「最新 GitHub Release tag 6.1.0」,不写「最新版本」。
整条流水线其实绑死在 Ubuntu。 rosdep install --os=ubuntu:noble 硬编码在三个脚本里,而 README 的 host 一行写着「Ubuntu 18.04/20.04」、CI 用的镜像是 ubuntu:24.04——三处口径互不一致。脚本里还有 lsusb、nproc、dpkg -s 这类 Linux 假设,尽管 README 把它当作跨平台方案介绍。失败处理也只有一条策略:立刻退出。每个主脚本头部都是 set -e、set -o nounset、set -o pipefail,全仓库没有一处网络重试、断点记录、trap 清理或回滚,唯一被静默吞掉的错误是 cp ... || :。恢复成本被显式转移给用户:删掉 firmware/ 重来。CI 也只跑 create → configure → build 三步,一次 flash 都没有,android 平台不在矩阵里——「四步」里的最后一步,没有自动化验证。
官方脚本里留着手工补丁。 build_firmware.sh 末尾有一段「Fix include paths」,把 build/include/<pkg>/<pkg>/ 压平成 build/include/<pkg>/(第 79–91 行);每个 create.sh 还要 touch 9 个 COLCON_IGNORE 去跳过编不过的包(rcl_logging_spdlog、rcl、rosidl_typesupport_introspection_cpp、rcpputils、test_tracetools、rcl_yaml_param_parser、rclc_examples、lttngpy、rmw_security_common)。这些是「能跑就行」的痕迹,也是这类胶水项目的常态——它们不是缺陷,是债务清单。同类的债还有一处小的:scripts/component 的组件表有 10 项,README 的 standalone 列表有 12 项(多了 Renesas e² studio 和 RT-Thread),说明这个列表靠人工同步。
README 里那句「没有已知限制」。 第 246 行的小标题是 Known Issues / Limitations,第 248 行的正文是 There are no known limitations.;而同一份 README 第 232 行写着「This software is not ready for production use」。两句话放在 58 个 open issue 和上面那一串补丁旁边,不太像严谨的声明,更像长期没人回头改的占位符。真要评估可用性,看 Purpose of the Project 那段更靠谱:明确说了没有为具体用例开发或测试过,安全相关场景要自己按 ISO 26262 之类标准调整。
项目资源汇总
官方资源
- GitHub:micro-ROS/micro_ros_setup
- 开源协议:Apache-2.0
- 更新日志:仓库内
CHANGELOG.rst - 贡献指南:仓库内
CONTRIBUTING.md(新增平台要放哪四个脚本,写在这里) - 官方文档站(已从
micro-ros.github.io迁到):micro.vulcanexus.org · Build System - 上手教程:first_application_rtos 教程
- 支持硬件列表(官方指向本仓库):micro.vulcanexus.org · 硬件支持页
- Docker 镜像:eProsima downloads-all(client + agent + 预编译示例)
相关资源
- micro-ROS-Agent(宿主机上的桥):micro-ROS/micro-ROS-Agent
- ESP-IDF 组件:micro-ROS/micro_ros_espidf_component
- Zephyr 模块:micro-ROS/micro_ros_zephyr_module
- STM32CubeMX / CubeIDE utils:micro-ROS/micro_ros_stm32cubemx_utils
- Arduino 预编译库:micro-ROS/micro_ros_arduino
- PlatformIO 库构建器:micro-ROS/micro_ros_platformio
媒体报道与延伸阅读
- DeepWiki 架构导读:deepwiki.com/micro-ROS/micro_ros_setup(第三方自动生成,含 System Architecture / Main Workflow / Platform Support Matrix 等章节,适合当阅读索引)
- Springer 书章《Micro-ROS》:DOI 10.1007/978-3-031-09062-2_2(14 位作者、53 页,覆盖 micro-ROS 的整体架构与设计取舍)
- 底层依赖 Micro-XRCE-DDS Client:eProsima/Micro-XRCE-DDS-Client(
rmw_microxrcedds之下的通信层) - 上游 ROS 2 C 客户端库:ros2/rclc(
rclc,跑在rcl之上、面向 MCU 的扩展)
核心要点回顾
- micro_ros_setup 不是 micro-ROS 的实现,是它的构建入口:528 星、377 次提交、21 位贡献者,全部代码是 Shell + 一点 Python,没有一行 C/C++。
- 它自己几乎不构建任何东西:真正的编译逻辑在
board.repos拉下来的freertos_apps/zephyr_apps里,这个仓库做的是环境准备、变量传递和脚本加载。它的核心资产是约定,不是代码。 - 这套约定有效,但边界很清楚:两段式构建(中间件压成一个
.a)、目录即插件、「加目录即加板子」只对一半 RTOS 成立、显式裁剪与隔离(17 个仓库 + 5 个关键 CMake 开关 + 清宿主环境)。能力边界写在client-colcon.meta里,不在文档里。
适合谁
- 手上有一块开发板、想让它在
ros2 topic list里出现的嵌入式 / 机器人开发者; - 想先评估 micro-ROS 可行性、还不想动平台自己构建系统的团队;
- 对「如何用文件系统约定做一个插件系统」感兴趣、愿意接受隐式接口代价的构建工具开发者。
一句话评价
它没让 ROS 2 变小,它只是把「变小」这件事变成了一条可以复现的命令。
