ros2 rolling 自定义的消息 <> python字典 <> Json字符串<>ROS2的std_msgs.msg 中的 String类型


TrajectoryRTU.srv
# ros2 pkg create pkg_interfaces --build-type ament_cmake --dependencies rosidl_default_generators sensor_msgs --license Apache-2.0
# 在ROS 2包的srv目录下创建 TrajectoryRTU.srv
# 电机驱动的轴 绝对坐标位置控制
uint16  id_group     # 动作组号
uint16  id_act       # 动作编号
string[] joint_name  # 关节功能名称  x_  y_  z_
uint8[] joint_id     # 关节控制器RTU通信地址,可以是伺服电机控制器、步进电机控制器或远程IO模块   2 3 4 
uint8[] fun_code     # 自定义的功能代号   1:坐标移动命令  2:查询状态命令 3:查询坐标命令  4:设备重新启动
float32[] displace   # 目标坐标,浮点数,单位 m 或 rad 或 0x00FF(OFF)0xFF00(ON)
uint16[]  speed_rpm  # 电机转速,RPM
---
float32[] displace     # 当前坐标,浮点数,单位 m 或 rad 或 0x00FF(OFF)0xFF00(ON)
uint8[] status       # 自定义的功能代号
bool[]  success         # 操作是否成功
string[] message       # 错误信息

关键函数代码与说明

ROS2消息转字典(支持基本类型和数组)不适用 ROS2消息内有ROSOS2消息
字典转ROS2消息(支持基本类型和数组) 不适用 字典内有字典
#!/usr/bin/env python3
#**************************
# 按照如下命令行方式打开VSCODE,方便调试
# source install/setup.bash && code .
#**************************
from typing import Dict, Any, Union, List, Optional
from pkg_interfaces.srv import TrajectoryRTU
from std_msgs.msg import String
import json
import array


class RosMessageConverter:
    @staticmethod
    def ros_message_to_dict(msg) -> Dict[str, Any]:
        """ROS2消息转字典(支持基本类型和数组)
        
        Args:
            msg: ROS2消息对象
            
        Returns:
            字典,数组自动转为list
            
        Raises:
            TypeError: 输入不是ROS2消息时
        """
        if not hasattr(msg, 'get_fields_and_field_types'):
            raise TypeError("Input is not a ROS2 message")

        result = {}
        # 获取消息字段信息
        fields = msg.get_fields_and_field_types().keys()
        
        for field in fields:
            value = getattr(msg, field)
            
            # 处理数组类型(包括array.array)
            if isinstance(value, (list, array.array)):
                result[field] = list(value)
            # 处理基本类型
            else:
                result[field] = value
                
        return result
    @staticmethod
    def dict_to_ros_message(msg_type, data: Dict[str, Any]):
        """字典转ROS2消息(支持基本类型和数组)
        
        Args:
            msg_type: ROS2消息类(如TrajectoryRTU.Request)
            data: 字典数据
            
        Returns:
            构建好的ROS2消息对象
            
        Raises:
            TypeError: 输入不是ROS2消息类时
        """
        if not hasattr(msg_type, 'get_fields_and_field_types'):
            raise TypeError("Input is not a ROS2 message class")

        msg = msg_type()
        fields = msg.get_fields_and_field_types().keys()
        
        for field in fields:
            if field not in data:
                continue
                
            value = data[field]
            current_value = getattr(msg, field)
            
            # 处理数组字段
            if isinstance(current_value, (list, array.array)):
                container = getattr(msg, field)
                del container[:]
                if isinstance(value, (list, tuple)):
                    container.extend(value)
            # 处理基本类型
            else:
                setattr(msg, field, value)
                
        return msg

    @staticmethod
    def set_ros_attrs(
        ros_msg_obj: Union[TrajectoryRTU.Request, TrajectoryRTU.Response],
        data_dict: Dict[str, Any]
    ) -> None:
        """安全设置ROS2消息的属性值
        
        Args:
            ros_msg_obj: ROS2消息对象
            data_dict: 包含属性值的字典
            
        Raises:
            ValueError: 如果输入参数无效
            TypeError: 如果类型不匹配
        """
        if not ros_msg_obj or not data_dict:
            raise ValueError("ros_msg_obj and data_dict cannot be None")
            
        if not isinstance(data_dict, dict):
            raise TypeError("data_dict must be a dictionary")

        for field, value in data_dict.items():
            if not hasattr(ros_msg_obj, field):
                continue
                
            attr = getattr(ros_msg_obj, field)
            
            if isinstance(attr, (list, array.array)):
                if not isinstance(value, (list, tuple, array.array)):
                    raise TypeError(f"Field {field} requires sequence type, got {type(value)}")
                del attr[:]#attr.clear()
                attr.extend(value)
            else:
                setattr(ros_msg_obj, field, value)

    @staticmethod
    def get_ros_attr(
        ros_msg_obj: Union[TrajectoryRTU.Request, TrajectoryRTU.Response],
        field: str
    ) -> Union[Any, List[Any]]:
        """安全获取ROS2消息的属性值
        
        Args:
            ros_msg_obj: ROS2消息对象
            field: 要获取的字段名
            
        Returns:
            字段值,数组类型会转换为list
            
        Raises:
            ValueError: 如果输入参数无效
            AttributeError: 如果字段不存在
        """
        if not ros_msg_obj or not field:
            raise ValueError("ros_msg_obj and field cannot be None")
            
        if not hasattr(ros_msg_obj, field):
            raise AttributeError(f"Field {field} does not exist")
            
        attr = getattr(ros_msg_obj, field)
        return list(attr) if isinstance(attr, (list, array.array)) else attr

    @staticmethod
    def dict_to_json(data_dict: Dict[str, Any]) -> Optional[str]:
        """字典转JSON字符串
        
        Args:
            data_dict: 要转换的字典
            
        Returns:
            JSON字符串或None(转换失败时)
        """
        if not isinstance(data_dict, dict):
            return None
            
        try:
            return json.dumps(data_dict)
        except (TypeError, ValueError) as e:
            print(f"JSON serialization error: {e}")
            return None

    @staticmethod
    def json_to_dict(json_str: str) -> Optional[Dict[str, Any]]:
        """JSON字符串转字典
        
        Args:
            json_str: JSON格式的字符串
            
        Returns:
            字典或None(解析失败时)
        """
        if not isinstance(json_str, str):
            return None
            
        try:
            return json.loads(json_str)
        except json.JSONDecodeError as e:
            print(f"JSON parse error: {e}")
            return None
        except TypeError as e:
            print(f"Invalid JSON format: {e}")
            return None


