Targetless sensor calibration

Targetless lidar-camera calibration from a rosbag you already have

No checkerboards. No scripted drive. No site visit. Upload a recording of normal operation and get extrinsics back with a confidence score per sensor. Four API calls; a job typically takes 15–25 minutes.

4 calls
upload, validate, calibrate, result
Confidence per sensor
plus quality flags on every result
Failed job, credit back
the run is refunded automatically

Every field named here is in the result payload. See the quickstart.

lidar_roof camera_front imu R, t R, t R, t Same engine for vehicles, robots and mobile rigs. reference_frame: lidar_roof
Extrinsics are the rotation and translation from each sensor frame to the reference frame. That is what the API returns.
How it works

Three steps, one afternoon

STEP 01

Check the bag locally

Run the free deepen-bag-check CLI on your machine. It reports which calibration types the recording qualifies for, and what is missing when it does not. Nothing leaves your network.

STEP 02

Send it to the API

Upload the recording and start a job. It runs async, so you poll instead of holding a connection. In-VPC deployment via AWS Marketplace is coming.

STEP 03

Read the extrinsics

Roll, pitch, yaw and translation per sensor pair, each with a confidence value, quality flags, and a link to a before/after overlay report.

Why targetless

Why targetless, and how it compares

Every route to lidar-camera extrinsic calibration asks something of you. Here is what each one asks.

Deepen CalibrateChoreographed-capture servicesTarget-based toolsDIY open-source toolboxes
Capture requiredExisting recording of normal operationPrescribed maneuver (published guides specify a figure-8 near textured structure)A capture session with the board in view of every sensorDepends on the module; usually a target session
Physical targetsNoneNoneCheckerboard, ChArUco or AprilGrid requiredUsually required
Turnaround15–25 minutes, async jobVendor-published cloud path is human-reviewed, about one business dayLocal run, plus target setup timeLocal run, plus integration time
DeliveryREST API, 4 calls, JSON resultFile package after a contact-form intakeCLI on your machineSource you build and wire yourself
Failure handlingStructured error, credit refunded automaticallyNot publishedCredits deducted on success only (per public pricing)Open an issue
What the result reportsPer-sensor confidence, quality flags, error stats, before/after overlay reportNot publishedPer vendor docsPer tool

Columns describe categories of multi-sensor calibration software, not specific vendors. Verified against public vendor documentation at time of writing.

seed extrinsic recovered extrinsic
Every job returns this overlay report and the seed-versus-recovered delta, so a reviewer can judge the result instead of trusting a number.
roll°pitch°yaw°px mpy mpz m
seed0.000.0090.000.100.001.20
recovered0.31-1.0289.870.14-0.021.31

Can you calibrate lidar and camera without a target?

Yes. Deepen Calibrate estimates lidar-camera extrinsics from a recording of normal driving or operation. No checkerboard, no AprilGrid, no calibration bay. Run the free CLI first to confirm the recording has enough motion and coverage.

How do I calibrate sensors from a rosbag?

Four calls. POST the .bag, .db3 or .mcap to /calibration/bags, POST to /validate with a calibration type, POST to /calibrations to start the job, then GET /result. The job is async; poll until it reports succeeded.

What happens if the calibration fails?

You get a structured error naming the reason, and the credit for that run is refunded automatically. Running deepen-bag-check locally beforehand catches most causes before you spend anything.

Which sensors and formats are supported?

ROS1 .bag, ROS2 .db3 and .mcap. Velodyne, Ouster, Hesai and RoboSense lidar, including raw Hesai packets. Raw and compressed camera topics, standard IMU messages, CAN vehicle speed. LiDAR-Camera and Multi-LiDAR run through the API today.

Two ways to run it

Two ways to run it, one engine

Both paths run the same validator and return the same result schema. Only the location changes.

Coming soon

AWS Marketplace — runs in your VPC

The engine runs in your AWS account against your own S3 bucket and compute. Built for data-residency and export-control reviews.

