蚁群算法实现三维避障轨迹规划(Matlab)

2023-05-16

关于蚁群算法

其实大多数优化算法都是“试错”的过程,不同的是如何利用在“试错”过程中得到的经验。蚁群算法在“试错”的过程中通过留下信息素对未来的试错过程加以提示,从而保证一定的收敛性。

代码分析

我写了一份matlab蚁群算法三维避障规划的代码,能保证收敛,但不够优秀,可以作为基础加以改进。

无论是GA、SA、PSO、还是本文讲到的蚁群,应用在三维轨迹规划中都存在一个问题:如何将轨迹表述成一个解以及如何生成一个从起始点到终点的解。在我的A*算法三维规划博客中通过分解空间成类似点云的形式来表述解。而在蚁群算法三维规划中,我采用的是对空间切片的方式(可能不够好,但是能用)。

读取参数

clc; clear; close all;
%% 参数读取与设置
obstacleMatrix = csvread("./data_csv/obstacleMatrix.csv");
RobstacleMatrix = csvread("./data_csv/RobstacleMatrix.csv")';
cylinderMatrix = csvread("./data_csv/cylinderMatrix.csv");
cylinderRMatrix = csvread("./data_csv/cylinderRMatrix.csv")';
cylinderHMatrix = csvread("./data_csv/cylinderHMatrix.csv")';
start = csvread("./data_csv/start.csv")';
goal = csvread("./data_csv/goal.csv")';

没有csv文件可以直接写这几个matrix,它们表述的是障碍物的坐标和半径等信息,obstacleMatrix为球障碍物的圆心坐标矩阵(n*3矩阵),RobstacleMatrix为对应半径向量(n*1向量),cylinderMatrix为圆柱体中心坐标(n*2矩阵,没有第三个维度,从z=0开始绘制圆柱体),cylinderRMatrix为对应圆柱体半径(n*1向量),cylinderHMatrix为对应圆柱体的高(n*1向量)。

设置参数

popNumber = 50;  % 蚁群个体数量
rou = 0.1;        % 挥发因子
bestFitness = []; % 每一代的最佳适应值储存列表
bestfitness = inf;% 初始化最佳适应值(本案例中越小越好)
everyIterFitness = [];
deltaX = 0.2; deltaY = 0.2; deltaZ = 0.2;
gridXNumber = floor(abs(goal(1) - start(1)) / deltaX);
gridYNumber = 50; gridZNumber = 50;
ybegin = start(2) - 20*deltaY; zbegin = start(3) - 20*deltaZ;
pheromone = ones(gridXNumber, gridYNumber, gridZNumber);
ycMax = 3; zcMax = 3; % 蚂蚁沿y轴最大变动格子数和沿z轴最大变动格子数
bestPath = []; 
iterMax = 150; 

其中deltaX为对x轴的切片步长:
在这里插入图片描述
deltaY和deltaZ为每一个切片下对Y轴和Z轴的网格划分步长。ybegin和zbegin为y和z的起始参考值,在信息素矩阵pheromone中利用y和z分别减去ybegin和zbegin再分别处以deltaY和deltaZ就是索引值了。相当于对空间离散坐标进行编码。gridXNumber为横向切片的数量。

绘制障碍物环境

%% 绘制障碍环境
figure(1)
[n,~] = size(obstacleMatrix);
for i = 1:n   %绘制静态球障碍物
    [x,y,z] = sphere();
    surfc(RobstacleMatrix(i)*x+obstacleMatrix(i,1),...
        RobstacleMatrix(i)*y+obstacleMatrix(i,2),...
        RobstacleMatrix(i)*z+obstacleMatrix(i,3));
    hold on;
end

[n,~] = size(cylinderMatrix);
for i = 1:n   %绘制圆柱体障碍物
    [x,y,z] = cylinder(cylinderRMatrix(i));
    z(2,:) = cylinderHMatrix(i);
    surfc(x + cylinderMatrix(i,1),y + cylinderMatrix(i,2),...
          z,'FaceColor','interp');
    hold on;
