Ouster PoseOptimizer guidance needed - preserving SLAM geometry while applying external position authority

We are processing mobile mapping data from an Ouster OS1-071 Rev 7.1, 64-channel sensor, firmware 3.2, with synchronized IMU. Data is recorded to OSF and processed with Ouster SDK 1.0.1.

We have an Ouster SLAM trajectory that is visually clean and preserves the local LiDAR geometry well. We also have an independent, timestamped XYZ trajectory in a projected metric coordinate system that we need to use for horizontal placement and vertical authority.

Our goal is to georeference the Ouster result while preserving the original SLAM geometry, scan-to-scan shape, and Ouster orientation behavior.

What we have tested:

  1. PoseOptimizer with sparse AbsolutePoseConstraint XYZ controls.

We created sparse absolute XYZ constraints and used the initial Ouster trajectory rotation in each constraint pose. The external trajectory supplied the XYZ translation. Although the numerical position residuals looked reasonable, the resulting point cloud showed visible geometric distortion: curves appeared stretched or folded, and vertical objects no longer held their expected shape.

  1. Direct per-column vertical translation.

We retained rigid XY placement and original Ouster rotations, then replaced the Z translation of each valid body_to_world pose from the external vertical authority. This caused visible vertical warping of the roadway and corridor.

  1. Single rigid left-side transform.

We applied one constant rigid transform to every source pose:

T_output = T_alignment @ T_source

This produces visually clean LiDAR geometry. Road shape, poles, wires, and trees remain coherent. However, we still need a supported method to bring the result into the correct external horizontal and vertical reference frame without introducing a non-rigid deformation.

We are writing the modified trajectory to OSF with the SDK Writer and preserving the original scan fields and timestamps.

Questions:

  1. Must external XYZ controls be transformed into the existing Ouster SLAM world frame before constructing AbsolutePoseConstraint objects?
  2. Does AbsolutePoseConstraint.pose represent the LiDAR sensor/body-to-world pose, or another pose convention?
  3. When preserving local LiDAR geometry is the primary requirement, is a rigid registration the recommended approach instead of forcing sparse external position constraints to zero residual?
  4. What is the recommended Ouster SDK workflow for applying sparse vertical control from an external surveyed elevation source without changing roll, pitch, yaw, or causing vertical road-surface warping?
  5. Is there an official example or recommended pattern for applying a smooth, low-frequency vertical correction to an Ouster SLAM trajectory while retaining the original local geometry?

We would appreciate any guidance, documentation, or example code showing the correct frame convention and recommended workflow.

After More Testing Follow-up:

Follow-up: we have now isolated a separate upstream failure inside stock Ouster SLAM before any external georeferencing, trajectory modification, Writer operation, or PoseOptimizer step.

Hardware and software:

  • Ouster OS1-071 Rev 7.1, 64-channel

  • Firmware 3.2

  • Synchronized IMU

  • Ouster SDK / CLI 1.0.1

  • 10 Hz LiDAR, ACCEL32_GYRO32_NMEA IMU profile

We isolated one 53-second PCAP segment containing the worst failure. The raw capture is intact:

  • 530 continuous LiDAR frames

  • 33,920 LiDAR packets

  • 4,240 IMU packets

  • No missing LiDAR frame IDs

  • No missing columns in the affected frames

  • Continuous individual IMU measurement timestamps at approximately 640 Hz

  • No abnormal raw gyro or accelerometer event

We also compared the Ouster IMU against an independent, time-aligned GNSS/INS IMU through the exact failed interval. Both systems show ordinary continuous vehicle motion. The vehicle remained near 18 m/s (about 40 mph); there was no stop, impact, major rotation spike, or missing IMU event.

However, when processing this raw PCAP with stock Ouster CLI using:

slam --deskew-method imu_deskew

the final Ouster per-column trajectory begins reducing forward translation. Normal within-frame travel is about 1.8 m per 100 ms LiDAR frame. During the failure it falls to approximately 0.24 m per frame for about five seconds, then later recovers.

Using constant_velocity on the same PCAP avoids the main sustained translation collapse. It retains approximately normal forward travel after a short transition. none fails much more broadly.

We inspected the SDK 1.0.1 C++ API and understand that InertialIntegrationImuDeskewMethod maintains internal feedback state, including previous corrected SLAM pose(s), solved world-frame linear velocity, IMU integration anchor, gravity estimate, and IMU biases. Its API includes:

set_last_pose(...)
finalize_after_registration(...)
solve_initial_velocity(...)
calc_poses_with_motion_model(...)
calc_corrected_poses_with_motion_model(...)

This suggests a possible feedback mechanism:

