如何发布接收消息
这篇文档只回答一件事:如何用 SpireMS 在 Python 和 C++ 里发布、订阅消息。
目标:
- 人能快速照着跑通
- AI 能直接提取代码和步骤
- 示例尽量短
- 示例不依赖你本地特定的视频或图片文件
1. 先决条件
1.1 启动 Core
先启动Core,只需要启动一次:
smscore如果你是源码开发环境,也可以直接:
python-mspirems.core1.2 Python 环境
推荐在项目根目录执行:
python-mpipinstall-e.如果你只是使用已发布版本:
python-mpipinstallspirems1.3 C++ 环境
先安装 C++ SDK:
cdspirems_cppmkdir-pbuildcdbuild cmake..sudomakeinstall如果系统还没有 OpenCV 和 CMake:
sudoaptupdatesudoaptinstall-ycmake libopencv-dev2. Python:最常用的发布与订阅
Python 常用导入:
fromspiremsimportPublisher,Subscriber,def_msgfromspiremsimportcvimg2sms,sms2cvimg,tensor2sms,sms2tensor2.1 字符串消息
消息类型:std_msgs::String
发布:
importtimefromspiremsimportPublisher,def_msg pub=Publisher("/demo/string","std_msgs::String")try:whileTrue:msg=def_msg("std_msgs::String")msg["data"]="hello spirems"pub.publish(msg)time.sleep(1.0)exceptKeyboardInterrupt:pub.kill()订阅:
fromspiremsimportSubscriberdefcallback(msg):print("recv:",msg["data"])sub=Subscriber("/demo/string","std_msgs::String",callback)sub.join()2.2 压缩图像消息
消息类型:sensor_msgs::CompressedImage
适合:
- 普通图像传输
- 跨机器传输
- 对带宽比较敏感的场景
发布:
importtimeimportcv2importnumpyasnpfromspiremsimportPublisher,cvimg2sms pub=Publisher("/demo/image","sensor_msgs::CompressedImage")try:whileTrue:img=np.zeros((480,640,3),dtype=np.uint8)cv2.putText(img,"SpireMS",(180,240),cv2.FONT_HERSHEY_SIMPLEX,2.0,(0,255,0),3)msg=cvimg2sms(img,frame_id="camera")pub.publish(msg)time.sleep(0.05)exceptKeyboardInterrupt:pub.kill()订阅:
importcv2fromspiremsimportSubscriber,sms2cvimgdefcallback(msg):img=sms2cvimg(msg)cv2.imshow("compressed image",img)cv2.waitKey(1)sub=Subscriber("/demo/image","sensor_msgs::CompressedImage",callback)sub.join()2.3 共享内存图像消息
消息类型:memory_msgs::RawImage
适合:
- 本机大图像低延迟传输
- 高频图像流
注意:
- 共享内存消息主要用于 Linux / macOS 本机进程间传输
- 发送端和接收端需要在同一台机器上
发布:
importtimeimportcv2importnumpyasnpfromspiremsimportPublisher pub=Publisher("/demo/raw_image","memory_msgs::RawImage")try:whileTrue:img=np.zeros((720,1280,3),dtype=np.uint8)cv2.circle(img,(640,360),120,(255,0,0),-1)cv2.putText(img,"SHM",(560,370),cv2.FONT_HERSHEY_SIMPLEX,2.0,(255,255,255),3)msg=pub.cvimg2sms_mem(img)pub.publish(msg)time.sleep(0.02)exceptKeyboardInterrupt:pub.kill()订阅:
importcv2fromspiremsimportSubscriber,sms2cvimgdefcallback(msg):img=sms2cvimg(msg)cv2.imshow("raw image",img)cv2.waitKey(1)sub=Subscriber("/demo/raw_image","memory_msgs::RawImage",callback)sub.join()2.4 共享内存 Tensor
消息类型:memory_msgs::Tensor
适合:
- 模型输入输出
- 任意维度数组
- 图像张量、特征图、点云特征等
发布:
importtimeimportnumpyasnpfromspiremsimportPublisher,tensor2sms pub=Publisher("/demo/tensor","memory_msgs::Tensor")try:whileTrue:tensor=(np.random.rand(3,224,224)*255).astype(np.float32)msg=tensor2sms(tensor,"demo_tensor_mem")pub.publish(msg)time.sleep(0.05)exceptKeyboardInterrupt:pub.kill()订阅:
fromspiremsimportSubscriber,sms2tensordefcallback(msg):tensor=sms2tensor(msg)print("shape:",tensor.shape,"dtype:",tensor.dtype,"mean:",float(tensor.mean()))sub=Subscriber("/demo/tensor","memory_msgs::Tensor",callback)sub.join()2.5 任意类型消息
最简单的“任意结构消息”写法是使用std_msgs::Null。
它的含义是:不强约束字段,你可以按需扩展字段。
发布:
importtimefromspiremsimportPublisher,def_msg pub=Publisher("/demo/any","std_msgs::Null")cnt=0try:whileTrue:msg=def_msg("std_msgs::Null")msg["cnt"]=cnt msg["name"]="spirems"msg["ok"]=Truemsg["pos"]=[1.0,2.0,3.0]msg["meta"]={"source":"python","version":1}pub.publish(msg)cnt+=1time.sleep(1.0)exceptKeyboardInterrupt:pub.kill()订阅:
fromspiremsimportSubscriberdefcallback(msg):print(msg)sub=Subscriber("/demo/any","std_msgs::Null",callback)sub.join()2.6 任意预定义类型消息
如果你要发送已经定义好的消息,建议优先用def_msg()初始化,再写字段。
例如geometry_msgs::Pose:
fromspiremsimportPublisher,def_msg pub=Publisher("/demo/pose","geometry_msgs::Pose")msg=def_msg("geometry_msgs::Pose")msg["position"]["x"]=1.0msg["position"]["y"]=2.0msg["position"]["z"]=3.0msg["orientation"]["x"]=0.0msg["orientation"]["y"]=0.0msg["orientation"]["z"]=0.0msg["orientation"]["w"]=1.0pub.publish(msg)3. C++:最常用的发布与订阅
常用头文件:
#include<sms_core.h>3.1 字符串消息
发布:
#include<sms_core.h>intmain(){sms::Publisherpub("/demo/string_cpp","std_msgs::String");while(true){nlohmann::json msg=sms::def_msg("std_msgs::String");msg["data"]="hello spirems from cpp";pub.publish(msg);sms::msleep(1000);}return0;}订阅:
#include<iostream>#include<sms_core.h>voidcallback(nlohmann::json msg){std::cout<<"recv: "<<msg["data"]<<std::endl;}intmain(){sms::Subscribersub("/demo/string_cpp","std_msgs::String",callback);sub.join();return0;}3.2 压缩图像消息
发布:
#include<opencv2/opencv.hpp>#include<sms_core.h>intmain(){sms::Publisherpub("/demo/image_cpp","sensor_msgs::CompressedImage");while(true){cv::Mat img=cv::Mat::zeros(480,640,CV_8UC3);cv::putText(img,"SpireMS C++",cv::Point(120,240),cv::FONT_HERSHEY_SIMPLEX,1.5,cv::Scalar(0,255,0),3);nlohmann::json msg=sms::cvimg2sms(img);pub.publish(msg);sms::msleep(50);}return0;}订阅:
#include<opencv2/opencv.hpp>#include<sms_core.h>voidcallback(nlohmann::json msg){cv::Mat img=sms::sms2cvimg(msg);cv::imshow("compressed image cpp",img);cv::waitKey(1);}intmain(){sms::Subscribersub("/demo/image_cpp","sensor_msgs::CompressedImage",callback);sub.join();return0;}3.3 共享内存图像消息
发布:
#include<opencv2/opencv.hpp>#include<sms_core.h>intmain(){sms::mem_init();sms::Publisherpub("/demo/raw_image_cpp","memory_msgs::RawImage");while(true){cv::Mat img=cv::Mat::zeros(720,1280,CV_8UC3);cv::rectangle(img,cv::Rect(300,180,680,360),cv::Scalar(255,0,0),-1);cv::putText(img,"SHM CPP",cv::Point(430,380),cv::FONT_HERSHEY_SIMPLEX,2.0,cv::Scalar(255,255,255),3);nlohmann::json msg=pub.cvimg2sms_mem(img);pub.publish(msg);sms::msleep(20);}return0;}订阅:
#include<opencv2/opencv.hpp>#include<sms_core.h>voidcallback(nlohmann::json msg){cv::Mat img=sms::sms2cvimg(msg);cv::imshow("raw image cpp",img);cv::waitKey(1);}intmain(){sms::mem_init();sms::Subscribersub("/demo/raw_image_cpp","memory_msgs::RawImage",callback);sub.join();return0;}3.4 共享内存 Tensor
发布:
#include<vector>#include<sms_core.h>intmain(){sms::mem_init();sms::Publisherpub("/demo/tensor_cpp","memory_msgs::Tensor");while(true){std::vector<float>data(3*4,0.0f);for(size_t i=0;i<data.size();++i){data[i]=static_cast<float>(i);}nlohmann::json msg=sms::tensor2sms(data.data(),{3,4},"demo_tensor_cpp_mem");pub.publish(msg);sms::msleep(1000);}return0;}订阅:
#include<iostream>#include<vector>#include<sms_core.h>voidcallback(nlohmann::json msg){std::vector<float>tensor_data;std::vector<int>tensor_shape;sms::sms2tensor(msg,tensor_data,tensor_shape);std::cout<<"shape: [";for(size_t i=0;i<tensor_shape.size();++i){std::cout<<tensor_shape[i]<<(i+1==tensor_shape.size()?"":", ");}std::cout<<"], first="<<tensor_data[0]<<std::endl;}intmain(){sms::mem_init();sms::Subscribersub("/demo/tensor_cpp","memory_msgs::Tensor",callback);sub.join();return0;}3.5 任意类型消息
发布:
#include<sms_core.h>intmain(){sms::Publisherpub("/demo/any_cpp","std_msgs::Null");intcnt=0;while(true){nlohmann::json msg=sms::def_msg("std_msgs::Null");msg["cnt"]=cnt++;msg["name"]="spirems_cpp";msg["ok"]=true;msg["pos"]={1.0,2.0,3.0};msg["meta"]={{"source","cpp"},{"version",1}};pub.publish(msg);sms::msleep(1000);}return0;}订阅:
#include<iostream>#include<sms_core.h>voidcallback(nlohmann::json msg){std::cout<<msg.dump(2)<<std::endl;}intmain(){sms::Subscribersub("/demo/any_cpp","std_msgs::Null",callback);sub.join();return0;}4. C++ 最小 CMakeLists.txt
如果你想把上面的 C++ 示例直接编译运行,可以使用下面这个最小CMakeLists.txt:
cmake_minimum_required(VERSION 3.10) project(spirems_demo) find_package(OpenCV REQUIRED) find_package(SpireMS REQUIRED) add_executable(str_sender str_sender.cpp) target_include_directories(str_sender PRIVATE ${SpireMS_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS}) target_link_libraries(str_sender ${SpireMS_LIBS} ${OpenCV_LIBS})编译:
mkdir-pbuildcdbuild cmake..make-j5. 如何选择消息类型
5.1 文本、小数据
优先用:
std_msgs::Stringstd_msgs::Numberstd_msgs::Boolean
5.2 已定义结构化消息
优先用:
geometry_msgs::*sensor_msgs::*spirecv_msgs::*
写法:
- 先
def_msg("消息类型") - 再按字段赋值
5.3 本机大图像/大 Tensor
优先用:
memory_msgs::RawImagememory_msgs::Tensor
特点:
- 延迟更低
- 拷贝更少
- 只适合同机共享内存场景
5.4 暂时没有固定结构
优先用:
std_msgs::Null
适合:
- 调试
- 快速验证
- 临时自由字段消息
6. 最常见问题
6.1No module named spirems
建议:
python-mpipinstall-e.或者在项目根目录运行:
PYTHONPATH=. python your_script.py6.2 共享内存消息收不到
先检查:
- 发送端和接收端是否在同一台机器
- 是否使用了
memory_msgs::RawImage/memory_msgs::Tensor - Linux / macOS 下共享内存扩展是否正常
6.3 图像能发但显示不出来
先检查:
- 接收端是否调用了
sms2cvimg(msg) cv2.imshow()后是否调用了cv2.waitKey(1)- 发送消息类型是否和订阅类型一致
7. 仓库内可参考的现成示例
Python:
test/string_sender_test.pytest/string_receiver_test.pytest/image_sender_test.pytest/image_receiver_test.pytest/share_mem_sender_test.pytest/share_mem_receiver_test.pytest/any_sender_test.pytest/any_receiver_test.pyspirems/demo/tensor_mem_pub_demo.pyspirems/demo/tensor_mem_sub_demo.py
C++:
spirems_cpp/demo/str_sender_test.cppspirems_cpp/demo/str_receiver_test.cppspirems_cpp/demo/image_sender_test.cppspirems_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
8. 一句话总结
- 小消息:直接
Publisher + def_msg + Subscriber - 图像跨机:
sensor_msgs::CompressedImage - 大图像本机:
memory_msgs::RawImage - 张量本机:
memory_msgs::Tensor - 临时自由结构:
std_msgs::Null
照着上面的最小示例复制运行即可。