一、代码原理
题目来自CUMT自主移动机器人,直线检测大作业
在自主移动机器人中,直线的提取多用于检测障碍物和检测两侧的道路,在图像处理中,直线的提取一般采用hough变换的方式进行提取,算法比较成熟,效果也较为理想,这里介绍另一种比较常用的直线提取算法,合并分裂算法。
分裂——合并算法是比较流行的直线提取的算法。该算法源于计算机视觉,并已经在许多的著作中被研究使用。顾名思义,合并分裂算法就是对一串点集中,在一条直线上的点进行合并,而不在一条直线上的两个点集进行分裂。
具体实现的流程如下:
在输入点集之后,对整个点集进行拟合,并计算各个点到点集的距离后进行排序,选择距离拟合曲线最远的点,作为点集的分裂点,将点集分离开,再对分离后的两个点集分别进行拟合,重复以上操作,最后得到拟合的直线。
分裂合并算法中,点集的变化示例图如下:
项目通过MATLAB实现,实验的数据通过激光雷达采集得到。
二、数据采集
根据作业的要求,我们采用极其有限的部分工具搭建了一个五边形,并使用激光雷达采集点云:
采集的软件使用SLAMTEC思岚科技官方提供的软件
激光雷达采集到的数据是一个没有后缀的文件,使用文本格式打开后可以发现,除文件头外,文件的数据包括了角度、距离以及数据的质量,即采集到的数据是以角坐标的形式存储的。
为了便于MATLAB直接读取,我们去掉了文件头以及数据的质量一列,并将数据保存为txt文件,
通过MATLAB,使用极坐标画图命令,将数据进行可视化后,
clear,clc;
data=dlmread('888.txt');
polarplot(data(:,1)/180*pi,data(:,2));
grid on;
三、MATLAB代码实现
(1)实现数据的读取,预处理和坐标轴的设定
clc,clear;
data=dlmread('888.txt');
[xdata,ydata]=pol2cart(data(:,1)/180*pi,data(:,2));
X=round(xdata+1000);
Y=round(ydata+1000);
plot(X,Y);
hold on;
plot(X(1,1),Y(1,1),'*');
T=[];
s=[X Y];
L={s};
axis([500 1500 0 1600])
(2)点集及其拟合数据的判断、存取
for i=1:length(L{1,:})
pause(0.02);
[m,n,t,k]=det_length(L{1,i});
a=500:0.1:1500;
b=t(1)*a+t(2);
plot(a,b);
if k==0
if ~isempty(m)&&length(m(:,1))~=1
L{end+1} = m;
end
if ~isempty(n)&&length(n(:,1))~=1
L{end+1} = n;
end
else
T(1:2,length(T)+1)=[t(1);t(2)];
end
if i==length(L)
break;
end
end
(3)自定义函数,实现点集的拟合,距离的计算和以及根据最远距离判断和分割点集
function [m,n,t,k]=det_length(s)
t = polyfit(s(:,1),s(:,2),1);
distance=abs(t(1)*s(:,1)- s(:,2)+t(2))/sqrt(t(1)^2+1);
[mdis,i]=max(distance);
if mdis>12
k=0;
m(:,1)=s(1:i-1,1);
m(:,2)=s(1:i-1,2);
n(:,1)=s(i+1:length(s(:,2)),1);
n(:,2)=s(i+1:length(s(:,2)),2);
plot(s(i,1),s(i,2),'o');
else
k=1;
m=[];
n=[];
end
到这里基本实现了绝大部分的内容,但是在代码实现的时候会大仙一个问题,即最后只检测到了两条直线,代码在运行的时候陷入了局部最优解中,每一次在迭代的时候,只排除了最边上的一个点,代码运行过程如下:
直线检测局部最优
其中直线为每一次迭代拟合的直线,圆圈为最远的点。
因此我们需要对现有的分割函数进行改进,即在检测到离拟合的直线最远的点为端点时,默认选择中点作为分割点,以此来保证能检测到所有的直线。
(4)改进后的分割函数
当检测到离拟合曲线最远距离的点是端点时,便连接点集的第一个点和最后一个点,形成一条直线
以该直线作为该点集拟合得到的直线,再进行判断和分割
function [m,n,t,k]=det_length(s)
t = polyfit(s(:,1),s(:,2),1);
distance=abs(t(1)*s(:,1)-s(:,2)+t(2))/sqrt(t(1)^2+1);
[mdis,i]=max(distance);
if mdis>12
k=0;
if i==1||i==length(distance)
t = polyfit([s(1,1);s(length(distance),1)],[s(1,2);s(length(distance),2)],1);
distance=abs(t(1)*s(:,1)-s(:,2)+t(2))/sqrt(t(1)^2+1);
[mdis,i]=max(distance);
end
m(:,1)=s(1:i-1,1);
m(:,2)=s(1:i-1,2);
n(:,1)=s(i+1:length(s(:,2)),1);
n(:,2)=s(i+1:length(s(:,2)),2);
plot(s(i,1),s(i,2),'o');
else
k=1;
m=[];
n=[];
end
改进后的分割效果:
直线检测
最后成功检测到所有的直线:
目前已经对算法进行了进一步的改进,能实现相似直线的合并以及端点的检测,后续看心情更新,完整的代码资源以及PPT后续会上传
完整资源以及代码:合并分裂算法MATLAB代码