配置 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 小时前
一个人已经有 Agent 了,我们为什么还要建设统 Agent 平台
人工智能·agent·ai编程
仙逆GPT5 小时前
ChatGPT、Codex实战:Sol、Terra、Luna怎么选?Reasoning不是越高越好
ai编程·codex·chatgptplus·gpt5.6·codex教程
百工蜂Agent5 小时前
万物皆工具
agent·ai编程
起个名字好难啊这也被占用了5 小时前
LangGraphjs可中断可恢复的AI工作流
人工智能·ai编程
NWU_白杨6 小时前
AI协作开发
ai编程
东小西7 小时前
番外篇四:《一个图搞定智能客服:用 StateGraph 把"条件路由"和"状态合并"玩明白》
openai·ai编程
全栈弄潮儿8 小时前
第一次让 AI 帮你写代码:用 JavaScript 完成一个待办事项清单
chatgpt·openai·ai编程
颜进强8 小时前
Claude Code - 26 效率三件套:cc-switch 切模型 · codeburn 算成本 · claude-hud 看状态
前端·后端·ai编程
东小西8 小时前
番外篇三:《ReactAgent 为什么能"想一步做一步"?扒开它的 Graph 内核》
openai·ai编程
答案answer9 小时前
VibeCoding 能做到什么程度?我用它做了一座 3D 数字博物馆
ai编程·three.js·vibecoding