Data flow: your sensor data never leaves your VPC.
  • Runs entirely inside your own AWS account
  • Fits data-residency and export-control requirements
  • Same calibration engine, same result format
Get notified
Supported inputs

Works with the rosbags you already record

Same reader on both sides: the CLI and the API parse identical formats, and neither needs ROS installed.

Container formats

ROS1 .bag · ROS2 .db3 · ROS2 .mcap

Lidar

Velodyne, Ouster, Hesai, RoboSense point clouds — plus native support for raw Hesai packet data

Camera

Raw and compressed (JPEG/PNG) image topics

IMU

Standard IMU messages, used for motion excitation and initial extrinsics

Vehicle

CAN / vehicle-speed data for lidar–vehicle calibration

Calibration types

LiDAR–Camera and Multi-LiDAR run through the API today. The CLI also validates LiDAR–IMU and LiDAR–Vehicle bags. Ask us to run those.

Topics are classified by message type, not by name, so your existing driver or stack naming convention works as-is.

API

From upload to extrinsics: four API calls

The same checks deepen-bag-check runs locally are what validate your bag server-side — nothing is re-checked differently.

POST /bags 201 uploaded POST /validate 200 eligible types POST /calibrations 202 queued GET /result 200 succeeded async — poll, typically 15–25 min failed → structured error + credit refunded
The whole contract. Status codes are the real ones.
1 · UPLOAD
Upload the recording

Multipart upload. The file is stored, not inspected, until you validate it.

curl
curl -X POST https://selfserve.calibrate.deepen.ai/calibration/bags \
  -H "X-Api-Key: $DEEPEN_API_KEY" \
  -F "file=@drive.mcap"

# -> 201 Created
{
  "bag_id": "bag_8f2c1a",
  "status": "uploaded",
  "size_bytes": 4831201280,
  "container_format": null,
  "created_at": "2026-07-28T18:04:11Z"
}
2 · VALIDATE
Validate for a calibration type

Returns the calibration types this bag is eligible for, and the reason each ineligible type failed.

curl
curl -X POST "https://selfserve.calibrate.deepen.ai/calibration/bags/bag_8f2c1a/validate?calibration_type=lidar_camera" \
  -H "X-Api-Key: $DEEPEN_API_KEY"

# -> 200 OK (trimmed)
{
  "bag_id": "bag_8f2c1a",
  "status": "warnings",
  "container_format": "ros2_mcap",
  "eligible_calibration_types": ["lidar_camera", "multi_lidar"],
  "ineligible_calibration_types": [
    { "type": "lidar_vehicle", "reason": "no CAN/vehicle-speed topic found" }
  ]
}
3 · CALIBRATE
Start the calibration job

Start a job for an eligible type. It runs async and returns a job id.

curl
curl -X POST https://selfserve.calibrate.deepen.ai/calibration/bags/bag_8f2c1a/calibrations \
  -H "X-Api-Key: $DEEPEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"calibration_type": "lidar_camera"}'

# -> 202 Accepted
{
  "calibration_id": "calib_5b19",
  "status": "queued",
  "calibration_type": "lidar_camera",
  "created_at": "2026-07-28T18:06:02Z"
}
4 · RESULT
Poll, then fetch the result

Poll until succeeded, then read extrinsics, confidence, quality flags, error stats and a report link per sensor.

curl
curl https://selfserve.calibrate.deepen.ai/calibration/calibrations/calib_5b19/result \
  -H "X-Api-Key: $DEEPEN_API_KEY"

# -> 200 OK (trimmed)
{
  "schema_version": "1.0",
  "calibration_id": "calib_5b19",
  "status": "succeeded",
  "extrinsic_camera_coordinate_system": "ROS_REP_103",
  "results": {
    "camera_front_wide": {
      "extrinsic_parameters": {
        "roll": 0.31, "pitch": -1.02, "yaw": 89.87,
        "px": 0.14, "py": -0.02, "pz": 1.31
      },
      "reference_frame": "lidar_roof",
      "confidence": 0.94
    }
  },
  "quality_flags": []
}

