Inertial Measurement Unit (IMU)#

Inertial Measurement Units (IMUs) are a type of sensor for measuring the acceleration of an object. These sensors are traditionally designed report linear accelerations and angular velocities, and function on similar principles to that of a digital scale: They report accelerations derived from net force acting on the sensor.
A naive implementation of an IMU would report a negative acceleration due to gravity while the sensor is at rest in some local gravitational field. This is not generally needed for most practical applications, and so most real IMU sensors often include a gravity bias and assume that the device is operating on the surface of the Earth. The IMU we provide in Isaac Lab includes a similar bias term, which defaults to +g. This means that if you add an IMU to your simulation, and do not change this bias term, you will detect an acceleration of \(+ 9.81 m/s^{2}\) anti-parallel to gravity acceleration.
Consider a simple environment with an Anymal Quadruped equipped with an IMU on each of its two front feet.
##
# Pre-defined configs
##
from isaaclab_assets.robots.anymal import ANYMAL_C_CFG # isort: skip
@configclass
class ImuSensorSceneCfg(InteractiveSceneCfg):
"""Design the scene with sensors on the robot."""
# ground plane
ground = AssetBaseCfg(prim_path="/World/defaultGroundPlane", spawn=sim_utils.GroundPlaneCfg())
# lights
dome_light = AssetBaseCfg(
prim_path="/World/Light", spawn=sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
)
# robot
robot = ANYMAL_C_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")
imu_RF = ImuCfg(prim_path="{ENV_REGEX_NS}/Robot/LF_FOOT", debug_vis=True)
imu_LF = ImuCfg(prim_path="{ENV_REGEX_NS}/Robot/RF_FOOT", gravity_bias=(0, 0, 0), debug_vis=True)
Here we have explicitly removed the bias from one of the sensors, and we can see how this affects the reported values by visualizing the sensor when we run the sample script

