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…