Skip to main content

Weeks 8-10: NVIDIA Isaac Platform

Master NVIDIA's cutting-edge platform for AI-powered robotics, combining high-fidelity simulation, GPU-accelerated perception, and reinforcement learning for humanoid robots.

Introduction to NVIDIA Isaac Platform

The Isaac platform represents NVIDIA's comprehensive solution for developing, training, and deploying AI-powered robots. It bridges the gap between simulation and reality using state-of-the-art graphics, physics, and machine learning.

Platform Components

Isaac Sim:
Photorealistic robot simulator built on NVIDIA Omniverse, providing physically accurate environments with RTX ray tracing and GPU-accelerated physics.

Isaac SDK:
Collection of GPU-accelerated libraries for perception, navigation, and manipulation. Includes pre-trained models and optimized algorithms.

Isaac Gym:
High-performance physics simulation for reinforcement learning, capable of running thousands of parallel environments on a single GPU.

Isaac ROS:
Hardware-accelerated ROS 2 packages leveraging NVIDIA GPUs for real-time perception and AI inference.

Why Isaac for Humanoid Robotics?

GPU Acceleration:
All components leverage CUDA for massive parallelization, essential for complex humanoid control and perception.

Sim-to-Real Transfer:
High-fidelity simulation reduces the reality gap, enabling policies trained in simulation to transfer directly to physical robots.

Pre-trained Models:
Access to NVIDIA's trained networks for object detection, pose estimation, and manipulation.

Scalability:
Train policies on thousands of parallel simulated robots, dramatically reducing training time.

Isaac Sim: Photorealistic Robot Simulation

Isaac Sim provides the most realistic robot simulation environment available, crucial for developing robust humanoid behaviors.

Installation

# Download from NVIDIA (requires registration)
# https://developer.nvidia.com/isaac-sim

# System Requirements:
# - Ubuntu 20.04/22.04
# - NVIDIA RTX GPU (2080 or better)
# - 32GB+ RAM recommended
# - NVIDIA Driver 525.60.11+

# Install via Omniverse Launcher or pip
pip install isaacsim

# Verify installation
python -c "from isaacsim import SimulationApp; print('Isaac Sim installed successfully')"

Basic Isaac Sim Setup

from isaacsim import SimulationApp

# Initialize simulation
simulation_app = SimulationApp({"headless": False})

from omni.isaac.core import World
from omni.isaac.core.objects import DynamicCuboid
from omni.isaac.core.prims import RigidPrim
import numpy as np

# Create world
world = World(stage_units_in_meters=1.0)

# Add ground plane
world.scene.add_default_ground_plane()

# Add object
cube = world.scene.add(
DynamicCuboid(
prim_path="/World/Cube",
name="test_cube",
position=np.array([0, 0, 1.0]),
scale=np.array([0.2, 0.2, 0.2]),
color=np.array([0.8, 0.2, 0.2])
)
)

# Reset world
world.reset()

# Simulation loop
for i in range(1000):
world.step(render=True)

# Get object state
position, orientation = cube.get_world_pose()
print(f"Step {i}: Position = {position}")

# Cleanup
simulation_app.close()

Loading URDF Robots in Isaac Sim

from isaacsim import SimulationApp
simulation_app = SimulationApp({"headless": False})

from omni.isaac.core import World
from omni.isaac.core.utils.stage import add_reference_to_stage
from omni.isaac.core.articulations import Articulation
from omni.isaac.core.utils.nucleus import get_assets_root_path
import numpy as np

# Create world
world = World()
world.scene.add_default_ground_plane()

# Import URDF robot
from omni.isaac.core.utils.extensions import enable_extension
enable_extension("omni.isaac.urdf")

from omni.isaac.urdf import _urdf

# Load humanoid robot (example: Unitree H1)
robot_path = "/path/to/robot.urdf"
imported_robot = _urdf.acquire_urdf_interface().parse_urdf(
urdf_path=robot_path,
import_config=_urdf.ImportConfig()
)

# Create articulation
robot = world.scene.add(
Articulation(
prim_path="/World/Robot",
name="humanoid_robot"
)
)

world.reset()

