> ## Documentation Index
> Fetch the complete documentation index at: https://docs.altnautica.com/llms.txt
> Use this file to discover all available pages before exploring further.

# LiDAR driver

> Implement a LidarDriver to add a new ranging sensor.

A `LidarDriver` yields point-cloud frames from a spinning,
solid-state, or single-line ranging device.

## The interface

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
from ados.sdk.drivers.lidar import (
    LidarDriver,
    LidarCandidate,
    LidarCapabilities,
    LidarSession,
    LidarFrame,
    LidarPoint,
)

class MyLidarDriver(LidarDriver):
    async def discover(self) -> list[LidarCandidate]:
        ...

    async def open(self, candidate, config) -> LidarSession:
        ...

    async def close(self, session) -> None:
        ...

    def capabilities(self, session) -> LidarCapabilities:
        ...

    async def frame_iterator(self, session):
        # async generator yielding LidarFrame
        ...

    async def set_param(self, session, param, value) -> None:
        # rpm, scan rate, filter mode
        ...
```

<Note>
  Writing the agent half in Rust? The trait equivalent is
  `ados_sdk::drivers::LidarDriver` (re-exported from the `drivers`
  module of the `ados-sdk` crate). It carries the same six methods
  (`discover`, `open`, `close`, `capabilities`, `frame_iterator`,
  `set_param`) over an `async-trait`, with `LidarCandidate`,
  `LidarCapabilities`, `LidarFrame`, and `LidarPoint` as the matching
  types. `LidarFrame` holds `timestamp_ns`, `sequence`, a `points`
  vector, and free-form `metadata`. Set `runtime: rust` in the
  manifest and the host treats the driver the same way it treats the
  Python one.
</Note>

## Capabilities

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
LidarCapabilities(
    min_range_m=0.1,
    max_range_m=40.0,
    horizontal_fov_deg=360.0,
    vertical_fov_deg=1.0,
    points_per_frame=1024,
    fps=10.0,
    has_intensity=True,
    has_dual_return=False,
)
```

## Frames

Each `LidarFrame` carries a list of `LidarPoint` in the sensor body
frame: `+x` forward, `+y` left, `+z` up. Coordinates are metres.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
LidarPoint(
    x=2.13, y=-0.42, z=0.05,
    intensity=0.74,
    return_index=0,
)
```

Drivers that report misses for some angles should skip those points
rather than emit zeros.

## Manifest permissions

```yaml theme={"theme":{"light":"github-light","dark":"github-dark"}}
agent:
  permissions:
    - id: sensor.lidar.register
    - id: hardware.uart        # most LiDARs are USB-CDC or UART
    - id: hardware.usb         # for a raw USB device behind a vendor SDK
```

## Single-line vs spinning

The agent does not distinguish single-beam rangefinders from full
3D LiDARs at the driver layer. A 1D rangefinder ships frames with
`points_per_frame=1`. The host's avoidance and SLAM consumers
already handle the degenerate case.

## See also

* [Driver layer](/developers/driver-layer) for the contract.
* [Hardware testing](/developers/hardware-testing) for replay
  patterns when no LiDAR is on the bench.
