主要内容

从激光雷达数据构建地图

此示例显示了如何在惯性测量单元(IMU)的帮助下,处理安装在车辆上的传感器的三维激光雷达数据,以逐步构建地图读数。此类地图可促进车辆导航的路径规划或用于定位。为了评估生成的地图,本示例还显示了如何将车辆轨迹与全球定位系统(GPS)记录进行比较。

概述

高清晰度(HD)地图是一种地图服务,可提供高达几厘米精度的精确道路几何结构。这种精度水平使高清地图适用于定位和导航等自动驾驶工作流。此类高清地图是通过三维激光雷达扫描、高精度GPS和/或IMU传感器以及ca构建地图生成的n可用于将车辆定位在几厘米以内。此示例实现了构建此类系统所需的功能子集。

在本例中,您将学习如何:

  • 加载、浏览并可视化记录的驾驶数据

  • 使用激光雷达扫描构建地图

  • 使用IMU读数改进地图

加载并浏览记录的驾驶数据

本例中使用的数据来自此GitHub®存储库,表示大约100秒的激光雷达、GPS和IMU数据。数据以MAT文件的形式保存,每个文件包含时间表。从存储库下载MAT文件并将其加载到MATLAB®工作区。

注:此下载可能需要几分钟时间。

baseDownloadURL='https://github.com/mathworks/udacity-self-driving-data-subset/raw/master/drive_segment_11_18_16/'; dataFolder=fullfile(tempdir,“驾驶室段11、18、16”, filesep);选择= weboptions (“超时”,Inf);lidarFileName=dataFolder+“lidarPointClouds.mat”;imuFileName=dataFolder+“iMuoOrientations.mat”;gpsFileName=dataFolder+“gpsSequence.mat”;folderExists =存在(dataFolder,“dir”);matfilesExist =存在(lidarFileName,“文件”)&&exist(imuFileName,“文件”...&&存在(gpsFileName,“文件”);如果~ folderExists mkdir (dataFolder);终止如果~ matfilesExist disp ('正在下载lidarPointClouds.mat(613 MB)…') websave(lidarFileName, baseDownloadURL +“lidarPointClouds.mat”、选择);disp (“下载imuOrientations。垫(1.2 MB)……”)websave(imuFileName,baseDownloadURL+“iMuoOrientations.mat”、选择);disp (“下载gpsSequence。垫(3 KB)……”)websave(gpsFileName,baseDownloadURL+“gpsSequence.mat”、选择);终止

首先,加载从Velodyne®HDL32E激光雷达保存的点云数据。激光雷达数据的每次扫描都使用点云对象。此对象使用K-d树数据结构对数据进行内部组织,以加快搜索速度。与每个激光雷达扫描相关的时间戳记录在时间时间表的可变部分。

%从MAT文件加载激光雷达数据数据=加载(lidarFileName);lidarPointClouds=data.lidarPointClouds;%显示前几行激光雷达数据头部(激光点云)
8×1时间表时间:8×1时间表时间:8×1时间:8×1时间表时间:1×1时间点点点云云,8×1时间表时间:8×1时间点云,8×1时间:8×1时间表时间:8×1时间表时间点云,8×1时间点云,8×1时间点云,1×1点云,UUUU\\\UUUUUUUUUUU,时间点,时间:1时间:1时间点,时间点云,时间点云,时间:8.8.8.8.1时间点云,时间点,时间点云,时间点云,时间:8.8.8×1时间点,时间点云,时间点,时间点云,时间点云,时间:8.8.8.8.8.8.8,时间点,时间点,时间点,时间点云,时间点,时间点云,时间点,时间点云,时间点,时间点云,时间点,时间点

从MAT文件加载GPS数据纬度经度海拔高度变量时间表用于将GPS设备记录的地理坐标存储在车辆上。

%从MAT文件加载GPS序列data =负载(gpsFileName);gpsSequence = data.gpsSequence;%显示前几行GPS数据头(gpsSequence)
4.4.4-4.4 -4.4 4-4.4-4.4-4.4-11-11.11-4 4.5 23:46:12.5 5 4.4 4-4 4-4 4 4.11 11:46:12.4 4 4:12.5 10 10 10:12.4 4 4 4:12.4 4 4 4 4.4 4.4 4 4.4 4-4 4 4 4 4-4-12.4 4 4-12.4 4-12.4 4 4 4 4 4 4 4 4-12.11 11 11-11 11 11 11-11 11 11 11 11-4 4 4.5 5 5 5 5 5 5 5 5 5 5 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11-5 5 5 5 5 5 5 23:13:10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 16.456737.4-122.11-42.523:46:17.457337.4-122.11-42.523:46:18.465637.4-122.11 -42.5

从MAT文件加载IMU数据。IMU通常由报告车辆运动信息的单个传感器组成。它们结合了多种传感器,包括加速度计、陀螺仪和磁强计。这个取向变量存储IMU传感器的报告方向。这些读数以四元数形式报告。每个读数指定为包含四个四元数部分的1×4矢量。将1×4矢量转换为四元数对象

%从MAT文件加载IMU录制数据=加载(imuFileName);imuOrientations=数据。imuOrientations;%将IMU录制转换为四元数类型imuOrientations=转换器变量(imuOrientations,“方向”“四元数”);%显示IMU数据的前几行头部(定向)
8.1)时间,时间,时间,时间表,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间,时间:11:11:11.4605[1×1四四四元数[1:3:46:11.4620[1×1四四元数]23:46:11:46:11:46:11:11:46:11:11:11:46:11:11:11:46:11:11:11.4611:46:11:11:11.4611:11.4611.4611:11.4611.4611.4655[11.4611.4655[1:11.4655[1.4655[1:11.4655[1.4655[1.4655[1.4655[1.461×1四元数]

为了了解传感器读数是如何得出的,请计算每个传感器的近似帧持续时间

lidarFrameDuration=中值(diff(lidarPointClouds.Time));gpsFrameDuration=中值(diff(gpsSequence.Time));imuFrameDuration=中值(diff(imuOrientations.Time));%将显示格式调整为秒lidarFrameDuration.Format=“年代”;gpsFrameDuration。格式=“年代”;imuFrameDuration。格式=“年代”%计算帧速率lidarRate=1/秒(lidarFrameDuration);gpsRate=1/秒(gpsFrameDuration);imuRate=1/秒(imuFrameDuration);%显示帧持续时间和速率流('激光雷达:%s, %3.1f Hz\n',char(lidarFrameDuration),lidarate;fprintf('全球定位系统:%s,%3.1f赫兹\n',char(gpsFrameDuration),gpsRate;fprintf('IMU:%s,%3.1f Hz\n'char (imuFrameDuration), imuRate);
激光雷达:0.10008秒,10.0赫兹GPS:1.0001秒,1.0赫兹IMU:0.002493秒,401.1赫兹

GPS传感器速度最慢,运行频率接近1 Hz。其次是激光雷达,运行频率接近10 Hz,其次是IMU,运行频率接近400 Hz。

可视化驾驶数据

要了解场景包含的内容,请使用流媒体播放器可视化记录的数据。要可视化GPS读数,请使用地质层.使用可视化激光雷达读数pcplayer

%创建地质图层以可视化流式地理坐标latCenter=gpsSequence.纬度(1);lonCenter=gpsSequence.经度(1);zoomLevel=17;gpsPlayer=地质层(latCenter、lonCenter、zoomLevel);绘制完整的路线绘图路线(gpsPlayer、gpsSequence.纬度、gpsSequence.经度);%确定玩家的限制Xlimits = [-45 45];%仪表Ylimits = [-45 45];Zlimits = [-10 20];%创建一个pcplayer以可视化来自激光雷达传感器的流点云lidarPlayer=pcplayer(xlimits、ylimits、zlimits);%自定义播放器轴标签包含(lidarPlayer。轴,‘X(m)’)ylabel(lidarPlayer.Axes,‘Y(m)’) zlabel (lidarPlayer。轴,‘Z(m)’)标题(lidarPlayer.Axes,“激光雷达传感器数据”%在屏幕上对齐玩家HelperAlignPlayer({gpsPlayer,lidarPlayer});%外环超过GPS读数(较慢信号)g=1:高度(GPS顺序)-1%从时间表中提取地理坐标纬度=gpsSequence.纬度(g);经度=gpsSequence.经度(g);%更新当前位置在GPS显示plotPosition (gpsPlayer,经度和纬度);%计算当前和下一次GPS读取之间的时间跨度timeSpan=时间范围(gpsSequence.Time(g),gpsSequence.Time(g+1));%提取在此时间跨度内记录的激光雷达帧lidarFrames=lidarPointClouds(时间跨度:);%内环激光雷达读数(更快的信号)l = 1:高度(lidarFrames)%提取点云ptCloud=lidarFrames.PointCloud(l);%更新激光雷达显示器视图(lidarPlayer、ptCloud);%暂停以减慢显示速度暂停(0.01)终止终止

使用记录的激光雷达数据绘制地图

激光雷达是一种功能强大的传感器,可以在其他传感器不起作用的环境中进行感知。它们提供了车辆环境的详细、完整的360度视图。

%隐藏玩家隐藏(gpsPlayer)隐藏(lidarPlayer)%选择一帧激光雷达数据来演示注册流程frameNum = 600;ptCloud = lidarPointClouds.PointCloud (frameNum);%显示并旋转ego视图以显示激光雷达数据HelpServiceSualizegView(ptCloud);

激光雷达可以用来制作厘米精度的高清地图,包括整个城市的高清地图。这些地图稍后可用于车内定位。构建这样一张地图的典型方法是对齐从移动车辆获得的连续激光雷达扫描,并将它们组合成单个大型点云。本示例的其余部分将探讨这种构建地图的方法。

  1. 结合激光雷达扫描:使用点云配准技术(如迭代最近点(ICP)算法或正态分布变换(NDT)算法)对齐连续激光雷达扫描。看见pcregistericppcregisterndt有关每种算法的更多详细信息,请参阅。本例使用NDT,因为它通常更精确,尤其是在考虑旋转时pcregisterndt函数返回将移动点云与参考点云对齐的刚性变换。通过依次组合这些变换,每个点云将变换回第一个点云的参考坐标系。

  2. 组合对齐扫描:注册新的点云扫描并将其转换回第一个点云的参考坐标系后,可以使用pcmerge

首先,获取与附近激光雷达扫描相对应的两点云。要加快处理速度,并在扫描之间积累足够的运动,请使用每十次扫描。

skipFrames = 10;frameNum = 100;固定= lidarPointClouds.PointCloud (frameNum);= lidarPointClouds移动。PointCloud (frameNum + skipFrames);

在注册之前,处理点云,以便在点云中保留与众不同的结构。这些预处理步骤包括以下步骤:*检测并移除接地层*检测并移除车辆

这些步骤在中有更详细的描述用激光雷达探测地面和障碍物的例子。在这个例子中helperProcessPointCloudhelper函数完成这些步骤。

fixedProcessed = helperProcessPointCloud(固定);movingProcessed = helperProcessPointCloud(移动);

在俯视图中显示原始和处理过的点云。在加工过程中去除了品红点。这些点对应于地平面和车辆。

hFigFixed=图;pcshowpair(固定,固定处理)视图(2);%调整视图以显示顶部视图helperMakeFigurePublishFriendly(hFigFixed);%在注册之前对点云进行下采样。下采样可提高%配准精度和算法速度。下降采样率=0.1;固定下降采样率=pcdownsample(固定已处理,“随机”,下采样率);移动下采样=pcdownsample(移动已处理,“随机”,降低采样率);

在对点云进行预处理后,使用NDT对其进行注册。在注册前后对对齐进行可视化。

regGridStep=5;tform=pcregisterndt(移动下采样、固定下采样、regGridStep);movingReg=pctransform(movingProcessed,tform);%在注册前后在俯视图中可视化对齐hFigAlign=图;子批次(121)pcshowpair(移动处理、固定处理)标题(“注册前”) view(2) subplot(122) pcshowpair(movingReg, fixedProcessed) title(注册后的)视图(2)helperMakeFigurePublishFriendly(hFigAlign);

注意点云在配准后对齐良好。即使点云是紧密对齐的,对齐仍然不是完美的。

接下来,使用合并点云pcmerge

mergeGridStep=0.5;ptCloudAccum=pcmerge(fixedProcessed,movingReg,mergeGridStep);hFigAccum=figure;pcshow(ptCloudAccum)标题(“累积点云”)视图(2)helperMakeFigurePublishFriendly(hFigAccum);

现在,对单个点云的处理管道已经很好地理解了,将它们放在一个循环中,覆盖整个记录的数据序列。的helperLidarMapBuilder这门课把所有这些放在一起updateMap类的方法接收一个新的点云,并完成前面详述的步骤:

  • 对点云进行处理,去除地平面和ego vehicleprocessPointCloud方法

  • 对点云进行下采样。

  • 估计合并上一个点云和当前点云所需的刚性变换。

  • 将点云变换回第一帧。

  • 将点云与累积点云地图合并。

此外,updateMap方法还接受用于初始化注册的初始转换估计。良好的初始化可以显著改善注册结果。相反,不良的初始化可能会对注册产生不利影响。提供良好的初始化也可以提高算法的执行时间。

为注册提供初始估计的一种常见方法是使用恒定速度假设。使用来自先前迭代的转换作为初始估计。

更新显示方法还创建和更新二维俯视图流点云显示。

创建一个地图构建器对象mapBuilder=helperLidarMapBuilder(“降采样率”,降低采样率);%设置随机数种子rng(0);closeDisplay=false;numFrames=高度(lidarPointClouds);tform=rigid3d;n=1:skipFrames:numFrames-skipFrames%获取第n个点云ptCloud=lidarPointClouds.PointCloud(n);%使用上一次迭代的转换作为初始估计%点云配准的当前迭代(等速)initTform = tform;%使用点云更新地图tform=updateMap(mapBuilder、ptCloud、initTform);%更新地图显示更新显示(mapBuilder、closeDisplay);终止

仅点云注册即可构建车辆所经过环境的地图。虽然地图可能显示局部一致,但它可能在整个层序上产生了显著的漂移。

将记录的GPS读数用作地面真实轨迹,以直观地评估构建地图的质量。首先转换GPS读数(纬度、经度、高度)到局部坐标系。选择与序列中第一个点云原点重合的局部坐标系。此转换使用两种转换计算:

  1. 将GPS坐标转换为本地笛卡尔东-北-北坐标拉丁美洲作用从轨迹起点开始的GPS位置用作参考点,并定义局部x、y、z坐标系的原点。

  2. 旋转笛卡尔坐标,使局部坐标系与第一个激光雷达传感器坐标对齐。由于不知道车辆上激光雷达和GPS的确切安装配置,因此会对其进行估计。

%选择参考点作为第一个GPS读数原点=[gpsSequence.纬度(1)、gpsSequence.经度(1)、gpsSequence.海拔(1)];%将GPS读数转换为本地东北向上坐标系[xEast,yNorth,zUp]=latlon2local(gpsSequence.纬度,gpsSequence.经度,...GPS序列、高度、原点);%估计轨迹开始时的大致方向,以对齐局部轨迹%激光雷达坐标系θ=中位数(atan2d(yNorth(1:15),xEast(1:15));R=[cosd(90θ)sind(90θ)0;-sind(90θ)cosd(90θ)0;0.1];%旋转ENU坐标以与激光雷达坐标系对齐地面轨迹=[xEast,yNorth,zUp]*R;

将地面真相轨迹叠加到构建的地图上。

按住(mapBuilder.Axes,“上”)散射(mapBuilder。轴,groundTruthTrajectory(:,1), groundTruthTrajectory(:,2),...“绿色”“填充”);helperAddLegend(mapBuilder.Axes,...“地图点”“估计轨迹”“地面真相轨道”});

初始转弯后,估计轨迹明显偏离地面真实轨迹。仅使用点云注册估计的轨迹可能会漂移,原因有很多:

  • 传感器发出的噪声扫描没有足够的重叠

  • 缺乏足够强大的功能,例如,在长道路附近

  • 不准确的初始变换,尤其是当旋转显著时。

%关闭地图显示updateDisplay(mapBuilder,true);

使用IMU方向来改进构建的地图

IMU是安装在平台上的电子设备。imu包含多个传感器,可以报告有关车辆运动的各种信息。典型的imu包括加速度计、陀螺仪和磁力计。IMU可以提供可靠的方位测量。

使用IMU读数为注册提供更好的初始估计。本例中使用的IMU报告的传感器读数已在设备上过滤。

%重置地图生成器以清除以前生成的地图重置(mapBuilder);%设置随机数种子rng(0);initTform=rigid3d;n=1:skipFrames:numFrames-skipFrames%获取第n个点云ptCloud=lidarPointClouds.PointCloud(n);如果n > 1由于IMU传感器以更快的速度报告读数,请收集%自上次激光雷达扫描以来报告的IMU读数。prevTime = lidarPointClouds。时间(n - skipFrames);currTime = lidarPointClouds.Time (n);timeSinceScan = timerange(prevTime, currTime);imuReadings = imuOrientations (timeSinceScan,“方向”);%使用IMU读数形成初始估计值initTform=helpercomputeinitialestimatefromu(imuReadings,tform);终止%使用点云更新地图tform=updateMap(mapBuilder、ptCloud、initTform);%更新地图显示更新显示(mapBuilder、closeDisplay);终止%在新地图上叠加地面真实轨迹按住(mapBuilder.Axes,“上”)散射(mapBuilder。轴,groundTruthTrajectory(:,1), groundTruthTrajectory(:,2),...“绿色”“填充”);helperAddLegend(mapBuilder.Axes,...“地图点”“估计轨迹”“地面真相轨道”});%捕获用于发布的快照斯内普诺;%关闭打开数字关闭([hFigFixed,hFigAlign,hFigAccum]);更新显示(mapBuilder,true);

使用来自IMU的方向估计显著改进了配准,导致更接近的轨迹和更小的漂移。

万博1manbetx辅助功能

helperAlignPlayers对齐流播放器的单元阵列,使它们在屏幕上从左到右排列。

作用HelperAlignPlayer(玩家)验证属性(玩家、{“细胞”}, {“向量”})hasAxes=cellfun(p)isprop(p,“斧头”),玩家,“UniformOutput”,对);如果~all(hasAxes)错误('预期所有查看器都具有Axis属性');终止屏幕大小=获取(groot,“拉”); 屏幕边距=[50100];PlayerSize=cellfun(@getPlayerSize,玩家,“UniformOutput”,false);playerSizes=cell2mat(playerSizes);maxHeightInSet=max(playerSizes(1:3:end));%垂直排列播放器,使最高的播放器距离屏幕100像素%。位置=圆形([屏幕边距(1),屏幕尺寸(4)-屏幕边距(2)-最大高度插入]);n=1:numel(players)player=players{n};hFig=祖先(player.Axes,“数字”)hFig.外部位置(1:2)=位置;向右设置下一个位置位置=位置+[50+hFig.外部位置(3),0];终止作用sz=getPlayerSize(查看器)%获取父图形容器h=祖先(viewer.Axes,“数字”)sz=h.外置(3:4);终止终止

HelperVisualizegView通过围绕中心旋转,在ego透视图中可视化点云数据。

作用player=helpervicesualizegoview(ptCloud)%创建pcplayer对象xlimits=ptCloud.xlimits;ylimits=ptCloud.ylimits;zlimits=ptCloud.zlimits;player=pcplayer(xlimits,ylimits,zlimits);%关闭轴线轴(player.Axes,“关”);%设置摄像头以显示自我视图camproj(球员。轴,“透视图”); camva(player.Axes,90);坎波斯(player.Axes,[0]);camtarget(player.Axes,[-1 0]);%将变换设置为旋转5度θ= 5;R = [cosd(theta) sind(theta) 0 0 -sind(theta) cosd(theta) 0 0 0 1 0 0 0 1];rotateByTheta = rigid3d (R);n=0:θ:359%按θ旋转点云ptCloud=pctransform(ptCloud,rotateByTheta);%显示点云查看(播放器、云端);暂停(0.05)终止终止

helperProcessPointCloud通过删除属于地平面或车辆的点来处理点云。

作用ptCloudProcessed = helperProcessPointCloud (ptCloud)%检查点云是否有组织isOrganized=~ismatrix(ptCloud.Location);%如果点云已组织,请使用基于范围的泛洪填充算法%(segmentGroundFromLidarData)。否则,请使用平面配件。地面分段方法=[“planefit”“rangefloodfill”];方法=地基分段方法(同构+1);如果方法==“planefit”%以参考法向量分段地面作为主平面%使用pcfitplane指向正z方向。有组织的%点云,考虑使用SexMeStudio Li DARDATA代替。最大距离=0.4;%仪表最大距离=5;%度refVector=[0,0,1];%z方向[~,地面指数]=pcfitplane(ptCloud,maxDistance,refVector,maxAngDistance);埃尔塞夫方法==“rangefloodfill”%使用基于范围的泛洪填充分割地面。地面指数=分段地面数据(ptCloud);其他的错误(“预期方法为‘planefit’或‘rangefloodfill’”终止在给定的传感器半径内,以点的形式分割自我车辆传感器位置=[0,0,0];半径=3.5;EgoIndex=findNeighborsInRadius(ptCloud,传感器位置,半径);%移除属于地面或车辆的点ptsToKeep=true(ptCloud.Count,1);ptsToKeep(groundindex)=false;ptsToKeep(egoindex)=false;%如果点云已组织,请保留组织结构如果isOrganized ptCloudProcessed=选择(ptCloud,查找(ptstokep),“OutputSize”“满”);其他的ptCloudProcessed=select(ptCloud,find(ptstokep));终止终止

帮助者从IMU获得初始评估使用IMU方向读数和先前估计的变换,估计NDT的初始变换。

作用tform=HelperComputeInitiatialestimateFromIMU(imuReadings,prevTform)%使用先前估计的转换初始化转换tform=prevTform;%如果没有IMU读数可用,则返回如果高度(imuReadings)<=1回来终止%IMU方向读数报告为四元数,表示%到主体框架的旋转偏移。计算方向变化%间隔期间第一次和最后一次报告的IMU方向之间%激光雷达扫描的结果。q1=模拟方向(1);q2=模拟方向(结束);%通过以下公式计算第一个和最后一个IMU读数之间的旋转偏移:%-从q2机架旋转到车身机架%-从车身框架旋转到q1框架q=q1*conj(q2);%转换为欧拉角yawPitchRoll =欧拉(q,“ZYX股票”“点”);放弃俯仰和滚转角度估计。只使用方向角估计%从IMU方向。yawPitchRoll(2:3)=0;%转换回旋转矩阵q=四元数(yawPitchRoll,“欧拉”“ZYX股票”“点”);R=rotmat(q,“点”);%使用计算旋转T形式T(1:3,1:3)=R';终止

helperAddLegend向轴添加图例。

作用helperAddLegend(hAx、标签)给坐标轴添加一个图例hLegend=图例(hAx,标签{:});%设置文本颜色和字体大小hLegend.TextColor=[1];hLegend.FontWeight=“大胆的”终止

helperMakeFigurePublishFriendly调整数字,以便发布捕获的屏幕截图是正确的。

作用helperMakeFigurePublishFriendly(hFig)如果~isempty(hFig)和&isvalid(hFig)hFig.Handleviability=“回调”终止终止

另请参阅

功能

对象

相关话题

外部网站