IMU#
An inertial measurement unit (IMU) reports the motion of a rigid link as an onboard sensor would: linear acceleration from an accelerometer, angular velocity from a gyroscope, and, optionally, the local magnetic field from a magnetometer. Use it to feed state estimators, train locomotion policies on realistic proprioception, or log ground-truth dynamics.
The complete script is examples/sensors/imu_franka.py. This page explains what the sensor measures and how to configure it; see Sensors for the pipeline every sensor shares.
Minimal example#
An IMU is attached to one link of a rigid entity. Identify the link by its owning entity and the link’s local index, then read the sensor after the scene is built:
end_effector = franka.get_link("hand")
imu = scene.add_sensor(
gs.sensors.IMU(
entity_idx=franka.idx,
link_idx_local=end_effector.idx_local,
)
)
scene.build()
for _ in range(1000):
scene.step()
data = imu.read() # IMUReturnType(lin_acc, ang_vel, mag)
acc = data.lin_acc # m/s^2, shape ([n_envs,] 3)
read() returns an IMUReturnType, a NamedTuple with three fields, each a tensor of shape ([n_envs,] 3):
Field |
Meaning |
Units |
|---|---|---|
|
linear acceleration (accelerometer) |
m/s² |
|
angular velocity (gyroscope) |
rad/s |
|
magnetic field (magnetometer) |
Tesla |
Frame and conventions#
All three fields are expressed in the sensor’s body frame, the frame of the attached link, rotated by any euler_offset you supply. They are not in the world frame, so they rotate with the link.
The accelerometer reports specific force: coordinate acceleration minus gravity. A sensor at rest therefore reads roughly (0, 0, 9.81) m/s² (the reaction to gravity along its local up axis), not zero. This matches real hardware, which cannot distinguish free fall from weightlessness.
When pos_offset moves the sensor off the link’s origin, Genesis World adds the tangential and centripetal terms (α × r and ω × (ω × r)), so a spinning link produces the acceleration an IMU would actually feel at that offset.
Attaching and offsetting the sensor#
entity_idx and link_idx_local place the sensor on a link; pos_offset and euler_offset move and rotate it relative to that link’s frame:
imu = scene.add_sensor(
gs.sensors.IMU(
entity_idx=franka.idx,
link_idx_local=end_effector.idx_local,
pos_offset=(0.0, 0.0, 0.15), # meters, in the link frame
# euler_offset=(0, 0, 0), # extrinsic x-y-z, degrees
draw_debug=True, # draw acc/gyro/mag arrows in the viewer
)
)
With draw_debug=True, the viewer shows three arrows at the sensor: red for acceleration, green for angular velocity, blue for the magnetic field.
Modeling sensor imperfections#
By default the IMU is ideal. Each channel (acc_*, gyro_*, and mag_*) takes the same family of parameters to reproduce real-hardware error, applied per axis:
imu = scene.add_sensor(
gs.sensors.IMU(
entity_idx=franka.idx,
link_idx_local=end_effector.idx_local,
pos_offset=(0.0, 0.0, 0.15),
# noise parameters
acc_cross_axis_coupling=(0.0, 0.01, 0.02),
gyro_cross_axis_coupling=(0.03, 0.04, 0.05),
acc_noise=(0.01, 0.01, 0.01),
gyro_noise=(0.01, 0.01, 0.01),
acc_random_walk=(0.001, 0.001, 0.001),
gyro_random_walk=(0.001, 0.001, 0.001),
delay=0.01,
jitter=0.01,
draw_debug=True,
)
)
Parameter |
Effect |
|---|---|
|
standard deviation of per-axis Gaussian white noise |
|
constant additive offset per axis |
|
standard deviation of the drift that accumulates over time |
|
axis misalignment; a scalar, a 3-vector, or a full 3×3 rotation matrix |
|
quantization step; |
Two timing parameters, shared across channels, are given in seconds:
delay: how far behind real time a read lags. Must be a multiple of the simulation timestepdt.jitter: a random extra delay sampled uniformly in[0, jitter)each step. Cannot exceeddelay.
The magnetometer also reads a global field, set by magnetic_field (default (0.0, 0.0, 0.5) T in the world frame) and returned in the body frame.
Reading measured versus ground-truth data#
read() returns the value with all configured imperfections applied. read_ground_truth() returns the same quantities with no noise, bias, drift, or delay, which is useful as a training target or for validation:
print("Ground truth data:")
print(imu.read_ground_truth())
print("Measured data:")
print(imu.read())
Both accept an optional envs_idx to select a subset of environments, and both are idempotent within a step: repeated calls in one control-loop timestep return the same value.
See also#
Sensors: the shared read/record pipeline,
history_length, and batchedread_sensors().Recording data: save IMU streams alongside the simulation.