Getting started¶
The Ouster SDK provides a Pose Optimizer API that allows developers to refine SLAM-generated trajectories and improve point cloud quality through constraint-based optimization.
The API is built around two primary components:
PoseOptimizer – The main class that processes trajectories, applies constraints, runs optimization algorithms export the refined poses
Constraints – Various constraint types (
PoseToPoseConstraint,PointToPointConstraint,AbsolutePoseConstraint,AbsolutePointConstraint) that define relationships for optimization
Prerequisites¶
Before running the Pose Optimizer, download the SLAM-processed OSF sample (loop.osf) from here . You will also need a JSON configuration file that defines both the solver parameters and the optimization constraints described below—you can grab a ready-to-test example for loop.osf at Example JSON File.
JSON Configuration File¶
The Pose Optimizer uses a single JSON configuration file to control two aspects of the run:
Solver parameters – Tunables that influence the optimizer’s behavior (iteration limits, loss configuration, trajectory constraint weights, etc.).
Constraint definitions – Rules that guide trajectory refinement by enforcing relationships between poses or specific points in the map. During optimization, these constraints let you impose limits, incorporate prior knowledge, or keep loop closures/keyframes consistent.
Constraint JSON File Structure¶
A valid constraint configuration file must contain:
key_frame_distance– Defines the spacing between key nodes (meters). Smaller values create more keyframes for finer corrections, at the cost of longer runtime. Value 0 means that all poses are keyframes.constraints– A list of constraint objects. Each entry must follow one of the schemas explained later on this page (ABSOLUTE_POSE,ABSOLUTE_POINT,POINT_TO_POINT,POSE_TO_POSE).
Example:
{
"key_frame_distance": 1.0,
"constraints": [
// constraint objects described in the next sections
]
}
Solver Parameters¶
The JSON file includes the following solver parameters with their default values:
Parameter |
Default |
Description |
|---|---|---|
|
200 |
Maximum solver iterations before termination |
|
TRIVIAL_LOSS |
Robust loss function: HUBER_LOSS, CAUCHY_LOSS, SOFT_L_ONE_LOSS, ARCTAN_LOSS, TRIVIAL_LOSS |
|
1.0 |
Scaling parameter for robust loss function (higher = less sensitive to outliers) |
|
1.0 |
Weight for rotational constraints during trajectory optimization |
|
1.0 |
Weight for translational constraints during trajectory optimization |
|
1.0 |
Distance threshold for keyframe selection (in meters) |
|
true |
Enable/disable solver progress printout during optimization |
|
false |
Fix the first node of the trajectory during optimization |
Constraint Definitions¶
Constraints represent known relationships between poses (position and orientation) or points (specific measured locations) in the trajectory that should be maintained or enforced during optimization. By applying these known relationships, we can refine the trajectory and the corresponding map, removing for drift and achieving global consistency across the entire path
The Pose Optimizer supports four constraint types currently:
Constraint Type |
Use Cases |
Frame selection details |
|---|---|---|
|
Constrain relative pose between two frame poses. Useful for loop closure refinement |
The timestamps in constraint have to match the timestamps of the first pose in each frame |
|
Match two selected points between two frames. Useful for loop closure or feature alignment |
The point indexes have to match the row and column indices of the staggered 2D image in each frame |
|
Fix poses to known world coordinates like GPS integration or reference frame alignment |
The timestamp can be any time within the whole recording duration |
|
Constrain a point to surveyed positions for Landmark, known targets alignment |
The point indexes have to match the row and column indices of the staggered 2D image in each frame |
Note
If neither an ABSOLUTE_POSE nor an ABSOLUTE_POINT constraint is provided, the optimizer automatically fixes the first node in the trajectory to keep the problem well-posed. The solver parameter fix_first_node in the config overrides this behavior (set it to true to always fix the first node, or false to make it free to move even absolute constraints exist).
Automatic Constraint Helpers¶
In addition to loading constraints from JSON or constructing them manually, the Pose Optimizer provides helper workflows for common refinement tasks:
- Loop-Closure Detection
PoseOptimizer.add_relative_loop_constraints(min_distance_m, cell_size_m, icp_score_threshold)walks the current key-frame trajectory, identifies spatial revisits with a grid-based hash, and adds loop-closurePOSE_TO_POSEconstraints automatically. It returns the number of constraints added. See the API page for code examples.- CLI Wrappers
ouster-cli source ... pose_optimize --auto-gpsand--auto-loopexpose the same workflows from the command line. In batch mode the combined flow solves once after GPS alignment before running loop detection; in viz mode loop generation is deferred until you pressL.
ABSOLUTE_POSE¶
Defines an absolute pose measurement relative to the world coordinate frame.
Required fields:
type: Must be exactly"ABSOLUTE_POSE".timestamp: Any timestamp within the OSF recording window (it does not have to match a frame’s first column).pose: One of:A dictionary with keys:
rx,ry,rz,x,y,z(rotation angles in radians, applied in X-Y-Z order).A list of 6 values (Euler angles in radians with X-Y-Z order and position) or 16 values (4x4 matrix).
Optional fields:
rotation_weight(float):Scales the quaternion axis-alignment residual applied to this pose.
Higher values increase sensitivity to rotational deviations; setting it to
0disables the rotational penalty.
translation_weight:A list of three float values.
Corresponds to confidence in the x, y, and z position.
Note
If translation_weight is omitted the optimizer assumes [1.0, 1.0, 1.0] internally.
If rotation_weight is omitted the optimizer assumes 1.0 internally.
These defaults apply to any constraint type below that supports the corresponding weight fields.
Setting a weight to 0 disables the corresponding penalty.
Below is an example JSON code block for an ABSOLUTE_POSE constraint:
{
"constraints": [
{
"type": "ABSOLUTE_POSE",
"timestamp": 1765338003762995576,
"pose": {
"rx": 0.0,
"ry": 0.0,
"rz": 0.0,
"x": 40,
"y": 30,
"z": 10
},
"rotation_weight": 1.0,
"translation_weight": [1.0, 1.0, 1.0]
}
]
}
ABSOLUTE_POINT¶
Fixes a single point from a frame to known coordinates in the trajectory map frame.
Required fields:
type: Must be exactly"ABSOLUTE_POINT".timestamp: Lidar frame timestamp (first valid column timestamp).row: Row index in the staggered image (0 <= row < frame.h).col: Column index in the staggered image (0 <= col < frame.w).return_idx: Which range return to use (1or2).absolute_position: Either an object with keysx,y,zor a list[x, y, z].
Optional fields:
translation_weight: List of three values describing confidence alongx,y, andz.
Below is an example JSON code block for an ABSOLUTE_POINT constraint:
{
"constraints": [
{
"type": "ABSOLUTE_POINT",
"timestamp": 1765338003762995576,
"row": 27,
"col": 1894,
"return_idx": 1,
"absolute_position": [40.0, 30.0, 10.0],
"translation_weight": [1.0, 1.0, 1.0]
}
]
}
POINT_TO_POINT¶
The POINT_TO_POINT constraint aligns a point from one point cloud to a corresponding point in another point cloud using 2D image coordinates from the lidar frames.
Required fields:
type: Must be"POINT_TO_POINT".timestamp1: Frame timestamp for the first point (must be the frame’s first valid timestamp).row1: Staggered image row index for the first point.col1: Staggered image column index for the first point.return_idx1: Which range return to use for the first point (1or2).timestamp2: Frame timestamp for the second point (frame’s first valid timestamp).row2: Staggered image row index for the second point.col2: Staggered image column index for the second point.return_idx2: Which range return to use for the second point (1or2).
Optional fields:
translation_weight: List of three numbers specifying axis confidence.
See Weight Fields for a detailed explanation.
Below is an example JSON code block for a POINT_TO_POINT constraint:
{
"constraints": [
{
"type": "POINT_TO_POINT",
"timestamp1": 1765338003762995576,
"row1": 27,
"col1": 1894,
"return_idx1": 1,
"timestamp2": 1765338746963563448,
"row2": 29,
"col2": 1221,
"return_idx2": 1,
"translation_weight": [0.2, 0.2, 0.2]
}
]
}
POSE_TO_POSE¶
The POSE_TO_POSE constraint links two lidar frames by matching the poses associated with each frame’s first valid column. Conceptually, it tells the optimizer “these two frames should maintain this relative motion,” which is especially useful for loop-closures or any place where you can align two point clouds. You can supply the translation/rotation directly (e.g., from manually aligned frames) or let the optimizer estimate it via ICP between the two frame point clouds.
Required fields:
type: Must be"POSE_TO_POSE".timestamp1: Frame timestamp for the first pose (frame’s first valid timestamp).timestamp2: Frame timestamp for the second pose (frame’s first valid timestamp).
Optional fields:
transformation: Defines the relative pose from pose 1 to pose 2.A dictionary (
rx,ry,rz,x,y,z) with rotation angles in radians, applied in X-Y-Z order, orA 16-element list (4x4 matrix)
If omitted, the transformation is auto-estimated using ICP matching.
rotation_weight(float): Scales the quaternion axis-alignment residual for the relative pose. Higher values enforce the measured rotation more strongly.translation_weight: List of three values.
Below is an example JSON code block for a POSE_TO_POSE constraint:
{
"constraints": [
{
"type": "POSE_TO_POSE",
"timestamp1": 1765338059263057992,
"timestamp2": 1765338723963851640,
"rotation_weight": 1.0,
"translation_weight": [1.0, 1.0, 1.0]
}
]
}
For detailed information about constraint types, mathematical foundations, and implementation examples, see Using the API.
Example JSON File¶
Here’s an example constraint configuration file that you can run with the sample loop.osf data:
{
"key_frame_distance": 2.0,
"traj_translation_weight": 10.0,
"traj_rotation_weight": 10.0,
"constraints": [
{
"type": "POSE_TO_POSE",
"timestamp1": 1765338059263057992,
"timestamp2": 1765338723963851640,
"translation_weight": [
1.0,
1.0,
1.0
],
"rotation_weight": 1.0
},
{
"type": "POINT_TO_POINT",
"translation_weights": [0.2, 0.2, 0.2],
"timestamp1": 1765338003762995576,
"row1": 27,
"col1": 1894,
"return_idx1": 1,
"timestamp2": 1765338746963563448,
"row2": 29,
"col2": 1221,
"return_idx2": 1
}
]
}