配置 Zenoh-Bridge-ROS2DDS 实现 HTTP/WS 推送及 JSON 转换

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 修复 的极简版代码。

⚠️ 前提条件

在使用这个方案前,你需要:

  1. 已安装 ROS2(Humble 或其他版本)
  2. Source ROS2 环境
  3. 安装 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 更新
这是最推荐的生产级方案!
相关推荐
洞窝技术4 小时前
告别 AI 过度工程:一文吃透 Ponytail 七层精简阶梯与落地实践
ai编程
Ai拆代码的曹操5 小时前
源码拆解:opencode 如何封装 Git 实现自动 diff 和 review
openai·ai编程
Ai拆代码的曹操5 小时前
opencode 源码:工作单元的生命周期管理
openai·ai编程
赫媒派6 小时前
MCP 协议升级:Stateless 化对开发者的影响
ai编程
小四的小六6 小时前
WebView 在 AI 时代的三个被低估的价值——从自己的 AI 产品里找到的答案
前端·openai·ai编程
小蠢驴打代码7 小时前
记忆库能通过测试,不等于回答值得信:Coding Agent Memory 的两层评估设计
github·ai编程
喂你一颗橘子糖7 小时前
我用 Codex 跑长任务:快照、Loop 和单线写入,解决三个失控问题
ai编程
李剑一7 小时前
瑞幸也AI上了?我用AI命令行帮我点了一杯咖啡,但是我花了不止一杯咖啡钱
aigc·openai·ai编程