image_transport 源码详细分析
工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros-perception/image_common/image_transport
版本:3.1.12,许可证 BSD。
image_transport 是 ROS 图像话题的 统一发布/订阅抽象层。应用代码只面对 sensor_msgs/Image 的 base topic,底层通过 pluginlib 插件 按需启用 raw、compressed、theora 等传输方式,从而在带宽受限场景下透明切换压缩格式,而不改业务逻辑。
1. 仓库结构
1 | image_transport/ |
构建产物:
| 目标 | 类型 | 说明 |
|---|---|---|
libimage_transport.so |
库 | 核心框架 |
libimage_transport_plugins.so |
库 | raw 插件实现 |
list_transports |
可执行 | 列出已声明/可加载 transport |
republish |
可执行 | 订阅一种 transport,发布另一种 |
2. 架构总览
设计核心:
- Publisher 端:一次
advertise()加载 多个 PublisherPlugin,每个 transport 各建一个 ROS publisher。 - Subscriber 端:一次
subscribe()只选 一个 SubscriberPlugin(由TransportHints或参数决定)。 - 按需发布:
publish()时只对 有订阅者 的 transport 调用插件,避免无谓压缩/编码开销。
3. 话题命名约定
3.1 图像 base topic
用户指定 base topic,例如 /camera/image。
3.2 各 transport 的实际话题
| transport | 实际话题 | 实现 |
|---|---|---|
| raw | /camera/image(即 base topic) |
RawPublisher/Subscriber 覆写 getTopicToAdvertise/Subscribe |
| compressed 等 | /camera/image/compressed |
SimplePublisherPlugin 默认:base + "/" + transport_name |
1 | virtual std::string getTopicToAdvertise(const std::string & base_topic) const |
3.3 camera_info 话题推导
1 | std::string getCameraInfoTopic(const std::string & base_topic) |
示例:
| base image topic | camera_info topic |
|---|---|
/camera/image |
/camera/camera_info |
/robot/head/rgb |
/robot/head/camera_info |
注意:camera_info 始终走 普通 rclcpp Publisher/Subscriber(raw CameraInfo),不经 image_transport 插件。
4. 插件系统
4.1 插件注册
default_plugins.xml 声明 raw 插件:
1 | <library path="image_transport_plugins"> |
manifest.cpp 导出:
1 | PLUGINLIB_EXPORT_CLASS(image_transport::RawPublisher, image_transport::PublisherPlugin) |
4.2 插件 lookup 命名
1 | static std::string getLookupName(const std::string & transport_name) |
Subscriber 对应 _sub 后缀。对外暴露的 transport 名(如 raw、compressed)由 getTransportName() 返回。
4.3 全局 ClassLoader 单例
1 | struct Impl |
进程内共享 plugin loader;create_publisher / create_subscription 自由函数直接使用 kImpl。
4.4 外部插件包(本工作区未包含)
典型独立包(通过 package.xml export <image_transport plugin=...> 注册):
| 包 | transport | 消息类型 |
|---|---|---|
compressed_image_transport |
compressed |
sensor_msgs/CompressedImage |
compressed_depth_image_transport |
compressedDepth |
深度压缩 |
theora_image_transport |
theora |
theora_image_transport/Packet |
可用 ros2 run image_transport list_transports 查看当前环境已声明/可加载的 transport。
5. 核心 API
5.1 两种使用风格
风格 A:自由函数(ROS 2 推荐)
1 | auto pub = image_transport::create_publisher(node.get(), "camera/image"); |
风格 B:ImageTransport 类(ROS 1 兼容风格)
1 | image_transport::ImageTransport it(node); |
ImageTransport 内部仍调用 create_publisher / create_subscription。
5.2 Publisher:多 transport 并行 advertise
1 | Publisher::Publisher(...) |
Publisher 白名单参数:
- 参数名:
<base_topic 相对路径,/ 换 .>.enable_pub_plugins - 例:topic
/camera/image→ 参数camera.image.enable_pub_plugins - 默认:所有已声明的 pub 插件(通常含 raw + compressed 等)
按需 publish:
1 | for (const auto & pub : impl_->publishers_) { |
5.3 Subscriber:单 transport 加载
1 | impl_->lookup_name_ = SubscriberPlugin::getLookupName(transport); |
若用户误订阅 transport 专用话题(如 /camera/image/compressed),会 WARN 提示应订阅 base topic 并设置 image_transport 参数。
5.4 TransportHints:选择订阅 transport
1 | TransportHints(const rclcpp::Node * node, |
常用 launch 参数:
1 | # 订阅 compressed 而非 raw |
命令行:_image_transport:=compressed
6. Camera 封装
6.1 CameraPublisher
组合 image_transport::Publisher + 原生 CameraInfo publisher:
1 | impl_->image_pub_ = image_transport::create_publisher(node, image_topic, custom_qos); |
publish(image, info) 分别发布两路消息(不做时间戳强制同步,调用方应保证 stamp 一致)。
6.2 CameraSubscriber
用 message_filters::TimeSynchronizer 同步 image + camera_info:
1 | impl_->image_sub_.subscribe(node, image_topic, transport, custom_qos); |
- image 经
SubscriberFilter(支持 compressed 等 transport) - camera_info 直接
message_filters::Subscriber<CameraInfo>
7. 插件开发模板
7.1 SimplePublisherPlugin<M>
子类只需实现:
getTransportName()— 返回"compressed"等publish(const Image&, const PublishFn&)— 编码后调用publish_fn(transport_msg)
基类负责创建 rclcpp::Publisher<M> 并管理生命周期。
7.2 SimpleSubscriberPlugin<M>
子类只需实现:
getTransportName()internalCallback(const M&, const Callback& user_cb)— 解码后调用user_cb(image)
7.3 Raw 插件(最简单参考)
Publisher 直接透传,话题即 base topic:
1 | void publish(const sensor_msgs::msg::Image & message, const PublishFn & publish_fn) const |
Subscriber 直接 user_cb(message)。
8. SubscriberFilter
将 Subscriber 包装为 message_filters::SimpleFilter<Image>,供 TimeSynchronizer、Chain 等使用:
1 | sub_ = image_transport::create_subscription( |
下游典型用法:RViz ImageTransportDisplay、DepthCloudDisplay 多话题同步。
9. 工具程序
9.1 list_transports
遍历 pub/sub 插件,打印 transport 名、所属包、加载状态(SUCCESS / LIB_LOAD_FAILURE / CREATE_FAILURE)。
9.2 republish
传输格式转换 relay 节点:
1 | # 从 compressed 订阅,以所有可用 transport 重新发布 |
10. 依赖关系
1 | image_transport |
11. 测试
CMake 启用 6 个 gtest(ROS 2 已迁移):
| 测试 | 覆盖点 |
|---|---|
test_camera_common |
getCameraInfoTopic |
test_publisher |
多插件 advertise、参数白名单 |
test_subscriber |
transport 加载、错误 topic WARN |
test_message_passing |
pub/sub 端到端消息传递 |
test_remapping |
topic remap |
test_single_subscriber_publisher |
SingleSubscriberPublisher |
12. ROS 2 迁移遗留
| 功能 | 状态 |
|---|---|
latch 参数 |
未实现(TODO ros2#464,参数被忽略) |
SubscriberStatusCallback / connect_cb |
未实现(CameraPublisher/ImageTransport 注释 TODO) |
tracked_object 生命周期绑定 |
忽略((void) tracked_object) |
.h 遗留头 |
仍存在,新代码用 .hpp |
list_transports 提示 |
仍写 catkin_make(ROS 1 文案) |
13. 典型数据流
场景:驱动 raw 发布 + RViz compressed 订阅
Publisher 同时 advertise /camera/image 和 /camera/image/compressed;只有 RViz 订阅 compressed 时,驱动侧才执行 JPEG 编码。
14. 与 image_common 栈关系
- 上游:相机驱动应使用
CameraPublisher+camera_info_manager - 下游:
image_pipeline、rviz2通过image_transport订阅 - 平行:
camera_info_manager管理标定;image_transport管理图像传输格式
15. 设计特点与局限
| 特点 | 说明 |
|---|---|
| 插件化扩展 | 新 transport 只需独立包 + XML 注册 |
| Publisher 多播、Subscriber 单选 | 发布端兼容所有订阅偏好,订阅端只选一种 |
| 按需编码 | 无订阅者不压缩,节省 CPU |
| 话题 remap 显式处理 | expand_topic_or_service_name 保证 compressed 子话题正确 remap |
| Camera 同步诊断 | 定时 WARN image/info 不同步 |
| 局限 | 说明 |
|---|---|
| camera_info 不支持压缩 transport | 仅 image 走插件 |
| Publisher 默认加载全部 transport | 插件多时有构造开销(可通过 enable_pub_plugins 限制) |
| 全局 static loader | 测试/多进程场景需注意 |
| Subscriber 五参 subscribeImpl | 部分旧插件未覆写带 SubscriptionOptions 版本会 ERROR 回退 |
16. 推荐阅读顺序
image_transport.hpp— 公共 API 全貌publisher.cpp+subscriber.cpp— 插件加载与 publish 逻辑raw_publisher.hpp/raw_subscriber.hpp— 最小插件示例simple_*_plugin.hpp— 编写 compressed 等插件的模板camera_subscriber.cpp— message_filters 同步模式republish.cpp— 理解 transport 转换- 外部
compressed_image_transport— 真实压缩插件实现
17. 小结
image_transport 是 ROS 图像通信的 传输抽象框架:Publisher 通过 pluginlib 同时 advertise 多种 transport 子话题,Subscriber 按参数选择一种并解码为 sensor_msgs/Image;内置 raw 插件,压缩/视频类 transport 由独立包扩展。CameraPublisher/Subscriber 在其上封装 image + camera_info 双话题约定,是相机驱动、image_pipeline 和 RViz 的共同基础设施。
如需,我可以把本文写入 ros2doc/ros-perception/image_common/image_transport源码详细分析.md,或继续分析 compressed_image_transport 的 JPEG 编解码实现。
正在加载留言…