# 使用示例
if __name__ == "__main__":
    # 创建并初始化Request
    request = TrajectoryRTU.Request()
    RosMessageConverter.set_ros_attrs(request, {
        'id_group': 1,
        'id_act': 2,
        'joint_name': ["x_", "y_", "z_"],
        'joint_id': [2, 3, 4],
        'fun_code': [1],
        'displace': [0.5, 0.3, 0.1],
        'speed_rpm': [100, 100, 100]
    })
    
    # 转换为字典
    request_dict = RosMessageConverter.ros_message_to_dict(request)
    print("Request as dict:", request_dict)
    
    # 从字典恢复
    recovered_request = RosMessageConverter.dict_to_ros_message(TrajectoryRTU.Request, request_dict)
    # 转换为JSON
    request_json = RosMessageConverter.dict_to_json(request_dict)
    print("Request as JSON:", request_json)
    
    # 从JSON恢复
    recovered_dict = RosMessageConverter.json_to_dict(request_json)
    print("Recovered dict:", recovered_dict)

 

二、电机类模板

from typing import Dict, Any, Union, List, Optional
import logging
#设置调试信息打印
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

class  Motor_Ctl:
   def __init__(self,**kwargs) -> None:
    self.speed    = kwargs.get('speed', 0)     # 无效
    self.displace = kwargs.get('displace', 0.0)#  int(self.displace) == 0x00FF 关闭
    self.acc      = kwargs.get('acc', 0)       # 无效

    # 声明参数并根据**kwargs设置,并给定默认值
    self.slave_id    = kwargs.get('slave_id', 5) # 驱动器通信地址
    self.ratio       = kwargs.get('ratio',  1)   #,无效
    self.radius      = kwargs.get('radius', 1)   #,无效
    self.pulses      = kwargs.get('pulses', 1)   #,无效
    pass 
   
   def reset_motor(self,**kwargs) -> bool:
     """设备初始化及消除报警"""
     return True
   
   def rd_status(self,**kwargs) -> Optional[Dict[str,Any]]:
     """读取设备状态"""
     pass
   
   def wr_goto(self,**kwargs) -> bool:
     """电机以设定加速度、最高速度,移动指定绝对坐标点"""
     pass
   def wr_srzp(self,**kwargs) -> bool:
     """电机搜索并停在零点"""
     pass
   def rd_position(self) ->float:
     """读取位置坐标"""
     pass
   def reset_sensor(self) ->float:
     """初始化传感器编码器及消除报警"""
     pass
   

 

posted @ 2025-07-18 10:46  辛河  阅读(43)  评论(0)    收藏  举报