librealsense realdds 网络流式传输(Streaming)指南:基于 DDS 订阅-发布模型的实时数据流架构与 open-streams 控制

librealsense realdds 网络流式传输(Streaming)指南:基于 DDS 订阅-发布模型的实时数据流架构与 open-streams 控制 librealsense realdds 网络流式传输Streaming指南基于 DDS 订阅-发布模型的实时数据流架构与 open-streams 控制【免费下载链接】librealsenseRealSense SDK项目地址: https://gitcode.com/GitHub_Trending/li/librealsense导读本文基于 librealsense 仓库中 realdds 组件的协议文档 streaming.md系统讲解 RealSense 相机在 DDSData Distribution Service网络中如何实现流式数据传输从一个流就是一个 Topic的基本模型到订阅即开流的生命周期管理、多客户端共享与组播带宽优化、ROS2 兼容的 Image/Imu 数据格式、帧元数据同步以及通过open-streams控制命令配置和锁定流 Profile 的完整语义。读完本文你将掌握 realdds 网络流式传输的端到端工作原理并能结合仓库源码理解open-streams、reset、commit等关键字段的真实行为。一、流Stream的本质一个 Topic一条单向数据通道在 realdds 的协议模型中一个流stream就是一个独立的 DDS Topic。数据只沿一个方向流动从服务器Server即相机或承载相机的软件作为发布者publisher流向客户端Client即订阅者 subscriber。Server (publisher) --数据-- Topic --数据-- Client (subscriber)这里的服务器并不一定是一台物理相机按照 use-cases.md 的定义它既可以是内置网络能力的物理设备也可以是一段专门的软件例如tools/dds下的 dds-server——通过它把一台 USB 连接的 RealSense 设备如 D455代理到网络上。客户端则是最终消费数据的用户可以启动/停止数据流并控制曝光时间、亮度等相机选项。流的命名与 Topic 根topic-root紧密相关。在 device.md 中可以看到完整的 Topic 结构topic-root/notification—— 服务器通知、应答等topic-root/control—— 客户端到服务器的请求topic-root/metadata—— 可选的流信息rt/topic-root_stream-name—— ROS2 兼容的 Image / Imu 流数据其中rt/前缀标识这是实时real-time的 ROS2 兼容流数据通道topic-root 由 discovery.md 中的device-info消息确定例如realsense/D405_123622270732因此实际流 Topic 形如rt/realsense/D405_123622270732_Color。这条 Topic 层次结构在 third-party/realdds/include/realdds/topics/readme.md 中有完整的树状说明。流的 QoS 设定流数据 Topic 使用如下 Quality of ServiceQoS配置QoS 项取值含义ReliabilityBEST_EFFORT尽力而为允许丢包适合高带宽实时视频DurabilityVOLATILE不保留历史数据只向当下在线的订阅者投递与control、notification等主题默认使用RELIABLE可靠传输形成鲜明对比控制与通知不容丢失而图像帧数据则可以容忍偶发丢失以换取实时性。在 device.md 的 Settings 部分客户端还可以通过 JSON 对每个 Topic 的 QoS 进行细粒度覆盖例如把 metadata 主题改为reliable。二、流的启停订阅即是开始无需任何命令realdds 流式传输最反直觉、也最核心的设计是客户端要开始接收数据不需要发送任何命令。订阅subscribe这个动作本身就足以触发流的启动服务器必须自行跟踪某个流 Topic 上的订阅者数量并据此从传感器上启动或停止对应的数据采集。也就是说开流是订阅驱动的第一个订阅者出现传感器开始出流最后一个订阅者消失传感器停止出流前提是存在物理传感器。这一点在 use-cases.md 的 Start Streaming / Stop Streaming 用例中得到印证协议层面并不存在显式的start-streaming控制消息流的启停完全由 DDS 的读写匹配reader/writer matching机制隐式完成。多客户端流是只读的天然可共享由于流是只读操作它可以被多个客户端同时共享一旦某条流正在传输任何订阅者都能看到相同的数据而不需要服务器为每个客户端复制一份。这为一机多客户端同时取帧的场景提供了天然支持这也是 realdds 相对于 USB 直连的一大优势见 use-cases.md 的愿景描述。不过多客户端共享的是正在流式传输的数据本身如果某个客户端想要改变正在流式传输的内容例如换一个分辨率那就必须通过open-streams控制命令见下文第五节。组播Multicast节省网络带宽的关键手段默认情况下当多个客户端访问同一台服务器时服务器必须向每个客户端单独发送一份数据报单播这在多客户端场景下会迅速耗尽网络带宽。组播Multicast的解决方案是让多个客户端共享一个组播 IP 地址服务器只需向这一个地址发送一次数据报即可被所有组播使能的客户端接收从而大幅节省网络带宽和处理时间。组播地址作为 device discovery 的一部分对外广播即出现在device-info消息的可选字段multicast-ip中客户端必须知道这个地址才能正确地监听该组播流在 device.md 的设备选项中还列出了multicast-ip空值表示禁用组播作为可配置项。多流订阅多个 Topic 即可要同时启动多条流例如 Depth 与 Color客户端只需同时订阅多个流 Topic即可无需其他额外操作。每条流各自独立地在rt/topic-root_stream-name上传输。三、不兼容的 Profile默认值必须彼此协调当流式传输被尝试启动即检测到订阅者时如果找不到与当前正在流式传输的 Profile 兼容的 Profile就会产生错误。原文档给出了一个非常具体的例子默认的DepthProfile 是 1280x800流式传输在Depth上启动随后有人尝试在Infrared上启动流式传输但Infrared没有 1280x800 的 Profile只有 1280x720于是报错。因此各流的默认 Profile 必须彼此一致并且要与预期的客户端尤其是 ROS2保持一致。这是因为同一个物理传感器例如 Stereo Module往往同时驱动 Depth 与 Infrared 两条流传感器无法在同一时刻以两套不同的分辨率和帧率工作。这个约束在 stream-configurations.md 中也有体现当红外左右双流存在时两条流必须提供完全相同的格式与编码即使传感器本身输出的是隔行交织格式也由服务器负责拆分成两条独立流。四、流格式ROS2 兼容的 Image 与 Imu流的类型决定了它的数据格式视频流使用 ROS2 的sensor_msgs/Image格式运动IMU流使用 ROS2 的sensor_msgs/Imu格式。这也是 realdds 追求ROS2 可直接读取的兼容性设计的一部分参见 topics/readme.md。视频流ROS2 Image 格式以下为协议的简化 IDL 参考struct Time { long sec; unsigned long nanosec; }; struct Header { Time stamp; string frame_id; }; struct Image { Header header; unsigned long height; unsigned long width; string encoding; octet is_bigendian; // false unsigned long step; sequence octet data; };关键约定encoding与当前设置的 Profile 格式format一致并且在帧与帧之间不应改变width、height、step、frame_id同样不应该在帧之间变化is_bigendian恒为false小端。从源码看realdds 在 include/realdds/topics/image-msg.h 中封装了该消息类型其 IDL 来自 ROS2 的common_interfaces仓库并在构建时预先用fastddsgen -typeros2生成详见 topics/readme.md 的 ROS2 一节。运动流ROS2 Imu 格式简化 IDL 参考struct Vector3 { double x; double y; double z; }; struct Quaternion { double x; double y; double z; double w; }; struct Imu { Header header; Quaternion orientation; double[9] orientation_covariance; Vector3 angular_velocity; double[9] angular_velocity_covariance; Vector3 linear_acceleration; double[9] linear_acceleration_covariance; };运动流的附加约定在 stream-configurations.md 中说明单位遵循 ROS2 标准加速度为m/s^2角速度为rad/sec时间戳始终以 Gyro 采样时刻为准即使 Accel 与 Gyro 是独立生成的Accel 数值要么反映最近一次收到的值要么更推荐被插值到与 Gyro 对齐运动校正服务器可输出原始传感器值也可输出经过标定含零偏与尺度校正后的值。默认情况下标定矩阵为单位阵即保留原始值推荐服务器直接输出校正后的值这样客户端如 librealsense无需再做处理。仓库中的 tools/rs-imu-calibration 工具可用于加速度计/陀螺仪的标定librealsense 侧还提供 Enable Motion Correction 选项来控制是否应用校正。视频流编码的补充红外流的mono8对红外流stream-configurations.md 明确要求编码设置为mono8对应 librealsense 的Y8且不得使用隔行interlaced格式。五、元数据与帧号ROS2 格式之外的补充通道ROS2 的 Image/Imu 格式中没有帧号frame number字段因此除非借助额外数据否则无法在协议层面传递帧号。这正是 metadata 主题存在的意义元数据是可选的没有元数据时帧将不会被编号或由客户端自行编号——这正是 librealsense 中的实际行为as happens in librealsense服务器生成的任何其他帧级信息也只能通过元数据通道传达。元数据主题位于device-topic-root/metadata使用BEST_EFFORT/VOLATILEQoS同样容忍丢失其消息形如{ stream-name:Color, header:{frame-number:1234, timestamp:123456789, timestamp-domain:0}, metadata:{Exposure:123, Gain:456} }其中header.timestamp是关键字段它必须与图像消息中的时间戳一致用于把元数据与图像同步起来协议推荐先发图像、后发元数据因为元数据更小、先到也不碍事。frame-number是可选的顺序帧号。若元数据丢失或时间戳不匹配图像只是没有元数据而已视频流照常工作。对应的字段名在 dds-topic-names.cpp 的namespace metadata中有完整的源码级定义stream-name、header、frame-number、timestamp、timestamp-domain、depth-units等。六、控制流的 Profileopen-streams命令详解虽然订阅即开流但客户端仍需要一种方式来配置将要流式传输的内容分辨率、格式、帧率。这就是open-streams控制命令的职责。6.1 基本语义请求而非命令open-streams尝试配置用于流式传输的 Profile但请注意它不启动流式传输它是一个请求可能失败尤其需要注意的是如果传感器已经在流式传输此命令很可能会失败具体行为由服务器决定。一个完整示例{ id: open-streams, stream-profiles: { Color: [30,rgb8,1280,720] } }字段语义如下字段类型说明idstring固定为open-streamsstream-profiles映射从stream-name到profile的映射profile是[frequency, format, width, height]四元数组与 initialization 中stream-header的profiles数组元素格式一致resetboolean默认false若为true则先遗忘此前所有open-streams把服务器所有传感器重置回默认 Profile再处理本次stream-profiles该映射可以为空commitboolean默认true即上例为true时open-streams之后流的配置状态被锁定直到收到下一次reset为false时后续open-streams请求可以累积前提是reset也为false。流式传输真正开始时会隐式触发一次 commit6.2 错误场景以下情况应当返回错误stream-name或profile找不到多个流共享同一个sensor-name见 initialization.md 的stream-header说明例如Stereo Module同时产出Depth和Infrared但它们配置的 Profile 互相不兼容流的 Profile 无法更改例如传感器已打开。这通常意味着一旦某条流正在流式传输它的 Profile 就不能被任何人修改——唯一的方法是先停止所有订阅。6.3 源码佐证open-streams的字段定义在 dds-topic-names.cpp 的namespace control中可以找到open_streams消息键的精确定义与协议文档一一对应namespace open_streams { std::string const id( open-streams, 12 ); namespace key { std::string const stream_profiles( stream-profiles, 15 ); std::string const reset( reset, 5 ); std::string const commit( commit, 6 ); } }6.4 隐式 Profile 与显式 ProfileStereo Module 的典型案例很多 Profile 彼此依赖。最典型的例子是Stereo Module传感器它同时产生Depth与Infrared两条流当你设置Depth的 Profile 时如果open-streams没有显式指定Infrared的 Profile那么Infrared必须隐式地跟着改变否则流式传输无法工作——因为传感器无法同时工作在两个不同的 FPS 或分辨率下当流式传输首次订阅开始时服务器必须把传感器相关的 Profile 对齐随后它们被提交committed不可再更改。原文档给出了一个极具代表性的推演场景假设Depth与Infrared的默认值都是 1280x720收到一个只针对Depth的open-streams改为 640x480Infrared应被隐式设置为相同分辨率如果可能的话随后Depth被订阅开始流式传输之后取消订阅停止流式传输第二个客户端此时订阅其中任意一条流——应该以什么分辨率出流答案是由于之前做过一次显式的分辨率控制同样的分辨率应当继续锁定直到收到reset为止。反之如果从未发送过open-streams控制那么所有 Profile 都是隐式的意味着流停止后一切恢复默认。这一显式配置即锁定的语义加上前文commit默认true的锁定行为共同保证了多客户端场景下流配置的可预期性显式配置一次长期有效想要回到默认只能靠reset。七、端到端串联从设备发现到数据到达的完整链路把 realdds 的各协议文档拼起来一条流从网络上有台相机到客户端拿到图像帧的完整链路如下发现服务器在realsense/device-info主题RELIABLE/VOLATILE见 discovery.md上广播device-info其中包含name、serial、product-line与关键的topic-root组播地址multicast-ip也在此声明初始化客户端根据topic-root订阅notification主题RELIABLE服务器检测到订阅者后按顺序广播device-header→可选device-options→ 每个流的stream-header→stream-options见 initialization.md。stream-header中的profiles数组就是open-streams里 profile 四元组的合法取值来源default-profile-index则指明默认 Profile控制客户端通过control主题发送open-streams等请求服务器通过notification主题回发应答应答携带sampleGUID序列号以关联请求见 control.md流式传输客户端订阅rt/topic-root_stream-name服务器据此启动传感器并持续发布 ROS2 兼容的 Image/Imu 消息需要帧号等信息时通过topic-root/metadata主题BEST_EFFORT补充发送。对应的服务器/客户端交互在 use-cases.md 中有形式化的用例描述其中明确发现阶段初始化消息的超时上限为30 秒停止流时有一个 librealsense 特有的限制——由于 librealsense 以传感器为单位启停流当 Infrared 1 与 Infrared 2 同时流式传输、仅请求停止其中一条时同属 Stereo Module 的两条流会一起停止realdds 协议本身没有传感器概念这是 librealsense API 的局限。在 librealsense 侧这一整套协议被封装为对上层透明的 DDS 设备只要以BUILD_WITH_DDS编译context就能自动发现 DDS 设备query_devices()返回的 DDS 设备与 USB 设备一样使用标准 librealsense API参见 discovery.md 的 librealsense 一节。八、总结流式传输的关键设计要点订阅即开流客户端无需发送开始命令服务器根据订阅者数量自动启停传感器只读共享流数据对所有订阅者可见多客户端天然共存带宽紧张时启用组播让服务器一次发送、多端接收QoS 分层流数据使用BEST_EFFORTVOLATILE以保实时控制/通知/发现使用RELIABLE以保可靠元数据默认BEST_EFFORT但可覆盖ROS2 兼容视频用Image、运动用Imu格式红外编码mono8运动单位为m/s^2与rad/sec帧号靠元数据ROS2 格式无帧号需要时通过可选的metadata主题按时间戳同步Profile 显式即锁定open-streams只配置不启动、可能失败reset: true回退默认commit默认true锁定配置且流式传输开始即隐式提交隐式 Profile 会随依赖关系自动对齐典型如 Stereo Module 的 Depth/Infrared因此各流默认 Profile 必须彼此一致并要顾及 ROS2 客户端。想深入探索协议的其他环节可以继续阅读本仓库中 realdds 的完整文档链device.md → notifications.md → initialization.md → control.md → metadata.md → stream-configurations.md以及 Topic 结构总览 topics/readme.md。【免费下载链接】librealsenseRealSense SDK项目地址: https://gitcode.com/GitHub_Trending/li/librealsense创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考