.. include:: definitions.rstinc

.. _ros2-label:

====
ROS2
====

.. figure:: images/tutorials/ros2_tutorial_icon.jpg
  :width: 400
  :alt: Video tutorial about ROS2.
  :target: https://www.youtube.com/watch?v=yj9oZS79Nxo


|AGXUnreal| has built in ROS2 support with no need for a separate ROS2 installation to work.
What is currently supported is message passing via the ``ROS2 Publisher`` and ``ROS2 Subscriber`` Components.
See :ref:`ros2-message-passing-label` for details about how these are used.
All messages types in the following packages are supported:

``std_msgs``, ``sensor_msgs``, ``builtin_interfaces``, ``rosgraph_msgs``, ``geometry_msgs`` and ``agx_msgs``.

There is also a mechanism for passing custom data types using the ``ROS2 Any Message Builder`` and ``ROS2 Any Message Parser`` Components, see :ref:`ros2-custom-types-label` for details.

Specifying ``QOS`` (Quality of Service) such as reliability, durability, history type and depth as well as domain ID is also supported, see :ref:`ros2-qos-label`.

ROS2 in |AGXUnreal| targets the version ``ROS2 Humble`` and specifically the DDS implementation ``Fast-DDS`` which is the default DDS implementation in ``ROS2 Humble``.
ROS2 generally does not guarantee compatibility between ROS2 versions and DDS vendors, but it should work in practice, especially when matching the DDS vendor types.
For details regarding troubleshooting, see :ref:`ros2-troubleshooting-label`.


.. _ros2-message-passing-label:

***************
Message passing
***************

Sending and receiving ROS2 messages in |AGXUnreal| is done via the ``ROS2 Publisher`` and ``ROS2 Subscriber`` Components.


######################
Sending a ROS2 Message
######################

To send a ROS2 message, simply add a new ``ROS2 Publisher`` Component in a Blueprint.
Then call one of the ``Send`` functions for the message type wanted.
Specify a topic and pass a message struct of the appropriate type. 
Below is an example of sending a ``std_msgs::Float32`` on topic ``mytopic``.

.. figure:: images/ros2/ros2_send_message.jpg
  :alt: Send ROS2 message
  :target: _images/ros2_send_message.jpg

  Sending a ROS2 message.

Note that a single ``ROS2 Publisher`` Component can be used to send messages on multiple topics and message types.
This is possible because the Component uses a pool of publishers internally, one for each topic.
Using multiple ``ROS2 Publisher`` Components is supported as well.
Passing messages of different types to the same topic is not supported.

If using C++, the same can be done by calling the appropriate Send function:

.. code-block:: c++

  Pub->SendStdMsgsFloat32(Msg, "mytopic");


########################
Receiving a ROS2 Message
########################

To receive a ROS2 message, simply add a new ``ROS2 Subscriber`` Component in a Blueprint.
Then call one of the ``Receive`` functions for the message type wanted.
Specify a topic and remember to always check the return value.
``true`` is returned if a message was received, ``false`` otherwise. 
Below is an example of receiving a ``std_msgs::Float32`` on topic ``mytopic`` and printing the received data to the screen.

.. figure:: images/ros2/ros2_receive_message.jpg
  :alt: Receive ROS2 message
  :target: _images/ros2_receive_message.jpg

  Receiving a ROS2 message.

Note that a single ``ROS2 Subscriber`` Component can be used to receive messages on multiple topics and message types.
This is possible because the Component uses a pool of subscribers internally, one for each topic.
Using multiple ``ROS2 Subscriber`` Components is supported as well.

If using C++, the same can be done by calling the appropriate Receive function:

.. code-block:: c++

  if (Sub->ReceiveStdMsgsFloat32(OutMsg, "mytopic"))
  {
    /* Use OutMsg */
  }


.. _ros2-custom-types-label:

******************
Using Custom Types
******************

Passing custom data types is possible by using the ``ROS2 Any Message Builder`` and ``ROS2 Any Message Parser`` Components.
These are helper classes that can serialize and deserialize custom data to and from a ``agx_msgs::Any`` message.
These support some of the more common built in integer, floating point, boolean, and string types as well as sequences of these.

The ``agx_msgs::Any`` message type as well as AnyMessageBuilder and AnyMessageParser are also avaiable `open-source on GitHub <https://github.com/Algoryx/agx-ros2-collection>`__  so that these can be used on machines that does not have |AGX| or |AGXUnreal| installed.

