mcpField
R
roadrunner-convert-lanelet2-to-rrhd

roadrunner-convert-lanelet2-to-rrhd

github.com/matlab数据与数据库1.2k71 次安装1 天前更新
代码仓库

关于 roadrunner-convert-lanelet2-to-rrhd

该技能指导将包含 Lanelet2 关系的 OSM 地图转换为 RoadRunner HD Map(.rrhd),供使用 MATLAB 导入地图的工作流程使用。它强调完整处理道路几何、拓扑及语义元素,并在写入前执行检查。

主要功能

  1. 01解析 Lanelet2 OSM 节点、道路和关系
  2. 02处理坐标投影与地理参考
  3. 03生成车道、路口及道路语义元素
  4. 04写入前执行对齐、空间和完整性检查

适用场景

  1. 01将 Lanelet2 .osm 地图转换为 .rrhd
  2. 02把含车道关系的地图导入 RoadRunner
  3. 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.yaml
references/
buildRRHD.md
detectGeoReference.md
discoverRelations.md
enforcementGate.md
extractGeometry.md
extractJunctions.md
extractLanelets.md
extractNonLaneElements.md
extractSemantics.md
extractTopology.md
importIntoRoadRunner.md
latlon2enu.md
mapLanelet2SignCode.md
placedObjects.md
readOSM.md
signCodeMapping.md
snapEndpoints.md
splitClosedRoads.md
scripts/
discoverWayTypes.m
parseOSM.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:确定坐标模式
  1. 节点有 local_x/local_y → 直接使用,并从 map_projector_info.yaml 的 MapOrigin 设置 geoRef(如果缺少 yaml 文件,则根据节点的 lat/lon 设置)
  2. 节点仅有 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 损坏)

这些规则不可妥协。每次转换都必须遵守:

  1. 边界几何不可变。 按解析时计算出的坐标(本地坐标或投影后坐标)原样存储 way 节点坐标。初次分配坐标后,绝不可翻转、重采样或修改边界点。若改动,对向车道之间共享的边界将会损坏。应改用 Alignment="Backward"。

  2. 每个 way 对应一个边界对象。 通过 wayToBndID map 去重。多个车道可通过不同的对齐方式引用同一边界。

  3. 通过左右方向点积和邻近度测试确定对齐方式。 计算 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 阈值检查。)

  4. 中心线密度:至少每米 1 个点。 使用 nPts = max(10, round(avgBoundaryLength), nLeftPts, nRightPts)。点数过少会导致 pchip 插值偏离真实中点。

  5. 仅中心线端点正交。 通过切线混合,确保中心线的起点和终点垂直。不得投影边界端点。

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。概要:

  1. 检测方向相反的边界: 使用支持闭环的检测方法(参阅上文的 splitClosedGeometry.md)
  2. 按密度确定点数并重新采样,使用弧长 pchip——max(10, round(avgLen), nLeft, nRight)
  3. 对重新采样后的边界取平均,得到中心线
  4. 确保端点正交,使用 50% 切线混合(闭环车道除外)
  5. 不要修改边界几何——改用 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资源路径
solidSolidWhiteAssets/Markings/SolidSingleWhite.rrlms
dashedDashedWhiteAssets/Markings/DashedSingleWhite.rrlms
solid_solidSolidDoubleYellowAssets/Markings/SolidDoubleYellow.rrlms
dashed_solidDashedSolidYellowAssets/Markings/DashedSolidYellow.rrlms
solid_dashedDashedSolidYellowAssets/Markings/DashedSolidYellow.rrlms(+ FlipLaterally=true)
Lanelet2 子类型RRHD 车道类型
road、highway、bus_laneDriving
crosswalk、walkwayCurveMarking(不是车道)
bicycle_laneBiking
emergency_laneShoulder
parkingParking

行驶方向:

  • 存在 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.

评论

支持 Markdown