The script looks like the following. I would like to program it in a way that it receives the published msg.Data and displays it in the scene.

using System;
using UnityEngine;
using TMPro;
namespace ROS2
{

/// <summary>
/// An example class provided for testing of basic ROS2 communication
/// </summary>
public class ROS2ListenerExample : MonoBehaviour
{
    [SerializeField]
    private TMPro.TMP_Text subscrib_text;
    private ROS2UnityComponent ros2Unity;
    private ROS2Node ros2Node;
    private ISubscription<std_msgs.msg.String> chatter_sub;

    void Start()
    {
        ros2Unity = GetComponent<ROS2UnityComponent>();
    }

    void Update()
    {
        if (ros2Node == null && ros2Unity.Ok())
        {
            ros2Node = ros2Unity.CreateNode("ROS2UnityListenerNode");
            chatter_sub = ros2Node.CreateSubscription<std_msgs.msg.String>(
              "chatter", msg => subscrib_text.SetText("Unity listener heard: [" + msg.Data + "]"));
        }
    }
}

}  // namespace ROS2

text

I got this error when trying to display console data in a scene using TextMeshPro in ros2-for-unity. The ideal output result would be to display the SUBSCRIBED data in the unity scene.

1

There are 1 best solutions below

1
derHugo On
  • Why add a new subscription every frame?

    I would assume this should be done only once

  • And then is it possible that callback is received asynchronous on a different thread?

    Most of Unity's API is only available on the Unity main thread - which is basically what the error message is telling you.

So what you could do is e.g.

public class ROS2ListenerExample : MonoBehaviour
{
    [SerializeField] private TMPro.TMP_Text subscrib_text;
    [SerializeField] private ROS2UnityComponent ros2Unity;
    private ROS2Node ros2Node;
    private ISubscription<std_msgs.msg.String> chatter_sub;

    // thread-save stack
    // using a stack because you seem not interested in continuous data flow but only the latest state
    // otherwise rather use a ConcurrentQueue instead
    private readonly ConcurrentStack<string> receivedMessages = new ();

    // this makes Start being run as a Coroutine
    // see https://docs.unity3d.com/Manual/Coroutines.html
    IEnumerator Start()
    {
        if(!ros2Unity) ros2Unity = GetComponent<ROS2UnityComponent>();

        yield return new WaitUntil(() => ros2Unity.Ok());

        ros2Node = ros2Unity.CreateNode("ROS2UnityListenerNode");
        chatter_sub = ros2Node.CreateSubscription<std_msgs.msg.String>("chatter", msg => 
        {
            // as this might be executed on a different thread
            // instead of directly touching Unity API rather only schedule the message for the next Update call
            receivedMessages.Push(msg.Data);
        });
    }

    void Update()
    {
        // handle latest received message in Update (= Unity main thread)
        if(receivedMessages.TryPop(out var message))
        {
            subscrib_text.SetText($"Unity listener heard: [{message}]");
        }

        receivedMessages.Clear();
    }
}