Publishing ROS2 Custom Message from Omniverse

Hello,

I am trying to publish my own custom ROS messages from Omniverse and I am running into a problem.

I defined the following dummy message inside of my package my_custom_msgs called `CharacterInfo.msg’ like so:

uint64 xpos
uint64 ypos
uint64 zpos

I then created an extension in Omniverse called my.custom.msgs, where I have a folder called packages where I copied the contents of $ROS_WORKSPACE/install/my_custom_msgs/lib/python3.8/site-packages/my_custom_msgs after building the package.

My extension adds this folder to the python path like so:

import os
import sys
import omni.ext
  
  
  
class Extension(omni.ext.IExt):
    def on_startup(self, ext_id):
        self._extension_path = None
        ext_manager = omni.kit.app.get_app().get_extension_manager()
  
        self._extension_path = ext_manager.get_extension_path(ext_id)
        sys.path.append(
            os.path.join(
                self._extension_path, "my", "custom", "msgs", "packages"))
  
        if os.environ.get("LD_LIBRARY_PATH"):
            os.environ["LD_LIBRARY_PATH"] = os.environ.get(
                "LD_LIBRARY_PATH") + ":{}/bin".format(self._extension_path)
        else:
            os.environ["LD_LIBRARY_PATH"] = "{}/bin".format(
                self._extension_path)
  
    def on_shutdown(self):
        if self._extension_path is not None:
            sys.path.remove(
                os.path.join(
                    self._extension_path, "my", "custom", "msgs", "packages"))
            self._extension_path = None

If I go then to Omniverse and activate my extension I can import the message in the Script editor without an issue like so:

from my_custom_msgs.msg import CharacterInfo

msg = CharacterInfo()

msg.xpos = 1
msg.ypos = 2
msg.zpos = 3

The problem comes when I define a publisher that uses this type of message:

import rclpy

node = rclpy.create_node('my_publisher')
pub = node.create_publisher(CharacterInfo, 'chatter', 10)

This gives me the following error:

2022-04-29 08:08:50 [1,925,474ms] [Error] [omni.kit.app.plugin] [py stderr]: UnsupportedTypeSupport: Could not import 'rosidl_typesupport_c' for package 'my_custom_msgs'

At:
  /home/user/.local/share/ov/pkg/isaac_sim-2021.2.1/exts/omni.isaac.ros2_bridge/omni/isaac/rclpy/rosidl_generator_py/import_type_support_impl.py(48): import_type_support
  /home/user/.local/share/ov/pkg/isaac_sim-2021.2.1/exts/omni.isaac.ros2_bridge/omni/isaac/rclpy/my_custom_msgs/msg/_character_info.py(35): __import_type_support__
  /home/user/.local/share/ov/pkg/isaac_sim-2021.2.1/exts/omni.isaac.ros2_bridge/omni/isaac/rclpy/rclpy/type_support.py(29): check_for_type_support
  /home/user/.local/share/ov/pkg/isaac_sim-2021.2.1/exts/omni.isaac.ros2_bridge/omni/isaac/rclpy/rclpy/node.py(1140): create_publisher
  executing: Python 0...(12): <module>

Here’s my extension.toml file for context:
[package]

# Semantic Versionning is used: https://semver.org/

version = "1.0.0"

# The title and description fields are primarily for displaying extension info in UI

title = "Simple UI Extension Template"

description="The simplest python extension example. Use it as a starting point for your extensions."

# Path (relative to the root) or content of readme markdown file for UI.

readme = "docs/README.md"

# URL of the extension source repository.

repository = ""

# One of categories for UI.

category = "Example"

# Keywords for the extension

keywords = ["kit", "example"]

# Use omni.ui to build simple UI

[dependencies]

"omni.kit.uiapp" = {}

# Main python module this extension provides, it will be publicly available as "import omni.hello.world".

[[python.module]]

name = "my.custom.msgs"

Any help would be highly appreciated.

Hi @jominga

I would like to share my experiences in creating the user extension External Extensions: ROS2 Bridge (add-on) that implements a custom message (add_on_msgs)

  • The message package (and everything compiled file related to Python) you want to load inside Omniverse must be compiled using the current Isaac Sim’s python version (3.7)… Your message is compiled using python3.8

  • In order to load the message, you need to include in the config/extension.toml file the binary files related to the message library like Isaac Sim (exts/omni.isaac.ros2_bridge/config/extension.toml) or omni.add_on.ros2_bridge do… and yes, the order matters

    Example (omni.add_on.ros2_bridge/config/extension.toml)

    [[native.library]]
    path = "bin/libadd_on_msgs__python.so"
    [[native.library]]
    path = "bin/libadd_on_msgs__rosidl_generator_c.so"
    [[native.library]]
    path = "bin/libadd_on_msgs__rosidl_typesupport_c.so"
    [[native.library]]
    path = "bin/libadd_on_msgs__rosidl_typesupport_fastrtps_c.so"
    [[native.library]]
    path = "bin/libadd_on_msgs__rosidl_typesupport_introspection_c.so"
    

That’s it… After fulfilling those points you will be able to import your custom ROS2 message… I think :)

Hello @toni.sm ,

thank you for your response. Could you give me some guidance on how to compile the package using Python 3.7?

I already installed Python3.7 and symlinked python3 to use python3.7.
However when I run the command colcon build it still uses python 3.8.

Hi @jominga

What steps did you follow to symlink python3?

This post from StackOverflow may help…

Hello @toni.sm ,

thanks for your guidance! I was able to compile the package with Python 3.7 and I added the binary files to my extension.toml and now the extension works!

But a new problem arose when I tried to subscribe to the message from the outside.
I wrote a minimal subscriber in Python that looks like this:

import rclpy
from rclpy.node import Node
  
from my_custom_msgs.msg import CharacterInfo
  
  
class MinimalSubscriber(Node):
  
    def __init__(self):
        super().__init__('minimal_subscriber')
        self.subscription = self.create_subscription(
            CharacterInfo,
            'chatter',
            self.listener_callback,
            10)
        self.subscription
  
    def listener_callback(self, msg):
        self.get_logger().info('I heard: "%s"' % msg.xpos)
  
  
def main(args=None):
    rclpy.init(args=args)
  
    minimal_subscriber = MinimalSubscriber()
  
    rclpy.spin(minimal_subscriber)
  
    minimal_subscriber.destroy_node()
    rclpy.shutdown()
  
  
if __name__ == '__main__':
    main()

After sourcing foxy and my workspace, if I run the script using Python 3.7 I get the following error:

Traceback (most recent call last):
  File "minimal_subscriber.py", line 2, in <module>
    from rclpy.node import Node
  File "/opt/ros/foxy/lib/python3.8/site-packages/rclpy/node.py", line 41, in <module>
    from rclpy.client import Client
  File "/opt/ros/foxy/lib/python3.8/site-packages/rclpy/client.py", line 22, in <module>
    from rclpy.impl.implementation_singleton import rclpy_implementation as _rclpy
  File "/opt/ros/foxy/lib/python3.8/site-packages/rclpy/impl/implementation_singleton.py", line 31, in <module>
    rclpy_implementation = _import('._rclpy')
  File "/opt/ros/foxy/lib/python3.8/site-packages/rclpy/impl/__init__.py", line 28, in _import
    return importlib.import_module(name, package='rclpy')
  File "/usr/lib/python3.7/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
ModuleNotFoundError: No module named 'rclpy._rclpy'
The C extension '/opt/ros/foxy/lib/python3.8/site-packages/rclpy/_rclpy.cpython-37m-x86_64-linux-gnu.so' isn't present on the system. Please refer to 'https://index.ros.org/doc/ros2/Troubleshooting/Installation-Troubleshooting/#import-failing-without-library-present-on-the-system' for possible solutions

If I run the script with Python 3.8 I get the following error:

Traceback (most recent call last):
  File "/opt/ros/foxy/lib/python3.8/site-packages/rosidl_generator_py/import_type_support_impl.py", line 46, in import_type_support
    return importlib.import_module(module_name, package=pkg_name)
  File "/usr/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 973, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'my_custom_msgs.my_custom_msgs_s__rosidl_typesupport_c'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "minimal_subscriber.py", line 37, in <module>
    main()
  File "minimal_subscriber.py", line 25, in main
    minimal_subscriber = MinimalSubscriber()
  File "minimal_subscriber.py", line 11, in __init__
    self.subscription = self.create_subscription(
  File "/opt/ros/foxy/lib/python3.8/site-packages/rclpy/node.py", line 1201, in create_subscription
    check_for_type_support(msg_type)
  File "/opt/ros/foxy/lib/python3.8/site-packages/rclpy/type_support.py", line 29, in check_for_type_support
    msg_type.__class__.__import_type_support__()
  File "/home/user/dev_ws/install/my_custom_msgs/lib/python3.7/site-packages/my_custom_msgs/msg/_character_info.py", line 27, in __import_type_support__
    module = import_type_support('my_custom_msgs')
  File "/opt/ros/foxy/lib/python3.8/site-packages/rosidl_generator_py/import_type_support_impl.py", line 48, in import_type_support
    raise UnsupportedTypeSupport(pkg_name)
rosidl_generator_py.import_type_support_impl.UnsupportedTypeSupport: Could not import 'rosidl_typesupport_c' for package 'my_custom_msgs'

I guess the problem now is that I compiled the package with Python 3.7 but my foxy distribution comes with Python 3.8.
Is there a way to force foxy to use Python 3.7?
What would be the best approach?

Hi @jominga

I’m glad to know that your extension works…

Now, and out of Isaac Sim, you just need to compile (from scratch and inside your workspace) the message package according to your ROS2 distribution in order to use it…