Registration returns an underestimated forward pose
    ->
IMU deskew receives a low corrected velocity / pose prior
    ->
the next scan is under-deskewed in translation
    ->
registration becomes less reliable
    ->
the low velocity persists for subsequent frames

The Python SDK binding exposes only final map-aligned poses and does not expose the pre-registration prediction, solved initial/ending velocity, registration residual, convergence status, or the set_last_pose / finalize_after_registration feedback calls.

Could you please advise:

  1. Is the above feedback interpretation consistent with how InertialIntegrationImuDeskewMethod and the LIO backend operate in SDK 1.0.1?

  2. Can imu_deskew lose its estimated forward velocity while raw IMU data remains normal, particularly during steady-speed vehicle travel where the accelerometer does not independently measure absolute forward speed?

  3. Is there a supported way to log or expose, per frame:

    • pre-registration predicted anchor pose;

    • initial and ending world-frame velocity;

    • post-registration corrected anchor pose;

    • ICP residual / correspondence count / convergence status;

    • whether the velocity solve succeeded or fell back?

  4. Are set_last_pose() and finalize_after_registration() intentionally unavailable in the Python binding? Is there a supported C++ example or diagnostic build path for observing this state?

  5. Is there any known issue or recommended configuration for cases where imu_deskew enters a sustained low-translation state while constant_velocity continues to produce a materially better trajectory from the same synchronized firmware-3.2 data?

We can provide the isolated PCAP, metadata JSON, frame IDs, and per-frame trajectory comparison if that would help reproduce the issue.

Failure area:

Rest of the PACP results are clean as the sample below:

Thank you.

1 Like

For Pose Optimizer questions:
Your method 3 is the right first step, and the SDK already implements it as PoseOptimizer.initialize_trajectory_alignment(). The distortion in method 1 most likely comes from mixing frames inside each constraint. The translation was in the external projected frame while the rotation was the SLAM world-frame rotation with the default rotation weight. Those two disagree by the global yaw between the frames, and the optimizer resolves the conflict by shearing the trajectory instead of rotating it rigidly.

Q: Do external controls need to be in the SLAM world frame?
A: The API does not require it,but each constraint must be in the same frame which mean they are internally consistent

Q: What does AbsolutePoseConstraint.pose represent?
A: It is the body-to-world transform at that timestamp, the same convention as LidarFrame.body_to_world and get_column_pose(), which is what SLAM writes.

Q: Rigid registration versus forcing residuals to zero?
A: Rigid first, soft controls second.

The optimizer is a pose graph. Consecutive key frames are linked by relative-pose edges that encode the SLAM motion you want to preserve, and your controls enter as extra edges pulling nodes toward the external positions. The ratio of control weight to trajectory weight decides how much low-frequency drift is absorbed versus how rigid the local geometry stays. Both trajectory weights default to 10, so start your control weights well below that and raise them only while the result visibly improves. Do not raise them until residuals vanish, because past that point you are writing control noise into the geometry.

The loss function matters as much as the weights. A PoseOptimizer built from a key-frame distance or from a default SolverConfig applies Huber loss with scale 1.0 to every edge, trajectory edges included. Set it to trivial loss for this workflow:

cfg.loss_function = mapping.LossFunction.TRIVIAL_LOSS

Why: a robust loss exists to discount outlier measurements, but the trajectory edges are not measurements, they are the SLAM geometry you are trying to keep, and Huber starts discounting exactly those edges once they deform past its knee, so a large deformation ends up costing almost the same as a small one.

With the default weights that knee sits at roughly this much relative-pose error between adjacent key frames: 0.1 m
Note that the JSON config path already defaults to trivial loss when loss_function is omitted, so this only bites when you construct the optimizer from Python or C++ without setting it

Q: Is there an offical example?
A: Not for vertical correction specifically. The closest official pattern is the GPS workflow, which uses the same mechanism: position-only constraints, rotation_weight 0, weak weights, one constraint every 100 m, then rigid alignment and solve. The CLI form is:

ouster-cli source slam.osf pose_optimize --auto-gps out.osf

You can try with different weights by flag like --gps-constraints-weights 0.001,0.001,0.0001

Q: Sparse vertical control without warping
A: Use AbsolutePoseConstraint with rotation_weight = 0 and translation_weight = [0, 0, wz]. X and Y of the target are ignored when their weights are zero. Place controls sparsely, tens to hundreds of meters apart, and let the trajectory edges spread the correction between them. Rotation stays a free variable held by the trajectory rotation weight, so roll, pitch and yaw only move as much as consistency with the new vertical profile demands. There is no per-node freeze-rotation flag; raising traj_rotation_weight makes the change negligible. Your method 2 is the opposite extreme: an infinitely stiff control at every column with no smoothing, changing Z without changing pitch, which is why the road surface warped.

