Generated banner for Matlab Import Driving Data
Generated logo for Matlab Import Driving Data

Matlab Import Driving Data

MathWorksOfficialCreated by MathWorks

  • Skill
  • Workflow
  • Software development
  • BSD-2-Clause
  • No issues found

From the author

Import recorded driving sensor data (GPS, camera, lidar, actor tracks, lanes) into scenariobuilder.* objects (GPSData, CameraData, LidarData, ActorTrackData, Trajectory, laneData) and run preprocessing — synchronize, offset correction, crop, normalizeTimestamps, convertTimestamps. Also: compute actor tracks from lidar when no annotations exist, attach camera/lidar mounting + intrinsics, export to MAT/workspace/timetable/script. Use for raw driving dataset files (KITTI, nuScenes, Waymo, Pandaset, ROS/ROS2 bags, .mat, .csv, .mp4) or driving/vehicle/sensor logs that need wrapping. drivingLogAnalyzer (DLA) is OPT-IN ONLY — invoke only on explicit user request ('DLA', 'open in DLA', 'inspect/explore/analyze the recording') or reported sensor problem (sync drift, timestamp mismatch, overlay misalignment). NEVER auto-launch DLA after wrapping (Rule 0). For 'build scenario / export to RoadRunner / drivingScenario / OpenSCENARIO / Unreal / simulate', hand off to matlab-use-scenario-builder.
— the description by MathWorks

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured pipeline for importing raw driving sensor data (GPS, camera, lidar, actor tracks) into standardized scenariobuilder objects. It handles essential preprocessing tasks such as synchronization, timestamp normalization, offset correction, and coordinate frame validation. The skill is designed to prepare data for downstream simulation, scenario generation, or interactive inspection, while strictly adhering to an opt-in policy for the drivingLogAnalyzer app to ensure efficient workflows.

What it does

  • Wraps raw sensor logs into standardized scenariobuilder objects.
  • Synchronizes multi-sensor data to a common timeline.
  • Normalizes and converts timestamps for consistent processing.
  • Computes actor tracks from lidar data when annotations are missing.
  • Attaches camera and lidar mounting parameters and intrinsics.
  • Exports processed data to MAT, workspace, or timetable formats.

When to use it

  • When you need to import raw driving datasets like KITTI, nuScenes, or ROS bags.
  • When you need to synchronize disparate sensor streams with different sample rates.
  • When you need to perform offset correction or crop recordings across multiple sensors.
  • When you need to prepare data for export to simulation environments like RoadRunner or Unreal.
  • When you need to inspect sensor alignment or data quality using the drivingLogAnalyzer.

Advantages

  • Ensures data consistency through standardized object wrappers.
  • Automates complex synchronization and timestamp alignment tasks.
  • Provides a clear, rule-based path for both automated workflows and manual inspection.
  • Reduces manual effort in mapping raw data formats to simulation-ready structures.

Usage examples

“Import my KITTI dataset files into scenariobuilder objects and synchronize them.”
The agent will inspect the dataset structure, construct GPSData, LidarData, and CameraData objects, perform timestamp normalization, and run the synchronize command.
“My lidar and camera timestamps are out of sync, can you help?”
The agent will wrap the data, identify the offset, and offer to open the drivingLogAnalyzer for you to visually verify the alignment.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/matlab/matlab-import-driving-data/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip matlab-import-driving-data.zip -d .agents/skills

Documentation by MathWorks

Driving Data Importer

This skill loads raw driving sensor data into scenariobuilder.* objects (GPSData, CameraData, LidarData, ActorTrackData, Trajectory, laneData) and provides the CLI for every preprocessing step DLA exposes (sync, crop, offset, normalize, convert timestamps). It also covers the drivingLogAnalyzer (DLA) app as an opt-in inspection tool — see Rule 0 below; DLA is never a default step.

After wrapping is done, the canonical next move is matlab-use-scenario-builder (trajectory smoothing, scene/scenario generation, lane localization, RoadRunner / drivingScenario / OpenSCENARIO / OpenDRIVE / OpenCRG / Unreal export).

Rule 0 — DLA is opt-in only (HARD RULE, READ FIRST)

Never call drivingLogAnalyzer unless the user explicitly asks for it or has reported a sensor-data problem DLA is built to debug. Auto-launching DLA after wrapping data is a first-attempt-success failure: it stalls the user (a UI app forces context-switch, scrub, click, confirm) and signals that the agent is not confident the import worked.