By serializing a custom data type to an ``agx_msgs::Any`` message using a ``ROS2 Any Message Builder``, the ``agx_msgs::Any`` message can then be sent via ROS2.
On the receiving side, the ``agx_msgs::Any`` message is deserialized using a ``ROS2 Any Message Parser`` to retrieve the custom data.
Below is an example of serializing and passing the equivalent of the following C++ struct:

.. code-block:: c++

   struct MyStruct
   {
      float MyFloat;
      std::vector<std::string> MyStrings;
   };

.. figure:: images/ros2/any_message_builder.jpg
  :alt: Sending a custom data type.
  :target: _images/any_message_builder.jpg

  Sending a custom data type.


Note that the ``Begin Message`` function must be called before starting to build a new message using the ``ROS2 Any Message Builder`` Component, as the image above shows.

To retrieve the custom data type, we receive the message and then use the ``ROS2 Any Message Parser`` Component to read the data back:

.. figure:: images/ros2/any_message_parser.jpg
  :alt: Receiving a custom data type.
  :target: _images/any_message_parser.jpg

  Receiving a custom data type.

Note that the ``Begin Parse`` function must be called each time before starting to read (or "parse") a new ``agx_msgs::Any`` message.
Also note that the order of write and read when using the ``ROS2 Any Message Builder`` and ``ROS2 Any Message Parser`` Components must match on the sending and receiving side.
The layout of the custom data type must also be known by both sides and serialized according to its data members.


.. _ros2-qos-label:

************************
QOS (Quality of Service)
************************

QOS or Quality of Service determines the lifetime behaviour of ROS2 messages.
QOS can be set for each ``ROS2 Publisher`` and ``ROS2 Subscriber`` Component in the Details Panel.

.. figure:: images/ros2/ros2_qos.jpg
  :alt: ROS2 Quality of Service settings.
  :target: _images/ros2_qos.jpg

  ROS2 Quality of Service settings.

Setting the Reliability, Durability, History and History Depth is supported.
By default these will be set to the same values as in ``ROS2 Humble``.
Changing the QOS setting on an existing ``ROS2 Publisher`` or ``ROS2 Subscriber`` during Play is not supported.


***********
Limitations
***********

Since only integer types ``uint8``, ``int32`` and ``int64`` is currently supported by Blueprints, some of the built in message types exposed in |AGXUnreal| has been modified to use these in place of other non-supported integer types.
These are converted internally to the correct integer types, so as a User no special logic needs to be written to convert these values.

Passing messages of different types to the same topic is not supported.
Changing the QOS setting on an existing ``ROS2 Publisher`` or ``ROS2 Subscriber`` during Play is not supported.

Since the type uint64 is not supported in Blueprints, data loss may occur when receiving messages containing members of uint64 type.
These will be cast to int64 automatically to work in Blueprints. 

.. _ros2-troubleshooting-label:

***************
Troubleshooting
***************

If messages are missed, or the first couple of messages are missed, it may be of use to check the ``QOS`` settings used.
By default the QOS settings are the same as in ``ROS2 Humble``.
More details on these settings can be seen (`here <https://docs.ros.org/en/humble/Concepts/Intermediate/About-Quality-of-Service-Settings.html>`__).

If no communication between a built in ``ROS2 Publisher`` and ``ROS2 Subscriber`` can be established it is possible that it is blocked by a firewall on the computer.
Check the firewall settings and unblock the process in case it has been blocked.

If no communication between a built in ``ROS2 Publisher`` or ``ROS2 Subscriber`` and an external ROS2 node running on a complete ROS2 installation can be established, ensure that the same version of ROS2 is used.
The built in ROS2 support in |AGXUnreal| targets ``ROS2 Humble``.
If this does not solve the problem, ensure that the DDS implementation ``Fast-DDS`` is used in the ROS2 installation.
This can be forced by setting the environment variable ``RMW_IMPLEMENTATION=rmw_fastrtps_cpp``. See the (`ROS2 documentation <https://docs.ros.org/en/humble/How-To-Guides/Working-with-multiple-RMW-implementations.html>`_) for details.
Also check the firewall settings if necessary.

If the creation of a ``agxIO::ROS2::Publisher`` or ``agxIO::ROS2::Subscriber`` hangs forever it is possible a seemingly rare bug in Fast-DDS is the culprit.
This seems to happen when creating a DDS Participant (which is done automatically by AGX Dynamcis) on Windows on rare occations.
The underlying reason why this can happen is not known, but a manual workaround is to do the following:
1. Turn off any process using ROS2.
2. Manually remove all files in ``C:\ProgramData\eprosima\fastrtps_interprocess`` (replace drive-letter if applicable).