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.

# Pre-defined configs
##
from isaaclab_assets.robots.anymal import ANYMAL_C_CFG  # isort: skip


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

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:

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