关于 roadrunner-convert-lanelet2-to-rrhd
该技能指导将包含 Lanelet2 关系的 OSM 地图转换为 RoadRunner HD Map(.rrhd),供使用 MATLAB 导入地图的工作流程使用。它强调完整处理道路几何、拓扑及语义元素,并在写入前执行检查。
主要功能
- 01解析 Lanelet2 OSM 节点、道路和关系
- 02处理坐标投影与地理参考
- 03生成车道、路口及道路语义元素
- 04写入前执行对齐、空间和完整性检查
适用场景
- 01将 Lanelet2 .osm 地图转换为 .rrhd
- 02把含车道关系的地图导入 RoadRunner
- 03从 OSM 生成含标志、屏障和限速的地图
MCP 相关
该技能要求通过 mcp__matlab__evaluate_matlab_code 执行生成的 MATLAB 转换代码,并使用 run_matlab_file 运行脚本。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ matlab/ matlab-agentic-toolkit --skill roadrunner-convert-lanelet2-to-rrhd安装后在 Claude Code 中输入/roadrunner-convert-lanelet2-to-rrhd确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
manifest.yamlreferences/buildRRHD.mddetectGeoReference.mddiscoverRelations.mdenforcementGate.mdextractGeometry.mdextractJunctions.mdextractLanelets.mdextractNonLaneElements.mdextractSemantics.mdextractTopology.mdimportIntoRoadRunner.mdlatlon2enu.mdmapLanelet2SignCode.mdplacedObjects.mdreadOSM.mdsignCodeMapping.mdsnapEndpoints.mdsplitClosedRoads.mdscripts/discoverWayTypes.mparseOSM.m详细介绍
Lanelet2 到 RRHD 转换器
将 Lanelet2 .osm 文件转换为 RoadRunner HD Map .rrhd 格式。
适用场景
- 将 Lanelet2
.osm文件转换为 RoadRunner HD Map.rrhd格式 - 通过 MATLAB 将 Lanelet2 地图导入 RoadRunner
- 从包含
type=lanelet关系的.osm源文件构建 RRHD - 需要完整流程:解析、几何、拓扑、语义、交叉路口、障碍物、标志和标线
不适用场景
- 输入是标准 OpenStreetMap 文件(含有 highway=* 道路,但没有
type=lanelet关系) - 输入是 OpenDRIVE
.xodr— 使用roadrunner-import-scene直接导入 - 不使用源文件从头构建 RRHD — 使用
roadrunner-rrhd-authoring - 仅需要查询资源路径 — 使用
roadrunner-asset-mapping
关键规则
- 执行代码时始终写入 .m 文件。 切勿将多行 MATLAB 代码直接放入
evaluate_matlab_code。写入.m文件后,使用run_matlab_file运行;出错时进行编辑。例外:如果用户要求“展示模式”或说“不要执行”,则直接展示内联代码,不写入文件。 - 所有流程步骤都是必需的。 不要在写入车道/边界后停止 — 必须构建交叉路口、曲线标线、障碍物、标志和限速。
- 边界几何不可更改。 切勿翻转、重采样、投影或修改边界点。
- 每条道路(way)对应一个边界对象。 通过
wayToBndID映射去重。 - 检测每条车道的对齐方向。 切勿将所有边界的方向硬编码为
"Forward"。 - 中心线密度:至少每米 1 个点。 使用
max(10, round(avgLen), nLeft, nRight)。 - 在调用
write()前运行强制检查关卡。 对齐、空间、扩展和完整性检查均为必需。 - 不得定义嵌套函数。 代码在脚本上下文中运行 — 所有逻辑都必须内联或使用匿名函数。
行为
生成执行下述转换流程的 MATLAB 代码。通过 mcp__matlab__evaluate_matlab_code 运行。无需预先编写脚本或调用 addpath — Claude 会根据这些说明在运行时生成所有代码。
必需:必须执行所有步骤。 不要在写入车道/边界后停止。若缺少交叉路口、曲线标线(停止线 + 人行横道)、障碍物、标志和限速,流程便不完整。OSM 中找到的每个元素都必须出现在输出 RRHD 中。
必需:步骤 3b 和 3c(发现)必须与步骤 3a 在同一个代码块中执行。 这些不是可选的后处理步骤,而是解析的一部分。发现循环代码已内联在下文的步骤 3 中。如果跳过 3b/3c,步骤 9 中的完整性检查将因断言错误而失败。
范围: 此技能仅支持 Lanelet2 OSM。如果输入是标准 OpenStreetMap 文件(含有 highway=* 道路,但没有 type=lanelet 关系),请勿尝试转换。请改为告知用户:“此文件似乎是标准 OpenStreetMap 道路网络,而不是 Lanelet2 地图。此技能仅支持将 Lanelet2 OSM 转换为 RRHD。”
配套技能(转换期间自动调用):
| 技能 | 调用时机 | 用途 |
|---|---|---|
roadrunner-rrhd-authoring | 步骤 9(构建 RRHD 对象) | 提供 roadrunner.hdmap.* 类/属性参考和构建模式 |
roadrunner-asset-mapping | 步骤 8(提取语义) | 将标线子类型、标志代码和障碍物类型解析为 RoadRunner 资源路径 |
这些技能按需加载 — 在生成 RRHD 构建代码或解析资源路径时阅读其参考资料。
技能边界: 此技能负责 Lanelet2 特有的所有内容(OSM 解析、节点引用拓扑、turn_direction 聚类、对向边界检测)。RRHD 对象构建模式(对齐算法、中心线合成、交叉路口多边形、API 参考)请参阅 roadrunner-rrhd-authoring。
坐标系
Lanelet2 OSM 节点可能使用以下任一方式:
local_x/local_y标签 — 已是本地米制坐标,直接用作几何 X/Y- 仅有
lat/lon属性 — 地理坐标,必须投影到本地米制坐标
步骤 1:检查 map_projector_info.yaml
Lanelet2 数据集通常会在 .osm 文件所在目录中包含 map_projector_info.yaml 文件。此文件指定投影方式和地图原点:
ProjectorType: TransverseMercator
VerticalDatum: WGS84
MapOrigin:
Latitude: 42.300945
Longitude: -83.698205
Altitude: 0
务必先查找此文件。 解析文件以获取 ProjectorType 和 MapOrigin:
projFile = fullfile(fileparts(osmFile), "map_projector_info.yaml");
if isfile(projFile)
txt = fileread(projFile);
latMatch = regexp(txt, 'Latitude:\s*([-\d.]+)', 'tokens');
lonMatch = regexp(txt, 'Longitude:\s*([-\d.]+)', 'tokens');
if ~isempty(latMatch) && ~isempty(lonMatch)
originLat = str2double(latMatch{1}{1});
originLon = str2double(lonMatch{1}{1});
end
end
如果 MapOrigin 为 [0, 0],则原点为隐式原点,应使用所有节点的质心。
步骤 2:确定坐标模式
- 节点有
local_x/local_y→ 直接使用,并从map_projector_info.yaml的 MapOrigin 设置geoRef(如果缺少 yaml 文件,则根据节点的 lat/lon 设置) - 节点仅有
lat/lon→ 使用 yaml 中的原点将坐标投影到本地 ENU 米制坐标(如果缺少 yaml 文件,则以节点质心作为备用原点)
步骤 3:将 lat/lon 投影到本地米制坐标(需要时)
% Origin from map_projector_info.yaml or centroid fallback
geoRef = [originLat, originLon];
% For each node, convert to local meters:
dLat = node.lat - originLat;
dLon = node.lon - originLon;
metersPerDegLat = 111132.92;
metersPerDegLon = 111132.92 * cosd(originLat);
x = dLon * metersPerDegLon; % East
y = dLat * metersPerDegLat; % North
z = node.ele; % Up (elevation)
设置 rrMap.GeoReference = [originLat, originLon],以便 RoadRunner 知道地图的地理位置。
重要: 此投影仅在解析节点时执行一次。所有下游几何(边界、中心线、障碍物、标志)均使用投影后的本地坐标。“边界几何不可变”规则(见下文)指投影后的坐标——初次解析后不得重新投影或修改这些坐标。
几何不变量(必须遵守 — 违反会导致 RRHD 损坏)
这些规则不可妥协。每次转换都必须遵守:
-
边界几何不可变。 按解析时计算出的坐标(本地坐标或投影后坐标)原样存储 way 节点坐标。初次分配坐标后,绝不可翻转、重采样或修改边界点。若改动,对向车道之间共享的边界将会损坏。应改用
Alignment="Backward"。 -
每个 way 对应一个边界对象。 通过
wayToBndIDmap 去重。多个车道可通过不同的对齐方式引用同一边界。 -
通过左右方向点积和邻近度测试确定对齐方式。 计算
dp = dot(leftDir, rightDir)(归一化的整体方向——这适用于阈值检查)。如果dp >= -0.3,则两条边界均为 Forward。如果dp < -0.3,则两条边界方向相反——使用邻近度(d_ls_re = norm(leftStart - rightEnd)与d_ls_rs = norm(leftStart - rightStart))确定哪条边界为 Backward。如果d_ls_re < d_ls_rs,则右侧为 Backward,左侧为 Forward。(注意:步骤 9 中“绝不可使用整体方向”的警告针对多采样点空间验证步骤,而非此处的 dp 阈值检查。) -
中心线密度:至少每米 1 个点。 使用
nPts = max(10, round(avgBoundaryLength), nLeftPts, nRightPts)。点数过少会导致 pchip 插值偏离真实中点。 -
仅中心线端点正交。 通过切线混合,确保中心线的起点和终点垂直。不得投影边界端点。
MATLAB 脚本约束
代码在 mcp__matlab__evaluate_matlab_code 中运行,该环境是脚本上下文(不是函数文件):
- 禁止定义嵌套函数——所有逻辑必须内联编写或使用匿名函数
- 禁止使用
function ... end块——这会报错:"Function definitions are not supported in this context" - 空类型化数组——使用
ClassName.empty,而不是[](例如,roadrunner.hdmap.AlignedReference.empty) - 名称=值构造函数——
RelativeAssetPath、AlignedReference要求使用名称=值语法:% CORRECT: rap = roadrunner.hdmap.RelativeAssetPath(AssetPath="Assets/Markings/StopLine.rrlms"); ar = roadrunner.hdmap.AlignedReference(Reference=ref, Alignment="Forward"); % WRONG (will error with "A name is expected"): rap = roadrunner.hdmap.RelativeAssetPath("Assets/Markings/StopLine.rrlms"); ar = roadrunner.hdmap.AlignedReference(bndID, "Forward"); containers.Map空值赋值——绝不要执行map(key) = []。请使用map(key) = zeros(1,0),或仅赋值非空值- 禁止链式调用
()()——geomMap(key)(:,1)会报错:"Using parentheses directly after parentheses is disallowed"。请先赋值给临时变量:% WRONG: min(geomMap(k)(:,1)) % CORRECT: pts = geomMap(k); minX = min(pts(:,1)); - 对元胞数组使用
unique()——unique(cellArray)要求输入为 cellstr(字符向量组成的元胞数组)。对于字符串数组,直接使用unique(stringArray)。对于混合元胞数组,使用unique(string(cellArray))。 containers.Map的值需要显式类型转换——从containers.Map获取的值可能会丢失其数值类型。进行数学运算前务必转换类型:例如,在传入cosd()、sind()等函数前使用double(geoRef(1))- 元胞数组结构体字段赋值——绝不要执行
cellArray{end+1} = x; cellArray{end}.field = val;(会因“dot indexing not supported for double”而报错)。请先构建结构体,再追加:% WRONG: stopLineWays{end+1} = w; stopLineWays{end}.wayID = wid; % CORRECT: w.wayID = wid; stopLineWays{end+1} = w; % OR: store IDs in a separate parallel cell array
流程(按顺序执行)
步骤 1:解析 OSM
参见 references/readOSM.md 和 scripts/parseOSM.m。
doc = xmlread(osmFile);
% Parse nodes: id, lat, lon, ele, local_x, local_y (from tags)
% Parse ways: id, nodeRefs[], tags (containers.Map)
% Parse relations: id, members[], tags (containers.Map)
步骤 2:验证格式
检查至少有一个 relation 的 type=lanelet。如果没有,则报错并清楚指出该文件不是 Lanelet2 文件。
hasLanelet = false;
relKeys = relations.keys;
for i = 1:numel(relKeys)
rel = relations(relKeys{i});
if rel.tags.isKey('type') && strcmp(rel.tags('type'), 'lanelet')
hasLanelet = true; break;
end
end
if ~hasLanelet
error('UNSUPPORTED FORMAT: This converter supports Lanelet2 OSM files only.');
end
步骤 3:提取 Lanelet 并发现所有非车道元素
此步骤包含三个强制子步骤,必须全部执行。
步骤 3a:提取 Lanelet
筛选 type=lanelet 的 relation。参见 references/extractLanelets.md。
% For each relation with type=lanelet:
% leftWayID = member with role="left"
% rightWayID = member with role="right"
% subtype, one_way, speed_limit, turn_direction from tags
步骤 3b:发现所有 Way 类型(强制——在步骤 3a 后立即执行)
参见 scripts/discoverWayTypes.m 中的参考实现。
**不要使用白名单。**扫描每个 way,根据 type 标签进行分类;对于复合类型,使用 strsplit(wType, '/'):
stopLineWays = {}; pedestrianMarkingWays = {}; zebraMarkingWays = {};
bikeMarkingWays = {}; zigzagWays = {};
fenceWays = {}; guardRailWays = {}; jerseyBarrierWays = {};
wallWays = {}; curbstoneWays = {};
trafficSignWays = {}; trafficLightWays = {};
unmappedWays = containers.Map;
wayKeys = ways.keys;
for i = 1:numel(wayKeys)
w = ways(wayKeys{i});
if ~w.tags.isKey('type'), continue; end
wType = w.tags('type');
typeParts = strsplit(wType, '/');
baseType = typeParts{1};
switch baseType
case 'stop_line', stopLineWays{end+1} = w;
case 'pedestrian_marking', pedestrianMarkingWays{end+1} = w;
case 'zebra_marking', zebraMarkingWays{end+1} = w;
case 'bike_marking', bikeMarkingWays{end+1} = w;
case {'zig-zag','zig_zag'}, zigzagWays{end+1} = w;
case 'fence', fenceWays{end+1} = w;
case 'guard_rail', guardRailWays{end+1} = w;
case 'jersey_barrier', jerseyBarrierWays{end+1} = w;
case 'wall', wallWays{end+1} = w;
case 'curbstone', curbstoneWays{end+1} = w;
case 'traffic_sign', trafficSignWays{end+1} = w;
case 'traffic_light', trafficLightWays{end+1} = w;
case {'line_thin','line_thick','virtual','road_border','rail','keepout','symbol'}
% Lane boundary types — handled via lanelet extraction
otherwise
if unmappedWays.isKey(wType), unmappedWays(wType) = unmappedWays(wType)+1;
else, unmappedWays(wType) = 1; end
end
end
步骤 3c:发现所有 Relation 类型(强制——在步骤 3b 后立即执行)
有关完整分类循环,请参见 references/discoverRelations.md。将所有非 lanelet relation 分类为:trafficSignRels、speedLimitRels、rightOfWayRels、trafficLightRels、multipolygonRels(包含 .building、.parking、.vegetation、.traffic_island、.walkway、.exit、.keepout 的结构体)以及 unmappedRels(包含计数的 containers.Map)。
**打印发现摘要和未映射元素。**绝不要静默丢弃任何内容。
步骤 4:构建几何
根据节点解析道路几何。有关必须遵循的实现模式,请参阅 references/extractGeometry.md。
自动检测 GeoReference: 如果不存在 local_x/local_y 标签,则计算所有节点纬度/经度的质心。绝不要使用 [0 0]。参阅 references/detectGeoReference.md。
优先级: 如果节点标签中存在 local_x/local_y,则使用这些标签。否则回退到纬度/经度 → ENU(参阅 references/latlon2enu.md):
east = (lon - geoRef(2)) * 111320 * cosd(geoRef(1));
north = (lat - geoRef(1)) * 110540;
必须:在检测方向之前处理闭环边界。 如果两条边界都是闭环(起点与终点之间的间隙 < 1.0 m),整体方向向量将接近零,dot(leftOverall, rightOverall) 不可靠(约为 0)。这会导致错误翻转,使中心线穿过赛道内部(例如,“宽度”变为 136 m,而不是 6.5 m)。完整的闭环边界处理算法(局部方向检测 + 起点对齐)请参阅 roadrunner-rrhd-authoring 技能中的 references/splitClosedGeometry.md。
必须遵循的中心线合成流程——完整算法及代码请参阅 roadrunner-rrhd-authoring 技能中的 references/synthesizeCenterLine.md。概要:
- 检测方向相反的边界: 使用支持闭环的检测方法(参阅上文的
splitClosedGeometry.md) - 按密度确定点数并重新采样,使用弧长
pchip——max(10, round(avgLen), nLeft, nRight) - 对重新采样后的边界取平均,得到中心线
- 确保端点正交,使用 50% 切线混合(闭环车道除外)
- 不要修改边界几何——改用
Alignment属性
关键:不要对边界几何应用正交端点投影。 多条车道(包括行驶方向相反的车道)会共用边界。修改共用边界的端点以匹配某一条车道的中心线方向,会破坏其他引用该边界的车道。方向关系由 Alignment 属性处理。
边界几何规则: 始终存储来自 OSM 节点的原始道路几何。使用稳健的多采样算法计算对齐方式(参阅 roadrunner-rrhd-authoring/references/alignmentRules.md)。不要对整体方向使用简单点积——这种方法在曲线几何上会失效。
所有几何都必须为 Nx3(包含来自 ele 标签的 Z 值,默认为 0)。
步骤 5:拆分闭合道路
RoadRunner 无法渲染自身闭合的几何。通用 N 向正交拆分算法请参阅 references/splitClosedRoads.md 和 roadrunner-rrhd-authoring 技能中的 references/splitClosedGeometry.md。
在所有连接处(包括闭合处)使用垂直横截面投影进行拆分。完整的 N 向算法请使用 roadrunner-rrhd-authoring/references/orthogonalSplit.md。
步骤 6:提取拓扑
以边界端点节点匹配为主要方法,以几何邻近度(阈值为 1.0 m)为备用方法。参阅 references/extractTopology.md。
关键规则:
- 匹配节点时考虑方向相反的边界(对右侧后向车道使用 dp 阈值 -0.3 和邻近度测试)
- 使用有效行驶方向应用方向相反过滤(对右侧后向车道反转原始方向)
- 应用左侧间隙过滤:当存在更好的替代项时,移除左侧边界间隙 > 3 m 的错误后继项
第 7 步:检测路口
完整算法和代码请参阅 references/extractJunctions.md。
主要方法: 使用 turn_direction 标签(straight、left、right)作为路口车道指示符。
备用方法: 如果(前驱节点有 >1 个后继节点)或(后继节点有 >1 个前驱节点),则该车道属于路口。
关键规则:
- 使用 8m 阈值进行 BFS 聚类——不要使用并查集(会导致跨越相距较远的交叉路口进行传递式串联)。后置筛选:确认每个成员与聚类质心的距离都在 12m 以内。
- 使用
boundary(pts,0.7)生成多边形形状——可生成效果良好的曲线多边形。对于boundary()凹度会排除车道的路口,可有选择地使用convhull。 - 每个聚类至少包含 2 条车道(可检测到 3 车道阈值会漏掉的小型 T 形交叉路口)
- 备用方法:检测未覆盖的汇入/分出节点——主要聚类完成后,检测至少有 2 条车道终止且至少有 2 条车道起始、但未被路口覆盖的节点。在这些节点处,根据
turn_direction车道添加路口。 - 将非转向车道添加到现有路口,前提是它们经过路口拓扑节点(提高多边形覆盖率)
- 从所有路口车道边界中移除标线(不要分配 ParametricAttributes)
- 优雅降级: 如果检测结果不可靠,则完全跳过路口——导入时在构建选项中使用
UseLaneGroups=true
第 8 步:提取语义
请参阅 references/extractSemantics.md。
| Lanelet2 边界子类型 | RRHD 标线 ID | 资源路径 |
|---|---|---|
solid | SolidWhite | Assets/Markings/SolidSingleWhite.rrlms |
dashed | DashedWhite | Assets/Markings/DashedSingleWhite.rrlms |
solid_solid | SolidDoubleYellow | Assets/Markings/SolidDoubleYellow.rrlms |
dashed_solid | DashedSolidYellow | Assets/Markings/DashedSolidYellow.rrlms |
solid_dashed | DashedSolidYellow | Assets/Markings/DashedSolidYellow.rrlms(+ FlipLaterally=true) |
| Lanelet2 子类型 | RRHD 车道类型 |
|---|---|
road、highway、bus_lane | Driving |
crosswalk、walkway | CurveMarking(不是车道) |
bicycle_lane | Biking |
emergency_lane | Shoulder |
parking | Parking |
行驶方向:
- 存在
turn_direction标签 →"Forward"(始终为单向) one_way=yes→"Forward"- 人行道/路缘 →
"Undirected" - 否则 →
"Bidirectional"
第 8b 步:从车道列表中过滤人行横道/步道
在构建 RRHD 车道对象之前,按子类型分离 lanelet:
subtype=crosswalk或subtype=walkway→ 转为 CurveMarkings(使用中心线作为几何形状,类型 =SimpleCrosswalk)- 其他所有子类型 → 构建为 RRHD Lanes
绝不要为人行横道/步道 lanelet 创建 Lane 对象。 它们会成为 CurveMarking 实例。
CurveMarkingType 扩展——不要混淆:
- 停止线 →
.rrlms(车道标线样式):Assets/Markings/StopLine.rrlms - 人行横道 →
.rrcws(人行横道样式):Assets/Markings/SimpleCrosswalk.rrcws
将 .rrcws 用于停止线会导致导入时出现“Asset file is missing”错误。
步骤 9:构建 RRHD 对象
完整的构建模式请参阅 references/buildRRHD.md。
关键规则:
- 必须先执行
rrMap = roadrunnerHDMap;(加载命名空间)。警告:在某些系统上,roadrunnerHDMap可能会启动一个后台 RoadRunner 实例。导入技能的连接逻辑通过重试处理此情况,详见roadrunner-import-scene。如果roadrunnerHDMap已启动 RoadRunner 实例,则不要另行调用roadrunner(InstallationFolder=...)。 - 所有
roadrunner.hdmap.*对象都使用先创建、后赋值的模式 - 剥除路口车道边界的标线(即不赋值 ParametricAttributes)
- 绝不要修改边界几何——按原样存储 OSM way 的节点坐标。多个车道(包括相反行驶方向的车道)会共用边界。任何修改(翻转、端点投影、重采样)都会破坏其他车道的边界。
- 每个 way 对应一个边界——使用
wayToBndID映射进行去重。第一个引用某个 way 的 lanelet 会创建边界;其他 lanelet 通过 alignment 共享该边界。 - 为每条车道检测边界对齐方式,使用
roadrunner-rrhd-authoring技能中 references/alignmentRules.md 所述的稳健多点采样算法。绝不要将所有边界的alignment = "Forward"硬编码——这是场景出错的首要原因。绝不要使用整体方向(geom(end,:)-geom(1,:))进行空间验证——对于高度弯曲的路段(例如分割后的半椭圆),弦方向不能代表行驶方向,因此会失效。该算法包含两个步骤:(1) 基于邻近关系检测方向;(2) 通过多数表决进行多点采样的局部切线空间验证。
**已验证的属性名称:**完整的类/属性表请参阅 roadrunner-rrhd-authoring 技能中的 references/apiReference.md。以下是几个不明显的映射:CurveMarking.MarkingTypeReference(不是 CurveMarkingTypeID)、Barrier.BarrierTypeReference(不是 BarrierTypeID)、Sign.Geometry(GeoOrientedBoundingBox,不是 BoundingBox)、SpeedLimit.Value + .VelocityUnit="Kph"、ParametricAttribution.Span=[0 1](不是 StartFraction/EndFraction)、MarkingReference.MarkingID 接受 Reference 对象(不是数值)。
**关键:**写入前过滤自引用拓扑(车道前驱/后继指向自身的情况),否则 RoadRunner 导入时会报错:"connectedLane.Object != this"。
步骤 10:后处理(必须执行——不要跳过)
对齐相连端点——后继车道的起点必须与前驱车道的终点一致(包括车道及边界)。对于间隙小于 1.0 m 的每对前驱→后继,将后继车道的起点对齐到前驱车道的终点。请参阅 references/snapEndpoints.md 和 roadrunner-rrhd-authoring 技能中的 scripts/snapConnectedEndpoints.m。
**高度冲突消解(必须执行):**Z 坐标相同且未连接的车道发生重叠时会导致草地伪影。使用图着色法,将较窄车道按每个 Z 层级抬高 +0.05 m。请参阅 roadrunner-rrhd-authoring 技能中的 scripts/resolveHeightConflicts.m。
**边界去重(必须执行):**Lanelet2 中相反方向的车道会引用不同的 way ID,而这些 way 可能包含方向相反但几何相同的数据。按 way 执行的 wayToBndID 去重只能处理相同 way 的复用;基于几何的去重则可以检测方向相反的重复项。算法请参阅 roadrunner-rrhd-authoring 技能中的 references/deduplicateBoundaries.md。
步骤 11:组装并写入
将所有数组赋给 rrMap(Lanes、LaneBoundaries、LaneMarkings、SpeedLimits、Junctions、CurveMarkingTypes、CurveMarkings、BarrierTypes、Barriers、SignTypes、Signs),并设置 rrMap.GeoReference = geoRef(1x2 [lat, lon])。调用 write(rrMap, outputFile)。请参阅 references/buildRRHD.md。
**Junctions 是可选的:**如果跳过了路口检测,请省略 rrMap.Junctions——默认值为空。导入时在构建选项中使用 UseLaneGroups=true。
步骤 9 子步骤——构建所有非车道 RRHD 对象
步骤 9 必须构建步骤 3b/3c 中的所有非车道元素。代码模式请参阅 references/extractNonLaneElements.md。子步骤:9a CurveMarkings(停车线、人行横道、自行车标线)、9b Barriers(围栏、护栏、混凝土隔离墩、墙、路缘石)、9c Signs(来自 way 和 relation——请参阅 references/signCodeMapping.md 和 references/mapLanelet2SignCode.md,了解标志类型的解析方式)、9d SpeedLimits、9e 放置对象(自定义 relation 解析请参阅 references/placedObjects.md)。
强制检查关卡(必须——在 write() 前运行)
你必须执行 references/enforcementGate.md 中的验证代码块。它会捕获三种最常见的转换错误(对齐、空间正确性、资源扩展名、完整性)。不要跳过或简化。
写入后
打印未映射元素的数量(建筑物、植被、信号灯——无法导入)。然后使用 roadrunner-import-scene 技能进行导入。构建选项请参阅 references/importIntoRoadRunner.md。
关键函数
| 函数 | 用途 |
|---|---|
xmlread(osmFile) | 将 OSM XML 解析为 DOM 文档 |
roadrunnerHDMap | 创建 HD Map 对象(加载命名空间) |
write(rrMap, file) | 将 HD Map 写入 .rrhd 文件 |
containers.Map | 存储解析后的节点、ways 和 relations |
pchip / interp1 | 对中心线进行弧长重采样 |
vecnorm | 计算聚类和邻近关系的距离 |
strsplit | 解析复合类型标签(type/subtype) |
约定
- 按解析结果原样存储 OSM way 的节点坐标——绝不修改边界几何
- 对所有索引查找(节点、ways、relations、几何)使用
containers.Map - 所有几何均为 Nx3,Z 值取自
ele标签(默认为 0) - 对路口使用基于邻近关系的 BFS 聚类(8m 阈值 + 12m 质心筛选),绝不使用 union-find
- 对路口多边形使用
boundary(pts,0.7);对于需要完整覆盖的小型路口,可选择性地使用convhull - 解析后打印发现情况摘要——绝不静默丢弃未映射元素
- 多面板图形使用
tiledlayout/nexttile(不要使用subplot)
版权所有 2026 The MathWorks, Inc.