LiDAR for Robots: 2D, 3D, and Solid-State Compared

Choosing a LiDAR sets the ceiling for your robot’s perception, mapping, and obstacle-avoidance performance. A 2D scanner costs under $150 and handles flat-floor navigation. A 3D spinning unit adds vertical structure but raises cost, power, and compute load by an order of magnitude. Solid-state designs cut moving parts and cost per point, but trade away field of view. This guide covers the physics, the hardware classes, the ROS2 integration, and a decision workflow you can apply to your own platform.

Quick Takeaways

  • 2D LiDAR (single scan plane, 270°–360° FOV) is the right default for indoor AMRs running Nav2 and slam_toolbox.
  • 3D LiDAR (16–128 channels) is required for outdoor, multi-floor, ramp, and overhead-obstacle scenarios.
  • Solid-state LiDAR (MEMS, flash, OPA, and prism-based hybrids) offers better shock tolerance and lower unit cost, usually with a narrower FOV.
  • Match the sensor to compute budget, mounting height, and environment, not to the highest channel count.
Class Typical FOV Typical Range Power Price Band Best For
2D spinning 360° × 1 plane 8–30 m 2–5 W $100–$3,000 Indoor AMR, AGV, hobby SLAM
3D spinning 360° × 30°–90° 60–200 m 8–25 W $1,500–$15,000+ Outdoor UGV, autonomous cars, drones
Solid-state / hybrid 60°–120° (up to 360° hybrid) 20–250 m 5–15 W $300–$5,000 Cost-sensitive 3D, vibration-heavy platforms

Prices and specs are approximate and vary by vendor and generation. Verify against current datasheets.

How LiDAR Measures Distance

Most robot LiDARs use time-of-flight (ToF). The emitter fires a short laser pulse (typically 905 nm or 1550 nm). The receiver times the return.

d = (c · Δt) / 2

d  = target distance (m)
c  = speed of light ≈ 2.998 × 10^8 m/s
Δt = round-trip time (s)

A target at 10 m returns in about 66.7 ns. Resolving 1 cm of range needs about 67 ps of timing precision, which is why dedicated TDC (time-to-digital converter) hardware sits in the receiver.

Low-cost 2D units like the RPLIDAR A1 use triangulation instead. A laser spot lands on a CMOS sensor at an offset that depends on distance:

d = (f · b) / x

f = lens focal length (mm)
b = baseline between laser and sensor (mm)
x = spot offset on the sensor (mm)

Triangulation accuracy degrades quadratically with range. That is why budget units top out near 8–12 m. ToF accuracy stays roughly constant.

FMCW LiDAR is an emerging alternative. It measures range and per-point radial velocity from a frequency-modulated beam. It is more complex and costly, and it appears mostly in automotive-grade sensors.

From Polar to Cartesian

Each return is a tuple (r, θ, φ): range, azimuth, and elevation. For a 3D scanner:

x = r · cos(φ) · cos(θ)
y = r · cos(φ) · sin(θ)
z = r · sin(φ)

For a 2D scanner, φ = 0, so x = r·cos(θ) and y = r·sin(θ).

2D LiDAR: The Indoor Workhorse

A 2D LiDAR spins one emitter/receiver pair in a single plane. ROS publishes its output as sensor_msgs/msg/LaserScan on the /scan topic.

Strengths

  • Low cost and low compute: a scan is only 360–3,000 floats.
  • Mature support in slam_toolbox, cartographer, and Nav2 costmaps.
  • Easy to calibrate: one TF transform from base_link to laser.

Limits

  • Blind to anything above or below the scan plane (table edges, hanging cables, stairs).
  • Pitch and roll of the chassis tilt the plane into the floor or the ceiling.
  • Reflective and glass surfaces produce dropouts.

Reference parts

Model Range Scan Rate Samples/s Angular Res. Interface
RPLIDAR A1 ~12 m 5.5–10 Hz 8k ~1° UART/USB
RPLIDAR S2 ~30 m 10 Hz 32k ~0.12° UART/USB
Hokuyo UST-10LX 10 m 40 Hz ~43k 0.25° Ethernet
SICK TiM571 25 m 15 Hz ~40k 0.33° Ethernet

Mount the sensor at a height that covers your robot’s most likely collision plane, typically 10–25 cm for floor-level AMRs.

3D LiDAR: Full Spatial Awareness

