memory_msgs消息详解
这篇文档梳理当前仓库中所有 memory_msgs::* 消息类型,内容以源码中的消息模板和实际编解码实现为准。
适合用途:
- 查
memory_msgs每个消息有哪些字段 - 理解共享内存消息里的
shm_name、offset、shm_fst是什么 - 给 Python / C++ / ROS2 / 可视化 / 录包回放做接口对接
说明:
- 本文主要依据
spirems/json_msgs/memory_msgs/*.json - 同时结合这些实现文件补充说明:
spirems/image_io/adaptor.pyspirems/exts/csms_shm.cppspirems_cpp/include/sms_helper.hspirems_cpp/src/sms_helper.cpp
- 模板字段是"最小字段集合"
- 实际运行时某些消息还会动态附加字段,尤其是
shm_fst
1. 消息总览
当前源码中共有 3 个 memory_msgs 消息:
memory_msgs::RawImagememory_msgs::PointCloudmemory_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 类型非常接近。
例如:
8UC1:uint8单通道8UC3:uint8三通道16UC1:uint16单通道32FC1:float32单通道
从当前源码实现看,常见支持包括:
8UC18UC28UC38UC416UC116UC216UC316UC432FC132FC232FC332FC4
Python 侧 cvimg2sms_mem() 当前支持的源图像 dtype:
uint8uint16float32
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 shape 和 dtype
这两个字段是 Tensor 最核心的元信息。
例如:
json
"shape": [3, 224, 224],
"dtype": "float32"
表示:
- 这是一个
3x224x224的 Tensor - 每个元素是
float32
6.2 当前支持的 dtype
从 Python/C++ 当前实现看,支持:
int8int16int32int64uint8uint16uint32uint64float32float64
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 发布端大致在做什么
发布端通常会:
- 找到或创建名为
shm_name的共享内存 - 根据当前数据大小,把数据写到
offset对应的位置 - 在 JSON 消息里只发送:
- 类型
- 形状/尺寸
- 数据类型
shm_nameoffsetshm_fst
所以真正走网络的 JSON 很小,大数据不走 socket。
7.2 订阅端大致在做什么
订阅端通常会:
- 收到 JSON 元数据
- 根据
shm_name找到共享内存 - 如果没见过这个共享内存,或者
shm_fst == 1,就重新初始化映射 - 从
offset开始读出这帧数据 - 再按
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.hspirems_cpp/src/sms_helper.cpp
共享内存扩展实现:
spirems/exts/csms_shm.cpp
可参考示例:
test/share_mem_sender_test.pytest/share_mem_receiver_test.pyspirems/demo/tensor_mem_pub_demo.pyspirems/demo/tensor_mem_sub_demo.pyspirems_cpp/demo/share_mem_sender_test.cppspirems_cpp/demo/share_mem_receiver_test.cppspirems_cpp/demo/tensor_mem_sender_test.cppspirems_cpp/demo/tensor_mem_receiver_test.cpp
12. 一句话总结
memory_msgs::RawImage:共享内存原始图像memory_msgs::PointCloud:共享内存点云memory_msgs::Tensor:共享内存 Tensor- 这三类消息真正的数据都不在 JSON 里,而在共享内存里
shm_name + offset + shm_fst是理解这组消息的关键