NVIDIA Isaac Sim Fundamentals
Learning Objectives
By the end of this chapter, you will be able to:
- Understand the NVIDIA Isaac Sim platform and its capabilities
- Set up and configure Isaac Sim for robotics simulation
- Create and import robot models into Isaac Sim
- Implement physics-based simulation scenarios
- Integrate Isaac Sim with ROS 2 for robot simulation
Introduction to NVIDIA Isaac Sim
NVIDIA Isaac Sim is a high-fidelity simulation environment for robotics built on NVIDIA Omniverse. It provides:
- GPU-accelerated physics simulation with PhysX
- Photorealistic rendering for sensor simulation
- High-fidelity sensor simulation (camera, LiDAR, IMU, etc.)
- Integration with Isaac ROS perception pipelines
- Nav2 navigation stack compatibility
- Support for reinforcement learning workflows
Installing and Setting Up Isaac Sim
Prerequisites
Before installing Isaac Sim, ensure you have:
- NVIDIA RTX GPU with CUDA support
- Ubuntu 20.04 or 22.04 LTS
- NVIDIA GPU drivers (version 510 or later)
- Docker and nvidia-docker2
- ROS 2 Humble Hawksbill
Installation Methods
Isaac Sim can be run in multiple ways:
- Docker Container (Recommended):
# Pull the Isaac Sim Docker image
docker pull nvcr.io/nvidia/isaac-sim:4.0.0
# Run Isaac Sim with proper GPU access
docker run --gpus all -it --rm \
--network=host \
--env "ACCEPT_EULA=Y" \
--env "NVIDIA_VISIBLE_DEVICES=all" \
--env "NVIDIA_DRIVER_CAPABILITIES=all" \
--volume $HOME/isaac-sim-cache:/isaac-sim-cache \
--volume $HOME/isaac-sim-assets:/isaac-sim-assets \
nvcr.io/nvidia/isaac-sim:4.0.0
- Omniverse Launcher:
# Download Omniverse Launcher from NVIDIA Developer site
# Install Isaac Sim extension through the launcher
Basic Configuration
# Example: Basic Isaac Sim setup script
import omni
from pxr import Gf, UsdGeom, Sdf
import carb
# Initialize Isaac Sim
def setup_isaac_sim():
"""Initialize Isaac Sim environment"""
# Set up physics scene
stage = omni.usd.get_context().get_stage()
# Create physics scene
scene_path = Sdf.Path("/World/physicsScene")
UsdGeom.SetDefaultPrim(stage, "physicsScene")
# Configure gravity
from omni.physx.scripts import physicsUtils
physicsUtils.add_physics_scene_to_stage(stage, scene_path)
# Set gravity (standard Earth gravity)
gravity_path = scene_path.AppendChild("gravity")
stage.GetPrimAtPath(scene_path).GetAttribute("physxScene:gravity").Set(
Gf.Vec3f(0.0, 0.0, -9.81)
)
carb.log_info("Isaac Sim environment initialized")
# Call setup function
setup_isaac_sim()
USD and Omniverse Concepts
Universal Scene Description (USD)
Isaac Sim uses USD as its core data model:
# Creating a USD stage
from pxr import Usd, UsdGeom, Gf, Vt
# Create a new stage
stage = Usd.Stage.CreateNew("my_robot.usd")
# Create a prim for the robot
robot_prim = stage.DefinePrim("/World/MyRobot", "Xform")
# Add a chassis to the robot
chassis_prim = stage.DefinePrim("/World/MyRobot/Chassis", "Xform")
chassis_xform = UsdGeom.Xform.Define(stage, "/World/MyRobot/Chassis")
# Set the chassis as a cube
chassis_mesh = UsdGeom.Cube.Define(stage, "/World/MyRobot/Chassis/Mesh")
chassis_mesh.GetSizeAttr().Set(1.0)
# Save the stage
stage.GetRootLayer().Save()
Physics Properties
# Example: Adding physics properties to a rigid body
from omni.physx.scripts import physicsUtils
from pxr import PhysxSchema
def add_rigid_body_properties(prim_path):
"""Add rigid body properties to a USD prim"""
stage = omni.usd.get_context().get_stage()
# Get the prim
prim = stage.GetPrimAtPath(prim_path)
# Add rigid body API
rigid_body_api = PhysxSchema.PhysxRigidBodyAPI.Apply(prim)
# Set mass
mass_api = UsdPhysics.MassAPI.Apply(prim)
mass_api.GetMassAttr().Set(1.0)
# Set center of mass
mass_api.GetCenterOfMassAttr().Set(Gf.Vec3f(0.0, 0.0, 0.0))
# Set moments of inertia
mass_api.GetDiagonalInertiaAttr().Set(Gf.Vec3f(0.1, 0.1, 0.1))
# Apply to a cube
add_rigid_body_properties("/World/MyRobot/Chassis/Mesh")
Creating Robot Models for Isaac Sim
Basic Robot Model
# Creating a differential drive robot in USD
def create_differential_drive_robot(robot_name, position):
"""Create a basic differential drive robot in USD"""
stage = omni.usd.get_context().get_stage()
# Create robot root
robot_path = f"/World/{robot_name}"
robot_prim = stage.DefinePrim(robot_path, "Xform")
# Create chassis
chassis_path = f"{robot_path}/Chassis"
chassis_xform = UsdGeom.Xform.Define(stage, chassis_path)
# Add cube as chassis
chassis_mesh = UsdGeom.Cube.Define(stage, f"{chassis_path}/Mesh")
chassis_mesh.GetSizeAttr().Set(0.5)
# Add materials
material_path = f"{robot_path}/Material"
material = UsdShade.Material.Define(stage, material_path)
# Set position
chassis_xform.AddTranslateOp().Set(Gf.Vec3f(*position))
# Create left wheel
left_wheel_path = f"{robot_path}/LeftWheel"
left_wheel_xform = UsdGeom.Xform.Define(stage, left_wheel_path)
left_wheel_mesh = UsdGeom.Cylinder.Define(stage, f"{left_wheel_path}/Mesh")
left_wheel_mesh.GetRadiusAttr().Set(0.1)
left_wheel_mesh.GetHeightAttr().Set(0.05)
left_wheel_xform.AddTranslateOp().Set(Gf.Vec3f(0, 0.15, -0.05))
# Create right wheel
right_wheel_path = f"{robot_path}/RightWheel"
right_wheel_xform = UsdGeom.Xform.Define(stage, right_wheel_path)
right_wheel_mesh = UsdGeom.Cylinder.Define(stage, f"{right_wheel_path}/Mesh")
right_wheel_mesh.GetRadiusAttr().Set(0.1)
right_wheel_mesh.GetHeightAttr().Set(0.05)
right_wheel_xform.AddTranslateOp().Set(Gf.Vec3f(0, -0.15, -0.05))
# Add rigid body properties to all parts
add_rigid_body_properties(f"{chassis_path}/Mesh")
add_rigid_body_properties(f"{left_wheel_path}/Mesh")
add_rigid_body_properties(f"{right_wheel_path}/Mesh")
return robot_path
# Create a robot
robot_path = create_differential_drive_robot("MyRobot", [0, 0, 0.2])
Adding Joints for Articulated Robots
# Example: Adding a revolute joint for a wheel
def add_revolute_joint(parent_path, child_path, joint_name):
"""Add a revolute joint between two bodies"""
stage = omni.usd.get_context().get_stage()
# Create joint
joint_path = f"{parent_path}/{joint_name}"
joint = PhysxSchema.PhysxRevoluteJoint.Define(stage, joint_path)
# Set joint properties
joint.GetAxisAttr().Set("Z")
joint.GetParentPathAttr().Set(parent_path)
joint.GetChildPathAttr().Set(child_path)
# Set limits
joint.GetLowerLimitAttr().Set(-1000.0)
joint.GetUpperLimitAttr().Set(1000.0)
# Set drive (for motor control)
joint.GetEnableAngularDriveAttr().Set(True)
joint.GetAngularDriveTypeAttr().Set("VELOCITY")
joint.GetAngularDriveTargetVelocityAttr().Set(0.0)
joint.GetAngularDriveDampingAttr().Set(100.0)
joint.GetAngularDriveStiffnessAttr().Set(1000.0)
# Add joints to robot wheels
add_revolute_joint("/World/MyRobot/Chassis/Mesh", "/World/MyRobot/LeftWheel/Mesh", "LeftWheelJoint")
add_revolute_joint("/World/MyRobot/Chassis/Mesh", "/World/MyRobot/RightWheel/Mesh", "RightWheelJoint")
Isaac Sim Extensions and API
Creating Custom Extensions
# Example: Custom extension for robot control
import omni.ext
import omni.kit.ui
from omni.kit.menu.utils import set_menu_enabled
import omni.usd
from pxr import Gf
class RobotControlExtension(omni.ext.IExt):
def on_startup(self, ext_id):
print("[robot_control] Robot Control Extension Startup")
# Create menu entry
self._window = omni.ui.Window("Robot Control", width=300, height=300)
self._build_ui()
def _build_ui(self):
with self._window.frame:
with omni.ui.VStack():
label = omni.ui.Label("Robot Control Panel")
with omni.ui.HStack():
omni.ui.Label("Linear Vel:")
self._linear_vel_field = omni.ui.FloatField()
self._linear_vel_field.model.set_value(0.5)
with omni.ui.HStack():
omni.ui.Label("Angular Vel:")
self._angular_vel_field = omni.ui.FloatField()
self._angular_vel_field.model.set_value(0.0)
self._control_btn = omni.ui.Button("Send Command")
self._control_btn.set_clicked_fn(self._send_command)
def _send_command(self):
linear_vel = self._linear_vel_field.model.get_value_as_float()
angular_vel = self._angular_vel_field.model.get_value_as_float()
# Here you would send the command to your robot
print(f"Sending command: linear={linear_vel}, angular={angular_vel}")
def on_shutdown(self):
print("[robot_control] Robot Control Extension Shutdown")
self._window = None
Sensor Simulation in Isaac Sim
# Example: Adding sensors to a robot
def add_camera_sensor(robot_path, sensor_name, position, orientation):
"""Add a camera sensor to a robot"""
stage = omni.usd.get_context().get_stage()
# Create sensor prim
sensor_path = f"{robot_path}/{sensor_name}"
sensor_xform = UsdGeom.Xform.Define(stage, sensor_path)
sensor_xform.AddTranslateOp().Set(Gf.Vec3f(*position))
sensor_xform.AddRotateXYZOp().Set(Gf.Vec3f(*orientation))
# Add sensor properties
from omni.isaac.sensor.scripts import sensorUtils
camera_sensor = sensorUtils.createRtxLidar(
stage,
sensor_path,
parent=robot_path,
translation=position
)
return sensor_path
def add_lidar_sensor(robot_path, sensor_name, position):
"""Add a LiDAR sensor to a robot"""
stage = omni.usd.get_context().get_stage()
sensor_path = f"{robot_path}/{sensor_name}"
# Create LiDAR sensor
from omni.isaac.range_sensor import _range_sensor
import omni.isaac.core.utils.prims as prim_utils
# Create sensor prim
lidar_prim = prim_utils.create_prim(
prim_path=sensor_path,
prim_type="Lidar",
translation=position,
parent_path=robot_path
)
return sensor_path
Isaac Sim-ROS Bridge
Setting up ROS Bridge
# Example: Configuring ROS bridge for Isaac Sim
def setup_ros_bridge():
"""Set up ROS bridge for Isaac Sim"""
import omni
from omni.isaac.core.utils.extensions import enable_extension
# Enable ROS bridge extension
enable_extension("omni.isaac.ros_bridge")
# Configure ROS topics
import rclpy
rclpy.init()
# Create ROS nodes for robot control
from geometry_msgs.msg import Twist
from nav_msgs.msg import Odometry
import rclpy.node
class IsaacSimRobotController(rclpy.node.Node):
def __init__(self):
super().__init__('isaac_sim_robot_controller')
# Create subscribers for robot commands
self.cmd_vel_sub = self.create_subscription(
Twist,
'/cmd_vel',
self.cmd_vel_callback,
10
)
# Create publishers for robot state
self.odom_pub = self.create_publisher(
Odometry,
'/odom',
10
)
# Timer for publishing state
self.timer = self.create_timer(0.1, self.publish_odom)
# Robot state
self.linear_vel = 0.0
self.angular_vel = 0.0
self.x_pos = 0.0
self.y_pos = 0.0
self.theta = 0.0
def cmd_vel_callback(self, msg):
"""Handle velocity commands"""
self.linear_vel = msg.linear.x
self.angular_vel = msg.angular.z
# Update robot in Isaac Sim
self.update_robot_velocity()
def update_robot_velocity(self):
"""Update robot velocity in Isaac Sim"""
# This would interface with Isaac Sim physics
pass
def publish_odom(self):
"""Publish odometry data"""
odom_msg = Odometry()
odom_msg.header.stamp = self.get_clock().now().to_msg()
odom_msg.header.frame_id = 'odom'
odom_msg.child_frame_id = 'base_link'
# Set position
odom_msg.pose.pose.position.x = self.x_pos
odom_msg.pose.pose.position.y = self.y_pos
# Convert theta to quaternion
from math import sin, cos
odom_msg.pose.pose.orientation.z = sin(self.theta / 2.0)
odom_msg.pose.pose.orientation.w = cos(self.theta / 2.0)
# Set velocities
odom_msg.twist.twist.linear.x = self.linear_vel
odom_msg.twist.twist.angular.z = self.angular_vel
self.odom_pub.publish(odom_msg)
# Start the controller node
controller = IsaacSimRobotController()
rclpy.spin(controller)
# Run ROS bridge setup
# setup_ros_bridge() # Call this in Isaac Sim environment
Best Practices for Isaac Sim
Performance Optimization
- Use Level of Detail (LOD): Reduce geometry complexity at distance
- Limit Physics Steps: Balance accuracy with performance
- Optimize Materials: Use efficient shader networks
- Batch Operations: Combine similar operations when possible
Scene Organization
# Example: Proper scene organization
def organize_scene():
"""Organize Isaac Sim scene properly"""
stage = omni.usd.get_context().get_stage()
# Create organized hierarchy
world_prim = stage.DefinePrim("/World", "Xform")
# Create separate namespaces for different elements
robots_prim = stage.DefinePrim("/World/Robots", "Xform")
environments_prim = stage.DefinePrim("/World/Environments", "Xform")
sensors_prim = stage.DefinePrim("/World/Sensors", "Xform")
lights_prim = stage.DefinePrim("/World/Lights", "Xform")
# Add physics scene
physics_scene = stage.DefinePrim("/World/PhysicsScene", "PhysicsScene")
# Add terrain
terrain_prim = stage.DefinePrim("/World/Environments/Terrain", "Xform")
Quality Assurance
- Validate Physics Properties: Ensure all objects have appropriate mass and collision
- Check Coordinate Systems: Verify ROS and USD coordinate compatibility
- Test Sensor Data: Validate that sensor outputs match expectations
- Verify Joint Limits: Ensure all joints have appropriate constraints
Exercises
- Create a simple differential drive robot in Isaac Sim with proper physics properties
- Add a camera and LiDAR sensor to your robot model
- Implement a ROS bridge to control your robot from external nodes
- Create a simple environment with obstacles for navigation testing
Summary
NVIDIA Isaac Sim provides a powerful simulation environment specifically designed for robotics applications. With its GPU-accelerated physics, photorealistic rendering, and seamless ROS integration, Isaac Sim enables realistic simulation for robotics development. By properly setting up the simulation environment, creating well-structured robot models, and implementing effective ROS bridges, developers can create high-fidelity simulations that bridge the gap between virtual and real-world robotics development.
Cross-References
This module extends the simulation concepts and provides:
- Foundation for Isaac ROS pipeline - where you'll connect Isaac Sim with ROS 2
- Advanced navigation in simulation - where you'll test navigation in high-fidelity environments
- Integration with VLA systems - where Isaac Sim provides training environments for voice-controlled robots