主要内容

本页采用了机器翻译。点击此处可查看最新英文版本。

使用 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

Figure contains an axes object with type geoaxes. The geoaxes object contains 3 objects of type line, polygon, point.

将地理空间表转换为道路中心表,以获得在交叉路口相交的两条道路的纬度和经度坐标。

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 高清地图道路网络,并修改道路以类似于实际道路,该道路由多车道和多车道边界组成。然后,按照实际场景,在车道边界上应用适当的车道标记。为了提高性能,随着地图中对象数量的增加,初始化高精地图的 LanesLaneBoundaries 属性。

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)")

Figure contains an axes object. The axes object with title RoadRunner HD Map of Road Intersection Scene, xlabel x (m), ylabel y (m) contains 3 objects of type line. One or more of the lines displays its values using only markers These objects represent Lane Boundaries, Lane Centers, Static Objects.

将 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 高清地图。为了验证导入的数据,您可以在场景编辑画布中选择控制点、车道、车道边界和静态目标,然后从属性窗格中查看它们的属性。

Preview of the imported RoadRunner HD Map data.

指定用于从导入的 RoadRunner 高清地图构建场景的选项。关闭重叠组以使 RoadRunner 在道路的几何重叠处创建自动交叉口。

enableOverlapGroupsOptions = enableOverlapGroupsOptions(IsEnabled=0);
buildOptions = roadrunnerHDMapBuildOptions(DetectAsphaltSurfaces=true,EnableOverlapGroupsOptions=enableOverlapGroupsOptions);

根据导入的 RoadRunner 高清地图数据构建和可视化场景。要构建场景,您必须拥有 RoadRunner Scene Builder 许可证。

buildScene(rrApp,"RoadRunner HD Map",buildOptions)

构建的 RoadRunner 场景包含相交的道路以及树木和建筑物。通过使用交叉口拐角工具调整交叉口拐角半径,修改交叉路口以接近实际的交叉口。

Built RoadRunner scene.

另请参阅

|

主题