Pose Engine output
Pose Engine generates output that can be trusted. Pose Engine does not generate an output when in an unknown state or if a solution can not be accurately determined.
Offline Pose Engine output
Offline pose results (from the remote service or on-prem Docker) can be delivered in an arbitrary format: CSV, standard ROS messages or custom messages in a ROS 1 bag or ROS 2 bag, or some other custom format.
Local map pose
World-from-body rigid transform in the local map frame, plus body-frame velocities.
Fields:
| Field | Description |
|---|---|
time_s |
Time of validity (seconds) |
px_m, py_m, pz_m |
Position in local map frame (meters) |
rx_rad, ry_rad, rz_rad |
Rotation as so(3) logarithm, see Rotation convention |
vx_mps, vy_mps, vz_mps |
Body-frame linear velocity: +X forward, +Y left, +Z up (m/s) |
wx_rps, wy_rps, wz_rps |
Body-frame angular velocity: roll, pitch, yaw rate (rad/s) |
Example CSV:
# world-from-body rigid transform, body-frame velocities
# world frame: local map origin (set at Platform Setup)
# <customer>_<log-id>_<sensor-cf>.local.csv
time_s, px_m, py_m, pz_m, rx_rad, ry_rad, rz_rad, vx_mps, vy_mps, vz_mps, wx_rps, wy_rps, wz_rps
1776371688.261120081, -0.991453, -13.979609, 0.898994, -0.013059700, -0.004910992, -0.945530403, 0.007306, -0.004017, 0.001259, -0.000613, -0.000246, 0.001082
Example ROS2 nav_msgs/Odometry message:
header:
stamp:
sec: 1776371688
nanosec: 261120081
frame_id: "local_map"
child_frame_id: "base_link" # matches <sensor-cf>
pose:
pose:
position:
x: -0.991453
y: -13.979609
z: 0.898994
orientation:
x: 0.000000 # quaternion from rotvec [-0.013059700, -0.004910992, -0.945530403]
y: -0.006530
z: -0.471857
w: 0.881679
covariance: [...]
twist:
twist:
linear:
x: 0.007306
y: -0.004017
z: 0.001259
angular:
x: -0.000613
y: -0.000246
z: 0.001082
covariance: [...]
ECEF pose
World-from-body rigid transform in the ECEF frame. Only generated when GNSS/INS data was present during mapping or is present in the input data.
Fields:
| Field | Description |
|---|---|
time_s |
Time of validity (seconds) |
px_m, py_m, pz_m |
Position in ECEF frame (meters) |
rx_rad, ry_rad, rz_rad |
Rotation as so(3) logarithm, see Rotation convention |
Example CSV:
# world-from-body rigid transform
# world frame: ECEF (only available if GNSS data is provided)
# <customer>_<log-id>_<sensor-cf>.ecef.csv
time_s, px_m, py_m, pz_m, rx_rad, ry_rad, rz_rad
1776371688.261120081, -2687738.311, -4291128.930, 3865469.473, 0.968923418, 0.190917898, -1.392946097
Example ROS2 geometry_msgs/PoseWithCovarianceStamped message:
header:
stamp:
sec: 1776371688
nanosec: 261120081
frame_id: "ecef"
pose:
pose:
position:
x: -2687738.311
y: -4291128.930
z: 3865469.473
orientation:
x: 0.435439 # quaternion from rotvec [0.968923418, 0.190917898, -1.392946097]
y: 0.085769
z: -0.626385
w: 0.639310
covariance: [...] # 6x6 row-major [x, y, z, rx, ry, rz]
Output format
Delivery format depends on your integration:
- CSV: typically used for initial evaluation and offline analysis
- ROS messages: standard nav_msgs/Odometry or custom message types, recorded to a ROS 1 bag or ROS 2 bag
- Custom formats: output can be serialized to match non-ROS pub/sub architectures or proprietary APIs on request
The CSV examples above use flat column naming that maps directly to the equivalent message fields regardless of delivery format.
Rotation convention
Rotation is stored as the so(3) logarithm (axis-angle vector form). The norm of [rx, ry, rz] is the rotation angle in radians; the unit vector is the rotation axis. Other rotation formats are available upon request.
To recover the rotation matrix from the axis-angle vector:
import numpy as np
def rodrigues(w):
t = np.linalg.norm(w)
if t < 1e-9:
return np.eye(3)
skew = np.array([[ 0, -w[2], w[1]],
[ w[2], 0, -w[0]],
[-w[1], w[0], 0]])
return np.eye(3) + np.sin(t)/t*skew + (1-np.cos(t))/t**2 * skew@skew
# Equivalent library calls:
# scipy.spatial.transform.Rotation.from_rotvec(w).as_matrix()
# kornia.geometry.conversions.angle_axis_to_rotation_matrix(w)
# pypose.mat_from_rotvec(w)
Applying the rigid transform
To transform a point from the sensor (body) frame to the world frame:
# w = [rx_rad, ry_rad, rz_rad] from a CSV row
# p = [px_m, py_m, pz_m] from a CSV row
R = rodrigues(w)
xyz_world = R @ xyz_body + p
Acausal and causal offline outputs
The remote service and on-prem Docker both support two output modes:
- Acausal: Pose Engine has access to the full data record. Outputs are at arbitrary timestamps and in an arbitrary coordinate frame. No odometry is generated.
- Causal (online emulation): Outputs are generated as if Pose Engine were running online, in an arbitrary coordinate frame. Includes odometric pose output.
Online Pose Engine output
Online pose results are standard ROS messages or other message type managed by non-ROS middleware published at configured rates and coordinate frames.
Odometric pose
| Property | Value |
|---|---|
| ROS message | nav_msgs/Odometry (see example) |
| Nominal output frequency | 100 Hz |
| Nominal output latency | <10 ms |
| Error behavior | Drifts smoothly but without bound from starting pose |
Timestamped bounded-error velocity estimates. Integrated into a pose, the output drifts smoothly but without bound from the starting pose, and does not reference the map.
Map-relative pose
| Property | Value |
|---|---|
| ROS message | geometry_msgs/PoseWithCovarianceStamped (see example) |
| Nominal output frequency | 10 Hz |
| Nominal output latency | <100 ms |
| Error behavior | Bounded error relative to map |
Timestamped 6DoF map-from-robot poses with bounded error. Referenced to the local map frame.
ECEF pose
| Property | Value |
|---|---|
| ROS message | geometry_msgs/PoseWithCovarianceStamped (see example) |
| Nominal output frequency | 10 Hz |
| Nominal output latency | <100 ms |
| Error behavior | Bounded error relative to absolute frame |
Timestamped 6DoF ECEF pose. Available only if mapping or localization vehicle has GNSS or INS.