When DLA IS allowed — only these two cases:

  1. Explicit user request — user types DLA, drivingLogAnalyzer, "open in DLA", "inspect / visualize / explore / replay / analyze the recording", "open the driving log analyzer".

  2. Reported sensor-data problem that DLA is the right tool for:

    • "the sensors look out of sync"

    • "camera and lidar timestamps don't match"

    • "actor cuboids float above the cars" (overlay alignment)

    • "I see missing frames / a gap in the timeline"

    • "the offset looks wrong / the timeline is shifted"

When DLA is NOT allowed (defaults — go straight to matlab-use-scenario-builder):

  • "Virtualize this data", "build a scenario from this data", "generate a scene", "export to RoadRunner / drivingScenario / OpenSCENARIO / OpenDRIVE / OpenCRG / Unreal", "simulate this drive", "I have data, do something with it".

  • Anything that has a downstream simulation or scenario target.

If you ever feel an urge to add a "let me open DLA so you can verify" step after wrapping — stop. Save the wrapped objects to sandbox/<dataset>_wrapped.mat, print a short summary, and hand off.

When to Use

  • User has raw driving dataset files (KITTI, nuScenes, Waymo, custom logs, ROS/ROS2 bags, .mat, .csv, .xls, video) and needs them loaded into scenariobuilder.* objects

  • User says "open in DLA", "drivingLogAnalyzer", "inspect / visualize / explore / replay / analyze this dataset" (triggers DLA — Rule 0 case 1)

  • User wants to map raw structs/tables/rosbag topics to GPSData, CameraData, LidarData, ActorTrackData, Trajectory, or laneData

  • User wants to attach camera/lidar Mounting Location / Mounting Angles / Intrinsics / Ego Origin Height

  • User asks for multi-sensor synchronization of any kind: "sync", "align sensors", "match sample rates", "resample to a common timeline", "sensor-to-sensor alignment"

  • User asks for offset correction ("drag-to-align", "time offset", "shift this sensor by X seconds")

  • User asks to crop / trim / extract a segment of a recording across sensors

  • User asks to normalize timestamps ("common t=0", "time origin", "POSIX to seconds", "datetime to numeric")

  • User wants to export sensor data to MAT, workspace, timetable, or a reproducible script

  • User needs to inspect dataset structure, identify available sensor modalities, validate calibration transforms, or check for pre-computed annotations

  • User needs to compute actor tracks from lidar when no annotations exist (clustering / detector / camera-based pipeline)

  • User reports a sensor-data problem (Rule 0 case 2) — wrap, then offer DLA as the debugging path

When NOT to Use

  • Do NOT auto-launch DLA after wrapping. If the user asked to virtualize / build a scenario / export to a sim format, finish wrapping and hand off to matlab-use-scenario-builder directly. (Rule 0.)

  • User wants to build / generate / export a scenario (RoadRunner, drivingScenario, OpenSCENARIO, OpenDRIVE, OpenCRG, Unreal) — wrap here, then hand off to matlab-use-scenario-builder.

  • User wants to smooth a trajectory, localize ego on a lane, correct height on a terrain scene, place static objects (signs/trees/poles), extract a road surface (OpenCRG) from lidar, generate 3D assets from images, add elevation to a map, or georeference point clouds — all matlab-use-scenario-builder

  • User wants to run sensor-fusion tracking (multiSensorTargetTracker, JPDA + smoother) to get cleaner tracks — matlab-use-scenario-builder Workflow 14

  • User is debugging general MATLAB code unrelated to dataset import — use matlab-debug-code

  • User wants to install a toolbox or check MATLAB products — use matlab-list-products / matlab-install-products

  • Task is about non-driving sensor data (medical imaging, audio, etc.) — out of scope

Boundary heuristic: wrapping into scenariobuilder.* and any sync/crop/offset/normalize CLI work belongs here. The moment the user says scenario / scene / RoadRunner / simulate / drive in a virtual world / export to OpenSCENARIO / virtualize — wrap, save, hand off. DLA stays parked unless invoked by name or summoned by a reported problem.

IMPORTANT — Execution Rules

Rule 1: Inspect Before Importing

