首页/目录/全部文章

全部文章

八个专题的源码、算法与协议笔记都在这里。

笔记列表

08 示例与性能工具

08 示例与性能工具

本文重点:走读 iceoryx_examples 中最有代表性的示例(每个示例的演示点 + 关键 API),iceperf 的测试方法与结果解读,tools/ 排障工具(iox-introspection-client),以及常用排障 checklist。
源码锚点:iceoryx_examples/*tools/introspection/

源码根目录:/home/cp/work2/ros2Learn/ros2_humble/src/eclipse-iceoryx/iceoryx(v2.0.6)

注意iceoryx_examples/ 目录带有 COLCON_IGNORE,ROS 2 Humble 工作区构建时不编译这些示例。想跑示例需单独构建 iceoryx(tools/iceoryx_build_test.sh 或 CMake -DEXAMPLES=ON)。所有多进程示例都要求先启动 iox-roudi


1. 示例总览

示例 演示点 关键 API
icehello 最小 pub/sub Publisher<T>::loan/publishSubscriber<T>::take
icedelivery typed/untyped 四种发布姿势 publishCopyOfpublishResultOf、untyped loan(size)
iceperf 各 IPC 技术延迟基准 ping-pong 轮转、多 payload 扫描
waitset 阻塞式多事件等待 WaitSet::attachState/attachEvent/wait
callbacks (listener) 事件驱动回调 Listener::attachEvent + UserTrigger
request_response 请求-响应模式 Client<Req,Res>Server<Req,Res>、sequenceId
user_header 自定义 user-header(时间戳) Publisher<Data, Header>getUserHeader()
singleprocess RouDi 与应用同进程 PoshRuntimeSingleProcessRouDi 内嵌
icediscovery 服务发现 ServiceDiscovery::findService、通配符

其余目录(*_in_c 为对应 C API 版本;icecrystal 演示内省、iceoptions 演示 QoS 选项、complexdata 演示 iceoryx 容器类型、ice_access_control 演示段权限、icedocker 演示容器部署、iceensemble 多发布者)从略。


2. icehello:最小可用样例

发布侧三步:init runtime → 建 publisher → loan/publish。CaPro 三元组 {"Radar", "FrontLeft", "Object"} 即”topic”:

1
2
3
4
5
6
7
8
9
10
11
12
auto loanResult = publisher.loan();
//! [loan]
//! [publish]
if (!loanResult.has_error())
{
auto& sample = loanResult.value();
// Sample can be held until ready to publish
sample->x = ct;
sample->y = ct;
sample->z = ct;
sample.publish();
}

订阅侧轮询 take(),返回 expected<Sample, ChunkReceiveResult>NO_CHUNK_AVAILABLE 是正常空态而非错误:

1
2
3
4
5
6
7
//! [receive]
auto takeResult = subscriber.take();
if (!takeResult.has_error())
{
std::cout << APP_NAME << " got value: " << takeResult.value()->x << std::endl;
}
//! [receive]

要点:sample 是 RAII 智能指针(iox::popo::Sample),未 publish 即析构会自动归还 chunk,不泄漏。运行:iox-roudi + 两个进程各跑一个可执行文件。


3. icedelivery:四种发布姿势与 untyped API

iox_publisher.cpp 依次演示 typed API 的四种用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//! [API Usage #1]
// * Retrieve a typed sample from shared memory.
// * Sample can be held until ready to publish.
// * Data is default constructed during loan
publisher.loan()
.and_then([&](auto& sample) {
sample->x = sampleValue1;
sample->y = sampleValue1;
sample->z = sampleValue1;
sample.publish();
})
.or_else([](auto& error) {
// Do something with error
std::cerr << "Unable to loan sample, error: " << error << std::endl;
});
//! [API Usage #1]
姿势 API 场景
#1 loan() 后就地填写 常规零拷贝
#2 loan(args...) 就地构造 有非默认构造参数
#3 publishCopyOf(obj) 小对象,接受一次拷贝
#4 publishResultOf(callable, args...) 由回调直接写入 loan 出的内存

iox_publisher_untyped.cpp / iox_subscriber_untyped.cpp 则是 UntypedPublisher::loan(payloadSize) 返回 void*UntypedSubscriber::take() 后手动 release——正是 06/07 篇中 CycloneDDS 与 dds-gateway 使用的形态。


4. waitset:阻塞等待

ice_waitset_basic.cpp 演示”attach 订阅者状态 + 阻塞 wait”的标准循环,并示范了信号安全退出(signal handler 里 markForDestruction 唤醒阻塞的 wait):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
while (keepRunning)
{
// We block and wait for samples to arrive.
auto notificationVector = waitset->wait();

for (auto& notification : notificationVector)
{
// ...
if (notification->doesOriginateFrom(&subscriber))
{
// Consume a sample
subscriber.take()
.and_then([](auto& sample) { std::cout << " got value: " << sample->counter << std::endl; })
.or_else([](auto& reason) {
std::cout << "got no data, return code: " << static_cast<uint64_t>(reason) << std::endl;
});
// We could consume all samples but do not need to.
// If there is more than one sample we will wake up again since the state of the subscriber is still
// iox::popo::SubscriberState::HAS_DATA in this case.
}
}
}

注意 attach 的是 stateSubscriberState::HAS_DATA,电平触发:只要还有数据 wait 立即返回)而非 eventDATA_RECEIVED,边沿触发:只在新数据到达时醒)。同目录其他文件依次演示:gateway(一个回调处理 N 个订阅者)、grouping(用 id 分组)、individual(每个附着对象单独处理)、timer_driven_execution(用 user-trigger 实现定时器)、trigger(自定义类实现可附着的 trigger 接口)。


5. callbacks:Listener 事件驱动

WaitSet 需要用户线程自己转循环;Listener 则起后台线程,事件到达即调用回调(这也是 CycloneDDS shm_monitor 的用法)。示例用两个订阅者 + 一个 4 秒心跳 UserTrigger

1
2
3
4
5
6
7
8
9
10
11
12
listener
.attachEvent(subscriberLeft,
iox::popo::SubscriberEvent::DATA_RECEIVED,
iox::popo::createNotificationCallback(onSampleReceivedCallback))
.or_else([](auto) {
std::cerr << "unable to attach subscriberLeft" << std::endl;
std::exit(EXIT_FAILURE);
});
listener
.attachEvent(subscriberRight,
iox::popo::SubscriberEvent::DATA_RECEIVED,
iox::popo::createNotificationCallback(onSampleReceivedCallback))

回调签名是普通函数指针 void(Subscriber<T>*)(源码注释强调:Listener 不持有回调所有权、且不支持捕获 lambda);ice_callbacks_listener_as_class_member.cpp 进一步演示用 _with_context_data 变体把 this 传进静态成员回调。


6. request_response:客户端/服务端

v2.0 新增的请求-响应模式。client loan 请求、设置 sequenceId、send(),随后轮询 take() 响应并校验序号:

1
2
3
4
5
6
7
8
9
10
11
12
13
//! [send request]
client.loan()
.and_then([&](auto& request) {
request.getRequestHeader().setSequenceId(requestSequenceId);
expectedResponseSequenceId = requestSequenceId;
requestSequenceId += 1;
request->augend = fibonacciLast;
request->addend = fibonacciCurrent;
std::cout << APP_NAME << " Send Request: " << fibonacciLast << " + " << fibonacciCurrent << std::endl;
request.send().or_else(
[&](auto& error) { std::cout << "Could not send Request! Error: " << error << std::endl; });
})
.or_else([](auto& error) { std::cout << "Could not allocate Request! Error: " << error << std::endl; });

server 侧(server_cxx_basic.cpp)对称:server.take() 拿请求 → server.loan(request) 借响应(自动关联该请求)→ 填结果 → send()client_cxx_waitset.cppserver_cxx_listener.cpp 分别演示与 WaitSet/Listener 组合。底层机制是一对共享内存队列 + RequestHeader/ResponseHeadericeoryx_posh/popo/rpc_header.hpp)。


7. user_header:自定义头(时间戳)

发布者模板第二参数指定 user-header 类型,loan 后通过 getUserHeader() 写元数据——与 payload 分离、不侵入消息类型:

1
2
3
4
5
6
7
8
9
10
11
//! [loan sample]
publisher.loan(Data{fibonacciCurrent})
.and_then([&](auto& sample) {
//! [loan was successful]
sample.getUserHeader().publisherTimestamp = timestamp;
sample.publish();

std::cout << APP_NAME << " sent data: " << fibonacciCurrent << " with timestamp " << timestamp << "ms"
<< std::endl;
//! [loan was successful]
})

同目录提供 untyped C++ 与 C 版本;C 版本 publisher_c_api.c 用的正是 iox_pub_loan_aligned_chunk_with_user_header + iox_chunk_header_to_user_header——与 CycloneDDS 挂 iceoryx_header_t 的做法(07 篇 §5.2/5.3)完全同构,是理解 SHM 集成的最佳热身示例。


8. singleprocess:内嵌 RouDi

演示不起独立 iox-roudi,把 RouDi 组件直接实例化在自己进程里,publisher/subscriber 以线程形式通信(也是集成测试的常用手法):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//! [roudi config]
iox::RouDiConfig_t defaultRouDiConfig = iox::RouDiConfig_t().setDefaults();
iox::roudi::IceOryxRouDiComponents roudiComponents(defaultRouDiConfig);
//! [roudi config]

//! [roudi]
constexpr bool TERMINATE_APP_IN_ROUDI_DTOR_FLAG = false;
iox::roudi::RouDi roudi(
roudiComponents.rouDiMemoryManager,
roudiComponents.portManager,
iox::roudi::RouDi::RoudiStartupParameters{iox::roudi::MonitoringMode::OFF, TERMINATE_APP_IN_ROUDI_DTOR_FLAG});
//! [roudi]

// create a single process runtime for inter thread communication
//! [runtime]
iox::runtime::PoshRuntimeSingleProcess runtime("singleProcessDemo");

关键差异:用 PoshRuntimeSingleProcess 替代 PoshRuntime::initRuntime()(后者走 IPC 通道向外部 RouDi 注册)。限制:单进程模式下没有跨进程通信,仅线程间。


9. icediscovery:服务发现

iox_find_service.cpp 演示 ServiceDiscovery 的同步查询,支持通配符(iox::capro::Wildcard):

1
2
3
4
5
6
7
//! [search for unique service]
serviceDiscovery.findService(iox::capro::IdString_t{"Radar"},
iox::capro::IdString_t{"FrontLeft"},
iox::capro::IdString_t{"Image"},
printSearchResult,
iox::popo::MessagingPattern::PUB_SUB);
//! [search for unique service]
  • iox_offer_service.cpp:建 publisher 即自动 offer 服务;
  • iox_wait_for_service.cpp:把 ServiceDiscovery attach 到 WaitSetServiceDiscoveryEvent::SERVICE_REGISTRY_CHANGED),阻塞等待特定服务上线;
  • iox_discovery_monitor.cpp:attach 到 Listener,服务注册表变化时回调——实现”发现即回调”的监控器。

10. iceperf:延迟基准

10.1 测试方法

leader/follower 两个进程做 ping-pong 往返:leader 发一个 payload,follower 原样回发,重复 N 次(默认 10000,可用 -n 调整)。单向延迟 = 总耗时 / (N × 2):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
iox::units::Duration IcePerfBase::latencyPerfTestLeader(const uint64_t numRoundTrips) noexcept
{
auto start = std::chrono::steady_clock::now();

// run the performance test
for (auto i = 0U; i < numRoundTrips; ++i)
{
auto perfTopic = receivePerfTopic();
sendPerfTopic(perfTopic.payloadSize, RunFlag::RUN);
}

auto finish = std::chrono::steady_clock::now();

constexpr uint64_t TRANSMISSIONS_PER_ROUNDTRIP{2U};
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(finish - start);
auto latencyInNanoSeconds =
(static_cast<uint64_t>(duration.count()) / (numRoundTrips * TRANSMISSIONS_PER_ROUNDTRIP));
return iox::units::Duration::fromNanoseconds(latencyInNanoSeconds);
}

payload 从 1KB 扫到 4MB,横向对比四种 IPC 技术(同一套 IcePerfBase 抽象的四个实现):

1
2
std::vector<std::tuple<uint32_t, iox::units::Duration>> latencyMeasurements;
const std::vector<uint32_t> payloadSizesInKB{1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096};
技术 实现文件 说明
POSIX MQ mq.cpp 内核消息队列(macOS 不支持)
Unix Domain Socket uds.cpp 内核 socket
iceoryx C++ API iceoryx.cpp untyped loan/publish/take
iceoryx C API iceoryx_c.cpp binding_c 同路径

iceoryx 侧的收发即 untyped 零拷贝(无 memcpy payload,仅写头部字段):

1
2
3
4
5
6
7
8
9
10
11
void Iceoryx::sendPerfTopic(const uint32_t payloadSizeInBytes, const RunFlag runFlag) noexcept
{
m_publisher.loan(payloadSizeInBytes).and_then([&](auto& userPayload) {
auto sendSample = static_cast<PerfTopic*>(userPayload);
sendSample->payloadSize = payloadSizeInBytes;
sendSample->runFlag = runFlag;
sendSample->subPackets = 1;

m_publisher.publish(userPayload);
});
}

10.2 运行与解读

1
2
3
iox-roudi -c iceoryx_examples/iceperf/roudi_config.toml   # 需要大 mempool(最大 payload 4MB)
./iceperf-bench-leader # 终端 2
./iceperf-bench-follower # 终端 3

结果为 Markdown 表格(| Payload Size [kB] | Average Latency [µs] |)。含义:MQ/UDS 的延迟随 payload 线性增长(两次内核拷贝),iceoryx 的延迟基本与 payload 无关(常数级,只传指针)——这正是零拷贝的核心卖点,payload 越大优势越显著。leader 还会先通过 iceoryx topic {"IcePerf","Settings","Generic"} 把测试参数发给 follower(iceperf_leader.cpp:104-113),因此即便只测 MQ/UDS 也需要 RouDi 在跑。


11. tools:排障工具

11.1 iox-introspection-client

tools/introspection/ 构建出 iox-introspection-client,本质是一个订阅 RouDi 内省 topic(Introspection 服务,见 iceoryx_posh/roudi/introspection_types.hpp)的 ncurses 客户端:

1
2
3
4
5
6
7
8
9
10
11
"  introspection [OPTIONS] [SUBSCRIPTION]\n"
" introspection --help\n"
" introspection --version\n"
...
" -t, --time <ms> Update period (in milliseconds) for the display of introspection data\n"
...
" Select which introspection data you would like to receive.\n"
" --all Subscribe to all available introspection data.\n"
" --mempool Subscribe to mempool introspection data.\n"
" --port Subscribe to port introspection data.\n"
" --process Subscribe to process introspection data.\n"

三类视图对应三类排障问题:

选项 显示内容 排障用途
--mempool 每个共享内存段、每档 chunk 尺寸的 total/used/min free mempool 是否耗尽、chunk 尺寸档位是否合理(printMemPoolInfointrospection_app.cpp:259
--process 已注册进程列表(PID、名字) 应用是否成功挂上 RouDi
--port 所有 publisher/subscriber 端口、CaPro 三元组、连接关系与 runtime 归属 topic 名对不上、pub/sub 没连上(如 CycloneDDS 场景下能看到 DDS_CYCLONE 服务名的端口)

用法示例:iox-introspection-client --all -t 1000

11.2 其他工具

  • tools/iceoryx_build_test.sh:一键构建 + 测试脚本(build-all 包含示例与内省);
  • iceoryx_posh/roudi 提供的 iox-roudi 本身支持 -c 指定 TOML 配置(段/mempool 布局)、-l 日志级别、-m 监控模式,是排障的第一现场。

12. 排障 checklist

症状 可能原因 检查/处置
应用启动即卡住,反复打印等待 RouDi,最终 terminate iox-roudi 没起 / 起晚了 pgrep iox-roudi;RouDi 必须先于所有应用(含开启 SHM 的 ROS 2 节点)启动
应用被 SIGKILL 后重启报”process already registered” RouDi 里残留旧进程注册 RouDi 监控模式(-m on)可自动清理;否则重启 RouDi
loan 返回 RUNNING_OUT_OF_CHUNKS / CycloneDDS 报 OUT_OF_RESOURCES mempool 耗尽或没有足够大的 chunk 档位 iox-introspection-client --mempool 看 min free;调大 RouDi TOML 中对应尺寸档位的 chunk 数量/大小(记得算上 ChunkHeader + user-header 开销)
日志 TOO_MANY_CHUNKS_HELD_IN_PARALLEL 订阅端 take 后不 release,持有超上限 检查是否遗漏 release/dds_return_loan/Sample 生命周期;C API 尤其容易漏 iox_sub_release_chunk
chunk 泄漏(mempool used 只增不减) publisher loan 后既不 publish 也不 release 内省 mempool 视图定位段;审查所有早退/异常路径
订阅端丢样本(iox_sub_has_lost_chunks 为真) 队列溢出(发布快于消费) 增大 queueCapacity(上限 256)、降低发布频率、或把 queueFullPolicy 设为阻塞发布者(配合发布端 subscriberTooSlowPolicy
段权限错误(无法 map 共享内存段) 应用用户不在段配置的用户组里 参考 iceoryx_examples/ice_access_control;检查 RouDi 段配置 TOML 的 reader/writer 用户组与进程属组
pub/sub 建了但对不上 CaPro 三元组不一致(含大小写) iox-introspection-client --port 对照两侧服务描述

13. 相关文档


下一篇

本篇是系列最后一篇,返回 README.md

Eclipse iceoryx 源码详细分析

Eclipse iceoryx 源码详细分析

工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/eclipse-iceoryx/iceoryx
版本:2.0.6iceoryx_posh/package.xml),语言 C++17(含 C binding),许可证 Apache 2.0

iceoryx 是面向**进程间通信(IPC)**的中间件,核心卖点是 真零拷贝(true zero-copy):Publisher 与 Subscriber 通过 POSIX 共享内存直接传递数据指针,payload 不经 socket 拷贝。在 ROS 2 Humble 中,它主要作为 CycloneDDS 共享内存传输层的后端(ENABLE_SHM + iceoryx_binding_c),也可通过 rmw_iceoryx 作为独立 RMW 使用。


1. 总体架构

与 CycloneDDS 的“协议栈 + API”不同,iceoryx 是纯 IPC 层,不负责 DDS/ROS 的发现与 QoS,只负责内存池 + 端口 + 数据分发

应用进程RouDi 守护进程POSIX Shared Memoryloan/publishtake/releasePublisher / ClientSubscriber / ServerPoshRuntimePortManagerMemoryManager / MePooServiceRegistryManagement Segment<br/>端口/元数据Payload Segment<br/>Chunk 数据
模块 路径 职责 ROS 2 包名
iceoryx_hoofs iceoryx_hoofs/ 基础库:容器、相对指针、POSIX 封装、无锁队列 iceoryx_hoofs
iceoryx_posh iceoryx_posh/ 核心中间件 + RouDi 守护进程 iceoryx_posh
iceoryx_binding_c iceoryx_binding_c/ C API(供 CycloneDDS 等 C 项目调用) iceoryx_binding_c
iceoryx_dds iceoryx_dds/ iceoryx ↔ DDS 网关(可选) 非 Humble 默认可选
tools/introspection tools/introspection/ 运行时 introspection 工具 iceoryx_introspection

源码规模:iceoryx_posh91 个 .cppiceoryx_hoofs36 个 .cpp,合计 1300+ 文件


2. 核心概念

2.1 RouDi(Routing and Discovery)

RouDi 是 iceoryx 的中心守护进程,必须在所有应用之前启动(ROS 2 中通常由 launch 或 iox-roudi 启动)。

职责(见 internal/roudi/roudi.hpp):

  • 创建并管理 共享内存段(management + payload)
  • 处理 Runtime 的 IPC 注册(进程注册、版本校验)
  • 通过 PortManager 分配 Publisher/Subscriber/Client/Server 端口
  • 维护 ServiceRegistry(服务发现)
  • 进程异常退出时 清理 chunk 与端口
  • 提供 Introspection(mempool/port/process 监控)

应用侧通过 PoshRuntime::initRuntime(name) 与 RouDi 建立 Unix Domain Socket 通道,请求创建端口。

2.2 ServiceDescription(三元组寻址)

iceoryx 用 Service / Instance / Event 三元组标识通信端点(类似 AUTOSAR ara::com):

1
2
/// @brief class for the identification of a communication event including information on the service, the service
/// instance and the event id.

CycloneDDS SHM 集成时,会将 DDS topic 名映射为这三元组。接口类型枚举还包括 DDSROS1 等,表明其设计面向多协议网关。

2.3 Chunk — 零拷贝的数据单元

Chunk 是共享内存中的传输胶囊,布局见 doc/design/chunk_header.md

1
2
3
+===================+===============+====================+============+
| ChunkHeader | User-Header | User-Payload | Padding |
+===================+===============+====================+============+

ChunkHeadermepoo/chunk_header.hpp)包含:

  • chunkSizechunkHeaderVersion
  • originId(Publisher 唯一 ID)、sequenceNumber
  • userHeaderSize / userPayloadSize / userPayloadAlignment
  • 通过 back-offset 从 user-payload 反查 ChunkHeader

设计约束:

  • 共享内存映射到各进程不同虚拟地址禁止裸指针,必须用相对/可重定位指针
  • 支持 record & replay 的版本号与 origin 追踪

3. iceoryx_hoofs — 基础库

hoofs = Healthy Overly Optimistic Foundation Stuff(项目自嘲式命名),提供无 STL 依赖(或最小依赖)的基础能力。

3.1 目录结构

子目录 内容
cxx/ expectedoptionalvectorstring 等轻量容器
concurrent/ 无锁队列 lockfree_queue、SOFI、TACO
posix_wrapper/ 共享内存、mutex、UDS、semaphore
internal/relocatable_pointer/ RelativePointer / relocatable_ptr
error_handling/ 统一错误处理
log/ 日志框架
platform/ linux/mac/qnx/win/unix 平台差异

3.2 相对指针(共享内存关键)

doc/design/relocatable_pointer.md 说明:

  • 各进程将同一段 SHM 映射到不同基址
  • relocatable_ptr:指针与 pointee 在同一段内,存偏移量
  • RelativePointer:跨段引用,通过全局 segment id 解析

RouDi 的 Port 数据结构、Chunk 队列、Subscriber 列表等都建立在相对指针之上,这是 iceoryx 能在多进程间安全传递“指针”的基础。

3.3 无锁队列

doc/design/lockfree_queue.md + concurrent/lockfree_queue.hpp:Subscriber 侧 chunk 队列采用无锁设计,降低 pub/sub 热路径上的锁竞争。


4. iceoryx_posh — 核心中间件

posh = Posix Shared Memory。源码按功能分目录:

1
2
3
4
5
6
7
8
iceoryx_posh/source/
├── capro/ # Capabilities & Protocol — ServiceDescription、发现消息
├── mepoo/ # Memory Pool — ChunkHeader、MemoryManager、SharedChunk
├── popo/ # Posix Objects — Publisher/Subscriber/Client/Server/WaitSet
├── roudi/ # RouDi 守护进程实现
├── runtime/ # PoshRuntime — 应用与 RouDi 的 IPC 接口
├── gateway/ # 网关相关
└── version/ # 版本与兼容性检查

4.1 MePoo(Memory Pool)

组件 说明
MePooConfig 配置多档 mempool:(chunkSize, chunkCount) 列表
MemoryManager 从 mempool 分配/释放 chunk
SharedChunk 带引用计数的 chunk 句柄
ChunkSettings user-payload/header 大小与对齐

默认 mempool 通过 MePooConfig::setDefaults() 设置;RouDi 配置文件(TOML)可覆盖。chunk 不足时 loan() 返回 AllocationError::RUNNING_OUT_OF_CHUNKS

4.2 popo — 面向用户的 API

popo = Posix Objects(仿 COM 命名)。

模式 核心操作
Publisher<T> Pub/Sub loan() → 写 payload → publish()
Subscriber<T> Pub/Sub take() / hasData()
Client / Server Request/Response loan() request → server 处理 → response
WaitSet 事件驱动 等待多个 subscriber/trigger
Listener 回调 异步通知
UntypedPublisher/Subscriber 底层 C binding 与中间件集成用

Publisher 典型流程(publisher_impl.hpp):

1
2
3
4
5
// 1. 从 mempool loan 一块共享内存
auto sample = publisher.loan();
// 2. 写入 sample->payload
// 3. 发布(ChunkDistributor 推送到所有 Subscriber 队列)
publisher.publish(std::move(sample));

publishCopyOf() 是带拷贝的便捷路径,不是零拷贝

4.3 Building Blocks — 内部构建块

Pub/Sub 内部分层(自底向上):

1
2
3
4
5
6
7
8
9
MemoryManager

ChunkSender ←→ ChunkReceiver
↓ ↓
ChunkDistributor ChunkQueuePopper
↓ ↓
PublisherPort SubscriberPort
↓ ↓
Publisher Subscriber
构建块 职责
ChunkSender 分配 chunk + 通过 ChunkDistributor 发送
ChunkReceiver 从 ChunkQueue 接收 chunk
ChunkDistributor 向多个 Subscriber 队列分发 SharedChunk,支持 history
ChunkQueuePusher/Popper 无锁队列两端

Publisher 与 Subscriber 匹配时,RouDi 的 PortManager 将 Subscriber 的 ChunkQueue 注册到 Publisher 的 ChunkDistributor(port_manager.hppacquirePublisherPortData / acquireSubscriberPortData)。

4.4 Port 架构(User / RouDi 分离)

每个 Port 有三层:

位置 作用
*PortData 共享内存 纯数据,无方法
*PortUser 应用进程 用户侧 API
*PortRouDi RouDi 进程 连接、清理、introspection

这种分离保证:RouDi 可直接操作 SHM 中的 port 元数据,而应用通过 User 层访问,进程崩溃时 RouDi 仍能 cleanup。

4.5 PoshRuntime

1
2
3
4
5
6
7
8
9
/// @brief The runtime that is needed for each application to communicate with the RouDi daemon
class PoshRuntime
{
public:
static PoshRuntime& initRuntime(const RuntimeName_t& name) noexcept;
virtual PublisherPortUserType::MemberType_t*
getMiddlewarePublisher(const capro::ServiceDescription& service, ...) noexcept = 0;
virtual SubscriberPortUserType::MemberType_t*
getMiddlewareSubscriber(const capro::ServiceDescription& service, ...) noexcept = 0;
  • 单例,initRuntime() 注册进程名(须唯一)
  • 通过 IPC 消息向 RouDi 请求创建 Publisher/Subscriber/Client/Server
  • 也支持 SingleProcess 模式(测试用,无需 RouDi)

4.6 Request/Response(ROS 2 Service 基础)

doc/design/request_response_communication.md

  • Client:ChunkSender 发 request + ChunkReceiver 收 response
  • Server:ChunkReceiver 收 request + ChunkSender 发 response
  • Request/Response Header 含 sequence ID,支持异步 RPC
  • 同一 ServiceDescription 只允许一个 Server

这与 ROS 2 rclcpp::Client / rclcpp::Service 的语义对齐。


5. iceoryx_binding_c — C 绑定

路径:iceoryx_binding_c/include/iceoryx_binding_c/

头文件 API
runtime.h iox_runtime_init()
publisher.h iox_pub_init(), iox_pub_loan_chunk(), iox_pub_publish_chunk()
subscriber.h iox_sub_take_chunk(), iox_sub_release_chunk()
chunk.h ChunkHeader 访问
service_description.h 三元组构造
wait_set.h / listener.h 事件等待

C binding 是 CycloneDDS SHM 集成的直接依赖——CycloneDDS 为 C 项目,不能直接用 C++ Publisher<T>


6. 与 CycloneDDS / ROS 2 的集成

6.1 CycloneDDS SHM 路径

CycloneDDS 编译选项 ENABLE_SHM=AUTO 会查找 iceoryx_binding_c,启用 DDS_HAS_SHM

关键桥接代码:cyclonedds/src/core/ddsi/src/ddsi_shm_transport.c

1
2
3
4
5
6
7
8
9
10
11
void *shm_create_chunk(iox_pub_t iox_pub, size_t size) {
// ...
iox_pub_loan_aligned_chunk_with_user_header(
iox_pub, &iox_chunk, (uint32_t)size,
IOX_C_CHUNK_DEFAULT_USER_PAYLOAD_ALIGNMENT,
sizeof(iceoryx_header_t), 8);
// ...
ice_hdr->data_size = (uint32_t)size;
ice_hdr->shm_data_state = IOX_CHUNK_UNINITIALIZED;
return iox_chunk;
}

数据流(同机 ROS 2 + CycloneDDS + SHM):

1
2
3
4
5
6
7
8
9
10
rclcpp::Publisher::publish(msg)
→ rmw_cyclonedds_cpp::dds_write
→ dds_write / dds_writecdr
→ [SHM 启用且订阅者在同机]
→ iox_pub_loan_chunk (共享内存)
→ 序列化 payload 到 chunk(仍有一次写入 SHM)
→ iox_pub_publish_chunk
→ Subscriber 侧 iox_sub_take_chunk
→ dds_read 直接读 SHM 指针(零拷贝取数据)
→ [否则] UDP/TCP RTPS 网络路径

注意:

  • 跨进程时 payload 仍要写入共享内存一次(不是完全无 touch),但 subscriber 侧不再拷贝到用户 buffer(loan 模式下)
  • 网络 RTPS 只传递 chunk 指针/通知(iceoryx 内部机制),不传 payload 本体
  • 需先启动 RouDi,且 Publisher/Subscriber 在同一 iceoryx domain

6.2 依赖关系(package.xml)

1
2
3
4
<!-- cyclonedds/package.xml -->
<depend>iceoryx_binding_c</depend>
<depend>iceoryx_posh</depend>
<depend>iceoryx_hoofs</depend>

6.3 iceoryx_dds 网关(可选)

iceoryx_dds/ 提供 iceoryx ↔ Cyclone DDS 双向网关:

  • gateway/iox_to_dds.hpp — iceoryx → DDS
  • gateway/dds_to_iox.hpp — DDS → iceoryx
  • 用于跨网络或异构系统桥接,Humble 默认 ROS 工作流不必须。

7. 配置与部署

7.1 RouDi 配置

  • TOML 配置文件(roudi_config.toml)指定 mempool 大小、segment 数量
  • 环境变量:IOX_ROUDI_CONFIG_FILE
  • 默认 mempool 档位覆盖常见消息大小(128B ~ 4MB 等)

7.2 运行顺序

1
2
3
4
5
6
7
# 1. 启动 RouDi(必须最先)
iox-roudi

# 2. 启动 ROS 2 节点(CycloneDDS SHM 模式)
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
# cyclonedds.xml 中启用 SharedMemory
ros2 run ...

7.3 平台支持

平台 SHM 访问控制 说明
Linux 支持 ROS 2 主平台
QNX 支持 汽车场景起源
macOS 可运行但无权限隔离
Windows 开发中

8. 事件驱动 API

机制 用途
WaitSet 阻塞等待多个 subscriber/guard condition(类似 rcl_wait
Listener 注册回调,事件到达时触发(类似 rclcpp executor 回调)
UserTrigger 手动触发事件
Notification 跨进程通知 subscriber 有新数据

CycloneDDS SHM 路径中,subscriber 收到 iceoryx notification 后才会去 take chunk。


9. 错误处理与健壮性

  • ConsumerTooSlowPolicy:Subscriber 队列满时,Publisher 可 BLOCK 或 DISCARD_OLDEST_DATA
  • 进程崩溃:RouDi 检测并 cleanup 泄漏的 chunk(UsedChunkList
  • 版本兼容:Runtime 注册时校验 VersionInfo(MAJOR/MINOR/PATCH)
  • expected<T, E>:Hoofs 提供的 Rust 风格错误返回,贯穿 loan/publish API

10. 与 CycloneDDS 的职责对比

维度 CycloneDDS iceoryx
定位 完整 DDS 中间件(发现/QoS/网络) 纯 SHM IPC 传输
发现 SPDP/SEDP RouDi + ServiceRegistry(本地)
传输 UDP/TCP/SHM 仅 SHM
数据单元 serdata (CDR) Chunk
API 语言 C (dds.h) C++ 为主 + C binding
守护进程 无(P2P) RouDi 必须
ROS 2 角色 默认 RMW 后端 CycloneDDS 的 SHM 加速层

11. 推荐阅读顺序

目标:理解 ROS 2 大消息零拷贝

  1. doc/design/chunk_header.md — Chunk 内存布局
  2. doc/design/relocatable_pointer.md — 为何不能用裸指针
  3. iceoryx_posh/include/iceoryx_posh/popo/publisher.hpppublisher_impl.hpp
  4. internal/popo/building_blocks/chunk_sender.hppchunk_distributor.hpp
  5. internal/roudi/port_manager.hpp — 端口如何匹配
  6. iceoryx_binding_c/include/iceoryx_binding_c/publisher.h — C API
  7. cyclonedds/src/core/ddsi/src/ddsi_shm_transport.c — 与 DDS 的接缝
  8. 示例:iceoryx_examples/icedelivery/iceoryx_examples/singleprocess/

目标:调试 SHM 问题

  1. 确认 RouDi 运行:iox-introspection-clienticeoryx_introspection
  2. 检查 mempool 耗尽、chunk 泄漏
  3. 查看 CycloneDDS SharedMemory 配置段

12. 设计特点小结

特点 说明
真零拷贝 Subscriber 直接读 SHM 中的 chunk,无 payload 拷贝
恒定延迟 传输时间与 payload 大小无关(仅指针/通知)
RouDi 中心化 内存与端口生命周期由守护进程统一管理
三层 Port 模型 Data(User/RouDi) 分离,支持 crash cleanup
Building Blocks 组合 ChunkSender/Distributor/Queue 可复用于 Pub/Sub 与 RPC
相对指针 解决 SHM 多映射地址问题
C binding 使 C 系中间件(CycloneDDS)可集成
汽车级起源 AUTOSAR 风格 ServiceDescription、QNX 支持

如果你希望,我可以:

  1. 把本文写入 ros2doc/cyclonedds/ 同级目录 ros2doc/iceoryx/Eclipse iceoryx 源码详细分析.md
  2. 继续深入 CycloneDDS ↔ iceoryx 的 topic 映射规则RouDi 启动与 mempool 配置 的源码级追踪

Eclipse iceoryx 源码分析文档

Eclipse iceoryx 源码分析文档

源码:ros2_humble/src/eclipse-iceoryx/iceoryx · v2.0.6
进程间零拷贝共享内存中间件 · ROS 2 Humble 中作为 CycloneDDS 的 SHM 传输后端


推荐阅读顺序

第一步:整体框架(必读)

顺序 文档 说明
1 00-整体框架与模块划分.md 定位、模块依赖、进程模型、数据流总览
2 01-hoofs基础库.md 无异常/无堆分配基础库、无锁原语、RelativePointer 指针相对化
3 02-mepoo共享内存与内存池.md Segment、MemPool/LoFFLi、ChunkHeader、跨进程引用计数
4 03-popo发布订阅与通信原语.md Publisher/Subscriber、Port 体系、WaitSet/Listener、request/response
5 04-RouDi守护进程与服务发现.md RouDi 启动、进程注册/心跳、PortManager、CaPro 发现
6 05-零拷贝数据路径详解.md loan → publish → take 全链路重点

第二步:集成与实践

文档 内容
06-C绑定与DDS网关.md iceoryx_binding_c C API、iox-dds-gateway(本仓 COLCON_IGNORE)
07-与CycloneDDS及ROS2集成.md ENABLE_SHM 全链路、SHEM locator、LoanedMessage、常见坑
08-示例与性能工具.md icehello/iceperf 等示例走读、introspection 排障 checklist

总览

文档 内容
Eclipse iceoryx 源码详细分析.md 单篇架构总览 + ROS 2 集成

模块 → 源码路径

模块 路径 职责
iceoryx_hoofs iceoryx_hoofs/ OS 抽象、容器、无锁原语、RelativePointer
iceoryx_posh iceoryx_posh/ 核心:mepoo / popo / roudi / runtime / capro
iceoryx_binding_c iceoryx_binding_c/ C API(CycloneDDS 用它接入)
iceoryx_dds iceoryx_dds/ DDS 网关(Humble 构建时 COLCON_IGNORE)
iceoryx_examples iceoryx_examples/ 示例与 iceperf 基准
tools tools/ iox-introspection-client 等

核心概念速记

概念 一句话
RouDi 守护进程:建共享内存、管端口与发现,不在数据路径上
Chunk 共享内存中的固定大小数据块(ChunkHeader + payload)
MemPool 固定大小块的无锁内存池(LoFFLi free-list)
loan/publish/take 借块写入 → 推相对指针入队 → 订阅端直接读,全程零拷贝
RelativePointer (segment id, offset) 编码,使指针在不同进程映射地址下有效
CaPro service/instance/event 三元组的发现协议

快速上手(ROS 2 零拷贝)

1
2
3
4
5
6
7
8
9
# 1. 启动 RouDi
iox-roudi

# 2. 启用 CycloneDDS 共享内存
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export CYCLONEDDS_URI='<CycloneDDS><Domain><SharedMemory><Enable>true</Enable></SharedMemory></Domain></CycloneDDS>'

# 3. 观察内存池占用
iox-introspection-client --mempool

细节与限制(QoS 约束、POD 类型要求、mempool 配置)见 07-与CycloneDDS及ROS2集成.md
排障从 08-示例与性能工具.md 的 checklist 开始。

对照阅读:../cyclonedds/README.md(网络路径) vs 本系列(同机共享内存路径)。

osrf_pycommon 源码详细分析

osrf_pycommon 源码详细分析

工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/osrf/osrf_pycommon
版本:2.1.7package.xml),构建类型 ament_python,Python ≥3.5,许可证 Apache 2.0

osrf_pycommon 是 OSRF(Open Source Robotics Foundation)维护的 Python 通用工具库,体量很小(约 47 个文件4 个子模块),不依赖 ROS 运行时。在 ROS 2 Humble 工作区中,它主要被 launch / launch_ros / launch_testing 依赖,承担子进程异步执行终端颜色处理CLI 扩展模式等基础能力。


1. 总体认识

1.1 设计原则(来自 docs/index.rst

  • 只使用标准库或极少量外部依赖(importlib-metadata
  • 尽量支持 Linux / macOS / Windows,不支持处优雅降级
  • 纯 Python 3,无 C 扩展

1.2 模块结构

1
2
3
4
5
6
7
8
9
10
osrf_pycommon/
├── osrf_pycommon/
│ ├── process_utils/ # 子进程执行(同步/异步)★ ROS 2 最常用
│ ├── terminal_color/ # ANSI 颜色与转义序列
│ ├── terminal_utils.py # 终端尺寸、is_tty
│ └── cli_utils/ # CLI verb 模式、参数解析辅助
├── tests/ # unittest 单元测试
├── docs/ # Sphinx 文档
├── setup.py
└── package.xml
ROS 2 主要消费者osrf_pycommonlaunchlaunch_roslaunch_testingprocess_utilsterminal_colorterminal_utilscli_utils

2. process_utils — 核心模块

路径:osrf_pycommon/process_utils/
这是 ROS 2 生态中使用最广泛的部分。

2.1 公开 API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from .async_execute_process import async_execute_process
from .async_execute_process import asyncio
from .async_execute_process import AsyncSubprocessProtocol
from .async_execute_process import get_loop

from .impl import execute_process
from .impl import execute_process_split
from .impl import which

__all__ = [
'async_execute_process',
'asyncio',
'AsyncSubprocessProtocol',
'get_loop',
'execute_process',
'execute_process_split',
'which',
]
函数/类 类型 用途
execute_process 同步生成器 逐行 yield 子进程 stdout(合并 stderr)
execute_process_split 同步生成器 stdout/stderr 分开 yield
async_execute_process asyncio 协程 异步启动子进程
AsyncSubprocessProtocol Protocol 类 异步 I/O 回调基类
get_loop 函数 获取/创建合适的事件循环
which 函数 查找可执行文件(shutil.which 兼容回退)

2.2 同步执行路径

1
2
3
execute_process / execute_process_split  (impl.py)
├── emulate_tty=False → execute_process_nopty.py
└── emulate_tty=True → execute_process_pty.py (Unix only)

nopty 实现execute_process_nopty.py):

  • 基于 subprocess.Popen + select.select(Unix)或 readline(Windows)
  • 按行缓冲输出,保留换行符
  • 可用 stderr_to_stdout 合并或分离 stderr

pty 实现execute_process_pty.py):

  • pty.openpty() 让子进程认为在 TTY 上运行
  • 子进程会输出彩色日志、启用行缓冲(如 Python -u 行为)
  • 注意 pty 数量有限,大量并行可能 OSError: out of pty devices
  • Windows 无 pty,自动回退 nopty

2.3 异步执行路径

1
2
3
async_execute_process  (async_execute_process_asyncio/impl.py)
├── emulate_tty=False → loop.subprocess_exec / subprocess_shell
└── emulate_tty=True → pty + connect_read_pipe

AsyncSubprocessProtocolasync_execute_process.py):

  • 继承 asyncio.SubprocessProtocol
  • 可覆写 on_stdout_received / on_stderr_received / on_process_exited
  • protocol.completeFuture,完成时结果为 return code

get_loopget_loop_impl.py):

  • 线程局部单例事件循环
  • Windows 强制使用 ProactorEventLoop(子进程管道需要)
  • 处理 Python 3.10 的 DeprecationWarning

2.4 which 回退实现

Python 3.3+ 优先用 shutil.which;旧版本使用 _which_backport,支持:

  • Windows PATHEXT.exe 等)
  • 相对路径 ./script
  • 目录排除(避免把目录当可执行文件)

3. 在 ROS 2 launch 中的关键作用

3.1 ExecuteLocal — 启动节点进程

launch/actions/execute_local.py 是最大消费者:

1
2
from osrf_pycommon.process_utils import async_execute_process
from osrf_pycommon.process_utils import AsyncSubprocessProtocol

ExecuteLocalasync_execute_process 启动 ros2 run、节点可执行文件等,并通过自定义 Protocol 将 stdout/stderr 转为 launch 事件(ProcessStdout / ProcessStderr)。

3.2 LaunchService 事件循环

1
2
3
4
5
6
7
loop = osrf_pycommon.process_utils.get_loop()
run_async_task = loop.create_task(self.run_async(
shutdown_when_idle=shutdown_when_idle
))
while True:
try:
return loop.run_until_complete(run_async_task)

整个 ros2 launch 的异步调度建立在 get_loop() 返回的事件循环上。

3.3 查找可执行文件

  • launch/substitutions/find_executable.pywhich
  • launch_ros/substitutions/executable_in_package.pywhich

用于解析 launch 文件中 $(find-pkg-prefix) 等替换后的可执行路径。

3.4 launch_testing 颜色剥离

  • launch_testing/tools/output.py
  • launch_testing/asserts/assert_output.py

使用 remove_ansi_escape_sequences 在断言输出时去掉 ANSI 转义,避免颜色码导致测试失败。


4. terminal_color — 终端颜色

路径:osrf_pycommon/terminal_color/

4.1 子模块

文件 职责
impl.py ansi()format_color()print_color()、颜色字典
ansi_re.py 正则匹配/剥离 ANSI 转义序列
windows.py Win32 API 彩色输出(借鉴 colorama 思路,不 hook stdout)

4.2 两种着色方式

1. 直接 ANSI 码

1
2
from osrf_pycommon.terminal_color import ansi
print(ansi('red') + "error" + ansi('reset'))

2. @{} 标记替换

1
2
from osrf_pycommon.terminal_color import format_color
print(format_color("This is @{bf}blue@{reset}."))

支持 @{redf} / @{rf} / @{r} 等多种简写;背景色必须带 b 后缀(如 @{rb})。

4.3 平台行为

  • Linux/macOS:正常输出 ANSI 转义
  • Windowsansi() 返回空字符串;需用 print_color()print_ansi_color_win32() 才能显示颜色
  • 可调用 disable_ansi_color_substitution_globally() 全局关闭颜色

4.4 ANSI 处理工具

1
2
3
4
5
6
def remove_ansi_escape_sequences(string):
"""
Removes any ansi escape sequences found in the given string and returns it.
"""
global _ansi_re
return _ansi_re.sub('', string)

正则 \033\[\d{1,2}[m] 匹配常见 SGR 序列;另有 split_by_ansi_escape_sequence 用于分段处理。


5. terminal_utils — 终端工具

单文件 terminal_utils.py,仅 3 个公开符号:

函数 说明
get_terminal_dimensions() 返回 (width, height);Unix 用 tput cols/lines,Windows 用 GetConsoleScreenBufferInfo
is_tty(stream) 判断 stream 是否为 TTY
GetTerminalDimensionsError 无法获取尺寸时抛出

用途相对独立,ROS 2 核心路径较少直接引用。


6. cli_utils — CLI 扩展模式

路径:osrf_pycommon/cli_utils/

6.1 verb_pattern — 插件式子命令

这是 ROS 2 早期 ros2 xxx verb 风格 CLI 的基础模式(ros2 topic echo 等),基于 setuptools entry_points 动态加载 verb:

函数 作用
list_verbs(group) 从 entry_point group 列出所有 verb 名
load_verb_description(name, group) 加载 verb 模块(含 mainprepare_arguments
create_subparsers(...) 为每个 verb 创建 argparse 子解析器
split_arguments_by_verb(args) 拆分 ros2 [全局选项] verb [verb选项]
call_prepare_arguments(func, parser, sysargs) 兼容 1/2 参数版本的 prepare_arguments

verb 模块约定结构(见 docs/cli_utils.rst):

1
2
3
4
5
6
verb = 'myverb'
description = '...'
def prepare_arguments(parser): ...
def main(args): ...
# 可选
def argument_preprocessor(args): ...

注意:当前 Humble 工作区中,ros2cli 不再直接依赖 osrf_pycommon,而是使用自有的 ros2cli 框架;cli_utils 仍可作为构建类似 CLI 的参考库,也被 colcon 等工具的历史版本使用过。

6.2 common — 参数解析辅助

函数 用途
extract_jobs_flags(arguments) 从 make 参数字符串中提取 -j8-l8--jobs=4
extract_argument_group(args, '--args') --args ... -- 分隔符提取参数组;支持 --- 转义

典型场景:构建工具需要把「传给 make 的并行参数」与「传给目标的参数」分开。


7. 依赖与构建

7.1 package.xml

1
<exec_depend>python3-importlib-metadata</exec_depend>

verb_pattern.list_verbsimportlib.metadata.entry_points() 发现插件;Python 3.8+ 内置,旧版通过 importlib-metadata 包提供。

7.2 setup.py 要点

  • ament_python 包,注册 resource/osrf_pycommon 索引
  • 测试 extra:flake8pytest
  • zip_safe=True

8. 测试结构

1
2
3
4
5
6
7
tests/
├── test_code_format.py # flake8
└── unit/
├── test_process_utils/ # 同步/异步/pty 子进程
├── test_terminal_color/
├── test_terminal_utils.py
└── test_cli_utils/

进程测试包含 stdout_stderr_ordering 等 fixture,验证 nopty/pty 模式下输出顺序行为。


9. 在 ROS 2 栈中的位置

1
2
3
4
5
6
7
8
9
10
用户: ros2 launch my_pkg launch.py


launch.LaunchService.run()
│ get_loop()

ExecuteLocal.execute()
│ async_execute_process(AsyncSubprocessProtocol, cmd)

子进程 (talker, listener, ...)
依赖 osrf_pycommon 使用内容
launch depend async_execute_process, get_loop, which
launch_ros depend which
launch_testing exec_depend remove_ansi_escape_sequences
launch_pytest exec_depend (传递依赖)

与 CycloneDDS、iceoryx 等不同,osrf_pycommon 不参与通信,只服务于 Python 工具链的运行时基础设施


10. 设计特点小结

特点 说明
轻量 4 模块、无重型依赖
跨平台子进程 Unix select + Windows readline;可选 pty 模拟 TTY
asyncio 集成 为 launch 提供统一的 ProactorEventLoop(Windows)
流式输出 生成器/Protocol 模式,适合实时日志转发
颜色可剥离 测试断言时可去掉 ANSI,避免误匹配
verb 插件模式 entry_points 驱动的可扩展 CLI 框架(历史影响 ros2cli 设计)
向后兼容 which 回退、importlib_metadata 双版本 API

11. 推荐阅读顺序

  1. launch 集成launch/actions/execute_local.py — 看 Protocol 如何包装子进程 I/O
  2. 异步基础process_utils/async_execute_process_asyncio/impl.py + get_loop_impl.py
  3. 同步/ptyprocess_utils/impl.pyexecute_process_nopty.py / execute_process_pty.py
  4. 测试断言launch_testing/asserts/assert_output.py — 颜色剥离用法
  5. CLI 模式docs/cli_utils.rst + cli_utils/verb_pattern.py
  6. 文档docs/process_utils.rstdocs/terminal_color.rst

如果你希望,我可以把本文写入 ros2doc/osrf_pycommon/,或继续分析 launch 中 ExecuteLocal 如何基于 AsyncSubprocessProtocol 转发 stdout 的完整调用链

ament_cmake_ros 源码详细分析

ament_cmake_ros 源码详细分析

工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros2/ament_cmake_ros
子包数量:2


1. 定位

ament_cmake_ros 目录含 2 个 ROS 2 包,工作区路径见下。


2. 子包列表

包名 版本 说明
ament_cmake_ros 0.10.0 The ROS specific CMake bits in the ament buildsystem.
domain_coordinator 0.10.0 A tool to coordinate unique ROS_DOMAIN_IDs across multiple p…

3. 在 ROS 2 Humble 栈中的关系

ros2总览.md 分层图。


4. 推荐阅读顺序

  1. 阅读各子包 package.xml 2. 入口源码 3. 下游依赖方

5. 小结

ament_cmake_ros 为含 2 个子包的源码树,是 ROS 2 Humble 发行版的一部分。

common_interfaces 源码详细分析

common_interfaces 源码详细分析

工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros2/common_interfaces
子包数量:13


1. 定位

common_interfaces 目录含 13 个 ROS 2 包,工作区路径见下。


2. 子包列表

包名 版本 说明
actionlib_msgs 4.9.1 A package containing some message definitions used in the im…
common_interfaces 4.9.1 common_interfaces contains messages and services that are wi…
diagnostic_msgs 4.9.1 A package containing some diagnostics related message and se…
geometry_msgs 4.9.1 A package containing some geometry related message definitio…
nav_msgs 4.9.1 A package containing some navigation related message and ser…
sensor_msgs 4.9.1 A package containing some sensor data related message and se…
sensor_msgs_py 4.9.1 A package for easy creation and reading of PointCloud2 messa…
shape_msgs 4.9.1 A package containing some message definitions which describe…
std_msgs 4.9.1 A package containing some standard message definitions.
std_srvs 4.9.1 A package containing some standard service definitions.
stereo_msgs 4.9.1 A package containing some stereo camera related message defi…
trajectory_msgs 4.9.1 A package containing some robot trajectory message definitio…
visualization_msgs 4.9.1 A package containing some visualization and interaction rela…

3. 在 ROS 2 Humble 栈中的关系

标准消息定义


4. 推荐阅读顺序

  1. 阅读各子包 package.xml 2. 入口源码 3. 下游依赖方

5. 小结

common_interfaces 为含 13 个子包的源码树,是 ROS 2 Humble 发行版的一部分。

console_bridge_vendor 源码详细分析

console_bridge_vendor 源码详细分析

工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros2/console_bridge_vendor
子包数量:1


1. 定位

Vendor / 第三方依赖打包包:通过 ExternalProject 或系统包 shim 为 ROS 2 构建提供固定版本的 console_bridge,本身几乎无 ROS 业务逻辑。


2. 子包列表

包名 版本 说明
console_bridge_vendor 1.4.1 Wrapper around console_bridge, providing nothing but a depen…

3. 核心组件

典型结构:CMakeLists.txt 下载/查找库 + package.xml 导出 find_package 依赖。消费方在 package.xml<depend> 本 vendor 包即可。


4. 在 ROS 2 Humble 栈中的关系

ros2总览.md 分层图。


5. 推荐阅读顺序

  1. 阅读各子包 package.xml 2. 入口源码 3. 下游依赖方

6. 小结

console_bridge_vendor 为单包仓库,提供 console_bridge_vendor 功能。

demos 源码详细分析

demos 源码详细分析

工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros2/demos
子包数量:21


1. 定位

demos 目录含 21 个 ROS 2 包,工作区路径见下。


2. 子包列表

包名 版本 说明
action_tutorials_cpp 0.20.9 C++ action tutorial cpp code
action_tutorials_interfaces 0.20.9 Action tutorials action
action_tutorials_py 0.20.9 Python action tutorial code
composition 0.20.9 Examples for composing multiple nodes in a single process.
demo_nodes_cpp 0.20.9 C++ nodes which were previously in the ros2/examples reposit…
demo_nodes_cpp_native 0.20.9 C++ nodes which access the native handles of the rmw impleme…
demo_nodes_py 0.20.9 Python nodes which were previously in the ros2/examples repo…
dummy_map_server 0.20.9 dummy map server node
dummy_robot_bringup 0.20.9 dummy robot bringup
dummy_sensors 0.20.9 dummy sensor nodes
image_tools 0.20.9 Tools to capture and play back images to and from DDS subscr…
intra_process_demo 0.20.9 Demonstrations of intra process communication.
lifecycle 0.20.9 Package containing demos for lifecycle implementation
lifecycle_py 0.20.9 Package containing demos for rclpy lifecycle implementation
logging_demo 0.20.9 Examples for using and configuring loggers.
pendulum_control 0.20.9 Demonstrates ROS 2’s realtime capabilities with a simulated …
pendulum_msgs 0.20.9 Custom messages for real-time pendulum control.
quality_of_service_demo_cpp 0.20.9 C++ Demo applications for Quality of Service features
quality_of_service_demo_py 0.20.9 Python Demo applications for Quality of Service features
topic_monitor 0.20.9 Package containing tools for monitoring ROS 2 topics.
topic_statistics_demo 0.20.9 C++ demo application for topic statistics feature.

3. 在 ROS 2 Humble 栈中的关系

官方 demo 节点


4. 推荐阅读顺序

  1. 阅读各子包 package.xml 2. 入口源码 3. 下游依赖方

5. 小结

demos 为含 21 个子包的源码树,是 ROS 2 Humble 发行版的一部分。

eigen3_cmake_module 源码详细分析

eigen3_cmake_module 源码详细分析

工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros2/eigen3_cmake_module
子包数量:1


1. 定位

Vendor / 第三方依赖打包包:通过 ExternalProject 或系统包 shim 为 ROS 2 构建提供固定版本的 eigen3_cmake_module,本身几乎无 ROS 业务逻辑。


2. 子包列表

包名 版本 说明
eigen3_cmake_module 0.1.1 Exports a custom CMake module to find Eigen3.

3. 核心组件

典型结构:CMakeLists.txt 下载/查找库 + package.xml 导出 find_package 依赖。消费方在 package.xml<depend> 本 vendor 包即可。


4. 在 ROS 2 Humble 栈中的关系

ros2总览.md 分层图。


5. 推荐阅读顺序

  1. 阅读各子包 package.xml 2. 入口源码 3. 下游依赖方

6. 小结

eigen3_cmake_module 为单包仓库,提供 eigen3_cmake_module 功能。

example_interfaces 源码详细分析

example_interfaces 源码详细分析

工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros2/example_interfaces
子包数量:1


1. 定位

example_interfaces 目录含 1 个 ROS 2 包,工作区路径见下。


2. 子包列表

包名 版本 说明
example_interfaces 0.9.3 Contains message and service definitions used by the example…

3. 在 ROS 2 Humble 栈中的关系

ros2总览.md 分层图。


4. 推荐阅读顺序

  1. 阅读各子包 package.xml 2. 入口源码 3. 下游依赖方

5. 小结

example_interfaces 为单包仓库,提供 example_interfaces 功能。