# Control loop
for i in range(1000):
# Get joint positions
joint_positions = robot.get_joint_positions()

# Set joint targets (standing pose)
target_positions = np.zeros(robot.num_dof)
robot.set_joint_position_targets(target_positions)

world.step(render=True)

simulation_app.close()

Sensors in Isaac Sim

RGB-D Camera

from omni.isaac.sensor import Camera
import numpy as np

# Create camera
camera = Camera(
prim_path="/World/Camera",
position=np.array([2.0, 2.0, 1.5]),
frequency=30,
resolution=(1280, 720),
orientation=np.array([0.92, -0.39, 0, 0]) # Quaternion
)

# Initialize
camera.initialize()

# Capture frame
world.step(render=True)
rgba = camera.get_rgba()
depth = camera.get_depth_linear_array()

print(f"Image shape: {rgba.shape}")
print(f"Depth range: {depth.min():.2f}m to {depth.max():.2f}m")

LiDAR Simulation

from omni.isaac.sensor import RotatingLidarPhysX
import numpy as np

# Create LiDAR
lidar = RotatingLidarPhysX(
prim_path="/World/Lidar",
name="lidar_sensor",
rotation_frequency=20, # Hz
horizontal_resolution=0.25, # degrees
num_channels=16,
horizontal_fov=360.0,
min_range=0.1,
max_range=30.0
)

lidar.initialize()

# Get point cloud
world.step(render=True)
point_cloud = lidar.get_current_frame()
points = point_cloud["points"]

print(f"Point cloud size: {len(points)}")

AI-Powered Perception

Object Detection with Isaac SDK

Isaac SDK provides GPU-accelerated perception algorithms optimized for robotics.

import isaac_ros_dnn_inference
from isaac_ros_dnn_inference import DnnInferenceNode
from sensor_msgs.msg import Image
import rclpy
from rclpy.node import Node

class ObjectDetector(Node):
def __init__(self):
super().__init__('object_detector')

# Initialize Isaac ROS DNN inference
self.dnn_node = DnnInferenceNode(
model_name='detectnet',
model_repository_paths=['/models'],
input_binding_names=['input_tensor'],
output_binding_names=['output_coverage', 'output_bbox'],
enable_dla=True # Use Deep Learning Accelerator
)

# Subscribe to camera
self.subscription = self.create_subscription(
Image, '/camera/rgb', self.image_callback, 10)

def image_callback(self, msg):
# Perform inference
detections = self.dnn_node.infer(msg)

for detection in detections:
self.get_logger().info(
f"Detected {detection.class_name} "
f"at ({detection.center_x}, {detection.center_y}) "
f"with confidence {detection.confidence:.2f}"
)

6DOF Pose Estimation

from isaac_ros_pose_estimation import FoundationPoseNode
import numpy as np

class PoseEstimator(Node):
def __init__(self):
super().__init__('pose_estimator')

# Initialize Foundation Pose (NVIDIA's 6DOF estimator)
self.pose_node = FoundationPoseNode(
mesh_file_path='/models/object.obj',
texture_path='/models/texture.png',
refine_iterations=5
)

self.camera_sub = self.create_subscription(
Image, '/camera/depth', self.depth_callback, 10)

def depth_callback(self, depth_msg):
# Estimate 6DOF pose
pose = self.pose_node.estimate_pose(depth_msg)

if pose.is_valid:
position = pose.position # [x, y, z]
orientation = pose.quaternion # [w, x, y, z]

self.get_logger().info(
f"Object pose: pos={position}, ori={orientation}"
)

Manipulation with Isaac

Grasp Planning

from omni.isaac.manipulators import PickPlaceController
from omni.isaac.manipulators.grippers import ParallelGripper
from omni.isaac.core.objects import DynamicCuboid
import numpy as np

# Setup manipulator
robot = world.scene.get_object("robot")
gripper = ParallelGripper(
end_effector_prim_path="/World/Robot/gripper",
joint_prim_names=["finger_joint1", "finger_joint2"]
)

# Create pick-place controller
controller = PickPlaceController(
name="pick_place",
robot_articulation=robot,
gripper=gripper
)

# Define target object
target = world.scene.get_object("target_cube")
target_position, _ = target.get_world_pose()

