Skip to content
Back to skills

Roadrunner Rrhd Authoring

ASecurity

Build RoadRunner HD Map entities in MATLAB — lanes, boundaries, markings, junctions, signs, signals, barriers, parking. Use when creating driving scenes from scratch, authoring road networks for simulation and testing automated driving systems, or assembling RRHD maps from Lanelet2 or other HD map sources.

  • 1,122 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 27, 2026
data-aigotestingdebugginggitapi

Works with

  • api

Security analysis

A100/100

Pro scans all 20 files and shows the line behind each finding

Scanned May 27, 2026

npx -y skills add matlab/matlab-agentic-toolkit --skill roadrunner-rrhd-authoring --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Roadrunner Rrhd Authoring?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Roadrunner Rrhd Authoring
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/matlab-roadrunner-rrhd-authoring/badge)](https://www.skillsdirectory.com/skills/matlab-roadrunner-rrhd-authoring)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: roadrunner-rrhd-authoring
description: >
  Build RoadRunner HD Map entities in MATLAB — lanes, boundaries, markings, junctions, signs,
  signals, barriers, parking. Use when creating driving scenes from scratch, authoring road
  networks for simulation and testing automated driving systems, or assembling RRHD maps
  from Lanelet2 or other HD map sources.
license: MathWorks BSD-3-Clause
metadata:
  author: MathWorks
  version: "1.0"
---

# RRHD Authoring Skill

Build RoadRunner HD Map (`.rrhd`) entities directly using the `roadrunnerHDMap` API in MATLAB. No external builder scripts required.

## When to Use

- Building RRHD maps from scratch (synthetic scenes, test roads)
- Assembling RRHD entities from converted map data (Lanelet2, OpenDRIVE)
- Adding lanes, boundaries, markings, junctions, signs, barriers, or parking to an HD Map
- Need verified `roadrunner.hdmap.*` class/property reference and construction patterns
- Debugging RRHD construction errors (wrong property names, alignment issues)

## When NOT to Use

- Converting Lanelet2 .osm files — use `roadrunner-convert-lanelet2-to-rrhd` (which invokes this skill)
- Looking up asset paths for markings/signs/barriers — use `roadrunner-asset-mapping`
- Importing finished .rrhd into RoadRunner — use `roadrunner-import-scene`
- Editing an existing RoadRunner scene interactively (this skill writes .rrhd files, not scene edits)

## Key Rules

- **Always write to .m files.** Never put multi-line MATLAB code directly in `evaluate_matlab_code`. Write to a `.m` file, run with `run_matlab_file`, edit on error.
- **Read references/apiReference.md** before writing any RRHD construction code.
- **Only build what the user asked for.** Do not add extra lanes, junctions, or objects unless explicitly requested.
- **`rrMap = roadrunnerHDMap;` must come first** — loads the namespace before any `roadrunner.hdmap.*` usage.
- **Create-then-assign pattern** — never pass constructor args (except `RelativeAssetPath` and `AlignedReference`).
- **Empty typed arrays** — use `ClassName.empty` not `[]`.
- **All geometry must be Nx3** — always include Z column (default 0 if flat).
- **Run enforcement gate before `write()`** — alignment, spatial, and geometry checks are mandatory.

## Critical API Rules

**You MUST create a `roadrunnerHDMap` object before using any `roadrunner.hdmap.*` classes** (lazy namespace loading):
```matlab
rrMap = roadrunnerHDMap;  % REQUIRED — loads the namespace
```

**Create-then-assign pattern** — never pass constructor args (except Name=Value for two classes):
```matlab
ref = roadrunner.hdmap.Reference;
ref.ID = "myID";  % assign after creation
```

**Name=Value constructors** — ONLY `RelativeAssetPath` and `AlignedReference` accept Name=Value:
```matlab
rap = roadrunner.hdmap.RelativeAssetPath(AssetPath="Assets/Markings/StopLine.rrlms");
ar = roadrunner.hdmap.AlignedReference(Reference=ref, Alignment="Forward");
% WRONG: positional args error with "A name is expected"
```

**Empty typed arrays** — never use `[]` for typed properties:
```matlab
lane.Predecessors = roadrunner.hdmap.AlignedReference.empty;  % CORRECT
lane.Predecessors = [];  % WRONG: "Value must be of type AlignedReference"
```

## Verified Property Names (R2025a+)

See [references/apiReference.md](references/apiReference.md) for the complete verified class/property table. Key gotchas:

| Common Mistake | Correct |
|---|---|
| `LeftBoundary` | `LeftLaneBoundary` |
| `RightBoundary` | `RightLaneBoundary` |
| `Speed` | `Value` (int32) |
| `Unit = "KPH"` | `VelocityUnit = "Kph"` |
| `ParametricAttrib` | `ParametricAttribution` |
| `pa.Value = mr` | `pa.MarkingReference = mr` |
| `pa.StartFraction/EndFraction` | `pa.Span = [0 1]` (double array) |
| `JunctionPhase` | `Phase` |
| `roadrunnerHDMap("file.rrhd")` | `rrMap = roadrunnerHDMap; read(rrMap, "file.rrhd")` |
| Nx2 geometry | Nx3 geometry (always include Z column) |
| `cm.CurveMarkingTypeID` | `cm.MarkingTypeReference` |
| `b.BarrierTypeID` | `b.BarrierTypeReference` |
| `s.SignTypeID` | `s.SignTypeReference` |
| `s.BoundingBox` | `s.Geometry` (takes GeoOrientedBoundingBox) |
| `bb.Position` | `bb.Center` (1x3 double) |
| `bb.Orientation` | `bb.GeoOrientation` ([h,p,r] double array) |
| `pg.OuterRing` | `pg.ExteriorRing` (Nx3 double) |
| `bt.AssetPath` | `bt.ExtrusionPath` (for BarrierType only) |
| `roadrunner.hdmap.GeoOrientation` | NOT a class — use `[h,p,r]` double array directly |
| `Lane.Predecessors = []` | `Lane.Predecessors = roadrunner.hdmap.AlignedReference.empty` |
| `lb.ParametricAttributes = []` | Cannot clear — skip assignment for no markings |
| `RelativeAssetPath("path")` | `RelativeAssetPath(AssetPath="path")` — Name=Value required |
| `AlignedReference(ref, "Fwd")` | `AlignedReference(Reference=ref, Alignment="Forward")` |
| `Junction.Geometry = polygon` | `Junction.Geometry = multiPolygon` (wrap in MultiPolygon) |
| `MarkingReference.MarkingID = 5` | `MarkingReference.MarkingID = ref` (Reference object, NOT numeric) |
| `SpeedLimit.Velocity` | `SpeedLimit.Value` (int32) + `.VelocityUnit = "Kph"` |

## Synthetic Scene Construction

### Lane Width and Geometry

Standard lane width: **3.7m** (US highway). Compute boundary positions by offsetting perpendicular to the lane center line direction.

### Shared Boundaries Between Adjacent Lanes

**Adjacent lanes MUST share boundary geometry.** The right boundary of lane N is the same object as the left boundary of lane N+1. For N lanes, create N+1 boundaries.

```
  LB_0 (left edge)
  ───────────────────
  │    Lane_1        │  left=LB_0(Fwd), right=LB_1(Fwd)
  ───────────────────
  LB_1 (shared)
  ───────────────────
  │    Lane_2        │  left=LB_1(Fwd), right=LB_2(Fwd)
  ───────────────────
  LB_2 (right edge)
```

### Alignment Rules

See [references/alignmentRules.md](references/alignmentRules.md) and [scripts/detectAlignment.m](scripts/detectAlignment.m) for complete rules, diagrams, and green-surface debugging.

Alignment specifies how boundary geometry direction relates to lane geometry direction:
- **Same direction → `"Forward"`**
- **Opposite direction → `"Backward"`**

**Two-step algorithm: proximity detection + spatial verification.**

A simple dot product is NOT sufficient — it misses cases where boundaries are spatially swapped (left boundary assigned to right side). The corrected algorithm:

```matlab
% Step 1: Proximity — are boundaries digitized same or opposite direction?
d_starts = norm(leftGeom(1,1:2) - rightGeom(1,1:2));
d_cross  = norm(leftGeom(1,1:2) - rightGeom(end,1:2));

if d_starts <= d_cross
    % Both boundaries go same direction — determine which direction
    laneDir = (leftGeom(end,1:2)+rightGeom(end,1:2))/2 ...
            - (leftGeom(1,1:2)+rightGeom(1,1:2))/2;
    laneDir = laneDir / norm(laneDir);
    leftNormal = [-laneDir(2), laneDir(1)];  % 90 deg CCW
    toLeft = mean(leftGeom(:,1:2)) - mean(rightGeom(:,1:2));
    if dot(toLeft, leftNormal) >= 0
        leftAlign = "Forward"; rightAlign = "Forward";
    else
        leftAlign = "Backward"; rightAlign = "Backward";
    end
else
    % Boundaries go opposite directions
    laneDirA = leftGeom(end,1:2) - leftGeom(1,1:2);
    laneDirA = laneDirA / norm(laneDirA);
    leftNormalA = [-laneDirA(2), laneDirA(1)];
    toLB = mean(leftGeom(:,1:2)) - mean(rightGeom(:,1:2));
    if dot(toLB, leftNormalA) >= 0
        leftAlign = "Forward"; rightAlign = "Backward";
    else
        leftAlign = "Backward"; rightAlign = "Forward";
    end
end
```

**Left/Right spatial verification** (MANDATORY — catches swapped boundaries):
```matlab
laneDir2D = laneGeom(end,1:2) - laneGeom(1,1:2);
laneDir2D = laneDir2D / norm(laneDir2D);
leftNormal = [-laneDir2D(2), laneDir2D(1)];  % 90 deg CCW
centerToBnd = mean(leftBndGeom(:,1:2)) - mean(laneGeom(:,1:2));
assert(dot(centerToBnd, leftNormal) > 0, ...
    'Left boundary is spatially on the RIGHT — swap boundaries or fix assignment');
```

Wrong alignment or swapped left/right causes **green grass instead of road surface**.

### Standard Marking Assets

| Marking ID | Asset Path |
|---|---|
| `SolidSingleWhite` | `Assets/Markings/SolidSingleWhite.rrlms` |
| `DashedSingleWhite` | `Assets/Markings/DashedSingleWhite.rrlms` |
| `SolidDoubleYellow` | `Assets/Markings/SolidDoubleYellow.rrlms` |
| `DashedSolidYellow` | `Assets/Markings/DashedSolidYellow.rrlms` |
| `SolidDashedYellow` | `Assets/Markings/SolidDashedYellow.rrlms` |

### Multi-Lane Road Recipe

1. Compute N lane center lines (evenly spaced at `laneWidth` intervals)
2. Compute N+1 boundary lines (edges + between each lane pair)
3. Assign shared boundaries (lane i: `left=LB_(i-1)`, `right=LB_i`)
4. Detect alignment via dot product
5. Apply markings (edges: `SolidSingleWhite`, divider: `SolidDoubleYellow`, separators: `DashedSingleWhite`)
6. Wire topology (if multiple segments, connect with pred/succ)
7. Snap connected endpoints — see [scripts/snapConnectedEndpoints.m](scripts/snapConnectedEndpoints.m)
8. Resolve height conflicts — see [scripts/resolveHeightConflicts.m](scripts/resolveHeightConflicts.m)
9. Assemble map and write

### Complete 2-Lane Road Example

```matlab
rrMap = roadrunnerHDMap;
w = 3.7;

% Lane Markings
lm1 = roadrunner.hdmap.LaneMarking; lm1.ID = "SolidWhite";
rap1 = roadrunner.hdmap.RelativeAssetPath; rap1.AssetPath = "Assets/Markings/SolidSingleWhite.rrlms";
lm1.AssetPath = rap1;
lm2 = roadrunner.hdmap.LaneMarking; lm2.ID = "DashedWhite";
rap2 = roadrunner.hdmap.RelativeAssetPath; rap2.AssetPath = "Assets/Markings/DashedSingleWhite.rrlms";
lm2.AssetPath = rap2;

% Boundaries (3 for 2 lanes)
lb = roadrunner.hdmap.LaneBoundary;
lb(2) = roadrunner.hdmap.LaneBoundary;
lb(3) = roadrunner.hdmap.LaneBoundary;
lb(1).ID = "LB_0"; lb(1).Geometry = [0 w 0; 100 w 0];
lb(2).ID = "LB_1"; lb(2).Geometry = [0 0 0; 100 0 0];
lb(3).ID = "LB_2"; lb(3).Geometry = [0 -w 0; 100 -w 0];

% Markings on boundaries
for i = [1 3]  % solid on edges
    pa = roadrunner.hdmap.ParametricAttribution; pa.Span = [0 1];
    ref = roadrunner.hdmap.Reference; ref.ID = "SolidWhite";
    mr = roadrunner.hdmap.MarkingReference; mr.MarkingID = ref; mr.FlipLaterally = false;
    pa.MarkingReference = mr;
    lb(i).ParametricAttributes = pa;
end
pa = roadrunner.hdmap.ParametricAttribution; pa.Span = [0 1];
ref = roadrunner.hdmap.Reference; ref.ID = "DashedWhite";
mr = roadrunner.hdmap.MarkingReference; mr.MarkingID = ref; mr.FlipLaterally = false;
pa.MarkingReference = mr;
lb(2).ParametricAttributes = pa;

% Lanes
ln = roadrunner.hdmap.Lane;
ln(2) = roadrunner.hdmap.Lane;
ln(1).ID = "Lane_1"; ln(1).Geometry = [0 w/2 0; 100 w/2 0];
ln(1).TravelDirection = "Forward"; ln(1).LaneType = "Driving";
ln(2).ID = "Lane_2"; ln(2).Geometry = [0 -w/2 0; 100 -w/2 0];
ln(2).TravelDirection = "Forward"; ln(2).LaneType = "Driving";

% Assign boundaries with alignment
bndIDs = ["LB_0","LB_1","LB_2"];
for i = 1:2
    arL = roadrunner.hdmap.AlignedReference;
    refL = roadrunner.hdmap.Reference; refL.ID = bndIDs(i);
    arL.Reference = refL; arL.Alignment = "Forward";
    ln(i).LeftLaneBoundary = arL;

    arR = roadrunner.hdmap.AlignedReference;
    refR = roadrunner.hdmap.Reference; refR.ID = bndIDs(i+1);
    arR.Reference = refR; arR.Alignment = "Forward";
    ln(i).RightLaneBoundary = arR;
end

% Assemble and write
rrMap.Lanes = ln;
rrMap.LaneBoundaries = lb;
rrMap.LaneMarkings = [lm1 lm2];
write(rrMap, "two_lane_road.rrhd");
```

## Enforcement Gate (MANDATORY before `write()`)

Run this validation block before writing any multi-lane map. Do NOT skip.

```matlab
%% --- ENFORCEMENT: Alignment computed (not all Forward) ---
lanes = rrMap.Lanes;
if numel(lanes) > 2
    nFwd = 0; nBwd = 0;
    for i = 1:numel(lanes)
        if lanes(i).LeftLaneBoundary.Alignment == "Forward", nFwd = nFwd+1;
        else, nBwd = nBwd+1; end
        if lanes(i).RightLaneBoundary.Alignment == "Forward", nFwd = nFwd+1;
        else, nBwd = nBwd+1; end
    end
    assert(nBwd > 0 || numel(lanes) <= 2, ...
        'ALIGNMENT ERROR: All boundaries Forward for %d lanes — compute dot product per lane.', numel(lanes));
    fprintf('Alignment: %d Forward, %d Backward — OK\n', nFwd, nBwd);
end

%% --- ENFORCEMENT: Left boundary spatially on left ---
bnds = rrMap.LaneBoundaries;
bndMap = containers.Map;
for i = 1:numel(bnds), bndMap(bnds(i).ID) = bnds(i).Geometry; end
nBadSide = 0;
for i = 1:numel(lanes)
    lGeom = lanes(i).Geometry;
    lDir = lGeom(end,1:2) - lGeom(1,1:2);
    lDir = lDir / norm(lDir);
    leftN = [-lDir(2), lDir(1)];
    leftBndID = lanes(i).LeftLaneBoundary.Reference.ID;
    if bndMap.isKey(leftBndID)
        leftBndGeom = bndMap(leftBndID);
        toBnd = mean(leftBndGeom(:,1:2)) - mean(lGeom(:,1:2));
        if dot(toBnd, leftN) < 0, nBadSide = nBadSide + 1; end
    end
end
assert(nBadSide == 0, ...
    'SPATIAL ERROR: %d lanes have left boundary on wrong side — fix alignment algorithm.', nBadSide);
fprintf('Spatial left-side check: PASS\n');

%% --- ENFORCEMENT: Geometry is Nx3 ---
bnds = rrMap.LaneBoundaries;
for i = 1:numel(bnds)
    assert(size(bnds(i).Geometry, 2) == 3, ...
        'Boundary %s geometry must be Nx3 (got Nx%d)', bnds(i).ID, size(bnds(i).Geometry,2));
end
fprintf('Geometry dimensions: PASS\n');

%% --- ENFORCEMENT: GeoReference set ---
assert(any(rrMap.GeoReference ~= 0), 'GeoReference is [0,0] — set lat/lon origin');
fprintf('GeoReference: PASS\n');
```

## Post-Processing

### Endpoint Snapping

For connected lanes (pred/succ), snap successor start to predecessor end. See [scripts/snapConnectedEndpoints.m](scripts/snapConnectedEndpoints.m) and [references/snapEndpoints.md](references/snapEndpoints.md).

### Height Conflict Resolution

Overlapping unconnected lanes at the same Z cause grass artifacts. Use graph coloring to assign Z-levels. See [scripts/resolveHeightConflicts.m](scripts/resolveHeightConflicts.m) and [references/resolveHeights.md](references/resolveHeights.md).

## Additional Entity Types

- **Lanes** — See [references/lanes.md](references/lanes.md)
- **Speed Limits** — See [references/speedLimits.md](references/speedLimits.md)
- **Lane Boundaries** — See [references/laneBoundaries.md](references/laneBoundaries.md)
- **Lane Groups** — See [references/laneGroups.md](references/laneGroups.md)
- **Lane Markings** — See [references/laneMarkings.md](references/laneMarkings.md)
- **Junctions** — See [references/junctions.md](references/junctions.md) and [references/junctionPolygon.md](references/junctionPolygon.md) for polygon construction
- **Barriers** — See [references/barriers.md](references/barriers.md)
- **Signs & Signals** — See [references/signs.md](references/signs.md), [references/signals.md](references/signals.md)
- **Static Objects** — See [references/staticObjects.md](references/staticObjects.md)
- **Stencil Markings** — See [references/stencilMarkings.md](references/stencilMarkings.md)
- **Curve Markings** — See [references/curveMarkings.md](references/curveMarkings.md)
- **Parking Spaces** — See [references/parkingSpaces.md](references/parkingSpaces.md)
- **Assemble Map** — See [references/assembleMap.md](references/assembleMap.md)
- **Validate Map** — See [references/validateMap.md](references/validateMap.md) and [scripts/validateRRHD.m](scripts/validateRRHD.m)
- **Arc Length to Parametric** — See [references/arcLengthToParametric.md](references/arcLengthToParametric.md)
- **Compute Bounds** — See [references/computeBounds.md](references/computeBounds.md)
- **Make Aligned Reference** — See [references/makeAlignedRef.md](references/makeAlignedRef.md)
- **Make Bounding Box** — See [references/makeBoundingBox.md](references/makeBoundingBox.md)
- **Make Marking Attribution** — See [references/makeMarkingAttrib.md](references/makeMarkingAttrib.md)
- **Make Reference** — See [references/makeRef.md](references/makeRef.md)
- **Make Signal Attribution** — See [references/makeSignalAttrib.md](references/makeSignalAttrib.md)
- **Make Speed Limit Attribution** — See [references/makeSpeedLimitAttrib.md](references/makeSpeedLimitAttrib.md)
- **Split Closed Geometry** — See [references/splitClosedGeometry.md](references/splitClosedGeometry.md)
- **N-Way Orthogonal Split** — See [references/orthogonalSplit.md](references/orthogonalSplit.md) — Generic algorithm to split closed-loop lanes into N segments with perfectly orthogonal joints. Use for tracks, ovals, circuits.
- **Synthesize Center Line** — See [references/synthesizeCenterLine.md](references/synthesizeCenterLine.md)

## Key Functions

| Function | Purpose |
|----------|---------|
| `roadrunnerHDMap` | Create HD Map object (loads namespace) |
| `read(rrMap, file)` | Read existing `.rrhd` file |
| `write(rrMap, file)` | Write HD Map to `.rrhd` file |
| `roadrunner.hdmap.Lane` | Lane entity (Geometry, TravelDirection, LaneType) |
| `roadrunner.hdmap.LaneBoundary` | Boundary entity (Geometry, ParametricAttributes) |
| `roadrunner.hdmap.LaneMarking` | Marking definition (ID, AssetPath) |
| `roadrunner.hdmap.Junction` | Junction area (Geometry as MultiPolygon) |
| `roadrunner.hdmap.AlignedReference` | Reference with alignment (Name=Value constructor) |
| `roadrunner.hdmap.RelativeAssetPath` | Asset path wrapper (Name=Value constructor) |
| `roadrunner.hdmap.ParametricAttribution` | Marking placement (Span, MarkingReference) |

## Known Limitations

- SignalTypes/Signals lost on RRHD write/read cycle — signals import not yet supported
- RoadRunner cannot render self-closing lanes — split using N-way orthogonal algorithm (see [references/orthogonalSplit.md](references/orthogonalSplit.md))
- Geometry must always be Nx3 (include Z=0 if flat)
- For track maps (closed-loop ovals): omit topology, Z bumps, and marking attributes — see orthogonalSplit.md Track-Specific Guidance

## Conventions

- Always use create-then-assign pattern for `roadrunner.hdmap.*` objects
- Use `RelativeAssetPath(AssetPath="...")` and `AlignedReference(Reference=ref, Alignment="...")` — Name=Value only
- Use `ClassName.empty` for empty typed arrays, never `[]`
- All geometry is Nx3 with Z column (default 0 for flat terrain)
- Shared boundaries: one object per way, multiple lanes reference with different alignments
- Use `tiledlayout`/`nexttile` for multi-panel figures (not `subplot`)

----

Copyright 2026 The MathWorks, Inc.

Files in this skill

  • SKILL.md18.1 KB
  • evals/evals.json9.7 KB
  • manifest.yaml430 B
  • references/alignmentRules.md8.3 KB
  • references/apiReference.md8.3 KB
  • references/arcLengthToParametric.md1.2 KB
  • references/assembleMap.md2.9 KB
  • references/barriers.md1.5 KB
  • references/computeBounds.md987 B
  • references/curveMarkings.md1.7 KB
  • references/junctionPolygon.md5.6 KB
  • references/junctions.md1.5 KB
  • references/laneBoundaries.md1.1 KB
  • references/laneGroups.md1.2 KB
  • references/laneMarkings.md1.6 KB
  • references/lanes.md3 KB
  • references/makeAlignedRef.md1.4 KB
  • references/makeBoundingBox.md1 KB
  • references/makeMarkingAttrib.md1.4 KB
  • references/makeRef.md881 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.