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

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, which allows you to access 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.

Note

The server shown in these steps has been connected to in Workstation Setup. Follow these steps first before proceeding.

../_images/core_api_tutorials_2_1.webp
  1. Add the assets by simply dragging them to the stage window or the viewport.

  2. Try to do the same thing through Python in the Hello World example.

  3. Create a new stage: File > new > Don’t Save

  4. Open the hello_world.py file by clicking the Open Source Code button in the Hello World window.

 1import isaacsim.core.experimental.utils.stage as stage_utils
 2from isaacsim.core.experimental.prims import Articulation
 3from isaacsim.examples.base.base_sample_experimental import BaseSample
 4from isaacsim.storage.native import get_assets_root_path
 5import carb
 6
 7
 8class HelloWorld(BaseSample):
 9    def __init__(self) -> None:
10        super().__init__()
11
12    def setup_scene(self):
13        # Add ground plane
14        ground_plane = stage_utils.add_reference_to_stage(
15            usd_path=get_assets_root_path() + "/Isaac/Environments/Grid/default_environment.usd",
16            path="/World/ground",
17        )
18
19        # Get the assets root path from the Nucleus server
20        assets_root_path = get_assets_root_path()
21        if assets_root_path is None:
22            carb.log_error("Could not find nucleus server with /Isaac folder")
23            return
24
25        # Add the Jetbot robot to the stage
26        asset_path = assets_root_path + "/Isaac/Robots/NVIDIA/Jetbot/jetbot.usd"
27        stage_utils.add_reference_to_stage(usd_path=asset_path, path="/World/Fancy_Robot")
28
29    async def setup_post_load(self):
30        # Wrap the Jetbot with the Articulation class for control
31        self._jetbot = Articulation("/World/Fancy_Robot")
32
33        # Print info about the Jetbot
34        print("Number of DOFs: " + str(self._jetbot.num_dofs))
35        print("DOF names: " + str(self._jetbot.dof_names))
36        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.

 1import isaacsim.core.experimental.utils.stage as stage_utils
 2from isaacsim.core.experimental.prims import Articulation
 3from isaacsim.examples.base.base_sample_experimental import BaseSample
 4from isaacsim.core.simulation_manager import SimulationManager
 5from isaacsim.storage.native import get_assets_root_path
 6import numpy as np
 7import carb
 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        # 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        # Add the Jetbot robot to the stage
29        asset_path = assets_root_path + "/Isaac/Robots/NVIDIA/Jetbot/jetbot.usd"
30        stage_utils.add_reference_to_stage(usd_path=asset_path, path="/World/Fancy_Robot")
31
32    async def setup_post_load(self):
33        # Wrap the Jetbot with the Articulation class for control
34        self._jetbot = Articulation("/World/Fancy_Robot")
35
36        # Register a physics callback to send actions every physics step
37        from isaacsim.core.simulation_manager.impl.isaac_events import IsaacEvents
38        self._physics_callback_id = SimulationManager.register_callback(
39            self.send_robot_actions, IsaacEvents.POST_PHYSICS_STEP
40        )
41
42    def send_robot_actions(self, dt, context):
43        # Apply random velocity targets to the wheel joints
44        # Jetbot has 2 DOFs: left_wheel_joint and right_wheel_joint
45        random_velocities = 5 * np.random.rand(1, 2)  # Shape: (1, num_dofs)
46        self._jetbot.set_dof_velocity_targets(random_velocities)
47
48    def physics_cleanup(self):
49        # Clean up callback when the extension is unloaded
50        if self._physics_callback_id is not None:
51            SimulationManager.deregister_callback(self._physics_callback_id)
52            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:

  1. Make the Jetbot move backwards (hint: use negative velocities).

  2. Make the Jetbot turn right (hint: apply different velocities to each wheel).

  3. 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:

 1import isaacsim.core.experimental.utils.stage as stage_utils
 2from isaacsim.core.experimental.prims import Articulation
 3from isaacsim.examples.base.base_sample_experimental import BaseSample
 4from isaacsim.core.simulation_manager import SimulationManager
 5from isaacsim.storage.native import get_assets_root_path
 6import numpy as np
 7import carb
 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        # Print available DOF names
32        print("Available DOFs:", self._jetbot.dof_names)
33
34        # Get indices for specific wheel joints
35        self._wheel_indices = self._jetbot.get_dof_indices(
36            ["left_wheel_joint", "right_wheel_joint"]
37        ).numpy()
38        print("Wheel indices:", self._wheel_indices)
39
40        # Register physics callback
41        from isaacsim.core.simulation_manager.impl.isaac_events import IsaacEvents
42        self._physics_callback_id = SimulationManager.register_callback(
43            self.send_robot_actions, IsaacEvents.POST_PHYSICS_STEP
44        )
45
46    def send_robot_actions(self, dt, context):
47        # Apply velocity targets to specific DOF indices
48        wheel_velocities = np.array([[10.0, 10.0]])  # Both wheels same speed = forward
49        self._jetbot.set_dof_velocity_targets(
50            wheel_velocities,
51            dof_indices=self._wheel_indices
52        )
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
../_images/core_api_tutorials_2_2.webp

Summary#

This tutorial covered the following topics:

  1. Adding NVIDIA Isaac Sim library components from a Nucleus Server

  2. Adding a robot to the stage using stage_utils.add_reference_to_stage()

  3. Wrapping a robot with the Articulation class for control

  4. Using set_dof_velocity_targets() to apply velocity control

  5. Registering physics callbacks with SimulationManager

  6. Controlling 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

Robot Specific Extensions

  • NVIDIA Isaac Sim provides several robot extensions such as isaacsim.robot.manipulators.examples.franka, isaacsim.robot.manipulators.examples.universal_robots, and many more. To learn more, check out the standalone examples located at standalone_examples/api/isaacsim.robot.manipulators/franka and standalone_examples/api/isaacsim.robot.manipulators/universal_robots/.