ros2 / rclpy

rclpy (ROS Client Library for Python)
Apache License 2.0
268 stars 221 forks source link

Decode ROS2 raw byte data using rclpy.serialization.deserialize_message #1273

Open EnzoGhisoni opened 2 months ago

EnzoGhisoni commented 2 months ago

I try to get data from a ros-foxglove-bridge (more detail on the scope here https://github.com/orgs/foxglove/discussions/991). I'm able to receive a ROS2 raw coded message in binary format but I'm not able to decode it properly. I tried to use deserialize_message to deserialize the data but I'm not able to make it work.

Required Info:

Steps to reproduce issue

Here is a standalone code with only the data received from the websocket hardcoded. The original message is a ROS2 std_msgs.msg String at the format:

data: Hello, world! Time(nanoseconds=1713770713401001821, clock_type=ROS_TIME)

from std_msgs.msg import String
from rclpy.serialization import deserialize_message

# Serialized message data
serialized_data = b'\x01\x03\x00\x00\x00\xb3\xa8\xbfFb\x89\xc8\x17\x00\x01\x00\x00I\x00\x00\x00Hello, world! Time(nanoseconds=1713770713401001821, clock_type=ROS_TIME)\x00\x00\x00\x00'

def main():

    message_type = String()

    decoded_data = deserialize_message(serialized_data, message_type)
    print(decoded_data)

if __name__ == "__main__":
    main()

Expected behavior

I would like to be able to decode the string message to get a deserialized value (to be able to also decode more advanced message.

Actual behavior

It shows the following error message: return _rclpy.rclpy_deserialize(serialized_message, message_type) rclpy._rclpy_pybind11.RMWError: failed to deserialize ROS message: rmw_serialize: invalid data size, at ./src/rmw_node.cpp:1727

I'm not sure if I'm not able to use the function without error because of validity of my data or just because of the provided arguments.

InvincibleRMC commented 1 month ago

I believe message_type = should be String not String()

fujitatomoya commented 1 month ago

serialized message seems to be wrong, the following works w/o any problem.

def main():
    msg = String()
    msg.data = 'Hello, world!'
    msg_serialized = serialize_message(msg)
    print(msg_serialized)
    msg_deserialized = deserialize_message(msg_serialized, String)
    print(msg_deserialized)

the output is,

b'\x00\x01\x00\x00\x0e\x00\x00\x00Hello, world!\x00'
std_msgs.msg.String(data='Hello, world!')
EnzoGhisoni commented 1 month ago

I have tried what you suggest @fujitatomoya and I'm able to successfully decode the string, the issue is that my data come from foxglove_bridge and the encoding seems to be a bit different.

To give a bit more context, I try to create a ws-client to subscribe to a topic with a foxglove_bridge (https://github.com/foxglove/ros-foxglove-bridge), so I have: ROS2 string publisher -> foxglove_bridge-> ws-client The data copy pasted in the question is the string received from the foxglove_bridge.

Here is the complete python script of the ws-client:

import asyncio
import websockets
import json

from std_msgs.msg import String
from rclpy.serialization import deserialize_message

from mcap.reader import make_reader
from mcap_ros2.reader import read_ros2_messages
from mcap_ros2.decoder import DecoderFactory

async def handle_event(websocket, message, subscribed):
    # Handle different types of events here
    print("Received event:", message)

    if subscribed == False:

        subscribed = True
        subsriber = {
            "op": "subscribe",
            "subscriptions": [
                {"id": 3, "channelId": 3} # replace by the selected channel id the subscribe
            ]
        }
        await websocket.send(json.dumps(subsriber))

    if type(message) == bytes:
        print("Start to deserialize")
        message_type = String()

        # Decode the message here

        #decoded_data = deserialize_message(serialized_message=message, message_type=message_type)

        # print(decoded_data)

async def connect_to_server():
    uri = "ws://0.0.0.0:9090"  # Change this to your server URI
    subscribed = False
    async with websockets.connect(uri, subprotocols=["foxglove.websocket.v1"]) as websocket:
        print("Connected to server.")
        try:
            while True:
                message = await websocket.recv()

                await handle_event(websocket, message, subscribed)
                subscribed = True
        except websockets.ConnectionClosed:
            print("Connection to server closed.")

async def main():
    await connect_to_server()

if __name__ == "__main__":
    asyncio.run(main())