# Execute pick
controller.forward(
picking_position=target_position + np.array([0, 0, 0.1]),
placing_position=np.array([0.5, 0.5, 0.2]),
approach_direction=np.array([0, 0, -1])
)

# Simulation loop
while controller.is_done() == False:
world.step(render=True)
controller.forward()

Contact-Rich Manipulation

from omni.isaac.core.utils.types import ArticulationAction
import numpy as np

class ForceController:
def __init__(self, robot):
self.robot = robot
self.target_force = 10.0 # Newtons

def apply_force_control(self):
# Get force/torque sensor reading
forces = self.robot.get_measured_joint_forces()

# Simple proportional controller
Kp = 0.1
joint_efforts = np.zeros(self.robot.num_dof)

# Control end-effector force
ee_force_error = self.target_force - forces[-1]
joint_efforts[-1] = Kp * ee_force_error

# Apply efforts
action = ArticulationAction(joint_efforts=joint_efforts)
self.robot.apply_action(action)

# Usage
force_ctrl = ForceController(robot)
for i in range(1000):
force_ctrl.apply_force_control()
world.step(render=True)

Reinforcement Learning with Isaac Gym

Isaac Gym enables training RL policies with massive parallelization. Train thousands of robots simultaneously on a single GPU.

Installation

# Clone Isaac Gym
git clone https://github.com/NVIDIA-Omniverse/IsaacGymEnvs.git
cd IsaacGymEnvs

# Install dependencies
pip install -e .

# Test installation
python train.py task=Cartpole headless=False

Custom Humanoid RL Environment

from isaacgym import gymapi, gymutil
from isaacgym.torch_utils import *
import torch
import numpy as np

class HumanoidEnv:
def __init__(self, num_envs=1024):
# Initialize Gym
self.gym = gymapi.acquire_gym()

# Simulation parameters
sim_params = gymapi.SimParams()
sim_params.dt = 1.0 / 60.0
sim_params.substeps = 2
sim_params.up_axis = gymapi.UP_AXIS_Z
sim_params.gravity = gymapi.Vec3(0.0, 0.0, -9.81)

# Physics engine (PhysX GPU)
sim_params.physx.use_gpu = True
sim_params.physx.solver_type = 1
sim_params.physx.num_position_iterations = 4
sim_params.physx.num_velocity_iterations = 1

# Create sim
self.sim = self.gym.create_sim(
0, 0, gymapi.SIM_PHYSX, sim_params)

# Create environments
self.num_envs = num_envs
self.envs = []
self.humanoid_handles = []

spacing = 2.0
lower = gymapi.Vec3(-spacing, -spacing, 0.0)
upper = gymapi.Vec3(spacing, spacing, spacing)

# Load humanoid asset
asset_root = "assets"
asset_file = "humanoid.urdf"
asset_options = gymapi.AssetOptions()
asset_options.fix_base_link = False
asset_options.default_dof_drive_mode = gymapi.DOF_MODE_EFFORT

humanoid_asset = self.gym.load_asset(
self.sim, asset_root, asset_file, asset_options)

# Create multiple environments
num_per_row = int(np.sqrt(num_envs))
for i in range(num_envs):
env = self.gym.create_env(self.sim, lower, upper, num_per_row)
self.envs.append(env)

# Spawn humanoid
pose = gymapi.Transform()
pose.p = gymapi.Vec3(0.0, 0.0, 1.0)
pose.r = gymapi.Quat(0.0, 0.0, 0.0, 1.0)

handle = self.gym.create_actor(
env, humanoid_asset, pose, "humanoid", i, 1)
self.humanoid_handles.append(handle)

# Initialize tensors for GPU computation
self.num_dofs = self.gym.get_asset_dof_count(humanoid_asset)
self.dof_states = torch.zeros(
(num_envs, self.num_dofs, 2), device='cuda')

def reset(self):
"""Reset all environments"""
# Reset to initial pose
initial_pose = torch.zeros(
(self.num_envs, self.num_dofs), device='cuda')

for i, env in enumerate(self.envs):
self.gym.set_dof_state_tensor_indexed(
self.sim,
gymtorch.unwrap_tensor(initial_pose[i]),
gymtorch.unwrap_tensor(torch.tensor([i], device='cuda')),
1
)