1 Like

For SLAM questions:

Your reading of the feedback loop is essentially correct. Two refinements change where we would
look first.

In SDK 1.0.1 the forward velocity is never an IMU observable. It is re-derived every frame in closed form from the ICP-corrected
displacement of the anchor pose. The accelerometer contributes only a per-frame velocity increment, and only once a gravity direction has
been estimated. Gravity is estimated only during near-stationary intervals, and once estimated it is never cleared or re-validated for the
rest of the run. The gates are:

relative anchor motion < 0.01 m
relative anchor rotation < 1 degree
| mean accel norm - g | < 0.5 m/s^2

At 18 m/s consecutive anchors are about 1.8 m apart, so these gates cannot be satisfied while driving. Whether the accelerometer
participated at all in your run therefore depends on how the run started, and we need you to confirm two things. Did the vehicle sit
stationary at the beginning of the recording? Did you run SLAM over the full recording, or over the isolated 53-second cut?

Q: Is the feedback interpretation consistent with the implementation?
A: Yes, with one correction. The solved velocity is not an inertial estimate. It is the ICP displacement between consecutive anchors divided by the elapsed time, plus an accelerometer term that is zero whenever no gravity estimate exists. An under-estimated ICP translation becomes the next prior directly

Q: Can imu_deskew lose forward velocity while the raw IMU is normal?
A: Yes, and steady-speed travel is the worst case, for three compounding reasons. The accelerometer cannot distinguish 18 m/s from 2.4 m/s. Scenes that are self-similar along the direction of travel like tunnel, open fields, parallel facades.

Q: Is there a supported way to log this state per frame?
A: No. None of the quantities you list are exposed. We will add ICP residual / correspondence count / convergence status soon.

Q: Are set_last_pose and finalize_after_registration intentionally unavailable in Python?
A: They are unavailable in python currently.

Q: Known issue or recommended configuration?
A: We have no tracked issue matching this case. Would you willing to share your data, our engineers will look into it

1 Like

Thank you. This confirms the feedback interpretation and is very helpful.

Our C++ diagnostic was run on a 53-second forensic PCAP crop from Run 014, surrounding the worst failure. The crop begins approximately 235 seconds into the original recording, while the vehicle was already moving.

The full original Run 014 recording did begin with the vehicle stationary before driving. It then ran cleanly for several minutes before the later mid-run collapse.

This is not limited to Run 014. We observed collapse behavior in Runs 001, 004, 011, and 014, with Run 014 being the worst and the one selected for detailed instrumentation. Other runs in the same collection sequence appeared visually clean.

Therefore, the crop alone cannot confirm the gravity-initialization state, but we do not believe the issue is solely a missing stationary-start condition. The system had already produced normal motion before ICP began reducing the accepted forward displacement and the velocity-feedback state decayed.

We will run SLAM continuously from the start of the original Run 014 recording, including the stationary period, and compare that result with the isolated moving crop.

Yes, we are willing to share the full original Run 014 recording, the 53-second PCAP subset, sensor metadata, and our frame-by-frame diagnostic logs with your engineers. Please let us know the preferred secure upload method and any specific logs or SDK build information you would like included.

Thank you again.

Could you try to upload the recording with this tool https://www.youtube.com/watch?v=UGiuOWjE-9k&feature=youtu.be.

Thank you :smiley:

Thank you. I uploaded the requested 53-second Run 014 forensic PCAP crop with its matching Ouster sensor metadata.

Recording link:

This crop contains the mid-run translation-collapse event reproduced with SDK 1.0.1 imu_deskew.

1 Like

can you regenerate the url link by “share with ouster”. I can’t open the above url link.

Please check https://www.youtube.com/watch?v=UGiuOWjE-9k&feature=youtu.be.

Where this crop falls in the original recording

The crop begins about 235 seconds after the original recording began, while the truck is already moving. It contains the worst mid-run translation-collapse event from Run 014.

The full recording started stationary and initially processed correctly. Similar collapse behavior has also appeared in Runs 1, 4, and 11, with Run 14 being the clearest example.

What we observe

The raw LiDAR and IMU data appear continuous through the event:

  • 530 LiDAR frames
  • 33,920 LiDAR packets
  • 4,240 IMU packets
  • No packet loss, timestamp reversals, invalid LiDAR columns, or abnormal raw IMU gap/spike was identified
  • PolyNav vehicle navigation remains near 18 m/s

