SpireMS的memory_msgs消息详解

memory_msgs消息详解

这篇文档梳理当前仓库中所有 memory_msgs::* 消息类型,内容以源码中的消息模板和实际编解码实现为准。

适合用途:

  • memory_msgs 每个消息有哪些字段
  • 理解共享内存消息里的 shm_nameoffsetshm_fst 是什么
  • 给 Python / C++ / ROS2 / 可视化 / 录包回放做接口对接

说明:

  • 本文主要依据 spirems/json_msgs/memory_msgs/*.json
  • 同时结合这些实现文件补充说明:
    • spirems/image_io/adaptor.py
    • spirems/exts/csms_shm.cpp
    • spirems_cpp/include/sms_helper.h
    • spirems_cpp/src/sms_helper.cpp
  • 模板字段是"最小字段集合"
  • 实际运行时某些消息还会动态附加字段,尤其是 shm_fst

1. 消息总览

当前源码中共有 3 个 memory_msgs 消息:

  • memory_msgs::RawImage
  • memory_msgs::PointCloud
  • memory_msgs::Tensor

它们的共同特点是:

  • 真正的大块数据不直接放在 JSON 消息体里
  • 消息里只放元数据和共享内存定位信息
  • 实际数据存在操作系统共享内存中

所以和 sensor_msgs::* 相比:

  • memory_msgs::* 更适合同机大数据高速传输
  • 但不能直接跨机器裸用
  • 也更依赖共享内存生命周期管理

2. 先理解 4 个核心概念

2.1 shm_name

shm_name 是共享内存名字。

作用:

  • 发布端和订阅端通过这个名字找到同一块共享内存
  • 可以理解成"共享内存通道名"

例如:

json 复制代码
"shm_name": "tensor_mem_url"

2.2 offset

offset 表示当前消息数据在这块共享内存中的起始偏移字节位置。

作用:

  • 一块共享内存里可以循环写入多帧数据
  • offset 告诉接收方这一帧从哪里开始读

2.3 shm_fst

shm_fst 表示"这是不是这块共享内存第一次被使用"。

这个字段非常重要:

  • 模板里不一定都写出来
  • 但运行时通常会自动附加

作用:

  • 当接收方第一次看到某个 shm_name
  • 或者 shm_fst == 1
  • 就需要重新 mmap / 初始化共享内存读取端

可以简单理解为:

  • 0:这块共享内存之前已经建好了
  • 1:这块共享内存第一次用,订阅端需要重新初始化映射

2.4 这些消息通常只适合同机

memory_msgs::* 的核心机制是 POSIX 共享内存。

这意味着:

  • 发送端和接收端通常必须在同一台机器上
  • Python 当前实现里明确要求 Linux / macOS
  • C++ 侧也依赖本机共享内存映射

所以:

  • 同机大图像、大 Tensor、高频点云:优先用 memory_msgs::*
  • 跨机传输:优先考虑 sensor_msgs::* 或压缩消息

3. 通用字段说明

这 3 个消息都有一些共同字段:

字段 类型 说明
type str 消息类型名
timestamp float 时间戳,通常单位为秒
frame_id str 坐标系或数据来源标识
shm_name str 共享内存名字
offset int 当前数据在共享内存中的字节偏移

其中:

  • type/timestamp/frame_id 是业务元信息
  • shm_name/offset 是共享内存定位信息

运行时常见附加字段:

字段 类型 说明
shm_fst int 是否首次使用该共享内存,常见值为 0/1

4. memory_msgs::RawImage

模板源码:

json 复制代码
{
    "type": "memory_msgs::RawImage",
    "timestamp": 0.0,
    "frame_id": "camera",
    "width": 0,
    "height": 0,
    "encoding": "8UC3",
    "shm_name": "",
    "offset": 0
}

字段说明:

字段 类型 含义
type str 固定为 memory_msgs::RawImage
timestamp float 图像时间戳
frame_id str 图像坐标系或相机名,默认 "camera"
width int 图像宽
height int 图像高
encoding str 图像数据类型和通道数
shm_name str 图像共享内存名字
offset int 图像起始偏移

运行时常见附加字段:

字段 类型 说明
shm_fst int 首次共享内存标记

4.1 encoding 是什么意思

encoding 的风格和 OpenCV 类型非常接近。

例如:

  • 8UC1uint8 单通道
  • 8UC3uint8 三通道
  • 16UC1uint16 单通道
  • 32FC1float32 单通道

从当前源码实现看,常见支持包括:

  • 8UC1
  • 8UC2
  • 8UC3
  • 8UC4
  • 16UC1
  • 16UC2
  • 16UC3
  • 16UC4
  • 32FC1
  • 32FC2
  • 32FC3
  • 32FC4

Python 侧 cvimg2sms_mem() 当前支持的源图像 dtype:

  • uint8
  • uint16
  • float32

4.2 典型用途

  • 同机原始图像传输
  • 大分辨率图像高速发布
  • 避免 JPEG/PNG 编码开销
  • 深度图、灰度图、多通道图传输

4.3 Python 里怎么生产 / 消费

发送:

python 复制代码
from spirems import Publisher

pub = Publisher("/demo/raw_image", "memory_msgs::RawImage")
msg = pub.cvimg2sms_mem(img)
pub.publish(msg)

或者:

python 复制代码
from spirems import cvimg2sms_mem

msg = cvimg2sms_mem(img, "my_raw_image_shm")

接收:

python 复制代码
from spirems import sms2cvimg

cvimg = sms2cvimg(msg)

4.4 最小示例

json 复制代码
{
  "type": "memory_msgs::RawImage",
  "timestamp": 1710000000.0,
  "frame_id": "camera_front",
  "width": 1920,
  "height": 1080,
  "encoding": "8UC3",
  "shm_name": "camera_front_raw",
  "offset": 0,
  "shm_fst": 1
}

4.5 与 sensor_msgs::CompressedImage 的区别

  • CompressedImage:消息体里直接带压缩后数据,适合跨机
  • RawImage:消息体里只带共享内存索引,适合同机高速传输

简单选择建议:

  • 本机实时大图:memory_msgs::RawImage
  • 跨机图像:sensor_msgs::CompressedImage

5. memory_msgs::PointCloud

模板源码:

json 复制代码
{
    "type": "memory_msgs::PointCloud",
    "timestamp": 0.0,
    "frame_id": "lidar",
    "width": 0,
    "height": 0,
    "encoding": "32FC1",
    "fields": ["intensity", "x", "y", "z", "scan_idx", "ring"],
    "shm_name": "",
    "offset": 0
}

字段说明:

字段 类型 含义
type str 固定为 memory_msgs::PointCloud
timestamp float 点云时间戳
frame_id str 点云坐标系,默认 "lidar"
width int 点云矩阵宽
height int 点云矩阵高
encoding str 点云底层数据编码
fields list[str] 每列字段含义
shm_name str 点云共享内存名字
offset int 点云起始偏移

运行时常见附加字段:

字段 类型 说明
shm_fst int 首次共享内存标记

5.1 fields 表示什么

fields 用来说明点云每一列的物理含义。

模板默认值是:

json 复制代码
["intensity", "x", "y", "z", "scan_idx", "ring"]

这只是默认示例,不代表必须固定成这样。

例如一个点云矩阵如果每个点是:

text 复制代码
[x, y, z, intensity]

那就可以写成:

json 复制代码
"fields": ["x", "y", "z", "intensity"]

5.2 当前实现的一个重要限制

从当前 Python/C++ 实现看:

  • memory_msgs::PointCloud 主要按 float32
  • 编码通常是 32FC1

尤其 C++ 里目前明确限制:

  • 仅支持 32FC1

所以如果你要发点云,最稳的格式是:

  • np.float32
  • 二维矩阵

5.3 典型用途

  • 激光雷达点云同机传输
  • 点云鸟瞰图生成前的数据传递
  • 避免大点云 base64 编码开销

5.4 Python 里怎么消费

python 复制代码
from spirems import sms2pcl

pcl = sms2pcl(msg)

返回值通常是:

  • np.ndarray
  • 形状近似为 (N, C) 或根据 width/height 解释出的矩阵

5.5 最小示例

json 复制代码
{
  "type": "memory_msgs::PointCloud",
  "timestamp": 1710000000.0,
  "frame_id": "lidar_front",
  "width": 120000,
  "height": 1,
  "encoding": "32FC1",
  "fields": ["x", "y", "z", "intensity"],
  "shm_name": "lidar_front_points",
  "offset": 0,
  "shm_fst": 1
}

5.6 与 sensor_msgs::PointCloud 的区别

  • sensor_msgs::PointCloud:消息体内直接携带点云数据,适合跨机
  • memory_msgs::PointCloud:共享内存存点云,适合同机高速传输

6. memory_msgs::Tensor

模板源码:

json 复制代码
{
    "type": "memory_msgs::Tensor",
    "timestamp": 0.0,
    "frame_id": "",
    "shape": [],
    "dtype": "uint8",
    "fields": ["x", "y", "z", "intensity"],
    "shm_name": "",
    "offset": 0,
    "shm_fst": 1
}

字段说明:

字段 类型 含义
type str 固定为 memory_msgs::Tensor
timestamp float Tensor 时间戳
frame_id str 数据来源标识
shape list[int] Tensor 形状
dtype str Tensor 元素类型
fields list[str] 可选字段含义说明,常用于结构化张量
shm_name str Tensor 共享内存名字
offset int Tensor 起始偏移
shm_fst int 首次共享内存标记

6.1 shapedtype

这两个字段是 Tensor 最核心的元信息。

例如:

json 复制代码
"shape": [3, 224, 224],
"dtype": "float32"

表示:

  • 这是一个 3x224x224 的 Tensor
  • 每个元素是 float32

6.2 当前支持的 dtype

从 Python/C++ 当前实现看,支持:

  • int8
  • int16
  • int32
  • int64
  • uint8
  • uint16
  • uint32
  • uint64
  • float32
  • float64

6.3 fields 是干什么的

Tensor 里的 fields 不是强制必须使用的主字段,更像是一个可选说明字段。

典型理解方式:

  • 如果这个 Tensor 实际表示结构化点、特征向量、时序状态
  • 可以用 fields 说明每一维或每一列的语义

例如:

json 复制代码
"fields": ["x", "y", "z", "intensity"]

但在普通图像 Tensor、模型特征 Tensor 场景里:

  • 你也可以基本忽略它

6.4 典型用途

  • 模型输入输出 Tensor 传输
  • 高分辨率图像作为 Tensor 传输
  • 同机大数组高速传输
  • 服务请求/响应中的共享内存 Tensor

6.5 Python 里怎么生产 / 消费

发送:

python 复制代码
from spirems import tensor2sms

msg = tensor2sms(tensor, "tensor_mem_url")

接收:

python 复制代码
from spirems import sms2tensor

tensor = sms2tensor(msg)

6.6 最小示例

json 复制代码
{
  "type": "memory_msgs::Tensor",
  "timestamp": 1710000000.0,
  "frame_id": "feature",
  "shape": [2, 3, 224, 224],
  "dtype": "float32",
  "fields": [],
  "shm_name": "backbone_feat_01",
  "offset": 0,
  "shm_fst": 1
}

7. 共享内存消息的实际传输语义

这部分很关键,因为只看模板不容易理解。

7.1 发布端大致在做什么

发布端通常会:

  1. 找到或创建名为 shm_name 的共享内存
  2. 根据当前数据大小,把数据写到 offset 对应的位置
  3. 在 JSON 消息里只发送:
    • 类型
    • 形状/尺寸
    • 数据类型
    • shm_name
    • offset
    • shm_fst

所以真正走网络的 JSON 很小,大数据不走 socket。

7.2 订阅端大致在做什么

订阅端通常会:

  1. 收到 JSON 元数据
  2. 根据 shm_name 找到共享内存
  3. 如果没见过这个共享内存,或者 shm_fst == 1,就重新初始化映射
  4. offset 开始读出这帧数据
  5. 再按 encoding / dtype / shape 还原成数组

7.3 为什么需要 offset

因为共享内存不是每发一帧就新建一次。

当前实现更像:

  • 在一块固定大小的大共享内存里循环写入
  • 每条消息只指出"这次写到哪里了"

好处:

  • 避免频繁创建销毁共享内存
  • 吞吐更高

8. Python / C++ 中与这些消息对应的主要函数

8.1 Python

图像:

  • cvimg2sms_mem()
  • sms2cvimg()
  • Publisher.cvimg2sms_mem()

点云:

  • sms2pcl()

Tensor:

  • tensor2sms()
  • sms2tensor()

8.2 C++

图像:

  • sms::cvimg2sms_mem()
  • sms::sms2cvimg()
  • sms::Publisher::cvimg2sms_mem()

点云:

  • sms::pcl2sms_mem()
  • sms::sms2pcl()

Tensor:

  • sms::tensor2sms<T>()
  • sms::sms2tensor<T>()

9. 最常见问题

9.1 为什么消息类型对了,但读不出来

常见原因:

  • 发送端和接收端不在同一台机器
  • 操作系统不支持当前共享内存实现
  • shm_name 找不到
  • dtype / encoding 不匹配
  • 共享内存已被清理

9.2 为什么 memory_msgs::RawImage 比压缩图快

因为它通常避免了:

  • JPEG/PNG 编码
  • base64 编码
  • 大块 socket 传输

代价是:

  • 更依赖本机环境
  • 不适合直接跨机

9.3 shm_fst 为什么有时模板里没写,但运行时能看到

因为这是实现层动态补上的运行时字段。

文档理解方式:

  • 模板字段:定义消息最小结构
  • 运行时字段:为了共享内存收发而自动附加

9.4 我应该什么时候用 memory_msgs::*

推荐场景:

  • 本机高频图像
  • 本机点云
  • 本机大 Tensor
  • 服务请求/响应中需要传大数组

不太推荐的场景:

  • 跨机器直接通信
  • 需要长期脱离共享内存环境保存原始消息

10. 最常见选型建议

图像:

  • 同机原始大图:memory_msgs::RawImage
  • 跨机压缩图:sensor_msgs::CompressedImage

点云:

  • 同机大点云:memory_msgs::PointCloud
  • 跨机点云:sensor_msgs::PointCloud

Tensor:

  • 同机大数组:memory_msgs::Tensor

11. 源码位置

消息模板目录:

  • spirems/json_msgs/memory_msgs

Python 主要实现:

  • spirems/image_io/adaptor.py

C++ 主要实现:

  • spirems_cpp/include/sms_helper.h
  • spirems_cpp/src/sms_helper.cpp

共享内存扩展实现:

  • spirems/exts/csms_shm.cpp

可参考示例:

  • test/share_mem_sender_test.py
  • test/share_mem_receiver_test.py
  • spirems/demo/tensor_mem_pub_demo.py
  • spirems/demo/tensor_mem_sub_demo.py
  • spirems_cpp/demo/share_mem_sender_test.cpp
  • spirems_cpp/demo/share_mem_receiver_test.cpp
  • spirems_cpp/demo/tensor_mem_sender_test.cpp
  • spirems_cpp/demo/tensor_mem_receiver_test.cpp

12. 一句话总结

  • memory_msgs::RawImage:共享内存原始图像
  • memory_msgs::PointCloud:共享内存点云
  • memory_msgs::Tensor:共享内存 Tensor
  • 这三类消息真正的数据都不在 JSON 里,而在共享内存里
  • shm_name + offset + shm_fst 是理解这组消息的关键
相关推荐
阿木实验室13 小时前
WSL系统下免费使用SpireCV-Pro教程
wsl·spirecv