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