end

bar1 = scatter3(start(1),start(2),start(3),80,"cyan",'filled','o','MarkerEdgeColor','k');hold on
bar2 = scatter3(goal(1),goal(2),goal(3),80,"magenta",'filled',"o",'MarkerEdgeColor','k');
text(start(1),start(2),start(3),'   起点'); text(goal(1),goal(2),goal(3),'   终点');
axis equal
set(gcf,'unit','centimeters','position',[30 10 20 15]);

主循环

for iter = 1:iterMax
    fprintf("程序已运行:%.2f%%\n",iter/iterMax*100);
    % 路径搜索
    [path, pheromone] = searchPath(popNumber, pheromone, start, goal, ycMax, zcMax,...
                                   deltaX, deltaY, deltaZ, obstacleMatrix,RobstacleMatrix,...
                                   cylinderMatrix, cylinderRMatrix, cylinderHMatrix,...
                                   ybegin, zbegin, gridYNumber, gridZNumber);
    % 路径适应值计算
    fitness = calFit(path, deltaX, start, goal);
    [newBestFitness, bestIndex] = min(fitness);
    everyIterFitness = [everyIterFitness, newBestFitness];
    if newBestFitness < bestfitness
        bestfitness = newBestFitness;
        bestPath = path(bestIndex, :, :);
    end
    bestFitness = [bestFitness, bestfitness];
    % 更新信息素
    cfit = 100 / bestfitness;
    iterNum = 0;
    for x = start(1) + deltaX : deltaX : goal(1) - 0.001
        iterNum = iterNum + 1;
        pheromone(iterNum, round((bestPath(:,iterNum+1,1)-ybegin)/deltaY), round((bestPath(:,iterNum+1,2)-zbegin)/deltaZ))...
        = (1 - rou) * pheromone(iterNum, round((bestPath(:,iterNum+1,1)-ybegin)/deltaY), round((bestPath(:,iterNum+1,2)-zbegin)/deltaZ)) + cfit;
    end
end

这里的searchPath为每一个迭代过程中每一只蚂蚁搜索出一条从起点到终点的路径。之后对这些蚂蚁的路径计算适应值(长度衡量),选出适应值最低的和历史最低比较从而更新历史最优解。最后是更新信息素,这里我剑走偏锋只用每一代最优路径来更新信息素而没有将所有蚂蚁的路径用来更新,后者我试了下在这个例子上不太好使容易不收敛。
信息素更新方式我采用的是:
I = ( 1 − r o u ) I + c / f i t I = (1-rou)I + c/fit I=(1rou)I+c/fit
rou是衰减因子,fit是路径适应值,c是一个可调因子。由于我的fit是路径长度,路径越短,信息素增加得越多。

路径搜索函数searchPath

function [path, pheromone] = searchPath(popNumber, pheromone, start, goal, ycMax, zcMax,...
                                        deltaX, deltaY, deltaZ, obstacleMatrix, RobstacleMatrix,...
                                        cylinderMatrix, cylinderRMatrix, cylinderHMatrix,...
                                        ybegin, zbegin, gridYNumber, gridZNumber)
% 获取从起点到终点的路径函数
path = []; % 用于记录所有蚂蚁的路径
for ant = 1:popNumber % 对于每一只蚂蚁
    path(ant, 1, 1:2) = start(2:3); % 只记录y和z轴坐标,x轴每次加deltaX
    nowPoint = start(2:3);
    iterNum = 0;
    for x = start(1) + deltaX : deltaX : goal(1) - 0.001 % 减去一个小数避免x直接取到goal(1)
        iterNum = iterNum + 1;
        nextPoint = [];
        p = [];   
        for y = -ycMax * deltaY : deltaY : ycMax * deltaY
            for z = -zcMax * deltaZ : deltaZ : zcMax * deltaZ
                nextPoint = [nextPoint; nowPoint + [y, z]];
                if nextPoint(end,1) > ybegin+0.01 && nextPoint(end,1) < ybegin + gridYNumber*deltaY && ...
                   nextPoint(end,2) > zbegin+0.01 && nextPoint(end,2) < zbegin + gridZNumber*deltaZ  % 判断是否越界(信息素矩阵大小已经定了,避免超出)
                    hValue = calHeuristicValue(nowPoint, nextPoint(end,:), goal, x, deltaX, obstacleMatrix,...
                                               RobstacleMatrix, cylinderMatrix, cylinderRMatrix, cylinderHMatrix);