return self.get_observations()

def get_observations(self):
"""Get observations for all environments (vectorized)"""
# Refresh state tensors
self.gym.refresh_dof_state_tensor(self.sim)
self.gym.refresh_actor_root_state_tensor(self.sim)

# Observation: [joint positions, joint velocities, body orientation]
obs = torch.cat([
self.dof_states[:, :, 0], # positions
self.dof_states[:, :, 1] # velocities
], dim=1)

return obs

def step(self, actions):
"""Apply actions and step simulation"""
# Apply joint torques
torques = actions.clone()
self.gym.set_dof_actuation_force_tensor(
self.sim, gymtorch.unwrap_tensor(torques))

# Step physics
self.gym.simulate(self.sim)
self.gym.fetch_results(self.sim, True)

# Get new observations
obs = self.get_observations()

# Compute rewards
rewards = self.compute_rewards(obs, actions)

# Check termination
dones = self.check_termination(obs)

return obs, rewards, dones, {}

def compute_rewards(self, obs, actions):
"""Reward function for humanoid locomotion"""
# Forward velocity reward
forward_velocity = obs[:, 0] # Example
velocity_reward = forward_velocity

# Energy penalty
energy_penalty = -0.01 * torch.sum(actions ** 2, dim=1)

# Alive bonus
alive_bonus = 1.0

total_reward = velocity_reward + energy_penalty + alive_bonus
return total_reward

def check_termination(self, obs):
"""Check if episode should terminate"""
# Terminate if robot falls
height = obs[:, 2] # Example: z-position
fallen = height < 0.5
return fallen

Training with PPO

from stable_baselines3 import PPO
from stable_baselines3.common.vec_env import VecEnv

# Wrapper to make Isaac Gym compatible with Stable Baselines3
class IsaacGymVecEnv(VecEnv):
def __init__(self, num_envs):
self.isaac_env = HumanoidEnv(num_envs=num_envs)
observation_space = gym.spaces.Box(
low=-np.inf, high=np.inf, shape=(48,), dtype=np.float32)
action_space = gym.spaces.Box(
low=-1.0, high=1.0, shape=(17,), dtype=np.float32)
super().__init__(num_envs, observation_space, action_space)

def reset(self):
obs = self.isaac_env.reset()
return obs.cpu().numpy()

def step_async(self, actions):
self.actions = torch.tensor(actions, device='cuda')

def step_wait(self):
obs, rewards, dones, info = self.isaac_env.step(self.actions)
return (obs.cpu().numpy(),
rewards.cpu().numpy(),
dones.cpu().numpy(),
info)

# Create vectorized environment
vec_env = IsaacGymVecEnv(num_envs=2048)

# Initialize PPO agent
model = PPO(
"MlpPolicy",
vec_env,
learning_rate=3e-4,
n_steps=2048,
batch_size=64,
n_epochs=10,
gamma=0.99,
gae_lambda=0.95,
clip_range=0.2,
verbose=1,
device='cuda'
)

# Train
model.learn(total_timesteps=10_000_000)

# Save model
model.save("humanoid_ppo")

Sim-to-Real Transfer

The reality gap is the difference between simulated and real-world behavior. Minimizing this gap is crucial for deploying RL policies on physical robots.

Domain Randomization

Randomize simulation parameters during training to make policies robust to real-world variation.

import random

class DomainRandomization:
def __init__(self, env):
self.env = env

def randomize_physics(self):
"""Randomize physics parameters"""
# Randomize friction
friction = random.uniform(0.5, 1.5)
self.env.gym.set_actor_surface_friction(
self.env.envs[0],
self.env.humanoid_handles[0],
0, # shape_index
friction
)

# Randomize mass
mass_scale = random.uniform(0.8, 1.2)
self.env.gym.set_actor_scale(
self.env.envs[0],
self.env.humanoid_handles[0],
mass_scale
)

def randomize_observations(self, obs):
"""Add sensor noise"""
noise_scale = 0.01
noise = torch.randn_like(obs) * noise_scale
return obs + noise

