CameraInfo ROS2 Topic to match real camera data

Isaac Sim Version

5.0.0

Operating System

Ubuntu 22.04

GPU Information

  • Model: 575.64.03
  • Driver Version: NVIDIA RTX 4000 SFF Ada

ROS2 Camera Info to match real camera Info data

Is there a way to make sure IsaacSim is publishing CameraInfo wrt to ROS2 standards, I am simulating D415 Realsense camera, which has these parameters:

 header: 
  seq: 0
  stamp: 
    secs: 1755680535
    nsecs: 615915298
  frame_id: "camera_color_optical_frame"
height: 480
width: 640
distortion_model: "plumb_bob"
D: [0.0, 0.0, 0.0, 0.0, 0.0]
K: [607.1807250976562, 0.0, 322.3876647949219, 0.0, 605.66943359375, 244.37399291992188, 0.0, 0.0, 1.0]
R: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]
P: [607.1807250976562, 0.0, 322.3876647949219, 0.0, 0.0, 605.66943359375, 244.37399291992188, 0.0, 0.0, 0.0, 1.0, 0.0]
binning_x: 0
binning_y: 0
roi: 
  x_offset: 0
  y_offset: 0
  height: 0
  width: 0
  do_rectify: False

but I am getting different values (fx, fy x10, and zero offsets) from IsaacSim:

header:
  stamp:
    sec: 4189
    nanosec: 750218512
  frame_id: camera_color_optical_frame
height: 480
width: 640
distortion_model: plumb_bob
d:
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
k:
- 6980.143878574075
- 0.0
- 320.0
- 0.0
- 6737.777930051699
- 240.0
- 0.0
- 0.0
- 1.0
r:
- 1.0
- 0.0
- 0.0
- 0.0
- 1.0
- 0.0
- 0.0
- 0.0
- 1.0
p:
- 6980.143878574075
- 0.0
- 320.0
- 0.0
- 0.0
- 6737.777930051699
- 240.0
- 0.0
- 0.0
- 0.0
- 1.0
- 0.0
binning_x: 0
binning_y: 0
roi:
  x_offset: 0
  y_offset: 0
  height: 0
  width: 0
  do_rectify: false

Steps to Reproduce

  1. Create a Camera with a ROS2 Node Graph

  2. Publish Camera Info.

Hi @belal.hmedan! You will need to do the conversions yourself and set corresponding camera parameters. Maybe you can refer to the example Camera Sensors — Isaac Sim Documentation

@zhengwang thanks for your kind hint, indeed that solves setting camera intrinsics, but it introduces another problem: expanded resolution:

header:
  stamp:
    sec: 0
    nanosec: 583333363
  frame_id: camera_color_optical_frame
height: 1024
width: 2048
distortion_model: plumb_bob
d:
- 0.0
- 0.0
- 0.0
- 0.0
- 0.0
k:
- 607.1807250976562
- 0.0
- 322.3876647949219
- 0.0
- 605.66943359375
- 244.37399291992188
- 0.0
- 0.0
- 1.0
r:
- 1.0
- 0.0
- 0.0
- 0.0
- 1.0
- 0.0
- 0.0
- 0.0
- 1.0
p:
- 607.1807250976562
- 0.0
- 322.3876647949219
- 0.0
- 0.0
- 605.66943359375
- 244.37399291992188
- 0.0
- 0.0
- 0.0
- 1.0
- 0.0
binning_x: 0
binning_y: 0
roi:
  x_offset: 0
  y_offset: 0
  height: 0
  width: 0
  do_rectify: false

This is the code I use:

