"""
Radar Module
============
This module provides data structures for representing radar sensors,
including intrinsic parameters, point cloud data, and calibration information.
Supports automotive radar sensors with configurable resolution, field of view,
and velocity measurement capabilities.
Classes
-------
RadarIntrinsics
Internal radar sensor parameters.
RadarPointCloudData
Metadata and optional point cloud data for a single radar scan.
Radar
Complete radar sensor representation.
Example
-------
Creating a radar sensor::
import numpy as np
from mimic_data_api.radar import Radar, RadarIntrinsics, RadarPointCloudData
from mimic_data_api.trajectory_numpy import WorldTrajectory
radar = Radar(
id=1,
name="front_radar",
extrinsics=np.eye(4),
intrinsics=RadarIntrinsics(
range=200.0,
azimuth_fov=2.0, # ~115 degrees
elevation_fov=0.35, # ~20 degrees
azimuth_resolution=[(0.0, 0.03), (1.0, 0.05)],
elevation_resolution=None,
min_speed_resolution=-50.0,
max_speed_resolution=50.0
),
trajectory=trajectory,
radar_pcds_data=[...]
)
"""
from pydantic import BaseModel, Field
import numpy as np
from mimic_data_api.extrinsic import SensorExtrinsic
from mimic_data_api.trajectory_numpy import WorldTrajectory
from mimic_data_api import RadarPointCloud
[docs]
class RadarIntrinsics(BaseModel):
"""Internal parameters for a radar sensor.
Defines the sensing characteristics of a radar including field of view,
angular resolution, range, and velocity measurement capabilities.
Attributes
----------
range : float
Maximum detection range in meters. Must be >= 0.
azimuth_fov : float
Horizontal field of view in radians. Must be >= 0.
elevation_fov : float
Vertical field of view in radians. Must be >= 0.
azimuth_resolution : list of tuple[float, float]
Azimuth (horizontal) angular resolution as a function of angle.
Each tuple contains (angle_in_radians, resolution_in_radians).
Resolution may vary across the field of view, hence the list format.
elevation_resolution : list of tuple[float, float] or None
Elevation (vertical) angular resolution for imaging radars.
Same format as ``azimuth_resolution``. ``None`` for non-imaging radars.
min_speed_resolution : float
Minimum measurable radial (Doppler) velocity in m/s.
Typically negative, representing approaching targets.
max_speed_resolution : float
Maximum measurable radial (Doppler) velocity in m/s.
Typically positive, representing receding targets.
Note
----
Resolution parameters are specified as (angle, resolution) pairs because
many radar sensors have angle-dependent resolution - typically better
resolution near boresight and degraded resolution at the edges of FOV.
Example
-------
>>> intrinsics = RadarIntrinsics(
... range=200.0, # 200m max range
... azimuth_fov=2.09, # 120 degrees
... elevation_fov=0.35, # 20 degrees
... azimuth_resolution=[
... (0.0, 0.017), # 1 degree at boresight
... (0.5, 0.026), # 1.5 degrees at 30 degrees off-axis
... (1.0, 0.035) # 2 degrees at edge of FOV
... ],
... elevation_resolution=None, # Non-imaging radar
... min_speed_resolution=-70.0,
... max_speed_resolution=70.0
... )
"""
range: float = Field(ge=0, description="Maximum range of the radar in meters")
azimuth_fov: float = Field(ge=0, description="Horizontal field of view in rads")
elevation_fov: float = Field(ge=0, description="Vertical field of view in rads")
azimuth_resolution: list[tuple[float, float]] = Field(
description="Azimuth resolution (in rads) at specific angles (in rads). Typically azimuth resolution is given for different range of azimuth."
)
elevation_resolution: list[tuple[float, float]] | None = Field(
description="Elevation resolution (in rads) at specific angles (in rads), for imaging radars."
)
min_speed_resolution: float = Field(
description="Minimum measurable radial speed in m/s."
)
max_speed_resolution: float = Field(
description="Maximum measurable radial speed in m/s."
)
[docs]
class RadarPointCloudData(BaseModel):
"""Metadata and optional point cloud data for a single radar scan.
This class stores information about individual radar detections,
including file location and timestamp.
Attributes
----------
filepath : str
Path to the point cloud file (PLY format).
radar_pcd : RadarPointCloud or None
Optional in-memory point cloud data. Not serialized to JSON.
timestamp : float
Timestamp in seconds corresponding to the middle of the scan.
Note
----
Radar point clouds typically include additional per-point attributes
such as radial velocity (Doppler), radar cross-section (RCS), and
signal-to-noise ratio (SNR).
Example
-------
>>> pcd_data = RadarPointCloudData(
... filepath="data/radar/scan_000.ply",
... timestamp=0.0
... )
"""
filepath: str = Field(description="Filepath to the point cloud data plyfile.")
radar_pcd: RadarPointCloud | None = Field(
default=None,
exclude=True,
description="Radar point-cloud to be used when loading a scene, will not be serialized in json file.",
)
timestamp: float = Field(
description="Timestamp of the radar point cloud in seconds that corresponds to the middle of the scan."
)
[docs]
class Radar(BaseModel):
"""Complete radar sensor representation.
This class encapsulates all information about a radar sensor including
its calibration, intrinsic parameters, trajectory, and captured point
cloud data.
Attributes
----------
id : int
Unique identifier for the radar within the sensor suite.
name : str
Human-readable name for the radar (e.g., "front_radar", "corner_radar").
extrinsics : SensorExtrinsic
4x4 transformation matrix from sensor frame to vehicle frame.
intrinsics : RadarIntrinsics
Internal radar parameters (FOV, resolution, range, velocity limits).
trajectory : WorldTrajectory
Time-series of radar poses in world coordinates.
radar_pcds_data : list of RadarPointCloudData
List of captured radar point clouds with their metadata.
Note
----
Radar sensors provide unique capabilities compared to LiDAR and cameras:
- Direct velocity measurement via Doppler effect
- Better performance in adverse weather (rain, fog, dust)
- Longer detection range for some targets
- Lower angular resolution compared to LiDAR
Example
-------
>>> radar = Radar(
... id=0,
... name="front_long_range_radar",
... extrinsics=np.eye(4),
... intrinsics=intrinsics,
... trajectory=trajectory,
... radar_pcds_data=[pcd_data]
... )
"""
id: int
name: str
extrinsics: SensorExtrinsic = Field(
description="Extrinsic calibration from sensor to vehicle frame"
)
intrinsics: RadarIntrinsics
trajectory: WorldTrajectory
radar_pcds_data: list[RadarPointCloudData] = Field(
description="List of point clouds captured by the radar sensor."
)
[docs]
def __eq__(self, other: object) -> bool:
"""Check equality with another Radar.
Performs deep comparison of all radar attributes including
calibration parameters, trajectory, and point cloud data.
Parameters
----------
other : object
The object to compare against.
Returns
-------
bool
``True`` if radars are equal, ``False`` otherwise.
"""
if not isinstance(other, Radar):
return False
if self.id != other.id:
return False
if self.name != other.name:
return False
if not np.allclose(self.extrinsics, other.extrinsics):
return False
if self.intrinsics != other.intrinsics:
return False
if self.trajectory != other.trajectory:
return False
if self.radar_pcds_data != other.radar_pcds_data:
return False
return True