Does Ouster SDK SLAM support a time-aligned external GNSS/INS XYZ prior during map building?

We are processing vehicle-mounted Ouster PCAP data for approximately 10-mile road runs with Ouster SDK / ouster-cli 0.16.1.

We have a separate, time-aligned PolyNav GNSS/INS trajectory that provides LiDAR-origin XYZ and attitude at the Ouster timestamps.

We need to know whether Ouster SLAM itself supports ingesting this external trajectory while SLAM is building the map, specifically as weighted per-timestamp XYZ position priors.

We verified the installed ouster-cli source <pcap> slam command exposes:

  • min/max range
  • voxel size
  • dump map
  • deskew method
  • initial pose

We did not find an option for an external trajectory, GPS/INS CSV, odometry input, or per-axis XYZ weights.

We also found pose_optimize --auto-constraints, but that appears to run after SLAM and use GPS fields already stored in the Ouster scan source. That is not the external PolyNav trajectory we need.

Questions:

  1. Is there an officially supported Ouster SDK or CLI method to supply a time-aligned external GNSS/INS XYZ trajectory to KISS SLAM during SLAM processing?
  2. If yes, can XYZ be weighted independently by axis, and is there an example for PCAP/OSF processing?
  3. If no, is post-SLAM pose_optimize the only supported Ouster path for external position constraints?
  4. Is there an official Ouster-recommended workflow for long vehicle road runs where GNSS/INS should control global trajectory while LiDAR scan matching controls local geometry?

We are looking for a supported Ouster method, API, or example that will allow slam to ingest xyz from an external gnss system as it develops the map.

Follow-up:

I found an Ouster staff reply from May 2025 stating that SDK 0.16 was planned to add support for integrating external odometry devices.

We are running the current Windows ouster-sdk / ouster-cli 0.16.1 package. I searched the installed CLI and Python package source. I can find only:

  • slam with range, voxel, deskew, and initial-pose settings; and
  • post-SLAM pose_optimize --auto-constraints, which uses GPS fields inside the Ouster source.

I cannot find a documented CLI or Python API for a continuous external odometry/GNSS/INS trajectory during SLAM.

Did external odometry support ship in SDK 0.16.1 under a different API, plugin, build option, or ROS interface? If so, what is the exact supported example for supplying a time-aligned external GNSS/INS pose stream with a LiDAR-to-INS calibration transform?

If it did not ship, is there a planned release/version for it?

  1. The integrated OusterCLI SLAM doesn’t handle the GNSS signal.
  2. N/A
  3. yes, currently

Hi,

The general approach to SLAM in the SDK at the moment is a 1 or 2 pass system. The passes are as follows:

  1. ouster-cli … slam (or the API version): A lidar odometry mapping pass (no loop closures). Takes into account data from IMU or with some work other source to provide for deskewing and initial guesses for the next scan match. Can be run online or offline.
  2. pose optimizer: Takes lidar odometry data from above and applies additional constraints such as GPS positions and can detect loop closures. Can only be run offline. This is the recommended workflow for anything involving long runs that need loop closures or global trajectory control.

There is no documented way to directly take in external GPS information other than that which was captured by the lidar’s GPS time sync setup. However, nothing stops one from putting information from other sources in the same format as what would have been captured by the sensor or using the pose optimizer APIs/config format directly to add these constraints to the optimization problem.

If you’d like I can go into how to put that information in from other sources into the standard format, but it has some significant limitations that make it not a good option (like it not supporting altitude). You’ll generally be better off using the API/config format as it is much more capable for this application.

You can see some details about it here:

Essentially what you’ll need to do (in somewhat-psuedocode) after slamming (ouster-cli slam save) the lidar data is:

import ouster.sdk.mapping as mapping
po = mapping.PoseOptimizer(
    osf_filename=osf_filename,
    key_frame_distance=2.0,
)
for each pose constraint you want to add: # e.g. add a constraint every N meters
    abs_pose = np.eye(4)
    # set the pose for your GPS/INS data in abs_pose
    # you'll have to convert into a euclidean coordinate frame here
    constraint = mapping.AbsolutePoseConstraint(
        # timestamp in nanoseconds of pose measurement above
        timestamp=np.uint64(1765338003762995576),
        pose=abs_pose,
        rotation_weight=1.0, # set to 0 if no rotation data provided
        translation_weight=np.array([1.0, 1.0, 1.0]))
    po.add_constraint(constraint)
po.save_config("whatever.json") # save the constraints you added to file

Then you can use this config file with the pose optimizer CLI and it will apply GPS/INS constraints.

Happy to go into more details if it would help, but this should be a starting point at least. This should all get more fleshed out on our side as time goes on.

Thank you, this is very helpful. We are trying to make sure we understand the intended SDK workflow before we build another custom path.

During capture, our Ouster and external navigation system a PolyNav 3000 GNSS/INS were hardware time-synchronized through a wired MTO/PTP time-sync setup. We also verified the Ouster time-sync status in Ouster Studio during capture. We therefore do not believe basic device connectivity, clock synchronization, or a software timestamp offset is the root issue.

Our immediate issue is that the lidar-odometry SLAM result can begin coherently, have a localized tracking failure, and then continue in an incorrect alignment. We understand that the SLAM pass has no loop closures and that Pose Optimizer is the offline path for global control.

We need clarification on your statement that SLAM can use IMU data, or “with some work other source to provide for deskewing and initial guesses for the next scan match.”

We have synchronized external navigation/INS data and can prepare it in a required supported format. We are not asking about a timestamp-offset workaround. We need to know the supported way to use that motion information during the offline SLAM pass.

  1. Can offline SDK SLAM accept user-supplied external motion data for deskewing and/or scan-matching initial guesses?
  2. What external data types are supported: raw gyro/accelerometer measurements, attitude, full timestamped poses, or another format?
  3. Is this supported through the Python API, C++ API, a custom ScanSource, an OSF stream, or a documented configuration interface?
  4. Can external motion data replace the native Ouster IMU, supplement it, or only provide initial pose guesses?
  5. What timestamp basis, units, coordinate-frame convention, and LiDAR-to-external-INS transform convention are required?
  6. Is there a minimal supported example or SDK source file we should use as the reference implementation?

Separately, after the SLAM pass, we need the output in a stable project coordinate frame across long road runs and separate processing windows. Based on your guidance, is the intended supported workflow:

  • run lidar odometry SLAM using the best available deskewing and motion initial guesses;
  • create timestamped external absolute-pose constraints from the navigation solution in a Euclidean/project frame;
  • apply those constraints through the Pose Optimizer API/config;
  • export the optimized trajectory/map?

If that is correct, please clarify:

  1. Can the Pose Optimizer constrain translation only, with rotation weight set to zero, when external attitude should not control the map?
  2. Can translation weights be set independently by axis, including a different treatment for vertical control?
  3. Is the API/config-based constraint path the recommended method for handling altitude, since the sensor-format GPS route has the limitation you mentioned?
  4. Can one optimized project frame be used consistently across separately processed road windows, provided their external absolute-pose constraints are in that same frame?

Finally, based on the localized tracking failure described above, would supplying supported external motion data for deskewing or scan-match initial guesses be an appropriate approach to evaluate for that failure mode, while Pose Optimizer handles longer-run global control and inter-window consistency?