def randomize_actions(self, actions):
"""Add actuator delays and noise"""
# Random delay (1-2 timesteps)
delay = random.randint(1, 2)

# Action noise
noise = torch.randn_like(actions) * 0.05

return actions + noise

# Apply during training
dr = DomainRandomization(env)
for episode in range(1000):
dr.randomize_physics()
obs = env.reset()

for step in range(max_steps):
obs = dr.randomize_observations(obs)
action = policy(obs)
action = dr.randomize_actions(action)
obs, reward, done, _ = env.step(action)

System Identification

Measure real robot parameters and match simulation to reality.

class SystemID:
def __init__(self):
self.measured_params = {}

def measure_joint_friction(self, robot, joint_idx):
"""
Apply known torques and measure actual velocities
to identify friction coefficients
"""
torques = np.linspace(0, 10, 50)
velocities = []

for torque in torques:
robot.set_joint_effort(joint_idx, torque)
time.sleep(0.1)
vel = robot.get_joint_velocity(joint_idx)
velocities.append(vel)

# Fit friction model: torque = b*vel + c*sign(vel)
# where b = viscous friction, c = coulomb friction
from scipy.optimize import curve_fit

def friction_model(vel, b, c):
return b * vel + c * np.sign(vel)

params, _ = curve_fit(friction_model, velocities, torques)
self.measured_params[f'joint_{joint_idx}_friction'] = params

return params

def update_simulation(self, sim_env):
"""Update simulation with measured parameters"""
for joint_idx in range(sim_env.num_dofs):
if f'joint_{joint_idx}_friction' in self.measured_params:
b, c = self.measured_params[f'joint_{joint_idx}_friction']
sim_env.set_joint_friction(joint_idx, viscous=b, coulomb=c)

Deployment Pipeline

class SimToRealDeployer:
def __init__(self, sim_model_path, real_robot_interface):
self.policy = torch.load(sim_model_path)
self.robot = real_robot_interface
self.observation_filter = ObservationFilter()

def deploy(self):
"""Deploy trained policy on real robot"""
obs = self.robot.get_state()

while True:
# Normalize observations (same as training)
obs_normalized = self.observation_filter.normalize(obs)

# Get action from policy
with torch.no_grad():
action = self.policy(obs_normalized)

# Safety checks
if not self.safety_check(action):
self.robot.emergency_stop()
break

# Execute action
self.robot.send_command(action)

# Get new observation
obs = self.robot.get_state()

time.sleep(0.01) # 100Hz control loop

def safety_check(self, action):
"""Verify action is safe"""
# Check joint limits
if torch.any(action > 1.0) or torch.any(action < -1.0):
return False

# Check torque limits
max_torque = 50.0 # Nm
if torch.any(torch.abs(action) > max_torque):
return False

return True

Practical Exercises

Week 8 Assignment: Isaac Sim Environment

Objective: Set up and customize Isaac Sim for humanoid robot simulation

Tasks:

  1. Install Isaac Sim and verify GPU acceleration
  2. Import a humanoid robot URDF file
  3. Add RGB-D camera and LiDAR sensors
  4. Create custom environment with obstacles
  5. Implement keyboard teleoperation

Deliverable:
Python script demonstrating sensor data visualization and robot control in Isaac Sim.

Week 9 Assignment: Perception Pipeline

Objective: Build AI-powered perception system

Tasks:

  1. Deploy Isaac ROS for object detection
  2. Implement 6DOF pose estimation for grasping
  3. Create ROS 2 nodes for perception pipeline
  4. Test on simulated camera feed from Isaac Sim
  5. Measure inference latency and accuracy

Deliverable:
Complete perception package with performance benchmarks and demo video.

Week 10 Assignment: RL Training

Objective: Train humanoid robot policy with reinforcement learning

Tasks:

  1. Set up Isaac Gym environment for locomotion
  2. Implement custom reward function
  3. Train PPO policy for walking behavior
  4. Apply domain randomization
  5. Document sim-to-real transfer strategy

Deliverable:
Trained model weights, training curves, and report analyzing policy behavior and transfer considerations.


Next: In Weeks 11-12, we'll explore humanoid robot kinematics, inverse kinematics solvers, and motion planning algorithms.