A 3D LiDAR stacks multiple emitters vertically (16, 32, 64, or 128 channels) on a rotating head. Output is a sensor_msgs/msg/PointCloud2 with fields x, y, z, intensity, and often ring and time.

Strengths

  • Captures ramps, curbs, overhangs, and vegetation.
  • Supports 6-DoF LiDAR-inertial odometry such as FAST-LIO2, LIO-SAM, and KISS-ICP.
  • Long range (100 m+) for high-speed platforms.

Limits

  • Raw clouds reach 300k–2.6M points/s. Downsampling and voxel filtering are mandatory on embedded compute.
  • Vertical resolution is coarse on 16-channel units, with 2° gaps between beams.
  • Moving parts wear. Spinning heads are sensitive to vibration and shock.
Model Channels Range Points/s Vertical FOV Power
Velodyne VLP-16 16 100 m ~300k ±15° ~8 W
Ouster OS1-32 32 ~90–120 m ~650k ±22.5° ~14–20 W
Ouster OS1-128 128 ~90–120 m ~2.6M ±22.5° ~14–20 W
Hesai XT32 32 ~120 m ~640k 31° ~10 W

Solid-State LiDAR: Fewer Moving Parts

“Solid-state” covers several architectures, and vendors use the term loosely:

Architecture Mechanism FOV Maturity Notes
MEMS mirror Micro-mirror steers beam 60°–120° Production Small residual moving part
Flash Illuminates whole scene, reads a SPAD/APS array 60°–120° Production (short range) No scanning, limited range
OPA Optical phased array steers beam electronically ~60° Early Truly solid-state, sidelobe challenges
Rotating-prism hybrid Risley prisms create non-repetitive pattern 60°–360° Production Often marketed as “semi-solid-state” (e.g., Livox)

Livox-style units use a non-repetitive scan pattern, so point density in the FOV grows with integration time. That helps static mapping. It also means a single frame is sparse. Algorithms such as FAST-LIO2 handle this well because they register points continuously against an IMU-propagated state rather than waiting on full frames.

Limits to plan around

  • Narrow FOV on forward-facing units. You need two or three for 360° coverage.
  • Cross-talk and interference between identical sensors on one robot.
  • Ambient-light sensitivity on some flash designs outdoors.

Master Comparison

Criterion 2D LiDAR 3D Spinning Solid-State
Vertical coverage None 30°–90° 25°–60° (typical)
Horizontal FOV 270°–360° 360° 60°–120° (360° hybrid)
Compute load Very low High Medium–High
Vibration tolerance Moderate Low–Moderate High
Cost per unit Lowest Highest Medium
SLAM approach 2D scan matching 3D ICP / LIO LIO with IMU
Failure modes Plane blind spots Bearing wear FOV gaps, interference

Hardware Interfacing and ROS2 Integration

Wiring Layout

  • UART/USB units (RPLIDAR): 5 V supply (check the motor current spike at startup, ~400 mA), USB-UART bridge to the compute board.
  • Ethernet units (Hokuyo, SICK, Ouster, Hesai, Livox): 12–24 V supply, static IP on a dedicated NIC, and a PTP or NTP time source for timestamp alignment with the IMU.
  • Keep the LiDAR and motor-driver power rails separate. BLDC switching noise on a shared 24 V bus produces ghost returns on some units.

ROS2 Launch File (2D LiDAR)

# lidar_bringup.launch.py
from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        # Driver node: publishes sensor_msgs/LaserScan on /scan
        Node(
            package='sllidar_ros2',
            executable='sllidar_node',
            name='sllidar_node',
            parameters=[{
                'serial_port': '/dev/ttyUSB0',   # udev symlink recommended
                'serial_baudrate': 115200,       # A1/A2 = 115200, S2 = 1000000
                'frame_id': 'laser',             # must match your URDF link
                'angle_compensate': True,        # uniform angular bins
                'scan_mode': 'Sensitivity',
            }],
            output='screen',
        ),
        # Static TF: base_link -> laser (x, y, z, yaw, pitch, roll, parent, child)
        Node(
            package='tf2_ros',
            executable='static_transform_publisher',
            arguments=['0.12', '0', '0.20', '0', '0', '0', 'base_link', 'laser'],
        ),
    ])

Run with ros2 launch your_pkg lidar_bringup.launch.py. Verify with ros2 topic hz /scan and ros2 topic echo /scan --once.

Scan Filtering Node (Python, rclpy)

This node clips range, removes the chassis self-hits, and publishes a cleaned scan.