However, during imu_deskew SLAM, the solved forward translation collapses. Per-frame travel falls from approximately 1.8 m per 10 Hz frame to approximately 0.24 m, even though the vehicle continues at road speed.

The failure begins around source frame 41976 and becomes severe shortly afterward. The SLAM trajectory then recovers later in the same crop.

Diagnostic interpretation

Our C++ diagnostic indicates that the predicted forward step is initially near the expected value, but the ICP-corrected anchor displacement becomes too small. That reduced solved displacement is then used as the next forward-velocity prior, producing a feedback collapse.

Examples from the diagnostic:

  • Frame 41976: predicted step about 1.763 m; ICP correction pulls backward about 0.251 m; final step about 1.518 m
  • Subsequent solved forward velocity declines from about 17 m/s toward less than 1 m/s
  • ICP correspondence counts and residual summaries remain apparently normal during the onset, so the bad local registration appears self-consistent rather than caused by an obvious data outage

Current tests

We are independently rerunning this same crop with imu_deskew and smaller voxel sizes, 0.50 m and 0.25 m, to determine whether the registration settings can prevent the collapse. Those results are under visual and navigation-based review.

We would appreciate guidance on reproducing and diagnosing this mid-run ICP translation collapse, especially whether there are internal convergence, correspondence-quality, degeneracy, or IMU/gravity-state signals your engineers can inspect from this recording.

Are you using a wheel encoder?

No wheel encoder is being used.

We have a separate PolyNav GNSS/INS system with independent RTK position, velocity, attitude, and IMU data. It remains near 18 m/s during the collapse, but it is used only as our independent diagnostic reference; it is not fed into Ouster SLAM or imu_deskew.

The uploaded PCAP was processed with Ouster sensor data and metadata only. The Ouster and PolyNav systems are PPS-synchronized, which lets us compare their timelines accurately.

Thank you again for the guidance. We have now separated our downstream placement issue from the upstream SLAM issue.

We confirmed that mixing external projected translation with SLAM-world rotation, or applying dense per-column corrections, can deform the cloud. We now preserve each source’s internal SLAM geometry and use only sparse, rigid external placement. That has substantially improved recovery of several intervals that originally appeared collapsed.

The remaining problem is upstream stock imu_deskew SLAM. The worst forward-motion collapses appear in sparse or repetitive road scenes, where ICP may not have enough stable geometry to maintain forward registration.

Our Phase 131.1 process retries each source at progressively smaller voxel sizes:

  • 1.40 m
  • 1.25 m
  • 1.10 m
  • 0.95 m

The goal is to retain more usable geometry in sparse scenes. We keep the first result that passes independent travel and continuity checks. Smaller voxels improve some sources, but a few still collapse at every size. That suggests point density helps, but does not fully address the loss of forward observability or inertial-state issue.

Our sensor is an OS1-071-64U-ASR Rev7, firmware 3.2.0, SDK 1.0.1, using ACCEL32_GYRO32_NMEA, PPS synchronization, and NMEA input. We have verified continuous synchronous IMU data in the PCAP.

Our remaining questions are:

  1. We process each PCAP/OSF in a separate new ouster-cli ... slam invocation. Does gravity, bias, or velocity initialization reset for every invocation? If so, must each individual source file begin with stationary samples, even when the sensor stayed powered during the full field session?
  2. Must stationary time be recorded inside the PCAP, or is being powered and stationary before recording begins sufficient? What stationary duration and conditions do you recommend before moving with vehicle-mounted imu_deskew?
  3. Can a later stop re-estimate gravity or bias, or otherwise improve estimator state? One problematic run began while moving, collapsed before its first meaningful stop, and looked better after approximately 36 seconds below 1 mph. We recognize that this is correlation, not proof.
  4. Is there a supported way to monitor stationary detection, gravity/bias initialization, IMU readiness, estimated velocity, ICP residuals, degeneracy, correspondence quality, or convergence? We need a dependable readiness signal if we are to enforce a field capture gate.
  5. Is there an Ouster-recommended PCAP duration, distance, or file size for offline vehicle SLAM? We split collection into multiple files and process each independently, so we need to know whether shorter files improve robustness or instead repeatedly remove needed initialization/context.
  6. When imu_deskew loses forward motion in sparse/repetitive road scenes, is constant_velocity an appropriate supported fallback? If so, what validation checks would you recommend before accepting it?

I checked your upload.At frame 41972 the velocity drops 7.31 m/s in one 100 ms frame (~73 m/s²), which is not physically plausible. ICP fails to estimate the correct pose change there, and the trajectory later corrects back against the IMU pose delta. Tunnel and highway scenes with degraded point clouds are a known weak spot for us today.