Always inspect the dataset structure first. Before writing any import code:

  1. List the top-level directory structure

  2. Identify what sensor modalities are available (GPS, lidar, camera, annotations)

  3. Determine if pre-computed annotations/labels exist (3D bounding boxes, tracks)

  4. Check calibration files for coordinate frame definitions

Rule 2: Check for Existing Annotations Before Computing Tracks

Never run a lidar tracker if the dataset already provides actor tracks or 3D bounding box annotations. Always check first:

  • Look for annotation files (object_detection.json, labels/, annotations/, tracking/)

  • Check if annotations are per-frame (temporal tracks) or single-keyframe only

  • If annotations exist, map them directly to ActorTrackData

  • If only keyframe annotations exist (no temporal tracking), inform the user and discuss options

Rule 3: Understand Dataset Structure Types

Driving datasets commonly have two types of recordings:

TypeDurationAnnotationsUse Case
Drives/LogsLong (1-10 min)Often noneEgo trajectory + road network
Sequences/ClipsShort (10-30s)Usually yes (keyframe or full)Actor tracks + ego

Always clarify which type the user's data is before proceeding. If data lacks annotations, inform the user that actor tracks must be computed (via lidar detection/tracking or camera detection) and set expectations about quality.

Rule 4: Validate Coordinate Frames

Before using any transform, verify:

  1. What coordinate frame convention the dataset uses (e.g., X-forward vs Y-forward)

  2. Whether extrinsic transforms are sensor-to-ego, sensor-to-vehicle, or sensor-to-sensor

  3. Validate by checking that transformed ground points have Z near 0 in ego frame

Rule 5: Report Data Summary to User

After initial inspection, always present a summary:

Dataset: <name>
Recording: <ID/name>
Duration: <X seconds>
Available sensors:
  - GPS: <format, sample count, rate>
  - Lidar: <format, frame count, rate, sensor model>
  - Camera: <format, frame count, rate, resolution>
  - Annotations: <YES/NO — type if yes>
Calibration: <available transforms>

Common Dataset Formats

GPS / GNSS / IMU
FormatHow to Read
JSON (lat/lon/alt arrays)jsondecode(fileread(file))
HDF5 (fields in groups)h5read(file, '/group/field')
CSVreadtable(file)
ROS bagscenariobuilder.GPSData("file.bag", "/topic")
NMEACustom parser needed

Key fields needed: timestamps, latitude, longitude, altitude

Lidar Point Clouds
FormatHow to Read
PCDpcread(file)
PLYpcread(file)
BIN (KITTI format)reshape(fread(fid,'single'),[4,Inf])' — columns: x,y,z,intensity
NPY (custom struct)Custom reader needed — parse header, read structured bytes
LAS/LAZlasFileReader(file) then readPointCloud

Important: Always check the point cloud coordinate frame. Common conventions:

  • X-forward, Y-left, Z-up (ROS/vehicle standard)

  • X-right, Y-forward, Z-up (some lidars)

  • X-forward, Y-right, Z-up (KITTI)

Camera Images
FormatHow to Read
Directory of imagesimageDatastore(dir)
Video fileVideoReader(file)
ROS bagrosbag then read image messages

Key info needed: timestamps, file paths, intrinsics, distortion model, extrinsics (camera-to-ego)

Calibration

Calibration files typically provide:

  • Intrinsics: focal length, principal point, distortion coefficients

  • Extrinsics: 4x4 homogeneous transforms between sensor frames

  • Distortion model: pinhole, fisheye (Kannala), equidistant, etc.

Common pitfall: The naming of extrinsic transforms is inconsistent across datasets. A field named lidar_extrinsics could mean:

  • lidar-to-ego (most common)

  • lidar-to-camera

  • ego-to-lidar (inverse)

Always verify by checking translation values against physical sensor mounting positions (e.g., lidar mounted ~1.7m high should have Z translation ~1.7 in lidar-to-ego).

3D Bounding Box Annotations

Common formats:

Per object:
  - class: "Car", "Truck", "Pedestrian", etc.
  - location_3d: [x, y, z] — center position in some reference frame
  - size: [length, width, height] in meters
  - orientation: quaternion or yaw angle
  - track_id: persistent ID across frames (if temporal tracking exists)

Frame of reference: Annotations may be in:

  • Ego/vehicle frame (most common for driving datasets)

  • World/global frame

  • Sensor frame (lidar or camera)