# scan_filter.py
import math
import rclpy
from rclpy.node import Node
from rclpy.qos import qos_profile_sensor_data
from sensor_msgs.msg import LaserScan

class ScanFilter(Node):
    def __init__(self):
        super().__init__('scan_filter')
        self.declare_parameter('min_range', 0.15)   # m, ignore chassis hits
        self.declare_parameter('max_range', 8.0)    # m, ignore noisy far returns
        # Sensor-data QoS: BEST_EFFORT matches most LiDAR drivers
        self.sub = self.create_subscription(
            LaserScan, '/scan', self.cb, qos_profile_sensor_data)
        self.pub = self.create_publisher(
            LaserScan, '/scan_filtered', qos_profile_sensor_data)

    def cb(self, msg: LaserScan):
        lo = self.get_parameter('min_range').value
        hi = self.get_parameter('max_range').value
        out = LaserScan()
        out.header = msg.header                     # keep timestamp + frame_id
        out.angle_min = msg.angle_min
        out.angle_max = msg.angle_max
        out.angle_increment = msg.angle_increment   # rad per beam
        out.time_increment = msg.time_increment
        out.scan_time = msg.scan_time
        out.range_min = lo
        out.range_max = hi
        # Replace invalid ranges with inf (the Nav2 "no return" convention)
        out.ranges = [
            r if (math.isfinite(r) and lo <= r <= hi) else float('inf')
            for r in msg.ranges
        ]
        out.intensities = msg.intensities
        self.pub.publish(out)

def main():
    rclpy.init()
    rclpy.spin(ScanFilter())

if __name__ == '__main__':
    main()

3D LiDAR Driver Notes

For an Ouster or Hesai unit, the vendor driver publishes PointCloud2 on /points (or /ouster/points). Configure:

  • Lidar mode (for Ouster, 1024x10 or 2048x10): higher horizontal resolution costs bandwidth and CPU.
  • Timestamp mode: use TIME_FROM_PTP_1588 when you have PTP, otherwise TIME_FROM_ROS_TIME.
  • Packet MTU: set Ethernet jumbo frames if the driver drops packets.

Downsample before SLAM. A VoxelGrid leaf size of 0.1–0.3 m is a typical starting point.

Choosing a Sensor: Decision Workflow

  1. Define the environment. Flat indoor floor → 2D. Ramps, outdoors, or overhangs → 3D or solid-state.
  2. Define the worst-case speed. Required range ≈ v · t_stop + safety margin. A 2 m/s robot with 1.5 s stop time needs at least 3 m plus margin. Use 8–10 m as a floor.
  3. Check compute. A Raspberry Pi 5 handles 2D SLAM comfortably. A 128-channel cloud needs a Jetson Orin or an x86 board.
  4. Check vibration. Legged robots, tracked vehicles, and drones favor solid-state or hybrid units.
  5. Count sensors for coverage. One 360° spinner versus three 120° solid-state units changes wiring, sync, and TF complexity.
  6. Validate with a rosbag. Record the candidate sensor in your real environment before committing.

Real-World Workflow: 2D SLAM and Navigation on an Indoor AMR

Scenario: A 40 kg differential-drive warehouse robot with an RPLIDAR S2 mounted 20 cm above the floor.

  1. Bring up the stack: driver, robot_state_publisher, wheel odometry, and the static TF above.
  2. Run mapping:
    ros2 launch slam_toolbox online_async_launch.py \  slam_params_file:=config/mapper_params_online_async.yaml
    

    Set max_laser_range: 20.0 and resolution: 0.05 (5 cm grid cells) in the params file.

  3. Drive the robot at under 0.5 m/s during mapping. Fast rotation smears scans and degrades loop closure.
  4. Save the map:
    ros2 run nav2_map_server map_saver_cli -f warehouse_map
    
  5. Configure the Nav2 costmap with an obstacle_layer fed by /scan_filtered and an inflation_layer radius slightly larger than the robot’s circumscribed radius.
  6. Test the failure cases: open doorways, glass walls, and forklift forks above scan height. These expose the 2D plane limitation. If forks are a real hazard, add a forward-tilted 3D unit or a depth camera.

Tuning tip: if the map drifts in long corridors, the cause is usually geometric degeneracy, not sensor noise. Add wheel-odometry fusion through robot_localization (EKF) or place a distinguishing feature in the corridor.

Common Integration Pitfalls

