使用 RoadRunner 高清地图构建具有交叉路口和静态目标的 RoadRunner 场景
此示例展示如何从包含道路交叉路口经纬度坐标的 Keyhole 标记语言 (KML) 文件创建 RoadRunner 高清地图。您可以将 RoadRunner 高清地图数据导入到 RoadRunner 中,并使用它构建包含道路交叉路口和周围静态目标(例如树木和建筑物)的 3D 场景。要导入道路数据文件,您必须拥有 Mapping Toolbox™ 许可证。
导入 KML 文件
在此示例中,您使用 KML 文件导入道路交叉路口的点形状以及建筑物和树木的多边形形状。此示例使用美国新泽西州 Whitesville Road 周围区域的地图数据(© 2022 by Google)。
使用 readgeotable 函数读取 KML 文件中的数据,该函数仅读取形状、名称和描述。
kmlRoadData = readgeotable("RoadIntersection.kml"); kmlTreeData = readgeotable("Trees.kml"); kmlBuildingData = readgeotable("Buildings.kml");
绘制道路坐标、显示建筑物并渲染树木位置的多边形。
geoplot(kmlRoadData) hold on geoplot(kmlTreeData) geoplot(kmlBuildingData) geobasemap topographic

将地理空间表转换为道路中心表,以获得在交叉路口相交的两条道路的纬度和经度坐标。
T1 = geotable2table(kmlRoadData,["Latitude","Longitude"]); [lat1,lon1] = polyjoin(T1.Latitude(1),T1.Longitude(1)); % Road centers of the first road meeting at the intersection [lat2,lon2] = polyjoin(T1.Latitude(2),T1.Longitude(2)); % Road centers of the second road meeting at the intersection
将地理空间表转换为多边形顶点表,以获得多边形内树木和建筑物的纬度和经度坐标。
T2 = geotable2table(kmlTreeData,["Latitude","Longitude"]); [lat3,lon3] = polyjoin(T2.Latitude(1),T2.Longitude(1)); % Polygon geometry to form trees inside of polygon [lat4,lon4] = polyjoin(T2.Latitude(2),T2.Longitude(2)); % Polygon geometry to form trees inside of polygon [lat5,lon5] = polyjoin(T2.Latitude(3),T2.Longitude(3)); % Polygon geometry to form trees inside of polygon T3 = geotable2table(kmlBuildingData,["Latitude","Longitude"]); lat6 = T3.Latitude; % Geometry for buildings lon6 = T3.Longitude; % Geometry for buildings
创建 RoadRunner 高清地图
创建一个空的 RoadRunner 高清地图。
rrMap = roadrunnerHDMap;
计算地理参考原点作为边界四边形的中心。
[latlim,lonlim] = geoquadline([lat1; lat2],[lon1; lon2]); lat0 = mean(latlim); lon0 = mean(lonlim);
设置感兴趣区域的地理参考。
rrMap.GeoReference = [lat0 lon0];
将经纬度坐标投影至 xy 地图坐标
从 RoadRunner 高清地图读取横轴墨卡托投影 CRS。
p = readCRS(rrMap);
将纬度和经度坐标投影到 xy 坐标。
% Road geometry data [x1,y1] = projfwd(p,lat1,lon1); [x2,y2] = projfwd(p,lat2,lon2); % Trees geometry data [x3,y3] = projfwd(p,lat3,lon3); [x4,y4] = projfwd(p,lat4,lon4); [x5,y5] = projfwd(p,lat5,lon5); % Buildings geometry data [x6,y6] = projfwd(p,lat6,lon6);
计算车道和车道边界的几何形状
创建交叉路口两条道路的线方程。
coefficients1 = polyfit([x1(1) x1(2)],[y1(1) y1(2)],1); coefficients2 = polyfit([x2(1) x2(2)],[y2(1) y2(2)],1);
使用直线方程计算车道几何形状。
m1 = coefficients1(1); c1 = coefficients1(2); % Specify lane widths, estimated using the measurement tool of Google Earth leftLanesWidth = [0 -3.659 -3.686 -3.643 -2.988]; rightLanesWidth = [2.820 3.764 3.698]; % Find cumulative width of all lanes to compute the geometries of parallel lanes leftLanesWidth = cumsum(leftLanesWidth); rightLanesWidth = cumsum(rightLanesWidth); rightLanesWidth = flip(rightLanesWidth); d1 = [rightLanesWidth leftLanesWidth]; m2 = coefficients2(1); c2 = coefficients2(2); % Specify lane widths, estimated using the measurement tool of Google Earth leftLanesWidth2 = [0 -3.614 -3.610 -3.661]; rightLanesWidth2 = [3.621 3.685]; % Find cumulative width of all lanes to compute the geometries of parallel lanes leftLanesWidth2 = cumsum(leftLanesWidth2); rightLanesWidth2 = cumsum(rightLanesWidth2); rightLanesWidth2 = flip(rightLanesWidth2); d2 = [rightLanesWidth2 leftLanesWidth2]; d = [d1 d2]; numLanes = size(d,2); x = cell(numLanes,1); [x{1:8}] = deal(x1); [x{9:14}] = deal(x2); m(1:8) = deal(m1); m(9:14) = deal(m2); c(1:8) = deal(c1); c(9:14) = deal(c2); y = cell(numLanes,1); for i = 1:numLanes y{i} = m(i)*x{i} + c(i) + d(i)*sqrt(1+m(i)^2); end
创建道路交叉路口
使用计算数据创建 RoadRunner 高清地图道路网络,并修改道路以类似于实际道路,该道路由多车道和多车道边界组成。然后,按照实际场景,在车道边界上应用适当的车道标记。为了提高性能,随着地图中对象数量的增加,初始化高精地图的 Lanes 和 LaneBoundaries 属性。
rrMap.Lanes(12,1) = roadrunner.hdmap.Lane; rrMap.LaneBoundaries(14,1) = roadrunner.hdmap.LaneBoundary;
将值分配给 Lanes 属性。
laneIds = ["Lane1","Lane2","Lane3","Lane4","Lane5","Lane6","Lane7","Lane8","Lane9","Lane10","Lane11","Lane12"]; travelDirection = ["Undirected","Forward","Forward","Backward","Backward","Backward","Undirected","Backward","Backward","Backward","Forward","Forward"]; for i = 1:size(rrMap.Lanes,1) rrMap.Lanes(i).ID = laneIds(i); rrMap.Lanes(i).TravelDirection = travelDirection(i); rrMap.Lanes(i).LaneType = "Driving"; if (i<8) % Assign coordinates to the Geometry properties of the lanes of the first road meeting at intersection rrMap.Lanes(i).Geometry = ([x{i} y{i}] + [x{i} y{i+1}])/2; elseif (i>8) % Assign coordinates to the Geometry properties of the lanes of the second road meeting at intersection rrMap.Lanes(i).Geometry = ([x{i} y{i+1}] + [x{i} y{i+2}])/2; end end
将 ID 及其相应的坐标分配给车道边界。
laneBoundaries = ["LB1","LB2","LB3","LB4","LB5","LB6","LB7","LB8","LB9","LB10","LB11","LB12","LB13","LB14"]; for i = 1:size(rrMap.LaneBoundaries,1) rrMap.LaneBoundaries(i).ID = laneBoundaries(i); rrMap.LaneBoundaries(i).Geometry = [x{i} y{i}]; end
使用车道边界 ID 将车道边界与其车道相关联。
for i = 1:size(rrMap.Lanes,1) if (i<8) % Associate lane boundaries of the first road meeting at intersection leftBoundary(rrMap.Lanes(i),"LB"+i,Alignment="Forward"); rightBoundary(rrMap.Lanes(i),"LB"+(i+1),Alignment="Forward"); else % Associate lane boundaries of the second road meeting at intersection leftBoundary(rrMap.Lanes(i),"LB"+(i+1),Alignment="Backward"); rightBoundary(rrMap.Lanes(i),"LB"+(i+2),Alignment="Backward"); end end
定义 RoadRunner 车道标记素材的文件路径。
% Define path to a dashed single white lane marking asset dashedSingleWhiteAsset = roadrunner.hdmap.RelativeAssetPath(AssetPath="Assets/Markings/DashedSingleWhite.rrlms"); % Define path to a solid white lane marking asset solidWhiteAsset = roadrunner.hdmap.RelativeAssetPath(AssetPath="Assets/Markings/SolidSingleWhite.rrlms"); % Define path to a solid double yellow lane marking asset doubleYellowAsset = roadrunner.hdmap.RelativeAssetPath(AssetPath="Assets/Markings/SolidDoubleYellow.rrlms"); rrMap.LaneMarkings(3,1) = roadrunner.hdmap.LaneMarking; [rrMap.LaneMarkings.ID] = deal("DashedSingleWhite","SolidWhite","DoubleYellow"); [rrMap.LaneMarkings.AssetPath] = deal(dashedSingleWhiteAsset,solidWhiteAsset,doubleYellowAsset);
指定标记跨越其车道边界的整个长度。然后,在靠近道路边缘的车道边界上分配实白色标记,在中间车道边界上分配虚线白色车道标记,并在中心车道边界上分配双黄色标记。
% Specify the span for markings as the entire lengths of their lane boundaries markingSpan = [0 1]; markingRefDSW = roadrunner.hdmap.MarkingReference(MarkingID=roadrunner.hdmap.Reference(ID="DashedSingleWhite")); markingAttribDSW = roadrunner.hdmap.ParametricAttribution(MarkingReference=markingRefDSW,Span=markingSpan); markingRefSY = roadrunner.hdmap.MarkingReference(MarkingID=roadrunner.hdmap.Reference(ID="DoubleYellow")); markingAttribSY = roadrunner.hdmap.ParametricAttribution(MarkingReference=markingRefSY,Span=markingSpan); markingRefSW = roadrunner.hdmap.MarkingReference(MarkingID=roadrunner.hdmap.Reference(ID="SolidWhite")); markingAttribSW = roadrunner.hdmap.ParametricAttribution(MarkingReference=markingRefSW,Span=markingSpan); % Assign the markings to lane boundaries [rrMap.LaneBoundaries.ParametricAttributes] = deal(markingAttribSW,markingAttribSW,markingAttribDSW,markingAttribSY,markingAttribDSW,markingAttribDSW,markingAttribSW,markingAttribSW, ... markingAttribSW,markingAttribDSW,markingAttribDSW,markingAttribSY,markingAttribDSW,markingAttribSW);
计算静态目标的几何形状
使用 helperInsidePolygon 辅助函数计算每个树多边形的顶点坐标。
[X3,Y3] = helperInsidePolygon(x3,y3); [X4,Y4] = helperInsidePolygon(x4,y4); [X5,Y5] = helperInsidePolygon(x5,y5); X = [X3'; X4'; X5']; Y = [Y3'; Y4'; Y5']; numOfTrees = numel(X);
创建静态目标
为树定义一个静态目标,然后将树添加到模型中并定义它们的属性。
% Define tree static object type path = roadrunner.hdmap.RelativeAssetPath(AssetPath="Assets/Props/Trees/Eucalyptus_Sm01.fbx"); rrMap.StaticObjectTypes(1) = roadrunner.hdmap.StaticObjectType(ID="StaticObjectType1",AssetPath=path); rrMap.StaticObjects(numOfTrees,1) = roadrunner.hdmap.StaticObject; objectRef1 = roadrunner.hdmap.Reference(ID="StaticObjectType1"); % Specify the length, width, and height values of the trees, estimated using Google Earth. length = 9; width = 9; height = 23; for i = 1:numOfTrees x = X(i); y = Y(i); aGeoOrientedBoundingBox = roadrunner.hdmap.GeoOrientedBoundingBox; aGeoOrientedBoundingBox.Center = [x y 0]; aGeoOrientedBoundingBox.Dimension = [length width height/2]; geoAngle3 = mathworks.scenario.common.GeoAngle3; geoAngle3.roll = -2; geoAngle3.pitch = -2; geoAngle3.heading = 90; geoOrientation3 = mathworks.scenario.common.GeoOrientation3; geoOrientation3.geo_angle = geoAngle3; aGeoOrientedBoundingBox.GeoOrientation = [deg2rad(-2) deg2rad(-2) deg2rad(90)]; treeID = "Tree" + string(i); rrMap.StaticObjects(i) = roadrunner.hdmap.StaticObject(ID=treeID, ... Geometry=aGeoOrientedBoundingBox, ... ObjectTypeReference=objectRef1); end
定义一个用于建筑物的静态目标,然后将建筑物添加到模型中并定义其属性。
numberOfBuildings = numel(x6); % Add static object type info path = roadrunner.hdmap.RelativeAssetPath(AssetPath="Assets/Buildings/Downtown_30mX30m_6storey.fbx"); % Define static object type rrMap.StaticObjectTypes(2) = roadrunner.hdmap.StaticObjectType(ID="StaticObjectType2",AssetPath=path); % Initialize elements in the StaticObjects property of the HD map object, for better performance as the number of objects in the map increases rrMap.StaticObjects(numberOfBuildings,1) = roadrunner.hdmap.StaticObject; objectRef2 = roadrunner.hdmap.Reference(ID="StaticObjectType2"); % Specify the length, width, and height values of the buildings, estimated using Google Earth. length = [26 26]; width = [26 46]; height = 28; ID = ["Building1","Building2"]; for i= 1:numberOfBuildings x = x6(i); y = y6(i); aGeoOrientedBoundingBox = roadrunner.hdmap.GeoOrientedBoundingBox; aGeoOrientedBoundingBox.Center = [x y 0]; aGeoOrientedBoundingBox.Dimension = [length(i) width(i) height/2]; geoAngle3 = mathworks.scenario.common.GeoAngle3; geoAngle3.roll = 0; geoAngle3.pitch = 0; geoAngle3.heading = 0.4697; geoOrientation3 = mathworks.scenario.common.GeoOrientation3; geoOrientation3.geo_angle = geoAngle3; aGeoOrientedBoundingBox.GeoOrientation = [deg2rad(-2) deg2rad(-2) deg2rad(90)]; rrMap.StaticObjects(i) = roadrunner.hdmap.StaticObject(ID=ID(i), ... Geometry=aGeoOrientedBoundingBox, ... ObjectTypeReference = objectRef2); end
设置地理边界和参考
设置 RoadRunner 高清地图的地理边界和参考会将场景集中在导入的道路上,使您能够将道路插入到场景中,而无需使用 RoadRunner 中的世界设置工具。
将地图的地理边界设置为中心边界的最小和最大坐标值。
geometries = [x1 y1; x2 y2]; geoBounds = [min(geometries) 0; max(geometries) 0]; rrMap.GeographicBoundary = geoBounds;
绘制车道中心和车道边界。
plot(rrMap,ShowStaticObjects=true) title("RoadRunner HD Map of Road Intersection Scene") xlabel("x (m)") ylabel("y (m)")

