Hello Robot#
Learning Objectives#
This tutorial details how to add and move a mobile robot in NVIDIA Isaac Sim in an extension application. After this tutorial, you will understand how to add a robot to the simulation and apply actions to its wheels using Python.
10-15 Minute Tutorial
Getting Started#
Prerequisites
Review Hello World prior to beginning this tutorial.
Begin with the source code of the Hello World example developed in the previous tutorial: Hello World.
Adding a Robot#
Begin by adding a NVIDIA Jetbot to the scene. You can do so by accessing the library of NVIDIA Isaac Sim robots, sensors, and environments located on a Omniverse Nucleus Server using Python, as well as navigate through it using the Content window, under Isaac Sim > robots > NVIDIA > jetbot.usd.
Note
The server shown in these steps has been connected to in Workstation Setup. Follow these steps first before proceeding.
Add the assets by simply dragging them to the stage window or the viewport.
Try to do the same thing through Python in the Hello World example.
Create a new stage: File > new > Don’t Save
Open the
hello_world.pyfile by clicking the Open Source Code button in the Hello World window.
Import packages:
1import carb
2import isaacsim.core.experimental.utils.stage as stage_utils
3from isaacsim.core.experimental.prims import Articulation
4from isaacsim.examples.base.base_sample_experimental import BaseSample
5from isaacsim.storage.native import get_assets_root_path
6
Setup scene:
1 # Add the Jetbot robot to the stage
2 asset_path = assets_root_path + "/Isaac/Robots/NVIDIA/Jetbot/jetbot.usd"
3 stage_utils.add_reference_to_stage(usd_path=asset_path, path="/World/Fancy_Robot")
Articulate the robot:
1 # Wrap the Jetbot with the Articulation class for control
2 self._jetbot = Articulation("/World/Fancy_Robot")
Complete code:
1# -- Begin importing Isaac packages -- #
2import carb
3import isaacsim.core.experimental.utils.stage as stage_utils
4from isaacsim.core.experimental.prims import Articulation
5from isaacsim.examples.base.base_sample_experimental import BaseSample
6from isaacsim.storage.native import get_assets_root_path
7
8# -- End of importing Isaac packages -- #
9
10
11class HelloWorld(BaseSample):
12 def __init__(self) -> None:
13 super().__init__()
14
15 def setup_scene(self):
16 # Add ground plane
17 ground_plane = stage_utils.add_reference_to_stage(
18 usd_path=get_assets_root_path() + "/Isaac/Environments/Grid/default_environment.usd",
19 path="/World/ground",
20 )
21
22 # Get the assets root path from the Nucleus server
23 assets_root_path = get_assets_root_path()
24 if assets_root_path is None:
25 carb.log_error("Could not find nucleus server with /Isaac folder")
26 return
27
28 # -- Begin adding Jetbot -- #
29 # Add the Jetbot robot to the stage
30 asset_path = assets_root_path + "/Isaac/Robots/NVIDIA/Jetbot/jetbot.usd"
31 stage_utils.add_reference_to_stage(usd_path=asset_path, path="/World/Fancy_Robot")
32 # -- End of adding Jetbot -- #
33
34 async def setup_post_load(self):
35 # -- Begin articulation -- #
36 # Wrap the Jetbot with the Articulation class for control
37 self._jetbot = Articulation("/World/Fancy_Robot")
38 # -- End of articulation -- #
39
40 # Print info about the Jetbot
41 print("Number of DOFs: " + str(self._jetbot.num_dofs))
42 print("DOF names: " + str(self._jetbot.dof_names))
43 print("Joint Positions: " + str(self._jetbot.get_dof_positions().numpy()))
Click the LOAD button to load the scene and see the Jetbot appear. Although it is being simulated, it is not moving. The next section walks through how to make the robot move.
Move the Robot#
In NVIDIA Isaac Sim, Robots are constructed of physically accurate articulated joints. Applying actions to these articulations make them move.
Next, apply random velocities to the Jetbot’s wheel joints to get it moving.
Importing SimulationManager:
1from isaacsim.core.simulation_manager import SimulationManager
2
Registering callbacks:
1 # Register a physics callback to send actions every physics step
2 from isaacsim.core.simulation_manager.impl.isaac_events import IsaacEvents
3
4 self._physics_callback_id = SimulationManager.register_callback(
5 self.send_robot_actions, IsaacEvents.POST_PHYSICS_STEP
6 )
Define command function:
1 def send_robot_actions(self, dt, context):
2 # Apply random velocity targets to the wheel joints
3 # Jetbot has 2 DOFs: left_wheel_joint and right_wheel_joint
4 random_velocities = 5 * np.random.rand(1, 2) # Shape: (1, num_dofs)
5 self._jetbot.set_dof_velocity_targets(random_velocities)
6
Complete code:
1import carb
2import isaacsim.core.experimental.utils.stage as stage_utils
3import numpy as np
4from isaacsim.core.experimental.prims import Articulation
5
6# -- Begin importing SimulationManager -- #
7from isaacsim.core.simulation_manager import SimulationManager
8
9# -- End of importing SimulationManager -- #
10from isaacsim.examples.base.base_sample_experimental import BaseSample
11from isaacsim.storage.native import get_assets_root_path
12
13
14class HelloWorld(BaseSample):
15 def __init__(self) -> None:
16 super().__init__()
17 self._physics_callback_id = None
18
19 def setup_scene(self):
20 # Add ground plane
21 ground_plane = stage_utils.add_reference_to_stage(
22 usd_path=get_assets_root_path() + "/Isaac/Environments/Grid/default_environment.usd",
23 path="/World/ground",
24 )
25
26 # Get the assets root path from the Nucleus server
27 assets_root_path = get_assets_root_path()
28 if assets_root_path is None:
29 carb.log_error("Could not find nucleus server with /Isaac folder")
30 return
31
32 # Add the Jetbot robot to the stage
33 asset_path = assets_root_path + "/Isaac/Robots/NVIDIA/Jetbot/jetbot.usd"
34 stage_utils.add_reference_to_stage(usd_path=asset_path, path="/World/Fancy_Robot")
35
36 async def setup_post_load(self):
37 # Wrap the Jetbot with the Articulation class for control
38 self._jetbot = Articulation("/World/Fancy_Robot")
39
40 # -- Begin registering callback -- #
41 # Register a physics callback to send actions every physics step
42 from isaacsim.core.simulation_manager.impl.isaac_events import IsaacEvents
43
44 self._physics_callback_id = SimulationManager.register_callback(
45 self.send_robot_actions, IsaacEvents.POST_PHYSICS_STEP
46 )
47 # -- End of registering callback -- #
48
49 # -- Begin sending actions -- #
50 def send_robot_actions(self, dt, context):
51 # Apply random velocity targets to the wheel joints
52 # Jetbot has 2 DOFs: left_wheel_joint and right_wheel_joint
53 random_velocities = 5 * np.random.rand(1, 2) # Shape: (1, num_dofs)
54 self._jetbot.set_dof_velocity_targets(random_velocities)
55
56 # -- End of sending actions -- #
57
58 def physics_cleanup(self):
59 # Clean up callback when the extension is unloaded
60 if self._physics_callback_id is not None:
61 SimulationManager.deregister_callback(self._physics_callback_id)
62 self._physics_callback_id = None
Click the LOAD button to load the scene and watch the Jetbot move with random velocities.
Note
Pressing STOP, then PLAY in this workflow might not reset the world properly. Use the RESET button instead.
Extra Practice#
This example applies random velocities to the Jetbot articulation controller. Try the following exercises:
Make the Jetbot move backwards (hint: use negative velocities).
Make the Jetbot turn right (hint: apply different velocities to each wheel).
Make the Jetbot stop after 5 seconds (hint: track elapsed time in the callback).
Controlling Specific Joints#
You can also control specific joints by their names or indices. Here’s how to get the wheel joint indices and apply velocities only to specific joints:
Getting wheel indices:
1 # Print available DOF names
2 print("Available DOFs:", self._jetbot.dof_names)
3
4 # Get indices for specific wheel joints
5 self._wheel_indices = self._jetbot.get_dof_indices(["left_wheel_joint", "right_wheel_joint"]).numpy()
6 print("Wheel indices:", self._wheel_indices)
Setting wheel velocities using indices:
1 # Apply velocity targets to specific DOF indices
2 wheel_velocities = np.array([[10.0, 10.0]]) # Both wheels same speed = forward
3 self._jetbot.set_dof_velocity_targets(wheel_velocities, dof_indices=self._wheel_indices)
Complete code:
1import carb
2import isaacsim.core.experimental.utils.stage as stage_utils
3import numpy as np
4from isaacsim.core.experimental.prims import Articulation
5from isaacsim.core.simulation_manager import SimulationManager
6from isaacsim.examples.base.base_sample_experimental import BaseSample
7from isaacsim.storage.native import get_assets_root_path
8
9
10class HelloWorld(BaseSample):
11 def __init__(self) -> None:
12 super().__init__()
13 self._physics_callback_id = None
14
15 def setup_scene(self):
16 # Add ground plane
17 ground_plane = stage_utils.add_reference_to_stage(
18 usd_path=get_assets_root_path() + "/Isaac/Environments/Grid/default_environment.usd",
19 path="/World/ground",
20 )
21
22 # Add the Jetbot robot to the stage
23 assets_root_path = get_assets_root_path()
24 asset_path = assets_root_path + "/Isaac/Robots/NVIDIA/Jetbot/jetbot.usd"
25 stage_utils.add_reference_to_stage(usd_path=asset_path, path="/World/Fancy_Robot")
26
27 async def setup_post_load(self):
28 # Wrap the Jetbot with the Articulation class
29 self._jetbot = Articulation("/World/Fancy_Robot")
30
31 # -- Begin getting indices -- #
32 # Print available DOF names
33 print("Available DOFs:", self._jetbot.dof_names)
34
35 # Get indices for specific wheel joints
36 self._wheel_indices = self._jetbot.get_dof_indices(["left_wheel_joint", "right_wheel_joint"]).numpy()
37 print("Wheel indices:", self._wheel_indices)
38 # -- End of getting indices -- #
39
40 # Register physics callback
41 from isaacsim.core.simulation_manager.impl.isaac_events import IsaacEvents
42
43 self._physics_callback_id = SimulationManager.register_callback(
44 self.send_robot_actions, IsaacEvents.POST_PHYSICS_STEP
45 )
46
47 def send_robot_actions(self, dt, context):
48 # -- Begin setting wheel velocity -- #
49 # Apply velocity targets to specific DOF indices
50 wheel_velocities = np.array([[10.0, 10.0]]) # Both wheels same speed = forward
51 self._jetbot.set_dof_velocity_targets(wheel_velocities, dof_indices=self._wheel_indices)
52 # -- End of setting wheel velocity -- #
53
54 def physics_cleanup(self):
55 if self._physics_callback_id is not None:
56 SimulationManager.deregister_callback(self._physics_callback_id)
57 self._physics_callback_id = None
Summary#
This tutorial covered the following topics:
Adding NVIDIA Isaac Sim library components from a Nucleus Server
Adding a robot to the stage using
stage_utils.add_reference_to_stage()Wrapping a robot with the
Articulationclass for controlUsing
set_dof_velocity_targets()to apply velocity controlRegistering physics callbacks with
SimulationManagerControlling specific joints by name or index
Next Steps#
Continue on to the next tutorial in the Essential Tutorials series, Adding a Manipulator Robot, to learn how to add a manipulator robot to the simulation.
Further Learning#
Nucleus Server
For an overview of how to best leverage a Nucleus Server, see the Nucleus Overview in NVIDIA Omniverse tutorial.
Robot Specific Extensions
NVIDIA Isaac Sim provides several robot extensions, including
isaacsim.robot.experimental.wheeled_robots, andisaacsim.robot.experimental.manipulators.examples. To learn more, check out the standalone examples located atstandalone_examples/api/isaacsim.robot.experimental.manipulators/frankaandstandalone_examples/api/isaacsim.robot.experimental.manipulators/universal_robots/.