zenoh-bridge-ros2dds配置http ws推送 以及转为json给前端使用
启动命令
./zenoh-bridge-ros2dds --listen tcp/0.0.0.0:7447 --listen ws/0.0.0.0:10000 --rest-http-port 8000
python将推送cdr格式的数据转为json 以便于前端进行使用
python
#!/usr/bin/env python3
"""
极简版:Zenoh CDR to JSON 转换器(已修复 ZBytes 兼容性问题)
"""
import zenoh
import json
import sys
from pycdr2 import IdlStruct
from dataclasses import dataclass
# ============================================================================
# 消息类型定义
# ============================================================================
@dataclass
class Vector3(IdlStruct):
x: float = 0.0
y: float = 0.0
z: float = 0.0
@dataclass
class Twist(IdlStruct):
linear: Vector3 = None
angular: Vector3 = None
def __post_init__(self):
if self.linear is None:
self.linear = Vector3()
if self.angular is None:
self.angular = Vector3()
# ============================================================================
# 核心逻辑
# ============================================================================
def parse_cdr(cdr_bytes):
"""解析 CDR 为 JSON"""
msg = Twist.deserialize(cdr_bytes) # 这里需要标准的 bytes
return {
"linear": {"x": msg.linear.x, "y": msg.linear.y, "z": msg.linear.z},
"angular": {"x": msg.angular.x, "y": msg.angular.y, "z": msg.angular.z}
}
def handle_message(sample):
"""处理消息"""
try:
# 【关键修改】将 Zenoh 的 ZBytes 转换为 Python bytes
payload_bytes = bytes(sample.payload)
# 解析
data = parse_cdr(payload_bytes)
# 发布 JSON
session.put(
f"{sample.key_expr}/json",
json.dumps(data).encode(),
encoding="application/json"
)
print(f"✓ {sample.key_expr} -> {sample.key_expr}/json")
except Exception as e:
print(f"✗ 处理错误: {e}")
import traceback
traceback.print_exc()
def start_service(topic="turtle1/cmd_vel", host="127.0.0.1", port=7447):
"""启动服务"""
global session
# 连接 Zenoh
conf = zenoh.Config()
conf.insert_json5("connect/endpoints", f'["tcp/{host}:{port}"]')
session = zenoh.open(conf)
print(f"✓ 已连接到 Zenoh: {host}:{port}")
print(f"✓ 订阅话题: {topic}")
print(f"✓ JSON 输出: {topic}/json")
print("\n按 Ctrl+C 停止...")
# 订阅并保持运行
session.declare_subscriber(topic, handle_message)
try:
import time
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n✓ 服务已停止")
if __name__ == "__main__":
topic = sys.argv[1] if len(sys.argv) > 1 else "turtle1/cmd_vel"
start_service(topic)
1. 安装依赖(只需要 requests 和 pycdr2)
pip install eclipse-zenoh
pip install pycdr2
2. 保存上面的脚本为 rest_cdr_to_json.py
3. 运行
python3 rest_cdr_to_json.py turtle1/cmd_vel
测试结果
powershell
zwy@zwy-virtual-machine:~$ curl -g -H "Accept: text/event-stream" http://localhost:8000/turtle1/cmd_vel/json
event:PUT
data:{"key":"turtle1/cmd_vel/json","value":{"angular":{"x":0.0,"y":0.0,"z":0.0},"linear":{"x":2.0,"y":0.0,"z":0.0}},"encoding":"application/json","timestamp":"7665343878108153936/9baa032258de093e26cd168f1af73023"}
好的!这是结合了 ROS2 标准消息包 和 ZBytes 修复 的极简版代码。
⚠️ 前提条件
在使用这个方案前,你需要:
- 已安装 ROS2(Humble 或其他版本)
- Source ROS2 环境
- 安装 Zenoh Python 库
bash
# 1. Source ROS2 环境
source /opt/ros/humble/setup.bash
# 2. 安装 Zenoh(如果还没装)
pip install eclipse-zenoh
✅ 完整代码(极简版 + ROS2 标准消息)
python
#!/usr/bin/env python3
"""
极简版:使用 ROS2 标准消息包解析 CDR
======================================
优势:
1. 无需手动定义消息类型(@dataclass)
2. 直接使用 ROS2 官方消息定义
3. 自动处理复杂嵌套类型
前提:
source /opt/ros/humble/setup.bash
"""
import zenoh
import json
import sys
# ============================================================================
# 导入 ROS2 标准库
# ============================================================================
# ROS2 序列化工具
import rclpy
from rclpy.serialization import deserialize_message
# ROS2 标准消息类型(按需导入)
from geometry_msgs.msg import Twist, Pose, Vector3, Quaternion
from nav_msgs.msg import Odometry
# ============================================================================
# 工具函数:ROS2 消息转字典
# ============================================================================
def msg_to_dict(msg):
"""
将 ROS2 消息对象转换为字典
支持嵌套消息和数组
"""
# 如果是基本类型,直接返回
if isinstance(msg, (int, float, str, bool)):
return msg
# 如果是列表,递归处理
if isinstance(msg, list):
return [msg_to_dict(item) for item in msg]
# 如果是 ROS2 消息对象(有字段定义)
if hasattr(msg, 'get_fields_and_field_types'):
result = {}
for field_name in msg.get_fields_and_field_types().keys():
value = getattr(msg, field_name)
result[field_name] = msg_to_dict(value)
return result
# 其他情况返回原值
return msg
# ============================================================================
# 核心逻辑
# ============================================================================
def parse_cdr(cdr_bytes, msg_type):
"""解析 CDR 为 JSON(使用 ROS2 标准反序列化)"""
# 使用 ROS2 的反序列化方法
msg = deserialize_message(cdr_bytes, msg_type)
# 转换为字典
return msg_to_dict(msg)
def handle_message(sample):
"""处理消息"""
try:
# 【关键修复】将 Zenoh 的 ZBytes 转换为 Python bytes
payload_bytes = bytes(sample.payload)
# 根据话题选择消息类型
topic_str = str(sample.key_expr)
if 'cmd_vel' in topic_str:
msg_type = Twist
elif 'pose' in topic_str and 'odom' not in topic_str:
msg_type = Pose
elif 'odom' in topic_str:
msg_type = Odometry
else:
msg_type = Twist # 默认
# 解析 CDR(使用 ROS2 标准反序列化)
data = parse_cdr(payload_bytes, msg_type)
# 发布 JSON
session.put(
f"{sample.key_expr}/json",
json.dumps(data).encode(),
encoding="application/json"
)
print(f"✓ {sample.key_expr} -> {sample.key_expr}/json")
except Exception as e:
print(f"✗ 处理错误: {e}")
import traceback
traceback.print_exc()
def start_service(topic="turtle1/cmd_vel", host="127.0.0.1", port=7447):
"""启动服务"""
global session
# 连接 Zenoh
conf = zenoh.Config()
conf.insert_json5("connect/endpoints", f'["tcp/{host}:{port}"]')
session = zenoh.open(conf)
print("=" * 60)
print("Zenoh CDR to JSON (ROS2 标准消息版)")
print("=" * 60)
print(f"✓ 已连接到 Zenoh: {host}:{port}")
print(f"✓ 订阅话题: {topic}")
print(f"✓ JSON 输出: {topic}/json")
print("=" * 60)
print("\n按 Ctrl+C 停止...")
# 订阅并保持运行
session.declare_subscriber(topic, handle_message)
try:
import time
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n✓ 服务已停止")
# ============================================================================
# 主程序
# ============================================================================
if __name__ == "__main__":
# 从命令行参数读取话题
topic = sys.argv[1] if len(sys.argv) > 1 else "turtle1/cmd_vel"
# 启动服务
start_service(topic)
🚀 使用方法
bash
# 1. Source ROS2 环境(必须!)
source /opt/ros/humble/setup.bash
# 2. 运行脚本
python3 ros2_cdr_to_json.py
# 3. 指定话题
python3 ros2_cdr_to_json.py turtle1/cmd_vel
💡 核心改进点
| 改进 | 说明 |
|---|---|
| 无需定义消息 | 直接导入 geometry_msgs.msg |
| 自动反序列化 | 使用 rclpy.serialization.deserialize_message |
| 通用转字典 | msg_to_dict 支持任意 ROS2 消息 |
| ZBytes 兼容 | bytes(sample.payload) 修复类型错误 |
📋 支持的消息类型
只需在导入部分添加更多消息类型即可支持:
python
# 在导入部分添加
from geometry_msgs.msg import Twist, Pose, PoseStamped, Transform
from sensor_msgs.msg import LaserScan, Image, JointState
from nav_msgs.msg import Odometry, Path
# ... 所有 ROS2 标准消息
然后在 handle_message 中添加类型判断:
python
if 'scan' in topic_str:
msg_type = LaserScan
elif 'image' in topic_str:
msg_type = Image
# ...
🎯 对比:之前的方案 vs 现在
| 特性 | 之前 | 现在(ROS2 标准包) |
|---|---|---|
| 消息定义 | 需要手动定义 @dataclass |
无需定义,直接导入 |
| 准确性 | 可能出错 | 官方定义,100% 准确 |
| 复杂消息 | 需要大量代码定义嵌套结构 | 自动处理嵌套 |
| 维护性 | 需要手动维护 | 跟随 ROS2 更新 |
| 这是最推荐的生产级方案! ✅ |