将 RoadRunner 高清地图保存到文件中。
write(rrMap,"RoadIntersection")将高清地图文件导入 RoadRunner 并构建场景
要使用 MATLAB 打开 RoadRunner,请指定项目的路径。此代码显示了 Windows® 中的示例项目文件夹。使用项目的指定路径打开 RoadRunner。
rrProjectPath = "C:\RR\MyProjects";
rrApp = roadrunner(rrProjectPath);将指定文件中的 RoadRunner 高清地图数据导入到当前打开的场景中,并构建地图。要构建场景,您必须拥有有效的 RoadRunner Scene Builder 许可证。
将 RoadRunner 高清地图文件复制到 RoadRunner 项目。
copyfile("RoadIntersection.rrhd","C:\RR\MyProjects\Assets")
指定 RoadRunner 高清地图的导入选项。
importOptions = roadrunnerHDMapImportOptions(ImportStep="Load");将 RoadRunner 高清地图导入 RoadRunner。
importScene(rrApp,"RoadIntersection.rrhd","RoadRunner HD Map",importOptions)
场景编辑画布显示场景的 RoadRunner 高清地图。为了验证导入的数据,您可以在场景编辑画布中选择控制点、车道、车道边界和静态目标,然后从属性窗格中查看它们的属性。

指定用于从导入的 RoadRunner 高清地图构建场景的选项。关闭重叠组以使 RoadRunner 在道路的几何重叠处创建自动交叉口。
enableOverlapGroupsOptions = enableOverlapGroupsOptions(IsEnabled=0); buildOptions = roadrunnerHDMapBuildOptions(DetectAsphaltSurfaces=true,EnableOverlapGroupsOptions=enableOverlapGroupsOptions);
根据导入的 RoadRunner 高清地图数据构建和可视化场景。要构建场景,您必须拥有 RoadRunner Scene Builder 许可证。
buildScene(rrApp,"RoadRunner HD Map",buildOptions)构建的 RoadRunner 场景包含相交的道路以及树木和建筑物。通过使用交叉口拐角工具调整交叉口拐角半径,修改交叉路口以接近实际的交叉口。
