Priceless

[Isaac Sim] OmniGibson에서 ROS2 Bridge 설정, ROS2 camera topic , TF topic추가 본문

Robotics/NVIDIA

[Isaac Sim] OmniGibson에서 ROS2 Bridge 설정, ROS2 camera topic , TF topic추가

Hyun__ 2025. 2. 7. 13:45

OmniGibson에서 ROS2를 사용하기

OmniGibson에서 rosbag 등을 사용하여 ROS를 통해 카메라 데이터를 받아오려고 한다

그렇게 하기 위해 Isaac Sim과 ROS2 를 연결할 Bridge를 설정해야 한다

 

유의 사항

OmniGibson에서 사용하는 Isaac Sim은

엔비디아에서 계속 지원해주는 버전이 아닌

4.1.0 버전을 포팅해서 사용한다

그러므로 공식적으로 ROS와 Isaac Sim의 모든 기능을 지원하지 않기 때문에

OmniGibson의 구성 환경에 맞게 실행해야 한다

 

환경

Ubuntu 22.04

ROS2 Humble

RTX 4070

OmniGibson 1.1.1

 

ROS2 설치 및 설정

아래 사이트를 참고한다

https://docs.omniverse.nvidia.com/isaacsim/latest/installation/install_ros.html#ros-and-ros-2-installation

 

ROS2 설치

ROS2가 설치되어 있지 않다면

ROS2를 본인 환경에 맞게 설치한다

외부 자료 참고 권장

 

(필요한 경우 아래도 설치)

카메라 토픽을 추가해야 하니

아래도 설치해본다

sudo apt install ros-humble-vision-msgs
sudo apt install ros-humble-ackermann-msgs

 

Isaac Sim에 ROS Bridge 설정

Isaac Sim ROS 2 Workspace를 사용하지 않는다면 

~/.ros/fastdds.xml 경로에 아래와 같은 파일을 추가한다

<?xml version="1.0" encoding="UTF-8" ?>

<license>Copyright (c) 2022-2024, NVIDIA CORPORATION.  All rights reserved.
NVIDIA CORPORATION and its licensors retain all intellectual property
and proprietary rights in and to this software, related documentation
and any modifications thereto.  Any use, reproduction, disclosure or
distribution of this software and related documentation without an express
license agreement from NVIDIA CORPORATION is strictly prohibited.</license>


<profiles xmlns="http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles" >
    <transport_descriptors>
        <transport_descriptor>
            <transport_id>UdpTransport</transport_id>
            <type>UDPv4</type>
        </transport_descriptor>
    </transport_descriptors>

    <participant profile_name="udp_transport_profile" is_default_profile="true">
        <rtps>
            <userTransports>
                <transport_id>UdpTransport</transport_id>
            </userTransports>
            <useBuiltinTransports>false</useBuiltinTransports>
        </rtps>
    </participant>
</profiles>

 

 

 

Cyclone DDS를 사용하여 ROS2 Bridge 실행 (필요 없음)

ROS2는 Fast DDS를 지원하지만 Isaac Sim은 Linux 환경에서 Cyclone DDS 도 지원한다

ROS2에서 Cyclone DDS를 사용하기 위해 세팅한다

https://docs.ros.org/en/humble/Installation/DDS-Implementations/Working-with-Eclipse-CycloneDDS.html

 

Eclipse Cyclone DDS — ROS 2 Documentation: Humble documentation

You're reading the documentation for an older, but still supported, version of ROS 2. For information on the latest version, please have a look at Jazzy. Eclipse Cyclone DDS Eclipse Cyclone DDS is a very performant and robust open-source DDS implementation

docs.ros.org

 

# install packages
sudo apt install ros-humble-rmw-cyclonedds-cpp

# switch to rmw_cyclonedds
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp

 

 

Workspace 설정

여기서부터는 OmniGibson에서 수행할 것이기 때문에

공식 docs에서 조금 수정한다

 

# For rosdep install command
sudo apt install python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool build-essential
# For colcon build command
sudo apt install python3-colcon-common-extensions

 

 

Omnigibson 디렉토리를 빌드한다

cd ~/Omnigibson
catkin build
source install/local_setup.bash

 

 

 

build 과정에서 패키지 버전 관련 오류가 종종 나타났는데

아래와 같이 해결 한다

pip install --upgrade <package name and version>

 

이후 build하면 정상적으로 빌드가 성공되고 

build, install, log 디렉토리가 생성된다

 

이렇게 마무리 할 수 있다

 

ROS2 Bridge 설정

이제 다시 OmniGibson example을 실행하여