Notice that the right front foot explicitly has a bias of (0,0,0). In the visualization, you should see that the arrow indicating the acceleration from the right IMU rapidly changes over time, while the arrow visualizing the left IMU points constantly along the vertical axis.
Retrieving values form the sensor is done in the usual way
def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene):
.
.
.
# Simulate physics
while simulation_app.is_running():
.
.
.
# print information from the sensors
print("-------------------------------")
print(scene["imu_LF"])
print("Received linear velocity: ", scene["imu_LF"].data.lin_vel_b)
print("Received angular velocity: ", scene["imu_LF"].data.ang_vel_b)
print("Received linear acceleration: ", scene["imu_LF"].data.lin_acc_b)
print("Received angular acceleration: ", scene["imu_LF"].data.ang_acc_b)
print("-------------------------------")
print(scene["imu_RF"])
print("Received linear velocity: ", scene["imu_RF"].data.lin_vel_b)
print("Received angular velocity: ", scene["imu_RF"].data.ang_vel_b)
print("Received linear acceleration: ", scene["imu_RF"].data.lin_acc_b)
print("Received angular acceleration: ", scene["imu_RF"].data.ang_acc_b)
The oscillations in the values reported by the sensor are a direct result of of how the sensor calculates the acceleration, which is through a finite difference approximation between adjacent ground truth velocity values as reported by the sim. We can see this in the reported result (pay attention to the linear acceleration) because the acceleration from the right foot is small, but explicitly zero.
Imu sensor @ '/World/envs/env_.*/Robot/LF_FOOT':
view type : <class 'omni.physics.tensors.impl.api.RigidBodyView'>
update period (s) : 0.0
number of sensors : 1
Received linear velocity: tensor([[ 0.0203, -0.0054, 0.0380]], device='cuda:0')
Received angular velocity: tensor([[-0.0104, -0.1189, 0.0080]], device='cuda:0')
Received linear acceleration: tensor([[ 4.8344, -0.0205, 8.5305]], device='cuda:0')
Received angular acceleration: tensor([[-0.0389, -0.0262, -0.0045]], device='cuda:0')
-------------------------------
Imu sensor @ '/World/envs/env_.*/Robot/RF_FOOT':
view type : <class 'omni.physics.tensors.impl.api.RigidBodyView'>
update period (s) : 0.0
number of sensors : 1
Received linear velocity: tensor([[0.0244, 0.0077, 0.0431]], device='cuda:0')
Received angular velocity: tensor([[ 0.0122, -0.1360, -0.0042]], device='cuda:0')
Received linear acceleration: tensor([[-0.0018, 0.0010, -0.0032]], device='cuda:0')
Received angular acceleration: tensor([[-0.0373, -0.0050, -0.0053]], device='cuda:0')
-------------------------------
Code for imu_sensor.py
1# Copyright (c) 2022-2025, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
2# All rights reserved.
3#
4# SPDX-License-Identifier: BSD-3-Clause
5
6# Copyright (c) 2022-2025, The Isaac Lab Project Developers.
7# All rights reserved.
8#
9# SPDX-License-Identifier: BSD-3-Clause
10
11"""Launch Isaac Sim Simulator first."""
12
13import argparse
14
15from isaaclab.app import AppLauncher
16
17# add argparse arguments
18parser = argparse.ArgumentParser(description="Example on using the IMU sensor.")
19parser.add_argument("--num_envs", type=int, default=1, help="Number of environments to spawn.")
20# append AppLauncher cli args
21AppLauncher.add_app_launcher_args(parser)
22# parse the arguments
23args_cli = parser.parse_args()
24
25# launch omniverse app
26app_launcher = AppLauncher(args_cli)
27simulation_app = app_launcher.app
28
29"""Rest everything follows."""
30
31import torch
32
33import isaaclab.sim as sim_utils
34from isaaclab.assets import AssetBaseCfg
35from isaaclab.scene import InteractiveScene, InteractiveSceneCfg
36from isaaclab.sensors import ImuCfg
37from isaaclab.utils import configclass
38
39##
40# Pre-defined configs
41##
42from isaaclab_assets.robots.anymal import ANYMAL_C_CFG # isort: skip
43
44
45@configclass
46class ImuSensorSceneCfg(InteractiveSceneCfg):
47 """Design the scene with sensors on the robot."""
48
49 # ground plane
50 ground = AssetBaseCfg(prim_path="/World/defaultGroundPlane", spawn=sim_utils.GroundPlaneCfg())
51
52 # lights
53 dome_light = AssetBaseCfg(
54 prim_path="/World/Light", spawn=sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
55 )
56
57 # robot
58 robot = ANYMAL_C_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")
59
60 imu_RF = ImuCfg(prim_path="{ENV_REGEX_NS}/Robot/LF_FOOT", debug_vis=True)
61
62 imu_LF = ImuCfg(prim_path="{ENV_REGEX_NS}/Robot/RF_FOOT", gravity_bias=(0, 0, 0), debug_vis=True)
63
64
65def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene):
66 """Run the simulator."""
67 # Define simulation stepping
68 sim_dt = sim.get_physics_dt()
69 sim_time = 0.0
70 count = 0
71
72 # Simulate physics
73 while simulation_app.is_running():
74
75 if count % 500 == 0:
76 # reset counter
77 count = 0
78 # reset the scene entities
79 # root state
80 # we offset the root state by the origin since the states are written in simulation world frame
81 # if this is not done, then the robots will be spawned at the (0, 0, 0) of the simulation world
82 root_state = scene["robot"].data.default_root_state.clone()
83 root_state[:, :3] += scene.env_origins
84 scene["robot"].write_root_link_pose_to_sim(root_state[:, :7])
85 scene["robot"].write_root_com_velocity_to_sim(root_state[:, 7:])
86 # set joint positions with some noise
87 joint_pos, joint_vel = (
88 scene["robot"].data.default_joint_pos.clone(),
89 scene["robot"].data.default_joint_vel.clone(),
90 )
91 joint_pos += torch.rand_like(joint_pos) * 0.1
92 scene["robot"].write_joint_state_to_sim(joint_pos, joint_vel)
93 # clear internal buffers
94 scene.reset()
95 print("[INFO]: Resetting robot state...")
96 # Apply default actions to the robot
97 # -- generate actions/commands
98 targets = scene["robot"].data.default_joint_pos
99 # -- apply action to the robot
100 scene["robot"].set_joint_position_target(targets)
101 # -- write data to sim
102 scene.write_data_to_sim()
103 # perform step
104 sim.step()
105 # update sim-time
106 sim_time += sim_dt
107 count += 1
108 # update buffers
109 scene.update(sim_dt)
110
111 # print information from the sensors
112 print("-------------------------------")
113 print(scene["imu_LF"])
114 print("Received linear velocity: ", scene["imu_LF"].data.lin_vel_b)
115 print("Received angular velocity: ", scene["imu_LF"].data.ang_vel_b)
116 print("Received linear acceleration: ", scene["imu_LF"].data.lin_acc_b)
117 print("Received angular acceleration: ", scene["imu_LF"].data.ang_acc_b)
118 print("-------------------------------")
119 print(scene["imu_RF"])
120 print("Received linear velocity: ", scene["imu_RF"].data.lin_vel_b)
121 print("Received angular velocity: ", scene["imu_RF"].data.ang_vel_b)
122 print("Received linear acceleration: ", scene["imu_RF"].data.lin_acc_b)
123 print("Received angular acceleration: ", scene["imu_RF"].data.ang_acc_b)
124
125
126def main():
127 """Main function."""
128
129 # Initialize the simulation context
130 sim_cfg = sim_utils.SimulationCfg(dt=0.005, device=args_cli.device)
131 sim = sim_utils.SimulationContext(sim_cfg)
132 # Set main camera
133 sim.set_camera_view(eye=[3.5, 3.5, 3.5], target=[0.0, 0.0, 0.0])
134 # design scene
135 scene_cfg = ImuSensorSceneCfg(num_envs=args_cli.num_envs, env_spacing=2.0)
136 scene = InteractiveScene(scene_cfg)
137 # Play the simulator
138 sim.reset()
139 # Now we are ready!
140 print("[INFO]: Setup complete...")
141 # Run the simulator
142 run_simulator(sim, scene)
143
144
145if __name__ == "__main__":
146 # run the main function
147 main()
148 # close sim app
149 simulation_app.close()