惯性测量单元 (IMU)

惯性测量单元 (IMU)#

IMU 传感器基本受力关系示意图

惯性测量单元 (IMU) 是用于测量物体加速度的一种传感器。这些传感器通常用于报告线性加速度和角速度,其工作原理与电子秤类似:它们报告的是**作用在传感器上的合力所产生的加速度**。

一个简单实现的 IMU 在静止于某个局部引力场中时,会报告一个由于重力导致的负加速度。然而,在大多数实际应用中,这并不是必需的,因此大多数真实的 IMU 传感器通常包含**重力偏置**,并假设设备运行在地球表面。Isaac Lab 提供的 IMU 也包含类似的偏置项,默认为 +g。这意味着如果你在仿真中添加一个 IMU 且不更改此偏置项,你将检测到一个与重力加速度方向相反的 \(+ 9.81 m/s^{2}\) 加速度。

设想一个简单的环境,其中 Anymal 四足机器人在其两条前腿上分别安装了一个 IMU。


@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)


def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene):
    """Run the simulator."""
    # Define simulation stepping
    sim_dt = sim.get_physics_dt()

在这里,我们显式地移除了其中一个传感器的偏置,然后在运行示例脚本时,通过可视化传感器数据来观察它对报告值的影响。

IMU 可视化效果

请注意,右前脚上的 IMU 具有显式的偏置 (0,0,0)。在可视化中,你会看到右侧 IMU 指示的加速度箭头随时间快速变化,而左侧 IMU 的箭头则始终沿着垂直轴指向一个固定方向。

以常规方式从传感器获取数据

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)

传感器报告的数值振荡直接源于其计算加速度的方式,即通过对仿真报告的相邻真实速度值进行有限差分近似计算。这一点可以从报告结果中看出(特别关注**线性加速度**),因为右前脚的加速度虽然很小,但明确为零。

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')
-------------------------------
imu_sensor.py的代码
  1# Copyright (c) 2022-2025, The Isaac Lab Project Developers.
  2# All rights reserved.
  3#
  4# SPDX-License-Identifier: BSD-3-Clause
  5
  6"""Launch Isaac Sim Simulator first."""
  7
  8import argparse
  9
 10from isaaclab.app import AppLauncher
 11
 12# add argparse arguments
 13parser = argparse.ArgumentParser(description="Example on using the IMU sensor.")
 14parser.add_argument("--num_envs", type=int, default=1, help="Number of environments to spawn.")
 15# append AppLauncher cli args
 16AppLauncher.add_app_launcher_args(parser)
 17# parse the arguments
 18args_cli = parser.parse_args()
 19
 20# launch omniverse app
 21app_launcher = AppLauncher(args_cli)
 22simulation_app = app_launcher.app
 23
 24"""Rest everything follows."""
 25
 26import torch
 27
 28import isaaclab.sim as sim_utils
 29from isaaclab.assets import AssetBaseCfg
 30from isaaclab.scene import InteractiveScene, InteractiveSceneCfg
 31from isaaclab.sensors import ImuCfg
 32from isaaclab.utils import configclass
 33
 34##
 35# Pre-defined configs
 36##
 37from isaaclab_assets.robots.anymal import ANYMAL_C_CFG  # isort: skip
 38
 39
 40@configclass
 41class ImuSensorSceneCfg(InteractiveSceneCfg):
 42    """Design the scene with sensors on the robot."""
 43
 44    # ground plane
 45    ground = AssetBaseCfg(prim_path="/World/defaultGroundPlane", spawn=sim_utils.GroundPlaneCfg())
 46
 47    # lights
 48    dome_light = AssetBaseCfg(
 49        prim_path="/World/Light", spawn=sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
 50    )
 51
 52    # robot
 53    robot = ANYMAL_C_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")
 54
 55    imu_RF = ImuCfg(prim_path="{ENV_REGEX_NS}/Robot/LF_FOOT", debug_vis=True)
 56
 57    imu_LF = ImuCfg(prim_path="{ENV_REGEX_NS}/Robot/RF_FOOT", gravity_bias=(0, 0, 0), debug_vis=True)
 58
 59
 60def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene):
 61    """Run the simulator."""
 62    # Define simulation stepping
 63    sim_dt = sim.get_physics_dt()
 64    sim_time = 0.0
 65    count = 0
 66
 67    # Simulate physics
 68    while simulation_app.is_running():
 69
 70        if count % 500 == 0:
 71            # reset counter
 72            count = 0
 73            # reset the scene entities
 74            # root state
 75            # we offset the root state by the origin since the states are written in simulation world frame
 76            # if this is not done, then the robots will be spawned at the (0, 0, 0) of the simulation world
 77            root_state = scene["robot"].data.default_root_state.clone()
 78            root_state[:, :3] += scene.env_origins
 79            scene["robot"].write_root_link_pose_to_sim(root_state[:, :7])
 80            scene["robot"].write_root_com_velocity_to_sim(root_state[:, 7:])
 81            # set joint positions with some noise
 82            joint_pos, joint_vel = (
 83                scene["robot"].data.default_joint_pos.clone(),
 84                scene["robot"].data.default_joint_vel.clone(),
 85            )
 86            joint_pos += torch.rand_like(joint_pos) * 0.1
 87            scene["robot"].write_joint_state_to_sim(joint_pos, joint_vel)
 88            # clear internal buffers
 89            scene.reset()
 90            print("[INFO]: Resetting robot state...")
 91        # Apply default actions to the robot
 92        # -- generate actions/commands
 93        targets = scene["robot"].data.default_joint_pos
 94        # -- apply action to the robot
 95        scene["robot"].set_joint_position_target(targets)
 96        # -- write data to sim
 97        scene.write_data_to_sim()
 98        # perform step
 99        sim.step()
100        # update sim-time
101        sim_time += sim_dt
102        count += 1
103        # update buffers
104        scene.update(sim_dt)
105
106        # print information from the sensors
107        print("-------------------------------")
108        print(scene["imu_LF"])
109        print("Received linear velocity: ", scene["imu_LF"].data.lin_vel_b)
110        print("Received angular velocity: ", scene["imu_LF"].data.ang_vel_b)
111        print("Received linear acceleration: ", scene["imu_LF"].data.lin_acc_b)
112        print("Received angular acceleration: ", scene["imu_LF"].data.ang_acc_b)
113        print("-------------------------------")
114        print(scene["imu_RF"])
115        print("Received linear velocity: ", scene["imu_RF"].data.lin_vel_b)
116        print("Received angular velocity: ", scene["imu_RF"].data.ang_vel_b)
117        print("Received linear acceleration: ", scene["imu_RF"].data.lin_acc_b)
118        print("Received angular acceleration: ", scene["imu_RF"].data.ang_acc_b)
119
120
121def main():
122    """Main function."""
123
124    # Initialize the simulation context
125    sim_cfg = sim_utils.SimulationCfg(dt=0.005, device=args_cli.device)
126    sim = sim_utils.SimulationContext(sim_cfg)
127    # Set main camera
128    sim.set_camera_view(eye=[3.5, 3.5, 3.5], target=[0.0, 0.0, 0.0])
129    # design scene
130    scene_cfg = ImuSensorSceneCfg(num_envs=args_cli.num_envs, env_spacing=2.0)
131    scene = InteractiveScene(scene_cfg)
132    # Play the simulator
133    sim.reset()
134    # Now we are ready!
135    print("[INFO]: Setup complete...")
136    # Run the simulator
137    run_simulator(sim, scene)
138
139
140if __name__ == "__main__":
141    # run the main function
142    main()
143    # close sim app
144    simulation_app.close()