$ python -m omnigibson.examples.robots.robot_control_example --quickstartxamples.robots.robot_control_exampl

windows > extensions 창에서 ROS2 BRIDGE를 실행한다

 

 

버튼을 누르면 터미널에서 아래와 같은 에러 문구가 뜰 수 있는데

본인은 아래와 같이 gcc를 새로 설치하여 해결했다

[48.680s] [ext: omni.isaac.ros2_bridge-2.31.4] startup
[48.707s] Attempting to load system rclpy
[48.719s] Could not import system rclpy: /home/user/anaconda3/envs/omnigibson/bin/../lib/libstdc++.so.6: version `GLIBCXX_3.4.30' not found (required by /opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/_rclpy_pybind11.cpython-310-x86_64-linux-gnu.so)
The C extension '/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/_rclpy_pybind11.cpython-310-x86_64-linux-gnu.so' failed to be imported while being present on the system. Please refer to 'https://docs.ros.org/en/{distro}/Guides/Installation-Troubleshooting.html#import-failing-even-with-library-present-on-the-system' for possible solutions
[48.719s] Attempting to load internal rclpy
[48.722s] Could not import internal rclpy: /home/user/anaconda3/envs/omnigibson/bin/../lib/libstdc++.so.6: version `GLIBCXX_3.4.30' not found (required by /opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/_rclpy_pybind11.cpython-310-x86_64-linux-gnu.so)
The C extension '/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/_rclpy_pybind11.cpython-310-x86_64-linux-gnu.so' failed to be imported while being present on the system. Please refer to 'https://docs.ros.org/en/{distro}/Guides/Installation-Troubleshooting.html#import-failing-even-with-library-present-on-the-system' for possible solutions
[48.722s] To use the internal libraries included with the extension please set the following environment variables to use with FastDDS (default) or CycloneDDS (ROS2 Humble only): 
RMW_IMPLEMENTATION=rmw_fastrtps_cpp
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/humble/lib

OR 

RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/humble/lib
Before starting Isaac Sim


>>>
conda install -c conda-forge gcc=12.1.0

 

다시 OmniGibson 환경을 실행한 후

ROS2 bridge를 작동한다

 

다행히 rclpy 다음 오류 없이 진행되었다

Running demo.
Press ESC to quit
Pressed None. Action: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0]
Pressed None. Action: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0]
[39.920s] [ext: omni.isaac.ros2_bridge-2.31.4] startup
[39.942s] Attempting to load system rclpy
Pressed None. Action: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0]

 

이 과정을 매번 하기 귀찮다면 

OmniGibson/omnigibson/omnigibson_4_1_0.kit 파일에

아래를 추가하면 자동으로 ROS2 Bridge가 켜진 상태로 실행된다

"omni.isaac.ros2_bridge" = {} # it works

 

 

ROS2 Camera Topic Tutorial

아래의 튜토리얼을 수행하여 정상적으로 작동하는지 확인한다

https://docs.omniverse.nvidia.com/isaacsim/latest/ros2_tutorials/index.html

 

windows > visual scripting > action graph 에서

공식 문서의 action graph는 아래와 같다

 

하지만 정상적으로 실행되지 않았고

디버깅한 결과 Isaac Sim One Simulation Frame 노드에서

다음 모듈로 데이터가 넘어가지 않았다

 

Isaac Sim One Simulation Frame 노드를 제거하여 그래프를 구성하고

Isaac Create Render Product 노드에서 로봇의 camera prim을 설정하면

 

ros2에서 /rgb 토픽으로 정상적으로 발행된다

 

depth와 segmentation 도 출력해본다

아래와 같이 action graph를 구성한다

 

depth 이미지도 잘 출력된다

semantic segmentation 이미지는

ROS2 에서 image를 출력하는 데이터 타입을 맞춰야 되는 듯 하다

 

ROS2 Bridge를 통해 Segmentation 이미지 출력하기

Semantic Segmentation 노드까지 설정한 후

발행되는 토픽을 확인하면

image와 label에 대한 정보가 따로 발행되는 것을 확인할 수 있다

$ ros2 topic echo /segment --once header: stamp: sec: 700 nanosec: 8369841 frame_id: sim_camera height: 720 width: 1280 encoding: 32SC1 is_bigendian: 0 step: 5120 
data: - 13 - 0 - 0 -  '...' 
(label class가 출력됨)

$ ros2 topic echo /semantic_labels --once 
data: '{"0":{"class":"BACKGROUND"},"1":{"class":"UNLABELLED"},"10":{"class":"carpet"},"13":{"class":"ottoman"},"14":{"class":"coffee_ta...'
(class에 대한 정보가 출력됨)

 

 