%                     pher = pheromone(iterNum, round((nextPoint(end,1) - ybegin)/deltaY), round((nextPoint(end,2) - zbegin)/deltaZ));
                    try
                        pher = pheromone(iterNum, round((nextPoint(end,1) - ybegin)/deltaY), round((nextPoint(end,2) - zbegin)/deltaZ));
                    catch
                        round((nextPoint(end,1) - ybegin)/deltaY)
                    end
                    p = [p, pher * hValue];
                else
                    p = [p,0]; %置零在轮盘赌中不可能被选中
                end
            end
        end
        % 轮盘赌选择下一坐标点
        p1 = p / sum(p); % 归一化
        pc = cumsum(p1);
        targetIndex = find(pc >= rand);
        targetNextPoint = nextPoint(targetIndex(1),:);
        path(ant, iterNum + 1, 1:2) = targetNextPoint;
        nowPoint = targetNextPoint;
    end
    path(ant, iterNum + 2, 1:2) = goal(2:3);
end
end

这里的大体思路为:已知切片i上一点的坐标,获取切片i+1上的坐标。而切片i+1上的横坐标已经定了即为第i切片的横坐标加上deltaX,纵坐标y和z坐标允许在第i切片的y和z的基础上做偏移,即正负ycMax*deltaY和正负zcMax*deltaZ。对每一个可行的坐标计算其启发值(使用calHeuristicValue函数),得到hValue并乘以该坐标对应的信息素从而得到一个值,值越大越容易被选中。最后使用轮盘赌的方式选取第i+1切片上的坐标点。这里需要注意的是避障检测在计算启发值函数calHeuristicValue函数中使用,如果坐标与障碍物碰撞则将启发值置为0,那么它乘以信息素也是0,在轮盘赌算法中0是不可能被选取的。因此与障碍物有交点的坐标是不可能被选中的。

对每一只蚂蚁采用上述思路都能找到一条路径,放入三维矩阵path中返回。path第一个维度表示是哪一只蚂蚁,第二个维度表示这只蚂蚁路径上的第几个切片,第三个维度表示这一切片上的y和z。

计算启发值函数calHeuristicValue

function h = calHeuristicValue(now, next, goal, x, deltaX, obstacleMatrix, RobstacleMatrix,...
                               cylinderMatrix, cylinderRMatrix, cylinderHMatrix)
% 判断下一个坐标点是否碰撞,若碰撞则将启发值置为0,在后续的轮盘赌点位选择时将不可能被选中
nextXYZ = [x, next];
flag = checkCol(nextXYZ, obstacleMatrix, RobstacleMatrix, cylinderMatrix, cylinderRMatrix, cylinderHMatrix);
% 计算启发值
d1 = getDist([x - deltaX, now], [x, next]);
d2 = getDist([x, next], goal);
D = 50 / (d1 + d2);
h = flag * D;
end

其中checkCol为碰撞检测,getDist为获取欧式几何距离函数:

function flag = checkCol(pos, circleCenter,circleR, cylinderCenter,cylinderR, cylinderH)
% 碰撞检测函数
[numberOfSphere, ~] = size(circleCenter);
[numberOfCylinder, ~] = size(cylinderCenter);
flag = true;
for i = 1:numberOfSphere
    if getDist(pos, circleCenter(i,:)) <= circleR(i)
        flag = false;
        break;
    end
end
for i = 1:numberOfCylinder
    if getDist(pos(1:2), cylinderCenter(i,:)) <= cylinderR(i) && pos(3) <= cylinderH(i)
        flag = false;
        break;
    end