class RealSense(Camera):
    def __init__(
        self,
        cam_prim_path: str,
        cam_link_name: str,
        height: int,
        width: int,
        cam_info_topic: str,
        cam_color_topic: str,
        cam_depth_topic: str,
        cam_pointcloud_topic: str,
        camera_matrix=None,
        distortion_coefficients=None,
        pixel_size: float = 3.0,
        f_stop: float = 1.8,
        focus_distance: float = 1.5,
        *args,
        **kwargs,
    ) -> None:
        self._cam_prim_path = cam_prim_path
        self._cam_link_name = cam_link_name
        self._height = height
        self._width = width
        self._cam_info_topic = cam_info_topic
        self._cam_color_topic = cam_color_topic
        self._cam_depth_topic = cam_depth_topic
        self._cam_pointcloud_topic = cam_pointcloud_topic

        # Defaults if not passed
        self._camera_matrix = camera_matrix or [
            [607.1807250976562, 0.0, 322.3876647949219],
            [0.0, 605.66943359375, 244.37399291992188],
            [0.0, 0.0, 1.0],
        ]
        self._distortion_coefficients = distortion_coefficients or [0.0, 0.0, 0.0, 0.0]
        self._pixel_size = pixel_size
        self._f_stop = f_stop
        self._focus_distance = focus_distance

        # Init parent Camera
        super().__init__(
            prim_path=self._cam_prim_path,
            frequency=30,
            resolution=(height, width),
            *args,
            **kwargs,
        )
        self.initialize()
        # Apply intrinsics + optical config
        self._configure_intrinsics()

        # Build OmniGraph for streaming topics
        self._create_realsense_omnigraph()

    def _create_realsense_omnigraph(self):
        config_dir = os.path.dirname(graph_config.__file__)
        graph_name = "realsense_camera_graph.yml"
        config_path_ = os.path.join(config_dir, graph_name)
        neura_graph = Graph(
            config_path=config_path_,
            prim_path=self._cam_prim_path,
            height=self._height,
            width=self._width,
            cam_info_topic=self._cam_info_topic,
            cam_color_topic=self._cam_color_topic,
            cam_depth_topic=self._cam_depth_topic,
            cam_pointcloud_topic=self._cam_pointcloud_topic,
        )
        og_key_create_node, og_keys_connect, og_keys_set_values = neura_graph.get_graph()
        keys = og.Controller.Keys
        graph_path = f"{self._cam_prim_path}/CameraActionGraph"
        (graph_handle, _, _, _) = og.Controller.edit(
            {
                "graph_path": graph_path,
                "evaluator_name": "execution",
                "pipeline_stage": og.GraphPipelineStage.GRAPH_PIPELINE_STAGE_SIMULATION,
            },
            {
                keys.CREATE_NODES: og_key_create_node,
                keys.SET_VALUES: og_keys_set_values,
                keys.CONNECT: og_keys_connect,
            },
        )
        return graph_handle

    def _configure_intrinsics(self):
        """
        Configure the camera intrinsics and optical properties from
        camera matrix and distortion coefficients.
        """
        ((fx, _, cx), (_, fy, cy), (_, _, _)) = self._camera_matrix

        # apertures in meters (pixel_size is microns -> convert to meters)
        horizontal_aperture = self._pixel_size * self._width * 1e-6
        vertical_aperture = self._pixel_size * self._height * 1e-6

        # focal lengths in meters
        focal_length_x = self._pixel_size * fx * 1e-6
        focal_length_y = self._pixel_size * fy * 1e-6
        focal_length = (focal_length_x + focal_length_y) / 2

        # Apply params to Isaac Sim Camera API
        self.set_focal_length(focal_length)
        self.set_focus_distance(self._focus_distance)
        self.set_lens_aperture(self._f_stop)
        self.set_horizontal_aperture(horizontal_aperture)
        self.set_vertical_aperture(vertical_aperture)

        self.set_clipping_range(0.2, 1.0e5)
        self.set_opencv_pinhole_properties(
            cx=cx, 
            cy=cy, 
            fx=fx, 
            fy=fy, 
            pinhole=self._distortion_coefficients
        )

Can you please guide me on how to set resolution to VGA resolution as well, when I pass 640 x 480 I get 2048 x 1024 ! that issue was not there before setting intrinsics.

Could you please check these two values you pass in the RealSense camera?

@zhengwang thanks for your rapid reply, I pass VGA resolution 640x480 which If I comment the intrinsics setting part looks OK, but as soon I set intrinsics I get the resolution expanded!

@belal.hmedan could you please share your script for us to replicate this issue? Not just how you define class RealSense, but also the script of you using this class and publish the data. Thanks!

Hello!

We noticed that this topic hasn’t received any recent responses, so we are closing it for now to help keep the forum organized.

If you’re still experiencing this issue or have additional questions, please feel free to create a new topic with updated details. When doing so, we recommend mentioning or linking to this original topic in your new post—this helps provide context and makes it easier for others to assist you.

Thank you for being part of the NVIDIA Isaac Sim community.

Best regards,
The NVIDIA Isaac Sim Forum Team