Retrieving Simulation Properties in a Custom User Extension for Isaac Sim

Hello Developers!

I’m trying to create a custom user extension in Isaac Sim 4.2 to log some simulation properties while the simulation is running. I’m really new to these topics and am trying to learn how to create an user extension. I was able to create the .csv files at runtime, but I’m struggling to get the current simulation properties, such as:

  • get_physical_dt()
  • current_time()
  • World objects’ aliases and poses

My goal is to create an isaac_logger extension to log the simulation time, simulation time step, render FPS, and total frames as shown below:

import os
import csv
import carb
import time
import omni.ext
import omni.physx
import omni.timeline
from datetime import datetime
from omni.isaac.core import SimulationContext

class IsaacLogger():
    def __init__(self):
        root_folder = "/..../..../.."
        self.sep = ';'
        logs_dir = os.path.join(root_folder, "isaac")
        os.makedirs(logs_dir, exist_ok=True)
        self.log_file = os.path.join(logs_dir, file_name)

        self.simulation_context = SimulationContext.instance()

        # Variables to control
        self.frame_count = 0
        self.total_frames = 0
        self.last_time = time.time()
        self.initial_time = time.time()
        self.fps = 0.0

        # CSV file
        self.csv_file = open(self.log_file, 'w', newline='')
        self.csv_writer = csv.writer(self.csv_file, delimiter=self.sep)

        # Headers
        self.csv_writer.writerow([
            "Timestamp (%Y-%m-%d_%H-%M-%S)",
            "Frame",
            "IsaacSim - Step Size (ms)",
            "IsaacSim - Simulation Time (ms)",
            "IsaacSim - Real Time (ms)",
            "OS - System Time (ms)",
            "IsaacSim - RTF",
            "OS - RTF",
            "IsaacSim - Render FPS (Hz)",
            "OS - Plugin FPS (Hz)",
            "Active Objects"
        ])

    def collect_metrics(self, event):
        # Get simulation step size (in ms)
        if self.simulation_context is not None:
            step_size = self.simulation_context.get_physics_dt() * 1000  # Convert to milliseconds
        else:
            step_size = 0

class IsaacLoggerExtension(omni.ext.IExt):

    def on_startup(self, ext_id):
        print("[isaac.logger] Isaac Logger startup")

        # Get the timeline interface
        self.timeline = omni.timeline.get_timeline_interface()
        if not self.timeline:
            carb.log_error("Timeline interface not found")
            return

        # Initialize the logger
        self.logger = None

        # Register the update callback to check the simulation time continuously
        self._update_sub = omni.kit.app.get_app().get_update_event_stream().create_subscription_to_pop(self._on_update)

    def _on_update(self, event):
        # Check if the simulation is playing
        if self.timeline.is_playing() and self.logger is None:
            self.logger = IsaacLogger()

        elif self.timeline.is_playing() and self.logger is not None:
            self.logger.collect_metrics(event)

        elif not self.timeline.is_playing() and self.logger is not None:
            self.logger.stop_logging()
            self.logger = None

    def on_shutdown(self):
        if self.logger is not None:
            self.logger.stop_logging()
            self.logger = None

        print("[isaac.logger] Isaac Logger shutdown")

However, I’m encountering an issue where physics_context is None, and I’m unable to retrieve all the properties I need. Could someone help me properly structure this code?

Thank you!