Pose Velocity Acceleration (PVA) Sensor

Pose Velocity Acceleration (PVA) Sensor#

The Pose Velocity Acceleration (PVA) sensor is a ground-truth sensor for reading the kinematic state of a frame in the simulation. It reports the sensor pose in the world frame, projected gravity, linear and angular velocities in the sensor frame, and coordinate accelerations in the sensor frame. Unlike Imu, the PVA sensor does not model proper acceleration from an accelerometer. Use the IMU sensor when the observation should include accelerometer-like gravity bias behavior.

The sensor can be attached to a rigid body or to a child prim under a rigid-body ancestor. If the configured prim is not itself rigid, Isaac Lab queries the closest rigid ancestor and composes the fixed transform to the requested prim with the configured sensor offset.

Consider a simple environment with an Anymal Quadruped equipped with PVA sensors on its front feet.

class PvaSensorSceneCfg(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")

    pva_LF = PvaCfg(prim_path="{ENV_REGEX_NS}/Robot/LF_FOOT", debug_vis=True)

    pva_RF = PvaCfg(prim_path="{ENV_REGEX_NS}/Robot/RF_FOOT", debug_vis=True)

Retrieving values from the sensor follows the same pattern as the other Isaac Lab sensors. The data fields are exposed as ProxyArray buffers and can be converted to Torch tensors with the torch property.

pva_data = scene["pva_LF"].data
print("Pose in world frame: ", pva_data.pose_w.torch)
print("Linear velocity in PVA frame: ", pva_data.lin_vel_b.torch)
print("Angular velocity in PVA frame: ", pva_data.ang_vel_b.torch)
print("Linear acceleration in PVA frame: ", pva_data.lin_acc_b.torch)
print("Angular acceleration in PVA frame: ", pva_data.ang_acc_b.torch)
print("Projected gravity in PVA frame: ", pva_data.projected_gravity_b.torch)

The complete demo can be run with:

./isaaclab.sh -p scripts/demos/sensors/pva_sensor.py
Code for pva_sensor.py
  1# Copyright (c) 2022-2026, 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"""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 PVA 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# demos should open Kit visualizer by default
 18parser.set_defaults(visualizer=["kit"])
 19# parse the arguments
 20args_cli = parser.parse_args()
 21
 22# launch omniverse app
 23app_launcher = AppLauncher(args_cli)
 24simulation_app = app_launcher.app
 25
 26"""Rest everything follows."""
 27
 28import torch
 29
 30import isaaclab.sim as sim_utils
 31from isaaclab.assets import AssetBaseCfg
 32from isaaclab.scene import InteractiveScene, InteractiveSceneCfg
 33from isaaclab.sensors import PvaCfg
 34from isaaclab.utils import configclass
 35
 36##
 37# Pre-defined configs
 38##
 39from isaaclab_assets.robots.anymal import ANYMAL_C_CFG  # isort: skip
 40
 41
 42@configclass
 43class PvaSensorSceneCfg(InteractiveSceneCfg):
 44    """Design the scene with sensors on the robot."""
 45
 46    # ground plane
 47    ground = AssetBaseCfg(prim_path="/World/defaultGroundPlane", spawn=sim_utils.GroundPlaneCfg())
 48
 49    # lights
 50    dome_light = AssetBaseCfg(
 51        prim_path="/World/Light", spawn=sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
 52    )
 53
 54    # robot
 55    robot = ANYMAL_C_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")
 56
 57    pva_LF = PvaCfg(prim_path="{ENV_REGEX_NS}/Robot/LF_FOOT", debug_vis=True)
 58
 59    pva_RF = PvaCfg(prim_path="{ENV_REGEX_NS}/Robot/RF_FOOT", debug_vis=True)
 60
 61
 62def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene):
 63    """Run the simulator."""
 64    # Define simulation stepping
 65    sim_dt = sim.get_physics_dt()
 66    sim_time = 0.0
 67    count = 0
 68
 69    # Simulate physics
 70    while simulation_app.is_running():
 71        if count % 500 == 0:
 72            # reset counter
 73            count = 0
 74            # reset the scene entities
 75            # root state
 76            # we offset the root state by the origin since the states are written in simulation world frame
 77            # if this is not done, then the robots will be spawned at the (0, 0, 0) of the simulation world
 78            root_pose = scene["robot"].data.default_root_pose.torch.clone()
 79            root_pose[:, :3] += scene.env_origins
 80            scene["robot"].write_root_link_pose_to_sim_index(root_pose=root_pose)
 81            root_vel = scene["robot"].data.default_root_vel.torch.clone()
 82            scene["robot"].write_root_com_velocity_to_sim_index(root_velocity=root_vel)
 83            # set joint positions with some noise
 84            joint_pos, joint_vel = (
 85                scene["robot"].data.default_joint_pos.torch.clone(),
 86                scene["robot"].data.default_joint_vel.torch.clone(),
 87            )
 88            joint_pos += torch.rand_like(joint_pos) * 0.1
 89            scene["robot"].write_joint_position_to_sim_index(position=joint_pos)
 90            scene["robot"].write_joint_velocity_to_sim_index(velocity=joint_vel)
 91            # clear internal buffers
 92            scene.reset()
 93            print("[INFO]: Resetting robot state...")
 94        # Apply default actions to the robot
 95        # -- generate actions/commands
 96        targets = scene["robot"].data.default_joint_pos.torch
 97        # -- apply action to the robot
 98        scene["robot"].set_joint_position_target_index(target=targets)
 99        # -- write data to sim
100        scene.write_data_to_sim()
101        # perform step
102        sim.step()
103        # update sim-time
104        sim_time += sim_dt
105        count += 1
106        # update buffers
107        scene.update(sim_dt)
108
109        # print information from the sensors
110        print("-------------------------------")
111        print(scene["pva_LF"])
112        print("Received linear velocity: ", scene["pva_LF"].data.lin_vel_b)
113        print("Received angular velocity: ", scene["pva_LF"].data.ang_vel_b)
114        print("Received linear acceleration: ", scene["pva_LF"].data.lin_acc_b)
115        print("Received angular acceleration: ", scene["pva_LF"].data.ang_acc_b)
116        print("-------------------------------")
117        print(scene["pva_RF"])
118        print("Received linear velocity: ", scene["pva_RF"].data.lin_vel_b)
119        print("Received angular velocity: ", scene["pva_RF"].data.ang_vel_b)
120        print("Received linear acceleration: ", scene["pva_RF"].data.lin_acc_b)
121        print("Received angular acceleration: ", scene["pva_RF"].data.ang_acc_b)
122
123
124def main():
125    """Main function."""
126
127    # Initialize the simulation context
128    sim_cfg = sim_utils.SimulationCfg(dt=0.005, device=args_cli.device)
129    sim = sim_utils.SimulationContext(sim_cfg)
130    # Set main camera
131    sim.set_camera_view(eye=[3.5, 3.5, 3.5], target=[0.0, 0.0, 0.0])
132    # design scene
133    scene_cfg = PvaSensorSceneCfg(num_envs=args_cli.num_envs, env_spacing=2.0)
134    scene = InteractiveScene(scene_cfg)
135    # Play the simulator
136    sim.reset()
137    # Now we are ready!
138    print("[INFO]: Setup complete...")
139    # Run the simulator
140    run_simulator(sim, scene)
141
142
143if __name__ == "__main__":
144    # run the main function
145    main()
146    # close sim app
147    simulation_app.close()