Always check which frame and transform to ego if needed.


Import Pipeline

Step 1: GPS → GPSData

Canonical GPSData construction is THREE lines, always together — bare scenariobuilder.GPSData(...) is incomplete. The post-construction convertTimestamps + normalizeTimestamps calls are part of the canonical construction, not optional cleanup. Downstream APIs (synchronize, trajectory, actorprops, localizeEgoUsingLanes, RoadRunner export) expect numeric timestamps starting at t=0.

% Load timestamps, lat, lon, alt from dataset
gpsData = scenariobuilder.GPSData(timestamps, latitude, longitude, altitude);

% Canonical post-construction pair (always run both)
convertTimestamps(gpsData, "numeric");
timeRef = normalizeTimestamps(gpsData);

If you skip these two lines, the object will silently fail later (sample-rate mismatches in synchronize, scenario time bounds wrong, RoadRunner export errors). Run them every time, even when you "just" construct a GPSData for inspection — and even when the prompt only says "construct the GPSData object."

Altitude handling:

  • If using OpenStreetMap roads: zero the altitude (altitude = zeros(...)) — OSM has no elevation

  • If using a scene with terrain elevation: keep real altitude

  • If altitude is missing: use zeros

Step 2: Annotations → ActorTrackData

When per-frame 3D annotations with track IDs exist:

% For each timestamp, collect track IDs and positions
timestamps = <Nx1 numeric>;
trackIDs = cell(N, 1);    % each cell: Mx1 string array
positions = cell(N, 1);   % each cell: Mx3 [x y z] in ego frame

