Autonomous_Exploration_Development_Environment的学习笔记(三)

CMU的自主环境探索利用matlab生成前向三次样条路径点

在CMU开源的自主环境探索算法中,机器人或者无人机的主要是靠matlab进行前向三次样条曲线生成路径点的方式进行的,这部分程序在matlab中运行并且生成相应的文件,从而为local_planner提供帮助。

1.采样路径的生成

利用车辆的运动学特性,进行时间维度上的离散,从而通过预测得到未来一段时间机器人的状态,从而实现了采样路径的生成。下面首先分析这一部分的代码。

dis = 1.0;                  // 前向预测的距离 单位:m
angle = 27;					// 车辆转向的角度 单位:度
deltaAngle = angle / 3;		// 将角度按照3°进行平分
scale = 0.65;				// 角度的缩放系数

在上述定义了一些机器人的初始的状态参数,可以根据你的机器人进行参数的修改

pathStartAll = zeros(4, 0);
pathAll = zeros(5, 0);
pathList = zeros(5, 0);
pathID = 0;
groupID = 0;

这些是一些关于路径输出的一些存储值。

for shift1 = -angle : deltaAngle : angle
    wayptsStart = [0, 0, 0;
                   dis, shift1, 0];
    
    pathStartR = 0 : 0.01 : dis;
    pathStartShift = spline(wayptsStart(:, 1), wayptsStart(:, 2), pathStartR);
    
    pathStartX = pathStartR .* cos(pathStartShift * pi / 180);
    pathStartY = pathStartR .* sin(pathStartShift * pi / 180);
    pathStartZ = zeros(size(pathStartX));
    
    pathStart = [pathStartX; pathStartY; pathStartZ; ones(size(pathStartX)) * groupID];
    pathStartAll = [pathStartAll, pathStart];
    
    for shift2 = -angle * scale + shift1 : deltaAngle * scale : angle * scale + shift1
        for shift3 = -angle * scale^2 + shift2 : deltaAngle * scale^2 : angle * scale^2 + shift2
                waypts = [pathStartR', pathStartShift', pathStartZ';
                          2 * dis, shift2, 0;
                          3 * dis - 0.001, shift3, 0;
                          3 * dis, shift3, 0];

                pathR = 0 : 0.01 : waypts(end, 1);
                pathShift = spline(waypts(:, 1), waypts(:, 2), pathR);

                pathX = pathR .* cos(pathShift * pi / 180);
                pathY = pathR .* sin(pathShift * pi / 180);
                pathZ = zeros(size(pathX));

                path = [pathX; pathY; pathZ; ones(size(pathX)) * pathID; ones(size(pathX)) * groupID];
                pathAll = [pathAll, path];
                pathList = [pathList, [pathX(end); pathY(end); pathZ(end); pathID; groupID]];
                
                pathID = pathID + 1;

                plot3(pathX, pathY, pathZ);
        end
    end
    
    groupID = groupID + 1
end

上述代码开启了三个for循环,也是路径生成的主要函数,我们一个一个for循环来进行解析;

wayptsStart = [0, 0, 0;
               dis, shift1, 0];
    
    pathStartR = 0 : 0.01 : dis;
    pathStartShift = spline(wayptsStart(:, 1), wayptsStart(:, 2), pathStartR);

上述中定义了wayptsStart矩阵,然后对(0,0)到(1,-27)按照0.01进行了数据的插值.

    pathStartX = pathStartR .* cos(pathStartShift * pi / 180);
    pathStartY = pathStartR .* sin(pathStartShift * pi / 180);
    pathStartZ = zeros(size(pathStartX));
    
    pathStart = [pathStartX; pathStartY; pathStartZ; ones(size(pathStartX)) * groupID];
    pathStartAll = [pathStartAll, pathStart];

这些变量主要是进行路径起始点的转换与存储的,最终所有的路径都存在了pathStartAll变量中。

    for shift2 = -angle * scale + shift1 : deltaAngle * scale : angle * scale + shift1
        for shift3 = -angle * scale^2 + shift2 : deltaAngle * scale^2 : angle * scale^2 + shift2
                waypts = [pathStartR', pathStartShift', pathStartZ';
                          2 * dis, shift2, 0;
                          3 * dis - 0.001, shift3, 0;
                          3 * dis, shift3, 0];

                pathR = 0 : 0.01 : waypts(end, 1);
                pathShift = spline(waypts(:, 1), waypts(:, 2), pathR);

                pathX = pathR .* cos(pathShift * pi / 180);
                pathY = pathR .* sin(pathShift * pi / 180);
                pathZ = zeros(size(pathX));

                path = [pathX; pathY; pathZ; ones(size(pathX)) * pathID; ones(size(pathX)) * groupID];
                pathAll = [pathAll, path];
                pathList = [pathList, [pathX(end); pathY(end); pathZ(end); pathID; groupID]];
                
                pathID = pathID + 1;

                plot3(pathX, pathY, pathZ);
        end
    end

