Creating Action Graph and Tf Trees from a python code

Isaac Sim Version

4.2.0
4.1.0
4.0.0
2023.1.1
2023.1.0-hotfix.1
Other (please specify):

Operating System

Ubuntu 22.04
Ubuntu 20.04
Windows 11
Windows 10
Other (please specify):

GPU Information

  • Model: NVIDIA RTX 3500 Ada Generation
  • Driver Version: 565.57.01

Topic Description

Detailed Description

I am trying to use an action graph from Python code to create the tf tree of a robot. I followed the examples in the standalone application folder but have been without success so far.

Error Messages

[locobot_loader.py-3] 2025-01-28 21:02:03 [42,032ms] [Error] [ROS2BridgeHandler] Failed to create action graph: OmniGraphError: Failed to connect /ActionGraph/OnPlaybackTick.outputs:tick -> /ActionGraph/ReadSimTime.inputs:execIn [locobot_loader.py-3] [ERROR] [1738098123.600073694] [locobot_loader]: Failed to initialize LocobotLoader: Failed to setup ROS 2 bridge transforms [locobot_loader.py-3] Starting kit application with the following args: ['/home/ubuntu/anaconda3/envs/env_isaacsim/lib/python3.10/site-packages/isaacsim/exts/omni.isaac.kit/omni/isaac/kit/simulation_app.py', '/home/ubuntu/anaconda3/envs/env_isaacsim/lib/python3.10/site-packages/isaacsim/apps/omni.isaac.sim.python.kit', '--/app/tokens/exe-path=/home/ubuntu/.local/share/ov/pkg/isaac-sim-4.2.0/kit', '--/persistent/app/viewport/displayOptions=3094', '--/rtx/materialDb/syncLoads=True', '--/rtx/hydra/materialSyncLoads=True', '--/omni.kit.plugin/syncUsdLoads=True', '--/app/renderer/resolution/width=1024', '--/app/renderer/resolution/height=768', '--/app/window/width=1440', '--/app/window/height=900', '--/renderer/multiGpu/enabled=True', '--/app/fastShutdown=True', '--ext-folder', '/home/ubuntu/.local/share/ov/pkg/isaac-sim-4.2.0/exts', '--ext-folder', '/home/ubuntu/.local/share/ov/pkg/isaac-sim-4.2.0/apps', '--/physics/cudaDevice=0', '--portable'] [locobot_loader.py-3] Passing the following args to the base kit application: ['--ros-args', '-r', '__node:=locobot_loader'] [locobot_loader.py-3] Warp 1.2.1 initialized: [locobot_loader.py-3] CUDA Toolkit 11.8, Driver 12.7 [locobot_loader.py-3] Devices: [locobot_loader.py-3] "cpu" : "x86_64" [locobot_loader.py-3] "cuda:0" : "NVIDIA RTX 3500 Ada Generation Laptop GPU" (12 GiB, sm_89, mempool enabled) [locobot_loader.py-3] Kernel cache: [locobot_loader.py-3] /home/ubuntu/.cache/warp/1.2.1 [locobot_loader.py-3] Traceback (most recent call last): [locobot_loader.py-3] File "/home/ubuntu/stable_ws/install/stable_trail_robotic/lib/stable_trail_robotic/locobot_loader.py", line 260, in main [locobot_loader.py-3] loader = LocobotLoader() [locobot_loader.py-3] File "/home/ubuntu/stable_ws/install/stable_trail_robotic/lib/stable_trail_robotic/locobot_loader.py", line 89, in __init__ [locobot_loader.py-3] raise RuntimeError("Failed to setup ROS 2 bridge transforms") [locobot_loader.py-3] RuntimeError: Failed to setup ROS 2 bridge transforms [locobot_loader.py-3] [locobot_loader.py-3] During handling of the above exception, another exception occurred: [locobot_loader.py-3] [locobot_loader.py-3] Traceback (most recent call last): [locobot_loader.py-3] File "/home/ubuntu/stable_ws/install/stable_trail_robotic/lib/stable_trail_robotic/locobot_loader.py", line 271, in main [locobot_loader.py-3] if loader: [locobot_loader.py-3] UnboundLocalError: local variable 'loader' referenced before assignment [locobot_loader.py-3] [locobot_loader.py-3] During handling of the above exception, another exception occurred: [locobot_loader.py-3] [locobot_loader.py-3] Traceback (most recent call last): [locobot_loader.py-3] File "/home/ubuntu/stable_ws/install/stable_trail_robotic/lib/stable_trail_robotic/locobot_loader.py", line 281, in <module> [locobot_loader.py-3] main() [locobot_loader.py-3] File "/home/ubuntu/stable_ws/install/stable_trail_robotic/lib/stable_trail_robotic/locobot_loader.py", line 274, in main [locobot_loader.py-3] if loader: [locobot_loader.py-3] UnboundLocalError: local variable 'loader' referenced before assignment [locobot_loader.py-3] Fatal Python error: Segmentation fault [locobot_loader.py-3] [locobot_loader.py-3] Current thread 0x000071c292bea740 (most recent call first): [locobot_loader.py-3] <no Python frame> [locobot_loader.py-3] [locobot_loader.py-3] Extension modules: omni.mdl.pymdlsdk._pymdlsdk, psutil._psutil_linux, psutil._psutil_posix, numpy.core._multiarray_umath, numpy.core._multiarray_tests, numpy.linalg._umath_linalg, numpy.fft._pocketfft_internal, numpy.random._common, numpy.random.bit_generator, numpy.random._bounded_integers, numpy.random._mt19937, numpy.random.mtrand, numpy.random._philox, numpy.random._pcg64, numpy.random._sfc64, numpy.random._generator, torch._C, torch._C._fft, torch._C._linalg, torch._C._nested, torch._C._nn, torch._C._sparse, torch._C._special, scipy._lib._ccallback_c, scipy.sparse._sparsetools, _csparsetools, scipy.sparse._csparsetools, scipy.sparse.linalg._isolve._iterative, scipy.linalg._fblas, scipy.linalg._flapack, scipy.linalg._cythonized_array_utils, scipy.linalg._flinalg, scipy.linalg._solve_toeplitz, scipy.linalg._matfuncs_sqrtm_triu, scipy.linalg.cython_lapack, scipy.linalg.cython_blas, scipy.linalg._matfuncs_expm, scipy.linalg._decomp_update, scipy.sparse.linalg._dsolve._superlu, scipy.sparse.linalg._eigen.arpack._arpack, scipy.sparse.csgraph._tools, scipy.sparse.csgraph._shortest_path, scipy.sparse.csgraph._traversal, scipy.sparse.csgraph._min_spanning_tree, scipy.sparse.csgraph._flow, scipy.sparse.csgraph._matching, scipy.sparse.csgraph._reordering, scipy.spatial._ckdtree, scipy._lib.messagestream, scipy.spatial._qhull, scipy.spatial._voronoi, scipy.spatial._distance_wrap, scipy.spatial._hausdorff, scipy.special._ufuncs_cxx, scipy.special._ufuncs, scipy.special._specfun, scipy.special._comb, scipy.special._ellip_harm_2, scipy.spatial.transform._rotation, PIL._imaging, PIL._imagingft, scipy.ndimage._nd_image, _ni_label, scipy.ndimage._ni_label, osqp._osqp, multidict._multidict, yarl._quoting_c, aiohttp._helpers, aiohttp._http_writer, aiohttp._http_parser, aiohttp._websocket, _cffi_backend, frozenlist._frozenlist, scipy.io.matlab._mio_utils, scipy.io.matlab._streams, scipy.io.matlab._mio5_utils, yaml._yaml, cv2, rcl_interfaces.rcl_interfaces_s__rosidl_typesupport_c, builtin_interfaces.builtin_interfaces_s__rosidl_typesupport_c (total: 80) [ERROR] [locobot_loader.py-3]: process has died [pid 87398, exit code -11, cmd '/home/ubuntu/stable_ws/install/stable_trail_robotic/lib/stable_trail_robotic/locobot_loader.py --ros-args

Additional Information

What I’ve Tried

This is the code I implemented for that purpose

#!/usr/bin/env python3

import time
import logging
from typing import Optional, Dict, List, Tuple

import omni.graph.core as og
from omni.isaac.core.utils.extensions import enable_extension
from omni.isaac.core.utils import prims
import carb

class ROS2BridgeHandler:
    """Manages ROS 2 bridge functionality and graph nodes for Isaac Sim ROS 2 integration"""
    
    def __init__(self, domain_id: int = 30):
        """Initialize ROS 2 bridge handler
        
        Args:
            domain_id (int): ROS_DOMAIN_ID for communication
        """
        # Setup logging
        self.logger = logging.getLogger("ROS2BridgeHandler")
        self.logger.setLevel(logging.INFO)
        
        # Store configuration
        self._domain_id = domain_id
        self._graph = None
        
        # Initialize bridge extension
        try:
            enable_extension("omni.isaac.ros2_bridge")
            time.sleep(1.0)  # Give time for extension to load
            self.logger.info("ROS 2 bridge extension enabled successfully")
        except Exception as e:
            self.logger.error(f"Failed to enable ROS 2 bridge extension: {str(e)}")
            raise

    def _validate_prim(self, prim_path: str) -> bool:
        """Validate if a prim path exists and is valid
        
        Args:
            prim_path (str): Path to validate
            
        Returns:
            bool: True if valid, False otherwise
        """
        try:
            if not prims.is_prim_path_valid(prim_path):
                self.logger.error(f"Invalid prim path: {prim_path}")
                return False
            self.logger.info(f"Validated prim path: {prim_path}")
            return True
        except Exception as e:
            self.logger.error(f"Error validating prim path: {str(e)}")
            return False

    def _create_graph_nodes(self, graph_path: str) -> Tuple[List, List]:
        """Create graph nodes and connections for ROS 2 bridge
        
        Args:
            graph_path (str): Path for the graph
            
        Returns:
            Tuple[List, List]: Nodes and connections lists
        """
        # Base nodes required for all setups
        nodes = [
            ("OnTick", "omni.graph.action.OnPlaybackTick"),
            ("ReadSimTime", "omni.isaac.core_nodes.IsaacReadSimulationTime"),
            ("Context", "omni.isaac.ros2_bridge.ROS2Context"),
            ("PublishTF", "omni.isaac.ros2_bridge.ROS2PublishTransformTree"),
            ("PublishClock", "omni.isaac.ros2_bridge.ROS2PublishClock"),
            ("QoSConfig", "omni.isaac.ros2_bridge.ROS2QoSProfile")
        ]
        
        # Create base connections
        connections = [
            ("OnTick.outputs:tick", "ReadSimTime.inputs:execIn"),
            ("OnTick.outputs:tick", "PublishTF.inputs:execIn"),
            ("OnTick.outputs:tick", "PublishClock.inputs:execIn"),
            ("ReadSimTime.outputs:simulationTime", "PublishTF.inputs:timeStamp"),
            ("ReadSimTime.outputs:simulationTime", "PublishClock.inputs:timeStamp"),
            ("Context.outputs:context", "PublishTF.inputs:context"),
            ("Context.outputs:context", "PublishClock.inputs:context"),
            ("QoSConfig.outputs:qosProfile", "PublishTF.inputs:qosProfile"),
            ("QoSConfig.outputs:qosProfile", "PublishClock.inputs:qosProfile")
        ]
        
        return nodes, connections

    def setup_transforms(self, prim_path: str, enable_joint_states: bool = True, parent_frame: str = "world") -> bool:
        """Setup ROS 2 transformations and joint states publishing"""
        try:
            # Validate inputs
            if not self._validate_prim(prim_path):
                return False

            
            graph_path = "/ActionGraph"  

            try:
                keys = og.Controller.Keys
                # Create graph with exact pattern from documentation
                self._graph = og.Controller.edit(
                    {
                        "graph_path": graph_path,
                        "evaluator_name": "execution"
                    },
                    {
                        keys.CREATE_NODES: [
                            ("OnPlaybackTick", "omni.graph.action.OnPlaybackTick"),
                            ("ReadSimTime", "omni.isaac.core_nodes.IsaacReadSimulationTime"),
                            ("Context", "omni.isaac.ros2_bridge.ROS2Context"),
                            ("PublishTF", "omni.isaac.ros2_bridge.ROS2PublishTransformTree"),
                            ("PublishClock", "omni.isaac.ros2_bridge.ROS2PublishClock")
                        ],
                        keys.CONNECT: [
                            ("OnPlaybackTick.outputs:tick", "ReadSimTime.inputs:execIn"),
                            ("OnPlaybackTick.outputs:tick", "PublishTF.inputs:execIn"),
                            ("OnPlaybackTick.outputs:tick", "PublishClock.inputs:execIn"),
                            ("ReadSimTime.outputs:simulationTime", "PublishTF.inputs:timeStamp"),
                            ("ReadSimTime.outputs:simulationTime", "PublishClock.inputs:timeStamp"),
                            ("Context.outputs:context", "PublishTF.inputs:context"),
                            ("Context.outputs:context", "PublishClock.inputs:context")
                        ],
                        keys.SET_VALUES: [
                            ("Context.inputs:domain_id", self._domain_id),
                            ("PublishTF.inputs:topicName", "/tf"),
                            ("PublishTF.inputs:targetPrims", [prim_path]),
                            ("PublishTF.inputs:parentFrameId", parent_frame),
                            ("PublishClock.inputs:topicName", "/clock")
                        ]
                    }
                )

                time.sleep(1.0)  

                
                if enable_joint_states:
                    og.Controller.edit(
                        self._graph,
                        {
                            keys.CREATE_NODES: [
                                ("PublishJointState", "omni.isaac.ros2_bridge.ROS2PublishJointState")
                            ],
                            keys.CONNECT: [
                                ("OnPlaybackTick.outputs:tick", "PublishJointState.inputs:execIn"),
                                ("ReadSimTime.outputs:simulationTime", "PublishJointState.inputs:timeStamp"),
                                ("Context.outputs:context", "PublishJointState.inputs:context")
                            ],
                            keys.SET_VALUES: [
                                ("PublishJointState.inputs:topicName", "/joint_states"),
                                ("PublishJointState.inputs:targetPrim", prim_path)
                            ]
                        }
                    )

                # Evaluate graph
                og.Controller.evaluate_sync(self._graph)
                time.sleep(0.5)  # Give time for evaluation

                self.logger.info("Successfully setup ROS 2 transforms")
                return True

            except Exception as e:
                self.logger.error(f"Failed to create action graph: {str(e)}")
                return False

        except Exception as e:
            self.logger.error(f"Failed to setup transforms: {str(e)}")
            return False
            
    def cleanup(self):
        """Cleanup graph resources"""
        try:
            if self._graph is not None:
                og.Controller.clear(self._graph)
                self._graph = None
                self.logger.info("ROS 2 bridge cleanup successful")
        except Exception as e:
            self.logger.error(f"Error during cleanup: {str(e)}")

After looking at the Action Graph in ROS2 Transform Trees and Odometry, I discovered that I was trying to connect two blocks (On Play Tick and Read Simulation Time) that shouldn’t be connected together

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.