Q: Does state reset per invocation?
A: Yes, completely. Each ouster-cli source slam call constructs a new SLAM engine and a new IMU
deskew object. It starts with zero gyro and accelerometer bias, no gravity vector, no velocity and no pose history

Q: Must stationary time be recored inside the PCAP.
A: Yes, have to inside the recording. Powered and stationary before recording has no effect. SLAM analyse data from the recording only. Place sensor stationary for 1 second should be enough. Stationary means under 1 dgree change and under 0.01 meter translation change

Q: A later stop does re-estimate.
A: A later full stop sets gravity if it was never set, and afterwards refines gravity and both biases through the same low-pass filter. Nothing is ever reset or invalidated

Q: Monitor stationary detection, IMU, ICP etc
A: in 1.01 we dont have it but we planned to add ICP convergence details soon

Q: File duration
A: You can split collect into multi recordings. I would process all recordings in one slam process. ouster-cli source part1.osf,part2.osf,part3.osf slam viz`. However it doesnt help to remove the trajectory collapse issue at frame 41972. it’s a point cloud feature degradation case. We will improve it in next a few release but using 1.0.1 there is no good way to eliminate this issue.

Q: Constant velocity fallback:
A: No, constant velocity also can’t fix this point cloud feature degradation issue. Constant velocity works better for non drone/handle cases and before FW 3.2 ouster sensor which we dont have IMU. The right fix will be detect the point cloud ICP feature quality and if bad quality then limit ICP pose correctness over IMU pose (in 1.0.1 we over write it in all cases and cause this failure). We will fix it and thanks for your feedback

Thank you again for the detailed explanation of the current ICP failure mode. It is helpful as we plan our next field test.

We also posted a separate question about slow rotational drift in longer runs:

We collect mobile road-corridor data. We are moving to continuous raw recordings with a real stationary period at the start of each processed session, but field work sometimes requires stopping one recording and starting another.

Our Ouster is PPS-synchronized, and the PCAP includes a time-aligned GNSS/INS feed with roll, pitch, and yaw. We have tested applying that attitude after SLAM through PoseOptimizer. It can improve leveling, but strong or dense corrections can distort otherwise clean LiDAR geometry.

Is there a supported, experimental, or planned way for ouster-cli source ... slam to use a PPS-aligned GNSS/INS attitude feed? Could it be useful at the start of a run and, if appropriate, later when LiDAR geometry is weak?

We are trying to understand whether the time-aligned attitude already in the recording could support SLAM in an Ouster-recommended way while retaining clean local LiDAR geometry.

We understand this may not solve every low-feature ICP failure. If Ouster has a relevant beta or early-access program, we would be interested in participating and can provide repeatable PPS-synchronized PCAP and GNSS/INS examples.

We tested again today with your recommendations and found an additional pattern that may help explain the rotation issue we are seeing.

This capture was one continuous 256-second vehicle run, but our recorder rolled it into two PCAP files:

  • ouster_capture_20260915_132818-000.pcap - 200.261 seconds
  • ouster_capture_20260915_132818-001.pcap - 56.011 seconds

There is essentially no gap between them; the second file begins about 18 microseconds after the first ends. The capture began stationary inside the recording and remained stationary for about 5.5 seconds before driving began.

We now understand that our offline workflow had been treating these rollover files as separate SLAM runs. Therefore, the second PCAP may have started a new SLAM process while the vehicle was already moving, even though it was part of the same field capture. Based on your guidance, that new process would begin without the gravity, bias, velocity, and pose history established in the first file.

We also found a separate rotation symptom while reviewing the resulting point cloud. The roadway is clean and level before a hard bend. As we drive through the bend, the roadway begins to tilt and warp. After the bend, short measurements across the pavement show apparent slopes of about 4.7%, 10.7%, and 11.1%, where the pavement should be essentially level.

We do not want to overstate the cause. We are not saying that gravity was lost. Our current interpretation is that the hard bend or changing scene geometry may have caused ICP to accept an incorrect pose/attitude correction, creating the visible rotation in the point cloud.

These two uploaded datasets show the continuous rollover files and the affected capture:

Could you please confirm the recommended workflow for this situation?

  1. Should continuous rollover PCAP files be processed together in one SLAM process, rather than as separate SLAM runs?
  2. If so, what is the supported PCAP command or conversion workflow?
  3. Would these hard-bend rotation examples be useful for your engineers as another reproducible example of the ICP feature-quality issue?

Thank you again for your help. We believe this may better explain why some runs remain clean while others develop rotation during the drive.

1 Like