这是内层的两个循环,可以看到shift2在一个值固定的情况下,shift3开始循环,按照上述的循环方式,一次shift3生成一条路径,编号加1,这样一个固定的shift2的情况下,会生成7条路径,而一个shift2循环会循环7次,所以在两层内循环中会生成49条路径。此时路径组的groupID=0,path_IDmax =49。由于在插值的时候,每一个shitf2下的七条路径都会经经过(1,27)点和(2,shift2)点,所以就会出现下图中每组路径有两个交点的情况。至于为什么这么设计,楼主也没想明白,根据后续算法需求,需要进行探测距离的转换,所以才这么设计了交点吧。
在这里插入图片描述

整个循环结束后,我们可以得到如下的路径图。
在这里插入图片描述

从上图中可以看出,总共有7个group,每个group有49条路径,总共有343条路径。如果是无人机的话,由于具有z轴方向的路径,所以无人机的路径会更多。

fileID = fopen('startPaths.ply', 'w');
fprintf(fileID, 'ply\n');
fprintf(fileID, 'format ascii 1.0\n');
fprintf(fileID, 'element vertex %d\n', size(pathStartAll, 2));
fprintf(fileID, 'property float x\n');
fprintf(fileID, 'property float y\n');
fprintf(fileID, 'property float z\n');
fprintf(fileID, 'property int group_id\n');
fprintf(fileID, 'end_header\n');
fprintf(fileID, '%f %f %f %d\n', pathStartAll);
fclose(fileID);

fileID = fopen('paths.ply', 'w');
fprintf(fileID, 'ply\n');
fprintf(fileID, 'format ascii 1.0\n');
fprintf(fileID, 'element vertex %d\n', size(pathAll, 2));
fprintf(fileID, 'property float x\n');
fprintf(fileID, 'property float y\n');
fprintf(fileID, 'property float z\n');
fprintf(fileID, 'property int path_id\n');
fprintf(fileID, 'property int group_id\n');
fprintf(fileID, 'end_header\n');
fprintf(fileID, '%f %f %f %d %d\n', pathAll);
fclose(fileID);

fileID = fopen('pathList.ply', 'w');
fprintf(fileID, 'ply\n');
fprintf(fileID, 'format ascii 1.0\n');
fprintf(fileID, 'element vertex %d\n', size(pathList, 2));
fprintf(fileID, 'property float end_x\n');
fprintf(fileID, 'property float end_y\n');
fprintf(fileID, 'property float end_z\n');
fprintf(fileID, 'property int path_id\n');
fprintf(fileID, 'property int group_id\n');
fprintf(fileID, 'end_header\n');
fprintf(fileID, '%f %f %f %d %d\n', pathList);
fclose(fileID);

从上述程序中可以看出存储了三个文件startPaths.ply,paths.ply,pathList.ply 三个文件,其中startPaths.ply中存储的是记录了第一次采样的路径点,是一个4×707的矩阵,其中一个矩阵大小为{x,y,z,groupID}。这里解释一下为啥是707个矩阵,是一个这个路径只保存了0-1之间的路径,一条路径只有101个点,所以7组路径是707个路径点。

pathAll:该文件则记录了三次采样所生成的所有路径点,是一个5 ∗ 103243 的矩阵,即在每一个路径组中,包含14749个路径点(103243/7 = 14749)。而每一条路径包括301个路径点(14749/7/7 = 301)。

pathList:该文件记录了每条路径的最后一个路径点,是一个5×343的矩阵,即7×7×7个末端点。

2.体素网格产生

对于碰撞检查,使用覆盖了传感器范围的体素网格。根据样条距离,传感器范围为3.2*4.5,在该区域生成体素网格,如下图所示,在这里考虑了车辆半径的遮挡。在生成体素网格时,定义如下参数,

voxelSize = 0.02; // 体素方格大小
searchRadius = 0.45; //
offsetX = 3.2;  // 范围
offsetY = 4.5;
voxelNumX = 161; //数量 offsetX/voxelSize 
voxelNumY = 451;

在外层循环中,从外向内计算,同采样的路径一样,靠近车体的位置Y的宽度也会随着scaleY减小。

for indX = 0 : voxelNumX - 1
    x = offsetX - voxelSize * indX;
    scaleY = x / offsetX + searchRadius / offsetY * (offsetX - x) / offsetX;
    for indY = 0 : voxelNumY - 1
        y = scaleY * (offsetY - voxelSize * indY);
    end
end

面对scaleY的计算详细说明:
在这里插入图片描述
根据相似三角形,可以算出黑色框包围的地方,这些地方就是需要生成体素网格的地方,按照提速网格的大小可以直接遍历进行生成,如果需要推导公式,则参考链接(https://blog.csdn.net/qq_39266065/article/details/119734986?spm=1001.2014.3001.5502)。

3.碰撞检测

在生成体素网格后,为了在局部路径的筛选中更高效,将每个网格与路径索引离线计算。通过查找路径集合中的路径点周围指定距离内的邻居,并且该邻居点属于体素网格的集合。最终得到每一个体素网格与待遮挡路径之间的索引关系表。
rangesearch()函数:查找某个点周围指定距离内的所有邻居。

[ind, dis] = rangesearch(pathAll(1 : 2, :)', voxelPoints, searchRadius);

最后记录了每个记录每个体素网格附近对应的路径号,从而生成了碰撞检测的文件。

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值