데이터 타입이 rgb와 같은 이미지가 아니기 때문에 

ROS2에서 바로 image로 출력할 수 없다

 

Viewport에서 제공하는 segmentation 이미지는

자체적으로 색을 입히는 듯하다

매번 실행할 때마다 label의 색이 조금씩 달라진다

 

 

그렇기에 간단하게 segmentation 이미지를 출력할 수 있는 파이썬 코드를 작성한다

 

아래 코드는 발행된 토픽을 받아서 컬러링한 후 

다시 발행하므로 실시간으로 데이터를 사용하기에는 무리가 있다

테스트 및 시각화용이다

 

아래 파이썬 파일을 실행하면 rqt에서도 확인할 수 있다

import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image
from std_msgs.msg import String
import numpy as np
import json
import cv2  # OpenCV 추가

class SemanticLabelMapper(Node):
    def __init__(self):
        super().__init__('semantic_label_mapper')

        # 구독할 토픽
        self.segment_sub = self.create_subscription(
            Image, '/semantic_segmentation', self.segment_callback, 10)
        self.labels_sub = self.create_subscription(
            String, '/semantic_labels', self.labels_callback, 10)

        # 변환된 Semantic Label Map을 발행할 토픽
        self.label_map_pub = self.create_publisher(Image, '/semantic_label_map', 10)
        self.colored_map_pub = self.create_publisher(Image, '/colored_semantic_map', 10)  # 컬러 맵을 위한 퍼블리셔 추가

        # Label ID -> Class Name 매핑을 저장할 변수
        self.label_dict = {}
        # 색상 맵 생성
        self.color_map = self.generate_color_map()

    def generate_color_map(self, num_classes=256):
        """고유한 RGB 색상을 생성하는 함수"""
        color_map = np.zeros((num_classes, 3), dtype=np.uint8)
        for i in range(num_classes):
            color_map[i] = [
                (i * 50) % 255,  # R
                (i * 70) % 255,  # G
                (i * 90) % 255   # B
            ]
        # 배경(0)은 검정색으로 설정
        color_map[0] = [0, 0, 0]
        return color_map

    def labels_callback(self, msg):
        """'/semantic_labels' 토픽을 받아서 JSON 형식으로 변환"""
        try:
            self.label_dict = json.loads(msg.data)
            self.get_logger().info("Received label mapping data")
        except json.JSONDecodeError as e:
            self.get_logger().error(f"Failed to decode label data: {e}")

    def segment_callback(self, msg):
        """'/segment' 이미지 데이터를 변환하여 '/semantic_label_map'으로 발행"""
        if not self.label_dict:
            self.get_logger().warn("Label dictionary is empty. Waiting for semantic_labels topic.")
            return

        # 이미지 데이터를 NumPy 배열로 변환
        segment_array = np.frombuffer(msg.data, dtype=np.int32).reshape((msg.height, msg.width))

        # Label ID -> Class Name 변환
        label_map = np.zeros(segment_array.shape, dtype=np.uint8)
        for label_id, info in self.label_dict.items():
            try:
                label_id_int = int(label_id)
                label_map[segment_array == label_id_int] = label_id_int
            except ValueError:
                continue

        # 컬러 맵 생성
        colored_map = np.zeros((msg.height, msg.width, 3), dtype=np.uint8)
        for i in range(len(self.color_map)):
            mask = (label_map == i)
            colored_map[mask] = self.color_map[i]

        # 원본 라벨 맵 발행
        output_msg = Image()
        output_msg.header = msg.header
        output_msg.height = msg.height
        output_msg.width = msg.width
        output_msg.encoding = "mono8"
        output_msg.is_bigendian = False
        output_msg.step = msg.width
        output_msg.data = label_map.tobytes()
        self.label_map_pub.publish(output_msg)

        # 컬러 맵 발행
        color_msg = Image()
        color_msg.header = msg.header
        color_msg.height = msg.height
        color_msg.width = msg.width
        color_msg.encoding = "rgb8"  # RGB 형식으로 변경
        color_msg.is_bigendian = False
        color_msg.step = msg.width * 3  # RGB는 픽셀당 3바이트
        color_msg.data = colored_map.tobytes()
        self.colored_map_pub.publish(color_msg)

        self.get_logger().info("Published semantic label map and colored map")

def main(args=None):
    rclpy.init(args=args)
    node = SemanticLabelMapper()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

 

 

실시간으로 사용하려면 어떻게 해야 할까?

 



 

 

'Robotics > NVIDIA' 카테고리의 다른 글

[NVIDIA Omniverse] OmniGibson 설치  (0) 2025.01.16