for i = 1:N
    % Get annotations for frame i
    frameAnnots = <filter annotations for this frame>;
    trackIDs{i} = string({frameAnnots.track_id}');
    positions{i} = [frameAnnots.x, frameAnnots.y, frameAnnots.z];
end

trackData = scenariobuilder.ActorTrackData(timestamps, trackIDs, positions);

If annotations are in world frame (not ego frame):

% Transform world positions to ego-relative positions
% ActorTrackData expects positions relative to ego at each timestamp
for i = 1:N
    worldPos = positions_world{i};
    egoPos = egoPositionAtTime(i);  % from GPS/odometry
    egoYaw = egoYawAtTime(i);
    R = [cos(egoYaw) sin(egoYaw) 0; -sin(egoYaw) cos(egoYaw) 0; 0 0 1];
    positions{i} = (worldPos - egoPos) * R';
end
Step 3: Camera → CameraData
% Match camera image files to timestamps
imageFiles = dir(fullfile(camDir, '*.jpg'));
camTimestamps = <parse timestamps from filenames or metadata>;

cameraData = scenariobuilder.CameraData(camTimestamps, ...
    fullfile(camDir, {imageFiles.name}'), Name="FrontCamera");
Step 4: Lidar → LidarData

Always wrap lidar via scenariobuilder.LidarData — this is the only wrapper DLA accepts and the only one downstream Scenario Builder APIs (Workflow 10 OpenCRG extraction, Workflow 11 georeferencing) consume. Do not hand DLA raw pointCloud arrays or paths.

% Match lidar files (.pcd, .ply, .las/.laz) to timestamps
lidarFiles = dir(fullfile(lidarDir, '*.pcd'));
lidarTimestamps = <parse timestamps from filenames or metadata>;

lidarData = scenariobuilder.LidarData(lidarTimestamps, ...
    fullfile(lidarDir, {lidarFiles.name}'), Name="OSLidar");

For multi-lidar setups, build one scenariobuilder.LidarData per sensor with a distinct Name (e.g., "OSLidar", "VLP32", "OuterLeft"). DLA will render each in its own pane.

Step 5: Synchronize All Sensors
convertTimestamps(gpsData, "numeric");
convertTimestamps(trackData, "numeric");

timeRef = normalizeTimestamps(gpsData);
normalizeTimestamps(trackData, timeRef);
synchronize(trackData, gpsData);
Step 6: Launch DLA — only when explicitly requested (Rule 0)

drivingLogAnalyzer does NOT accept programmatic sensor inputs. It opens the app; the user then imports sensors via the GUI (Import → From Workspace). Make sure the wrapped objects are in the base workspace, then launch the app bare:

% Wrapped objects must already exist in the base workspace
% (gpsData, cameraData, lidarData, trackData)
drivingLogAnalyzer;   % user clicks Import → From Workspace

Do NOT call forms like drivingLogAnalyzer(sensors, Plot=true) or drivingLogAnalyzer(gpsData, Plot=true) — those signatures are not supported and will error. Remember Rule 0: launch DLA only when the user explicitly asks for it or reports a sensor-data problem DLA is built to debug. (See workflow-driving-log-analyzer.md for the full opt-in recipe.)


When No Annotations Exist — Computing Actor Tracks from Lidar

If the dataset has NO pre-computed 3D bounding boxes or temporal tracks, actor tracks must be computed. Always inform the user about:

  1. This requires significant processing and tuning

  2. Results depend heavily on scene complexity (open highway = good, dense urban = poor)

  3. Deep learning detectors (PointPillars, CenterPoint) give much better results than clustering

Option A: Deep Learning Detector (Preferred)
% Requires a trained model (e.g., PointPillars)
detector = pointPillarsObjectDetector(net, pcRange, classNames, anchorBoxes);
bboxes = detect(detector, ptCloud);
Option B: Classical Pipeline (Clustering + Tracking)
% Per frame:
% 1. Transform lidar to ego frame
% 2. Remove ground plane
% 3. Filter ROI
% 4. Euclidean clustering
% 5. Size filtering
% 6. Feed detections to tracker (JPDA or GNN)

Known limitations of clustering approach:

  • Dense urban scenes: parked cars, buildings, and road infrastructure form continuous surfaces that cannot be segmented into individual objects

  • Mega-clusters: thousands of points merging into single clusters spanning 20+ meters

  • False positives: poles, signs, trees, walls pass size filters

  • Works best on: highways, open roads with isolated vehicles

Option C: Camera-Based Detection
% Use pretrained camera detector
detector = vehicleDetectorYOLOv2();  % or vehicleDetectorFasterRCNN()
[bboxes, scores] = detect(detector, img);

Limitation: Gives 2D boxes only. Requires depth estimation or lidar fusion for 3D positions.


Projection: Lidar Points → Camera Image

To verify detection alignment or overlay lidar on camera:

% Transform lidar to camera frame
T_lidar2cam = inv(T_cam2ego) * T_lidar2ego;  % compose transforms
pts_cam = (T_lidar2cam * [pts_lidar, ones(N,1)]')';

% Project to pixels (pinhole model)
inFront = pts_cam(:,3) > 0;
u = fx * pts_cam(inFront,1) ./ pts_cam(inFront,3) + cx;
v = fy * pts_cam(inFront,2) ./ pts_cam(inFront,3) + cy;

% Display
imshow(img); hold on;
scatter(u, v, 1, depth, 'filled');

For fisheye cameras: Standard pinhole projection will have errors at image edges. Use the camera's distortion model for accurate projection.


Checklist Before Starting Import

  • [ ] What sensors does the dataset provide?

  • [ ] Are there pre-computed annotations/3D bounding boxes?

  • [ ] Are annotations per-frame (temporal) or keyframe-only?

  • [ ] What coordinate frame are positions in?

  • [ ] What is the calibration transform naming convention?

  • [ ] Does altitude data exist? Will OSM or scene-based roads be used?

  • [ ] What is the recording duration? Need to crop?


Copyright 2026 The MathWorks, Inc.


Files

references/sensor-import-api.md


name: sensor-import-api description: How to wrap recorded sensor data into scenariobuilder.* objects (GPSData, ActorTrackData, CameraData, LidarData, Trajectory) using direct constructors or the recordedSensorData factory. Includes field-mapping tables for the two common dataset schemas (consolidated sensorData.mat vs per-sensor-folder + timestamps/*.mat) and a list of sensor types that have NO scenariobuilder wrapper.

Sensor Import API — Wrapping Recorded Data into scenariobuilder.*

Parent skill: SKILL.md (driving-data-importer). Load this when a user gives you a dataset in any layout and you need to convert raw structs/cells/files into scenariobuilder.* objects ready for drivingLogAnalyzer inspection (workflow-driving-log-analyzer.md). After cleaning + wrapping, hand the resulting objects to the scenario-builder skill for trajectory(...), actorprops, lane localization, or RoadRunner / drivingScenario / OpenSCENARIO / OpenDRIVE / OpenCRG export.

Coverage table — what has a wrapper, what does not

Sensor typeWrapper classNotes
GPS / GNSSscenariobuilder.GPSDatatimestamps + lat/lon/alt
Local-frame ego waypointsscenariobuilder.Trajectoryusually built from trajectory(gpsData,...), but can be constructed directly
Camera (image sequence or video)scenariobuilder.CameraDataaccepts file lists, image arrays, video files, or rosbag topics
Lidar (point clouds)scenariobuilder.LidarDataaccepts pointCloud arrays, file lists, or rosbag topics
Actor / object tracks (with track IDs)scenariobuilder.ActorTrackDatarequires per-frame track-ID lists
Radarnoneno scenariobuilder.RadarData. Keep raw struct, do NOT fabricate a wrapper.
IMUnoneno scenariobuilder.IMUData. Keep raw struct, do NOT fabricate a wrapper.
Per-frame 3D detections without track IDs (e.g., CubeRCNN corners3D only)scenariobuilder.ActorTrackData with placeholder per-frame IDsDLA's camera Actors overlay projects them natively when CameraData has Intrinsics + Mounting Location/Angles + Ego Origin Height. Use IDs "det_<frameIdx>_<i>" so each cuboid is unique within the frame. Full conversion in per-frame-detections-to-actortrackdata.md (in the matlab-use-scenario-builder skill).

Rule of thumb: if help scenariobuilder.<X>Data does not return a help page, the wrapper does not exist — do not invent a constructor. Plot/visualize raw radar/IMU/per-frame detections with regular MATLAB plotting.

Two ways to construct: direct vs factory

Both forms are equivalent. Pick the factory when constructing a mix of sensors in one loop; pick direct constructors when you want explicit type names in code review.

%% Direct constructors
gpsData    = scenariobuilder.GPSData(timestamps, lat, lon, alt);
cameraData = scenariobuilder.CameraData(camTimestamps, imageFiles);
lidarData  = scenariobuilder.LidarData(lidarTimestamps, pcdFiles);
trackData  = scenariobuilder.ActorTrackData(trkTimestamps, trackIDs, positions);
egoTraj    = scenariobuilder.Trajectory(timestamps, waypoints);

%% recordedSensorData factory — type-string dispatch
gpsData    = recordedSensorData("gps",        timestamps, lat, lon, alt);
cameraData = recordedSensorData("camera",     camTimestamps, imageFiles);
lidarData  = recordedSensorData("lidar",      lidarTimestamps, pcdFiles);
trackData  = recordedSensorData("actorTrack", trkTimestamps, trackIDs, positions);
egoTraj    = recordedSensorData("trajectory", timestamps, waypoints);   % Nx3

Both forms accept the same name-value arguments as their direct constructors (Name, Attributes, SensorParameters for camera/lidar, Category/Dimension/Orientation/Velocity/Speed/Age for actor tracks).

Schema A — consolidated sensorData.mat

A single .mat file at the dataset root containing all sensors as fields/tables. Common in many MathWorks examples (Pandaset, VSI quick exports).

S = load(fullfile(dataDir, "sensorData.mat"));
gpsData    = scenariobuilder.GPSData(S.GPS.Timestamp, S.GPS.Latitude, S.GPS.Longitude, S.GPS.Altitude);
cameraData = scenariobuilder.CameraData(S.Camera.Timestamp, S.Camera.ImagePath);
trackData  = scenariobuilder.ActorTrackData(S.Tracks.Timestamp, S.Tracks.TrackID, S.Tracks.Position, ...
    Category=S.Tracks.Category, Dimension=S.Tracks.Dimension, Orientation=S.Tracks.Orientation);

Field-name case sensitivity: Timestamp vs Timestamps vs timestamp varies between datasets. Always disp(fieldnames(S.<Sensor>)) (or disp(S.<Sensor>.Properties.VariableNames') for tables) before accessing.

Schema B — per-sensor folders + separate timestamps/*.mat

Common in larger recordings (VSI-Labs PostProcessed, polysync exports, ros2bag-derived dumps). Each sensor lives in its own folder with binary frames (.jpeg, .pcd, .png), and a sibling timestamps/<Sensor>.mat carries the time vector. There may also be a configs/<Sensor>_CameraParameters.mat per camera.

<dataDir>/
├── ExternalRGBCamera/*.jpeg
├── OSLidar/*.pcd
├── GPS/GPSData.mat        (cell array of per-sample structs)
├── Radar/RadarData.mat    (cell array of per-sample structs — no wrapper)
├── timestamps/
│   ├── ExternalRGBCamera.mat
│   ├── OSLidar.mat
│   ├── GPS.mat
│   └── Radar.mat
└── configs/
    └── ExternalRGBCamera_CameraParameters.mat

Build each sensor with the matching timestamp file:

%% Camera — image-file list + separate timestamps
extDir   = fullfile(dataDir, "ExternalRGBCamera");
extFiles = dir(fullfile(extDir, "*.jpeg"));
[~, o]   = sort({extFiles.name}); extFiles = extFiles(o);
imgList  = fullfile(extDir, {extFiles.name}');                      % N-by-1 cell of full paths
camTs    = load(fullfile(dataDir, "timestamps", "ExternalRGBCamera.mat")).timestamps;

cameraData = scenariobuilder.CameraData(camTs, imgList, Name="ExternalRGB");

%% Lidar — pcd-file list + separate timestamps
osDir    = fullfile(dataDir, "OSLidar");
osFiles  = dir(fullfile(osDir, "*.pcd"));
[~, o]   = sort({osFiles.name}); osFiles = osFiles(o);
pcdList  = fullfile(osDir, {osFiles.name}');                         % N-by-1 cell of full paths
osTs     = load(fullfile(dataDir, "timestamps", "OSLidar.mat")).timestamps;

osLidar  = scenariobuilder.LidarData(osTs, pcdList, Name="OSLidar");

%% GPS — cell array of per-sample structs needs flattening into column vectors
gpsRaw = load(fullfile(dataDir, "GPS", "GPSData.mat")).GPSData;       % N-by-1 cell
nGPS   = numel(gpsRaw);
lat = zeros(nGPS,1); lon = zeros(nGPS,1); alt = zeros(nGPS,1);
for k = 1:nGPS
    lat(k) = gpsRaw{k}.Latitude;
    lon(k) = gpsRaw{k}.Longitude;
    alt(k) = gpsRaw{k}.Altitude;
end
gpsTs   = load(fullfile(dataDir, "timestamps", "GPS.mat")).timestamps;

gpsData = scenariobuilder.GPSData(gpsTs, lat, lon, alt, Name="GPS");

Attach camera parameters via SensorParameters (required for DLA actor/lane overlay)

DLA's actor/lane overlay path requires cameraData.SensorParameters to be a struct with all four fields:

FieldTypeConstraint
MountingLocation1×3 double [x y z] (m, vehicle frame)finite
MountingAngles1×3 double [yaw pitch roll] (deg)finite
IntrinsicscameraIntrinsics objectrequired
EgoOriginHeightscalar double (m)must be > 0 (DLA rejects 0 with "must be a positive and finite scalar")

A bare cameraIntrinsics will be accepted by the property setter but DLA's overlay will refuse it. Always build the full struct.

% From ROS-style K (row-major 9x1) + D (5x1 plumb_bob [k1 k2 p1 p2 k3])
camCfg = load(fullfile(dataDir, "configs", "ExternalRGBCamera_CameraParameters.mat")).CameraParams;
K = reshape(camCfg.K, 3, 3).';
intr = cameraIntrinsics([K(1,1) K(2,2)], [K(1,3) K(2,3)], [imgH imgW], ...
    RadialDistortion=[camCfg.D(1) camCfg.D(2) camCfg.D(5)], ...
    TangentialDistortion=[camCfg.D(3) camCfg.D(4)]);

cameraData.SensorParameters = struct( ...
    "MountingLocation", [1.5, 0, camHeight], ...   % see "Mounting geometry" below
    "MountingAngles",   [0, 0, 0], ...
    "Intrinsics",       intr, ...
    "EgoOriginHeight",  0.001);
Mounting geometry — agent guidance

Actor Position(:,3) from typical ground-truth or detector pipelines is already in ego frame with z ≈ 0 (vehicles sit at ground level). The agent never invents calibration — it uses what the dataset provides and explicitly tells the user what the defaults are.

Recommended workflow when the dataset ships only intrinsics + CameraHeight:

  1. Auto-fill with the defaults below and print them clearly to the user.

  2. Direct the user to the DLA camera-overlay pane and say: "verify boxes sit on the ground; if pitch is off or boxes float, give me the real MountingAngles / MountingLocation from your calibration."

  3. Update only the field the user corrects — do not silently retune all four.

FieldDefaultWhy
MountingLocation(1) (forward)1.5 mTypical ADAS rig; rarely off by enough to ruin overlay.
MountingLocation(2) (lateral)0Centered.
MountingLocation(3) (height)CameraHeight from source, else 1.5 mLargest geometric impact after intrinsics.
MountingAngles [yaw pitch roll][0 0 0] degPitch is the second-most-impactful param after intrinsics — even 2–3° tilts cuboids vertically. Surface this to the user.
EgoOriginHeight0.001 mJust needs to be positive to clear DLA's validator. Do NOT put CameraHeight here — that lifts every actor by camHeight and they project at the horizon (the "boxes in the air" failure mode).
When the dataset has no intrinsics

Leave cameraData.SensorParameters empty and tell the user:

"No camera intrinsics found in the source. DLA actor/lane overlay will be disabled until you provide fx, fy, cx, cy (and optionally imageSize, RadialDistortion, TangentialDistortion) from the camera calibration file."

Do not fabricate a pinhole guess from image size — a plausible-but-wrong overlay is worse than a disabled one (it produces silently misaligned cuboids that a user may not notice).

After wrapping — what happens next

The default next step is NOT to launch DLA. After the sensors are wrapped:

  1. Save the wrapped objects (save sandbox/<dataset>_wrapped.mat gpsData cameraData lidarData trackData).

  2. Print a one-screen summary (sensor counts, durations, sample rates).

  3. Hand off to matlab-use-scenario-builder if the user asked to virtualize / build a scenario / generate a scene / export to RoadRunner / drivingScenario / OpenSCENARIO / OpenDRIVE / OpenCRG / Unreal / simulate.

drivingLogAnalyzer is opt-in only (Rule 0 in SKILL.md). Launch it only when the user explicitly asks (DLA, drivingLogAnalyzer, "open in DLA", "inspect / visualize / explore / replay / analyze the recording") or reports a sensor-data problem DLA is built to debug (sync drift, timestamp mismatch, overlay misalignment, missing frames, shifted timeline).

When DLA is invoked:

% drivingLogAnalyzer takes no programmatic arguments — it imports from the
% MATLAB workspace via its UI. Just ensure the wrapped objects are in scope:
drivingLogAnalyzer;   % then user clicks Import → From Workspace

DLA handles synchronization, scrubbing, and per-pane visualization automatically; do not build a manual VideoWriter dashboard for inspection. See workflow-driving-log-analyzer.md for the full opt-in recipe.

Sensors without a wrapper — radar, IMU

These do NOT have a scenariobuilder.* class. Keep them as their loaded form (cell array of structs, struct of arrays, or table) and visualize with regular MATLAB:

  • Radar BEV: scatter(x, y, 36, radial_speed, "filled") on Cartesian axes — see the radar pane in any of the example scripts.

  • IMU: plot(t, [linAccel angVel]) — diagnostics only; not consumed by actorprops / RR export.

Per-frame 3D detections — wrap into ActorTrackData for DLA

CubeRCNN / KITTI / nuScenes-style detections (per-frame corners3D + labels + scores, no persistent IDs) DO belong in ActorTrackData — DLA's camera pane has a built-in Actors overlay that projects the cuboids onto the image as long as the CameraData has Intrinsics + Mounting Location/Angles + Ego Origin Height attached. Assign placeholder per-frame IDs ("det_<frameIdx>_<i>") so each cuboid is unique within its frame. Full conversion (camera→vehicle rotation, center/dimension/yaw mapping, the ActorTrackData constructor, and the DLA hand-off) is in per-frame-detections-to-actortrackdata.md (in the matlab-use-scenario-builder skill).

If detections need to participate in the scenario (RR export, actorprops, etc., where persistent track IDs are required), run a tracker first (matlab-use-scenario-builder Workflow 14, sensor-fusion tracking) to assign IDs, then rebuild ActorTrackData from the tracker output.


Copyright 2026 The MathWorks, Inc.


Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
MathWorks
Publisher
MathWorks (Official)
Source
matlab/matlab-agentic-toolkit · skills-catalog/automotive/matlab-import-driving-data · commit 2a7f215
Licence
BSD 2-Clause License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE.md)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by MathWorks.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download matlab-import-driving-data.zip