Simulation with Moving People
In this tutorial, you will learn how to add moving people to your standalone simulations. This is useful for dynamic applications such as target tracking, surveillance, and search and rescue missions.
Note
Here we follow a different approach to simulate moving people than what is provided in the official NVIDIA Isaac Sim documentation. We believe that our API is simpler and more intuitive to use in most user cases. I also needed this for my own Ph.D. research :)
0. Preparation
This tutorial assumes that you have followed the Your First Simulation section first.
1. Code
The tutorial corresponds to the 9_people.py
example in the examples
directory.
1#!/usr/bin/env python
2"""
3| File: 9_people.py
4| License: BSD-3-Clause. Copyright (c) 2024, Marcelo Jacinto. All rights reserved.
5| Description: This files serves as an example on how to build an app that makes use of the Pegasus API to run a simulation
6| where people move around in the world.
7"""
8
9# Imports to start Isaac Sim from this script
10import carb
11from isaacsim import SimulationApp
12
13# Start Isaac Sim's simulation environment
14# Note: this simulation app must be instantiated right after the SimulationApp import, otherwise the simulator will crash
15# as this is the object that will load all the extensions and load the actual simulator.
16simulation_app = SimulationApp({"headless": False})
17
18# -----------------------------------
19# The actual script should start here
20# -----------------------------------
21import omni.timeline
22from omni.isaac.core.world import World
23from omni.isaac.core.utils.extensions import disable_extension, enable_extension
24
25EXTENSIONS_PEOPLE = [
26 'omni.anim.people',
27 'omni.anim.navigation.bundle',
28 'omni.anim.timeline',
29 'omni.anim.graph.bundle',
30 'omni.anim.graph.core',
31 'omni.anim.graph.ui',
32 'omni.anim.retarget.bundle',
33 'omni.anim.retarget.core',
34 'omni.anim.retarget.ui',
35 'omni.kit.scripting',
36 'omni.graph.io',
37 'omni.anim.curve.core',
38]
39
40for ext_people in EXTENSIONS_PEOPLE:
41 enable_extension(ext_people)
42
43# Enable/disable ROS bridge extensions to keep only ROS2 Bridge
44disable_extension("omni.isaac.ros_bridge")
45enable_extension("omni.isaac.ros2_bridge")
46
47# Update the simulation app with the new extensions
48simulation_app.update()
49
50# -------------------------------------------------------------------------------------------------
51# These lines are needed to restart the USD stage and make sure that the people extension is loaded
52# -------------------------------------------------------------------------------------------------
53import omni.usd
54omni.usd.get_context().new_stage()
55
56import numpy as np
57
58# Import the Pegasus API for simulating drones
59from pegasus.simulator.params import ROBOTS, SIMULATION_ENVIRONMENTS
60from pegasus.simulator.logic.interface.pegasus_interface import PegasusInterface
61from pegasus.simulator.logic.people.person import Person
62from pegasus.simulator.logic.people.person_controller import PersonController
63from pegasus.simulator.logic.graphical_sensors.monocular_camera import MonocularCamera
64from pegasus.simulator.logic.backends.px4_mavlink_backend import PX4MavlinkBackend, PX4MavlinkBackendConfig
65from pegasus.simulator.logic.backends.ros2_backend import ROS2Backend
66from pegasus.simulator.logic.vehicles.multirotor import Multirotor, MultirotorConfig
67from pegasus.simulator.logic.interface.pegasus_interface import PegasusInterface
68
69# Example controller class that make a person move in a circle around the origin of the world
70# Note: You could create a different controller with a different behaviour. For instance, you could:
71# 1. read the keyboard input to move the person around the world.
72# 2. read the target position from a ros topic,
73# 3. read the target position from a file,
74# 4. etc.
75class CirclePersonController(PersonController):
76
77 def __init__(self):
78 super().__init__()
79
80 self._radius = 5.0
81 self.gamma = 0.0
82 self.gamma_dot = 0.3
83
84 def update(self, dt: float):
85
86 # Update the reference position for the person to track
87 self.gamma += self.gamma_dot * dt
88
89 # Set the target position for the person to track
90 self._person.update_target_position([self._radius * np.cos(self.gamma), self._radius * np.sin(self.gamma), 0.0])
91
92
93# Auxiliary scipy and numpy modules
94from scipy.spatial.transform import Rotation
95
96# -------------------------------------------------------------------------------------------------
97# Define the PegasusApp class where the simulation will be run
98# -------------------------------------------------------------------------------------------------
99class PegasusApp:
100 """
101 A Template class that serves as an example on how to build a simple Isaac Sim standalone App.
102 """
103
104 def __init__(self):
105 """
106 Method that initializes the PegasusApp and is used to setup the simulation environment.
107 """
108
109 # Acquire the timeline that will be used to start/stop the simulation
110 self.timeline = omni.timeline.get_timeline_interface()
111
112 # Start the Pegasus Interface
113 self.pg = PegasusInterface()
114
115 # Acquire the World, .i.e, the singleton that controls that is a one stop shop for setting up physics,
116 # spawning asset primitives, etc.
117 self.pg._world = World(**self.pg._world_settings)
118 self.world = self.pg.world
119
120 # Launch one of the worlds provided by NVIDIA
121 #self.pg.load_environment(SIMULATION_ENVIRONMENTS["Curved Gridroom"])
122 self.pg.load_asset(SIMULATION_ENVIRONMENTS["Curved Gridroom"], "/World/layout")
123
124 # Check the available assets for people
125 people_assets_list = Person.get_character_asset_list()
126 for person in people_assets_list:
127 print(person)
128
129 # Create the controller to make on person walk around in circles
130 person_controller = CirclePersonController()
131 p1 = Person("person1", "original_male_adult_construction_05", init_pos=[3.0, 0.0, 0.0], init_yaw=1.0, controller=person_controller)
132
133 # Create a person without setting up a controller, and just setting a manual target position for it to track
134 p2 = Person("person2", "original_female_adult_business_02", init_pos=[2.0, 0.0, 0.0])
135 p2.update_target_position([10.0, 0.0, 0.0], 1.0)
136
137 # Create the vehicle
138 # Try to spawn the selected robot in the world to the specified namespace
139 config_multirotor = MultirotorConfig()
140 # # Create the multirotor configuration
141 mavlink_config = PX4MavlinkBackendConfig({
142 "vehicle_id": 0,
143 "px4_autolaunch": True,
144 "px4_dir": "/home/marcelo/PX4-Autopilot"
145 })
146 config_multirotor.backends = [
147 PX4MavlinkBackend(mavlink_config),
148 ROS2Backend(vehicle_id=1,
149 config={
150 "namespace": 'drone',
151 "pub_sensors": False,
152 "pub_graphical_sensors": True,
153 "pub_state": True,
154 "pub_tf": False,
155 "sub_control": False,})]
156
157 # Create a camera
158 config_multirotor.graphical_sensors = [MonocularCamera("camera", config={"update_rate": 60.0})]
159
160 Multirotor(
161 "/World/quadrotor",
162 ROBOTS['Iris'],
163 0,
164 [0.0, 0.0, 0.07],
165 Rotation.from_euler("XYZ", [0.0, 0.0, 0.0], degrees=True).as_quat(),
166 config=config_multirotor,
167 )
168
169 # Set the camera of the viewport to a nice position
170 self.pg.set_viewport_camera([5.0, 9.0, 6.5], [0.0, 0.0, 0.0])
171
172 # Reset the simulation environment so that all articulations (aka robots) are initialized
173 self.world.reset()
174
175 # Auxiliar variable for the timeline callback example
176 self.stop_sim = False
177
178 def run(self):
179 """
180 Method that implements the application main loop, where the physics steps are executed.
181 """
182
183 # Start the simulation
184 self.timeline.play()
185
186 # The "infinite" loop
187 while simulation_app.is_running() and not self.stop_sim:
188 # Update the UI of the app and perform the physics step
189 self.world.step(render=True)
190
191 # Cleanup and stop
192 carb.log_warn("PegasusApp Simulation App is closing.")
193 self.timeline.stop()
194 simulation_app.close()
195
196def main():
197
198 # Instantiate the template app
199 pg_app = PegasusApp()
200
201 # Run the application loop
202 pg_app.run()
203
204if __name__ == "__main__":
205 main()
2. Explanation
To start a pre-programmed simulation with moving people, you need to ensure that the People
extension provided by NVIDIA is enabled. Note, in Isaac 4.1.0 we also need to create a new stage for the people extension to start properly.
23from omni.isaac.core.utils.extensions import disable_extension, enable_extension
24
25EXTENSIONS_PEOPLE = [
26 'omni.anim.people',
27 'omni.anim.navigation.bundle',
28 'omni.anim.timeline',
29 'omni.anim.graph.bundle',
30 'omni.anim.graph.core',
31 'omni.anim.graph.ui',
32 'omni.anim.retarget.bundle',
33 'omni.anim.retarget.core',
34 'omni.anim.retarget.ui',
35 'omni.kit.scripting',
36 'omni.graph.io',
37 'omni.anim.curve.core',
38]
39
40for ext_people in EXTENSIONS_PEOPLE:
41 enable_extension(ext_people)
42
43# Enable/disable ROS bridge extensions to keep only ROS2 Bridge
44disable_extension("omni.isaac.ros_bridge")
45enable_extension("omni.isaac.ros2_bridge")
46
47# Update the simulation app with the new extensions
48simulation_app.update()
49# -------------------------------------------------------------------------------------------------
50# These lines are needed to restart the USD stage and make sure that the people extension is loaded
51# -------------------------------------------------------------------------------------------------
52import omni.usd
53omni.usd.get_context().new_stage()
We also need to import the Person
and the PersonController
classes. This follows the same strategy adopted for the vehicles and the control backends.
61from pegasus.simulator.logic.people.person import Person
62from pegasus.simulator.logic.people.person_controller import PersonController
The PersonController
is an interface class that a user defined controller must inherit from. This controller is responsible for defining the behavior of the person in the simulation.
In this example, our controller makes a person walk in circles, but you can define any behavior you want. For instance, you could:
read the keyboard input to move the person around the world.
read the target position from a ros topic,
read the target position from a file,
etc…
69# Example controller class that make a person move in a circle around the origin of the world
70# Note: You could create a different controller with a different behaviour. For instance, you could:
71# 1. read the keyboard input to move the person around the world.
72# 2. read the target position from a ros topic,
73# 3. read the target position from a file,
74# 4. etc.
75class CirclePersonController(PersonController):
76
77 def __init__(self):
78 super().__init__()
79
80 self._radius = 5.0
81 self.gamma = 0.0
82 self.gamma_dot = 0.3
83
84 def update(self, dt: float):
85
86 # Update the reference position for the person to track
87 self.gamma += self.gamma_dot * dt
88
89 # Set the target position for the person to track
90 self._person.update_target_position([self._radius * np.cos(self.gamma), self._radius * np.sin(self.gamma), 0.0])
Note
To check all the functions that you can implement in your controller, check the PersonController
class in the API reference.
The next step is to create a person in the simulation. But, you let’s imagine you don’t know which 3D models are available. Well, you can call the static method Person.get_character_asset_list()
function to list all the available models.
124 # Check the available assets for people
125 people_assets_list = Person.get_character_asset_list()
126 for person in people_assets_list:
127 print(person)
Now that you know which models you can load, you can create a person in the simulation. You can set the initial position, orientation, and the controller that will define the behavior of the person. Note that if you just want to send a person to a given position manually (without using a controller), you can! Just check “Person 2”.
129 # Create the controller to make on person walk around in circles
130 person_controller = CirclePersonController()
131 p1 = Person("person1", "original_male_adult_construction_05", init_pos=[3.0, 0.0, 0.0], init_yaw=1.0, controller=person_controller)
132
133 # Create a person without setting up a controller, and just setting a manual target position for it to track
134 p2 = Person("person2", "original_female_adult_business_02", init_pos=[2.0, 0.0, 0.0])
135 p2.update_target_position([10.0, 0.0, 0.0], 1.0)
3. Execution
Now let’s run the Python script:
ISAACSIM_PYTHON examples/9_people.py
This should open a stage with a blue ground-plane with an 3DR Iris vehicle model in it. The simulation should start playing automatically and the stage being rendered. You will see 2 people being simulated with one of them walking in circles.
If you open QGroundControl
you can control the vehicle.