Adding a Manipulator Robot#
Learning Objectives#
This tutorial introduces a manipulator robot to the simulation, a Franka Panda. It describes how to add the robot to the scene and execute a pick-and-place operation. After this tutorial, you will have more experience using manipulator robots and controlling them with the Robot Motion API in NVIDIA Isaac Sim.
15-20 Minute Tutorial
Getting Started#
Prerequisites
Review Hello Robot prior to beginning this tutorial.
This tutorial uses standalone Python scripts. Run them with a Python environment where Isaac Sim is installed:
Creating the Scene with a Franka Robot#
Use ManipulationScenario to add the ground and a configured Franka asset.
The scenario owns a plain Articulation; robot-specific USD variants, named
joint defaults, controller frame, measured link, and grasp offset are declarative data.
Scene setup is therefore separate from control logic.
1"""Create a scene with ground, Franka robot, and blue cube."""
2
3import argparse
4
5parser = argparse.ArgumentParser()
6parser.add_argument("--test", action="store_true")
7args, _ = parser.parse_known_args()
8
9from isaacsim import SimulationApp
10
11simulation_app = SimulationApp({"headless": False})
12
13import isaacsim.core.experimental.utils.app as app_utils
14
15app_utils.enable_extension("isaacsim.robot_motion.examples")
16
17from isaacsim.core.experimental.objects import Cube
18from isaacsim.core.experimental.prims import GeomPrim, RigidPrim
19from isaacsim.core.simulation_manager import SimulationManager
20from isaacsim.robot_motion.examples.manipulation import ManipulationScenario
21
22DEVICE = "cpu"
23
24# Robot-specific defaults and tool transforms come from the named configuration.
25scenario = ManipulationScenario("franka")
26articulation = scenario.setup_scene()
27
28# Create a blue cube for the robot to pick up
29cube_shape = Cube(
30 paths="/World/Cube",
31 positions=[0.5, 0.0, 0.0258],
32 sizes=1.0,
33 scales=[0.0515, 0.0515, 0.0515],
34 colors="blue",
35)
36GeomPrim(paths=cube_shape.paths, apply_collision_apis=True)
37RigidPrim(paths=cube_shape.paths)
38
39SimulationManager.setup_simulation(dt=1.0 / 60.0, device=DEVICE)
40physics_scene = SimulationManager.get_physics_scenes()[0]
41physics_scene.set_enabled_gpu_dynamics(False)
42app_utils.play()
43app_utils.update_app(steps=20)
44
45step_count = 0
46max_test_steps = 60
47while simulation_app.is_running():
48 simulation_app.update()
49 step_count += 1
50 if args.test and step_count >= max_test_steps:
51 break
52
53scenario.cleanup()
54app_utils.stop()
55simulation_app.close()
Run the script. A window opens with the Franka robot and cube in the scene; the simulation runs until you close the window.
The scenario provides the simulation boundary:
read_robot_state()reads named joints and the measured controller siteapply_robot_state()applies controller output to the articulationsync_world()updates the RMPFlow collision worldarticulation.reset_to_default_state()restores the configured robot defaults
Using the Pick-and-Place Task#
PickPlaceTask creates the scene and constructs the controllers directly.
An RMPFlow arm controller runs with a robot-independent PickPlaceController
and an observable gripper controller. They communicate through RobotState;
no robot subclass or inverse-kinematics wrapper is required.
1"""Pick-and-place using the generic motion-generation controller."""
2
3import argparse
4
5parser = argparse.ArgumentParser()
6parser.add_argument("--test", action="store_true")
7args, _ = parser.parse_known_args()
8
9from isaacsim import SimulationApp
10
11simulation_app = SimulationApp({"headless": False})
12
13import isaacsim.core.experimental.utils.app as app_utils
14
15app_utils.enable_extension("isaacsim.robot_motion.examples")
16
17from isaacsim.core.simulation_manager import SimulationManager
18from isaacsim.robot_motion.examples.manipulation import PickPlaceTask
19
20DEVICE = "cpu"
21
22# The task composes RMPFlow, the feedback-driven state machine, and the gripper.
23task = PickPlaceTask()
24task.setup_scene()
25
26SimulationManager.setup_simulation(dt=1.0 / 60.0, device=DEVICE)
27physics_scene = SimulationManager.get_physics_scenes()[0]
28physics_scene.set_enabled_gpu_dynamics(False)
29app_utils.play()
30# Run a few steps so the articulation's physics tensor entity is valid before `task.initialize()`
31app_utils.update_app(steps=20)
32task.initialize()
33task.reset()
34
35# Main loop: run one pick-place step each physics frame until done
36step_count = 0
37max_test_steps = 600
38while simulation_app.is_running():
39 simulation_app.update()
40 step_count += 1
41 if app_utils.is_playing():
42 if not task.is_done:
43 task.step(1.0 / 60.0)
44 if task.failed:
45 raise RuntimeError(str(task.status()["failure_reason"]))
46 else:
47 print("Pick-and-place completed")
48 app_utils.pause()
49 if args.test:
50 break
51 if args.test and step_count >= max_test_steps:
52 raise RuntimeError("Pick-and-place did not complete within the test step budget")
53
54task.cleanup()
55app_utils.stop()
56simulation_app.close()
Run the script. The robot automatically executes all phases of picking up and placing the cube.
Understanding the Pick-and-Place State Machine#
PickPlaceController generates controller-frame targets for these phases:
Phase |
Description |
Completion condition |
|---|---|---|
|
Move above the pick goal |
Measured tool pose is stable at the target |
|
Move to the pick goal |
Measured tool pose is stable at the target |
|
Close the gripper |
Tool pose is stable and the gripper reports completion |
|
Lift the object |
Measured tool pose is stable at the target |
|
Move above the place goal |
Measured tool pose is stable at the target |
|
Move to the place goal |
Measured tool pose is stable at the target |
|
Open the gripper |
Tool pose is stable and the gripper reports completion |
|
Move away from the placed object |
Measured tool pose is stable at the target |
Each active phase has a timeout that produces a failure; time alone never marks a motion phase complete. Pick and place goals are captured when the controller resets. Customize the task by changing its cube and place positions before scene setup:
1# Goals are captured at reset; configure task positions before scene setup.
2task = PickPlaceTask(cube_positions=[(0.4, 0.2, 0.0258)])
3task.place_position = np.asarray([-0.4, 0.2, 0.0258], dtype=np.float32)
4task.setup_scene()
Complete code:
1"""Customize the generic pick-and-place task."""
2
3import argparse
4
5parser = argparse.ArgumentParser()
6parser.add_argument("--test", action="store_true")
7args, _ = parser.parse_known_args()
8
9from isaacsim import SimulationApp
10
11simulation_app = SimulationApp({"headless": False})
12
13import isaacsim.core.experimental.utils.app as app_utils
14
15app_utils.enable_extension("isaacsim.robot_motion.examples")
16
17import numpy as np
18from isaacsim.core.simulation_manager import SimulationManager
19from isaacsim.robot_motion.examples.manipulation import PickPlaceTask
20
21DEVICE = "cpu"
22
23# -- Begin custom setup -- #
24# Goals are captured at reset; configure task positions before scene setup.
25task = PickPlaceTask(cube_positions=[(0.4, 0.2, 0.0258)])
26task.place_position = np.asarray([-0.4, 0.2, 0.0258], dtype=np.float32)
27task.setup_scene()
28# -- End of custom setup -- #
29
30SimulationManager.setup_simulation(dt=1.0 / 60.0, device=DEVICE)
31physics_scene = SimulationManager.get_physics_scenes()[0]
32physics_scene.set_enabled_gpu_dynamics(False)
33app_utils.play()
34# Run a few steps so the articulation's physics tensor entity is valid before `task.initialize()`
35app_utils.update_app(steps=20)
36task.initialize()
37task.reset()
38
39# Main loop: run one pick-place step each physics frame until done
40step_count = 0
41max_test_steps = 600
42while simulation_app.is_running():
43 simulation_app.update()
44 step_count += 1
45 if app_utils.is_playing():
46 if not task.is_done:
47 task.step(1.0 / 60.0)
48 if task.failed:
49 raise RuntimeError(str(task.status()["failure_reason"]))
50 else:
51 print("Pick-and-place completed")
52 app_utils.pause()
53 if args.test:
54 break
55 if args.test and step_count >= max_test_steps:
56 raise RuntimeError("Pick-and-place did not complete within the test step budget")
57
58task.cleanup()
59app_utils.stop()
60simulation_app.close()
See Also#
For an overview of the motion-generation API and available controllers, see Robot Motion (Experimental).
For a complete standalone pick-and-place example with robot, USD, URDF, XRDF, and test options, see
standalone_examples/api/isaacsim.robot_motion.examples/manipulation/pick_place.py.
Summary#
This tutorial covered the following topics:
Adding a configured Franka as a plain
ArticulationwithManipulationScenarioConstructing RMPFlow, pick-and-place, and gripper controllers explicitly
Exchanging measured and desired state through
RobotStateUsing feedback-driven pick-and-place phases with explicit failure semantics
Next Steps#
Continue to the next tutorial in our Essential Tutorials series, Adding Multiple Robots, to learn how to add multiple robots to the simulation.