Symptom Likely Cause Fix
Scan shows no data in RViz QoS mismatch Set RViz reliability to Best Effort
Map “ghosts” during turns TF timestamp offset Sync clocks, check time_increment
Phantom obstacles at robot edge Chassis in the beam path Raise min_range or mask angles
Dropouts on glossy floor Specular reflection Tilt the sensor 1–2°, change scan mode
3D cloud drops packets NIC buffer too small Raise net.core.rmem_max, use jumbo frames
Odometry drift in SLAM Missing IMU deskew Enable motion compensation per point timestamp

FAQ

What is the difference between 2D and 3D LiDAR?

A 2D LiDAR scans one horizontal plane and outputs a LaserScan. A 3D LiDAR scans multiple vertical channels and outputs a PointCloud2 containing full spatial structure. 2D is cheaper and lighter on compute. 3D detects obstacles above and below the scan plane.

Is solid-state LiDAR better than spinning LiDAR?

Not universally. Solid-state units have fewer moving parts, tolerate vibration better, and often cost less per point. Most have a narrower field of view, so you may need several for 360° coverage. Spinning units give full horizontal coverage from one sensor.

What LiDAR range do I need for my robot?

Use required range ≈ v · t_stop + safety margin. A robot moving at 1 m/s with a 1 s stopping time needs at least 1 m plus margin. Indoor AMRs commonly use 8–12 m. Outdoor vehicles at speed need 60–100 m or more.

Can LiDAR work outdoors in sunlight and rain?

Yes, with limits. Sunlight raises background noise and shortens effective range on some 905 nm units. Rain, fog, and dust cause false returns and attenuation. Choose a sensor with a documented ambient-light rating, and filter by intensity and multi-return mode.

Hot this week

Humanoids in Factories and Warehouses: What They Actually Do Today

Which humanoid robots do real work in factories and warehouses today? See deployment data, ROI math and ROS 2 code. Read the 2026 guide.

Debugging Robot Code: Logs, Serial Output, rosbag, and Foxglove

Debug robot code faster with ROS 2 logging, serial capture, rosbag2 recording, and Foxglove. Copy the configs and scripts into your stack today.

Build a Mapping Robot With SLAM From Scratch

Build a SLAM mapping robot from scratch: wiring, odometry, LiDAR, ROS 2 slam_toolbox config and tuning. Follow the full guide and map your first room.

Depth Cameras: Stereo, Time-of-Flight, and Structured Light Compared

Compare stereo, time-of-flight, and structured light depth cameras: accuracy, range, sunlight, ROS2 setup. Pick the right sensor for your robot.

Python vs C++ for Robotics: Which Should You Use?

Python vs C++ for robotics compared: latency, ROS2 performance, control loops, and code examples. Learn which language fits your robot. Read the guide.

Topics

Humanoids in Factories and Warehouses: What They Actually Do Today

Which humanoid robots do real work in factories and warehouses today? See deployment data, ROI math and ROS 2 code. Read the 2026 guide.

Debugging Robot Code: Logs, Serial Output, rosbag, and Foxglove

Debug robot code faster with ROS 2 logging, serial capture, rosbag2 recording, and Foxglove. Copy the configs and scripts into your stack today.

Build a Mapping Robot With SLAM From Scratch

Build a SLAM mapping robot from scratch: wiring, odometry, LiDAR, ROS 2 slam_toolbox config and tuning. Follow the full guide and map your first room.

Depth Cameras: Stereo, Time-of-Flight, and Structured Light Compared

Compare stereo, time-of-flight, and structured light depth cameras: accuracy, range, sunlight, ROS2 setup. Pick the right sensor for your robot.

Python vs C++ for Robotics: Which Should You Use?

Python vs C++ for robotics compared: latency, ROS2 performance, control loops, and code examples. Learn which language fits your robot. Read the guide.

Build a ROS 2 Rover: Raspberry Pi 5, Motors, and LiDAR

Build a ROS 2 rover with Raspberry Pi 5, RPLIDAR C1, and a Pico motor controller. Copy the code, wire it, and map your room. Start building now.

Imitation Learning for Robots: Teach by Demonstration

Learn imitation learning for robots: behavior cloning, DAgger, ACT, and diffusion policies with ROS2 code. Build your first policy today.

ROS 2 Installation and First Steps: Nodes, Topics, and Messages

Install ROS 2 Lyrical on Ubuntu 26.04, then build your first node, topic, and custom message with working rclpy code. Follow the tutorial now.

Related Articles

Popular Categories