These calls run against the live API. Swap in the key you get at checkout. A failed job returns a structured error and refunds the credit automatically — you do not pay for a run that produced no extrinsics.

Free · open · local

Check eligibility before you pay

deepen-bag-check reads your .bag, .db3 or .mcap on your own machine. No upload, no account, no ROS install. It is the same validator that gates every paid job, so a pass here is a pass there.

Terminal
# install (pip install works too, inside a venv)
pipx install deepen-bag-check

# check a bag against a calibration type
deepen-bag-check drive.mcap --for lidar-camera

Open source, Apache-2.0 — view the source on GitHub.

Output
deepen-bag-check 1.0.0 — drive.mcap
container: ros2_mcap
status: WARNINGS (exit code 1)

Topics:
  /sensor/camera/front_wide/image/compressed  [camera_compressed]  20.0 Hz
  /sensor/lidar/roof/points  [lidar]  10.1 Hz  vendor=hesai

Checks:
  [WARN] camera_info_present: no paired CameraInfo topic
  [PASS] pointcloud_field_schema: fields map cleanly (vendor=hesai)
  [PASS] motion_excitation: sufficient rotational excitation

Eligible calibration types: lidar_camera, multi_lidar

What it checks

  • Container format & schema — catches a corrupt or unsupported file before it becomes a confusing failure downstream
  • Topic classification by message type, never by name
  • Per-vendor point cloud field mapping, so mismatched intensity or ring fields don't surprise you later
  • Camera intrinsics presence, sensor sync, coverage, and motion excitation
  • Machine-readable JSON output for CI, or a human-readable report for a quick look
0 Passed — no issues found.
1 Passed with warnings — usable, quality may be degraded.
2 Failed — not usable for the requested calibration type.

Start with a bag you already have

Check it free with the CLI. If it qualifies, one API key gets you extrinsics the same session.

Get an API key Run the free check
Get API access

Pay per calibration, or per month

Card checkout issues your API key immediately. Failed jobs are refunded as credits.

Standard calibration

Up to 2 sensors, recordings up to 10 GB. No commitment.

$75per calibration
Up to 2 sensors · recordings up to 10 GB
  • Pay per completed job, no monthly commitment
  • Re-run after every sensor swap or knock
  • Same hosted API, same result schema
Start now

Complex calibration

Multi-lidar and multi-camera rigs, larger recordings. No commitment.

$250per calibration
Multi-lidar / multi-camera rigs · large recordings
  • Pay per completed job, no monthly commitment
  • Covers larger sensor counts & recording sizes
  • Same hosted API, same result schema
Start now

Unlimited monthly

Flat rate, priority processing, direct line to the team. Built for bring-up and integration months.

$15,000per month
Unlimited calibrations · priority processing · direct support
  • Flat monthly rate, no per-job billing
  • No per-job accounting during bring-up
  • Priority processing and a direct line to the team
Start now Prefer an invoice? Contact sales

Enterprise & AWS Marketplace

Private offers, volume terms, and in-VPC deployment billed through your existing AWS account.

Contact us
Private offers & procurement through AWS Marketplace
  • Private offers & volume pricing
  • In-your-VPC deployment (AWS Marketplace)Coming soon
  • Procurement through your existing AWS bill
Contact us

Standard vs. complex is based on sensor count and recording size at submission time — exact thresholds are confirmed at checkout.

Payments via Stripe. Checkout is handled by Stripe — Deepen never sees or stores your card details.

Per-customer API keys. API access is scoped to a key issued to your account, not shared credentials.

Data handling by path. Hosted API: your recording is uploaded to Deepen for processing. AWS Marketplace (in-VPC): your data never leaves your AWS account.

Get in touch

Tell us about your rig

Sensors, rig type, recording format, and which path you want. We reply with access details.

We'll only use this to follow up about API access.