end
if pos(3) <= 0, flag = false; end
end
function d = getDist(x,y)
d = sqrt(sum((x - y).^2));
end

计算适应值函数calFit

function f = calFit(path, deltaX, start, goal)
% 计算适应值函数
[n,m,~] = size(path);
x = [start(1) : deltaX : goal(1) - 0.001, goal(1)];
for i = 1:n
    f(i) = 0;
    for j = 1:m-1
        f(i) = f(i) + getDist([x(j), path(i,j,1), path(i,j,2)], [x(j+1), path(i,j+1,1), path(i,j+1,2)]);
    end
end
end

采用的是以路径长度作为适应值,即适应值越小越好,返回每个蚂蚁路径的适应值,用于后续搜索最小适应值和最优路径以及更新信息素。

绘制最佳路径以及适应值变化图

% 绘制路径
x = [start(1):deltaX:goal(1)-0.001,goal(1)];
[~,m] = size(x);
path_ = [];
for i = 1:m
    path_ = [path_;bestPath(:,i,1),bestPath(:,i,2)];
end
bar3 = plot3(x, path_(:,1), path_(:,2),'LineWidth',2,'MarkerSize',7,'Color','r');
legend([bar1,bar2,bar3],["起始点","终止点","无人机航迹"])
% 绘制适应值变化图
figure(2)
plot(bestFitness,'LineWidth',2,'Color','r'); hold on;
plot(everyIterFitness,'LineWidth',2,'Color','b')
legend('历史最佳个体适应度','每代最佳个体适应度')
title('适应度变化趋势'); xlabel('迭代次数'); ylabel('适应度值'); grid on;

结果

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

the future

感觉我写的代码效率有点低,运行得比较慢,另外切片的这种方式让轨迹不够平滑。这个代码有很多超参数需要调…不想调了…

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

蚁群算法实现三维避障轨迹规划(Matlab) 的相关文章

  • ubuntu系统安装串口转485驱动步骤详解

    ubuntu系统安装串口转485驱动步骤详解 本人使用的是宸曜的工控机 自带com DB9形式的串口 通过BIOS F2进入设置页面 将com0设置成485 模式2 线模式 此为电脑设置截图 采用485两线制接线模式 即端口2 5 8 通过
  • 基于gazebo的多无人车自主导航编队仿真学习

    基于gazebo的多无人车自主导航编队仿真学习 最近正在研究多车编队 xff0c 多车协同自主导航 xff0c 参考古月居 githua 链接 https www guyuehome com 8915 链接 https github com
  • 基于gazebo的无人车编队仿真实战总结(二)

    基于gazebo的无人车编队仿真实战总结 xff08 二 xff09 上一篇博客是通过古月居的多机器人编队仿真考虑讲前期做的基于阿克曼转向的仿真模型 xff0c 进行三台无人小车的编队仿真 问题 1 将无人小车的仿真模型 xff0c 按照博
  • 关于yolov5的调试环境搭建亲测有效ubuntu18.04 +ros+melodic+anaconda+pytorch+torchvision+cuda10.2+cudnn

    运行环境ubuntu18 04 43 ros 43 melodic 43 anaconda3 43 py3 8 43 torch1 12 1 43 torchvision 0 13 1 43 cuda10 2 43 cudnn 1 首先安装
  • yolov5的ros封装移植

    yolov5在上一篇中已经完美运行 但是在工程项目中 需要在ros中使用yolov5进行目标检测 首先 新建ros的工作空间 mkdir span class token operator span p span class token o
  • ubuntu虚拟机安装Cmake

    安装 Cmake官网下载linux下的安装包 xff1a https cmake org download 拷贝进虚拟机中 打开终端 xff0c 进入安装包所在目录tar xzvf tar gz 解压安装包为了便于管理 xff0c 将解压后
  • 经验分享一:UART 可进入空闲中断,DMA却没数据

    条件 xff1a UART DMA配置没有问题 时钟也都使能了 xff0c 对照寄存器表 xff0c 发现该配置的寄存器都配置的没问题 xff0c 调试发现每次接收到数据 xff0c 可以顺利进入空闲中断 且读取 UART 34 数据接收寄
  • Visual Studio 2022 C++ CLR 的艰难除 Bug

    请看下面一段代码 xff1a 运行结果 xff1a 这是一个Button xff0c 要用到这段代码是因为字符串出了问题 xff1a 肯定是我写的类出问题了 xff0c 便是我在控制台下测试是正常的 代码 xff1a 运行结果 xff1a
  • UBX 协议报文整理

    UBX 协议报文整理 UBX 协议的报文格式如下 xff1a 帧头 2 byte CLASS ID MESSAGE ID 2 byte 消息长度 2 byte PAYLOAD校验和 2 byte 帧头 由两个字节组成 xff0c 即0xB5
  • 将串口接收的数据绘制成波形图(使用matlab或Visual Scope)

    一 串口通信配置 结合stm32固件库 xff08 或其它类型单片机 xff09 中usart相关的函数 xff0c 配置好串口通信的寄存器 xff0c 确定 xff08 数据位 停止位 波特率等等 xff09 xff0c 本文主要介绍两种
  • linux下makefile、make、Cmake的区别

    Makefile make工具 linux下makeflie和make的用法 makefile与make详解 人们通常利用make工具来自动完成编译工作 这些工作包括 xff1a 如果仅修改了某几个源文件 xff0c 则只重新编译这几个源文
  • 常见的HTTP请求报文头

    目录 AcceptCookieConnectionCache ControlHostRefererUser Agent 参考链接 HTTP请求行 请求头 请求体详解 关于常用的http请求头以及响应头详解 Accept Accept app
  • C++中::和:的意思

    C 43 43 中的 1 类作用域 指明成员函数所属的类 span class token class name M span span class token operator span span class token function
  • Git 基础知识--打Tag、团队协作

    打 Tag 简述 Git 可以给历史中的某一个提交打上标签 tag xff0c 以示重要 人们一般用 tag 功能来标记发布节点 xff08 v1 0 xff09 tag 与 分支很像 xff0c 区别在于 xff1a 轻量标签 tag 是
  • 第七章——VINS系统初始化

    前言 这一章主要内容是讲的VINS系统初始化的事 xff0c 内容上还是比较全面丰满的 xff0c 有一些有疑问的点我之后读了代码会在博客里补上 一句话总结初始化 xff1a 以优化量与观测值构建残差 xff0c 提取优化量构成最小二乘问题
  • GIT系列之标签

    1 标签列表 git tag 在控制台打印出当前仓库的所有标签 git tag l 39 v1 39 搜索符合模式的标签 2 打标签 git标签分为两种类型 xff1a 轻量标签和附注标签 轻量标签是指向提交对象的引用 附注标签则是仓库中的
  • printf重定向(重新定义发送数据的方向)

    1 串口使用 printf 需要对 printf 重定向 也就是需要重定义 fputc xff08 xff09 这个函数 xff08 这个函数是printf的底层函数 xff09 转 int fputc span class token p
  • 解决nvcc找不到的问题/bin/sh:1:nvcc:not found

    这里写自定义目录标题 问题描述方法探索解决方法 问题描述 在执行make指令进行编译的时候 xff0c 遇到问题 34 bin sh 1 nvcc not found 34 xff0c 如图所示 其原因是未找到nvcc xff0c 于是开始
  • GPS的Heading, Course, and Crab Angle不同与区别

    看了老外这个文章终于知道GPS的Heading Course and Crab Angle不同与区别 xff0c 好东西分享给大家 http www chrobotics com library heading course and cra
  • 交换机对数据帧的转发和过滤

    大家好呀 xff0c 我是请假君 xff0c 今天又来和大家一起学习数通了 xff0c 今天要分享的知识是交换机对数据帧的转发和过滤 一 单播帧的转发 xff1a 交换机根据MAC地址表项进行数据帧转发 上图中 xff0c PCA发出数据帧

随机推荐