智能车摄像头开源—6 边线信息提取
目次一、媒介
二、直角拐点求取
1、根据生长方向找拐点
1. 左侧上转左拐点
2. 左侧右转上拐点
3. 右侧上转右拐点
4. 右侧左转上拐点
2、根据X坐标跳变寻找拐点
3、组合判断拐点
4、逆透视求角度找拐点
三、边界点求取
四、对位边界行
五、圆弧拐点求取
六、求每侧边线斜率及截距
七、拟合曲线计算方差
八、求取左右侧边线差值
九、TOF测距
十、总函数调用
十一、总结
一、媒介
本文主要讲授二维数组边线的信息提取,此中主要包罗:直角拐点、圆弧拐点、边界点个数等。
二、直角拐点求取
1、根据生长方向找拐点
岂论是常见的八邻域求边线,还是本人优化过的八向迷宫求取边线,都可以得到每个边线点的生长方向,由此我们可以判断直角拐点。
因摄像头为斜视,因此获取图像中的直角拐点并非严酷的直角拐点,而是锐角,由此我们根据生长方向判断拐点时,
此处基于本人的八向迷宫讲授拐点求取。八向迷宫的生长方向差别于八邻域的0~7,而是特别的数字用来代表(可以参考之前的文章:智能车摄像头开源—1.2 核心算法:自适应八向迷宫(下)):
-2, 1, 4
-3, 3
-4,-1, 2 直角拐点主要需求四类:上转左,上转右,左转上,右转上。这里的上、下、左、右为生长的大抵方向,比方上转左拐点,即边线大要上向上生长,经过上转左拐点后,转为大要上向左生长。此中,上转左和右转上只会在左侧边线出现,而上转右,左转上只会出现在右侧边线(二维边线)。当碰到十字路口时(正入十字,斜入另说),会碰到上述四个拐点,循环找四个拐点给标记位,即可初步判出十字元素。不可太过绝对。但同时不可太过简单,否则导致误判,在一定程度上,误判比漏判结果更为严峻。
读者可求取一张十字的图像,因摄像头斜视原因,会发现向上生长的边线为斜线,向左或向右生长的边线较趋近于平滑的直线
下基于八向迷宫的八个方向分别讲授四个拐点,每个拐点有三个判断严酷程度,每个拐点需根据前后几个点的生长方向加以判断。
1. 左侧上转左拐点
宽松判断:前方几个点的生长方向为 -2 或 1 或 4 ,后方几个点生长方向为 -2 或 -3 或 -4;
中等判断:前方几个点的生长方向为 1 或 4 ,后方几个点生长方向为 -3 或 -4;
严酷判断:前方几个点的生长方向为 1 或 4 ,后方几个点生长方向为 -3 ;
此中宽松判断会和圆弧拐点误判,中等判断常用,严酷判断要求过高,根据自身需求调用。
/**
* 函数功能: 寻找上转左拐点
* 特殊说明: 分为三个严格等级,根据每个点的生长方向判断
* 寻找方式为遍历二维数组边线的每个点
* 形参: uint8 Grade 选择判定严格等级,常用2,即中等等级
*
* 示例: Get_L_Up_Turn_Left_Point_1(2);
* 返回值: 无
*/
uint8 L_Up_Turn_Left_Point_1 = {0}; //存储上转左拐点的坐标
uint8 L_Up_Turn_Left_Point_Flag_1 = 0; //上转左拐点存在标志位,找到时置1
uint8 L_Up_Turn_Left_Point_Position_1 = 0;//记录位置,即是二维数组中的第几个点
float L_Up_Turn_Left_Point_Angle_1 = 0; //记录找到的拐点的角度(逆透视求取)
void Get_L_Up_Turn_Left_Point_1(uint8 Grade)
{
uint8 i = 0;
L_Up_Turn_Left_Point_Flag_1 = 0;
L_Up_Turn_Left_Point_1 = 0;
L_Up_Turn_Left_Point_1 = 0;
L_Up_Turn_Left_Point_Position_1 = 0;
L_Up_Turn_Left_Point_Angle_1 = 0; //将各个参数清零
switch(Grade)
{
case 1: //严格判断
{
for (i = 4; i < (L_Statics - 4); i++) //L_Statics 为左侧边线点个数
{
if((L_Line >= (Y_Border_Min + 2)) && (L_Line <= (Y_Border_Max - 2))) //L_Line为存储左侧边线的二维数组,拐点必须满足不在于左右两侧边界上
{
if ((L_Grow_Dir == 4 || L_Grow_Dir == 1) && (L_Grow_Dir == 4 || L_Grow_Dir == 1) &&
(L_Grow_Dir == -3) && (L_Grow_Dir == -3) && (L_Grow_Dir == -2 || L_Grow_Dir == -3)) //L_Grow_Dir存储每个边线点的生长方向
{
L_Up_Turn_Left_Point_1 = L_Line;
L_Up_Turn_Left_Point_1 = L_Line;
L_Up_Turn_Left_Point_Flag_1= 1;
break;
}
}
}
break;
}
case 2: //中等判断
{
for (i = 4; i < (L_Statics - 4); i++)
{
if((L_Line >= (Y_Border_Min + 2)) && (L_Line <= (Y_Border_Max - 2)))
{
if ((L_Grow_Dir == 4 || L_Grow_Dir == 1) && (L_Grow_Dir == 4 || L_Grow_Dir == 1) &&
(L_Grow_Dir == -3 || L_Grow_Dir == -4) && (L_Grow_Dir == -3 || L_Grow_Dir == -4) &&
(L_Grow_Dir == -2 || L_Grow_Dir == -3 || L_Grow_Dir == -4))
{
L_Up_Turn_Left_Point_1 = L_Line;
L_Up_Turn_Left_Point_1 = L_Line;
L_Up_Turn_Left_Point_Flag_1= 1;
break;
}
}
}
break;
}
case 3: //宽松判断
{
for (i = 4; i < (L_Statics - 4); i++)
{
if((L_Line >= (Y_Border_Min + 2)) && (L_Line <= (Y_Border_Max - 2)))
{
if ((L_Grow_Dir == 4 || L_Grow_Dir == 1 || L_Grow_Dir == -2) && (L_Grow_Dir == 4 || L_Grow_Dir == 1 || L_Grow_Dir == -2) &&
(L_Grow_Dir == -3 || L_Grow_Dir == -4 || L_Grow_Dir == -2) && (L_Grow_Dir == -3 || L_Grow_Dir == -4 || L_Grow_Dir == -2) &&
(L_Grow_Dir == -2 || L_Grow_Dir == -3 || L_Grow_Dir == -4))
{
L_Up_Turn_Left_Point_1 = L_Line;
L_Up_Turn_Left_Point_1 = L_Line;
L_Up_Turn_Left_Point_Flag_1= 1;
break;
}
}
}
break;
}
}
if(L_Up_Turn_Left_Point_Flag_1 == 1)
{
L_Up_Turn_Left_Point_Position_1 = i; //记录拐点的位置
L_Up_Turn_Left_Point_Angle_1 = Get_Turn_Point_Angle(L_Line, L_Line, L_Line, L_Line, L_Line, L_Line); //逆透视求取拐点角度,后续补充
}
} 2. 左侧右转上拐点
宽松判断:前方几个点的生长方向为 2 或 3 或 4 ,后方几个点生长方向为 4 或 1 或 -2;
中等判断:前方几个点的生长方向为 2或 3 ,后方几个点生长方向为 4 或 1;
严酷判断:前方几个点的生长方向为 3 ,后方几个点生长方向为 4 或 1 ;
与左侧上转左拐点同理,下面几个都不做过多讲述。
/**
* 函数功能: 寻找右转上拐点
* 特殊说明: 分为三个严格等级,根据每个点的生长方向判断
* 寻找方式为遍历二维数组边线的每个点
* 形参: uint8 Grade 选择判定严格等级,常用2,即中等等级
*
* 示例: Get_L_Right_Turn_Up_Point_1(2);
* 返回值: 无
*/
uint8 L_Right_Turn_Up_Point_1 = {0};
uint8 L_Right_Turn_Up_Point_Flag_1 = 0;
uint8 L_Right_Turn_Up_Point_Position_1 = 0;
float L_Right_Turn_Up_Point_Angle_1 = 0;
void Get_L_Right_Turn_Up_Point_1(uint8 Grade)
{
uint8 i = 0;
L_Right_Turn_Up_Point_Flag_1 = 0;
L_Right_Turn_Up_Point_1 = 0;
L_Right_Turn_Up_Point_1 = 0;
L_Right_Turn_Up_Point_Position_1 = 0;
L_Right_Turn_Up_Point_Angle_1 = 0;
switch(Grade)
{
case 1:
{
for (i = 5; i < (L_Statics - 4); i++)
{
if((L_Line >= (Y_Border_Min + 2)) && (L_Line <= (Y_Border_Max - 2)))
{
if ((L_Grow_Dir == 4 || L_Grow_Dir == 1) && (L_Grow_Dir == 4 || L_Grow_Dir == 1) && (L_Grow_Dir == 4 || L_Grow_Dir == 1) &&
(L_Grow_Dir == 3) && (L_Grow_Dir == 3) && (L_Grow_Dir == 4 || L_Grow_Dir == 1))
{
L_Right_Turn_Up_Point_1 = L_Line;
L_Right_Turn_Up_Point_1 = L_Line;
L_Right_Turn_Up_Point_Flag_1= 1;
break;
}
}
}
break;
}
case 2:
{
for (i = 5; i < (L_Statics - 4); i++)
{
if((L_Line >= (Y_Border_Min + 2)) && (L_Line <= (Y_Border_Max - 2)))
{
if ((L_Grow_Dir == 4 || L_Grow_Dir == 1) && (L_Grow_Dir == 4 || L_Grow_Dir == 1) && (L_Grow_Dir == 4 || L_Grow_Dir == 1) &&
(L_Grow_Dir == 2 || L_Grow_Dir == 3) && (L_Grow_Dir == 2 || L_Grow_Dir == 3) &&
(L_Grow_Dir == 4 || L_Grow_Dir == 1))
{
L_Right_Turn_Up_Point_1 = L_Line;
L_Right_Turn_Up_Point_1 = L_Line;
L_Right_Turn_Up_Point_Flag_1= 1;
break;
}
}
}
break;
}
case 3:
{
for (i = 5; i < (L_Statics - 4); i++)
{
if((L_Line >= (Y_Border_Min + 2)) && (L_Line <= (Y_Border_Max - 2)))
{
if ((L_Grow_Dir == 4 || L_Grow_Dir == 1 || L_Grow_Dir == -2) && (L_Grow_Dir == 4 || L_Grow_Dir == 1 || L_Grow_Dir == -2) &&
(L_Grow_Dir == 4 || L_Grow_Dir == 1 || L_Grow_Dir == -2) &&
(L_Grow_Dir == 2 || L_Grow_Dir == 3 || L_Grow_Dir == 4) && (L_Grow_Dir == 2 || L_Grow_Dir == 3 || L_Grow_Dir == 4) &&
(L_Grow_Dir == 4 || L_Grow_Dir == 1))
{
L_Right_Turn_Up_Point_1 = L_Line;
L_Right_Turn_Up_Point_1 = L_Line;
L_Right_Turn_Up_Point_Flag_1= 1;
break;
}
}
}
break;
}
}
if(L_Right_Turn_Up_Point_Flag_1 == 1)
{
L_Right_Turn_Up_Point_Position_1 = i;
L_Right_Turn_Up_Point_Angle_1 = Get_Turn_Point_Angle(L_Line, L_Line, L_Line, L_Line, L_Line, L_Line);
}
} 3. 右侧上转右拐点
宽松判断:前方几个点的生长方向为 -2 或 1或 4 ,后方几个点生长方向为 2或 3或 4;
中等判断:前方几个点的生长方向为 -2或 1,后方几个点生长方向为 2或 3;
严酷判断:前方几个点的生长方向为 -2或 1,后方几个点生长方向为 3 ;
/**
* 函数功能: 寻找上转右拐点
* 特殊说明: 分为三个严格等级,根据每个点的生长方向判断
* 寻找方式为遍历二维数组边线的每个点
* 形参: uint8 Grade 选择判定严格等级,常用2,即中等等级
*
* 示例: Get_R_Up_Turn_Right_Point_1(2);
* 返回值: 无
*/
uint8 R_Up_Turn_Right_Point_1 = {0};
uint8 R_Up_Turn_Right_Point_Flag_1 = 0;
uint8 R_Up_Turn_Right_Point_Position_1 = 0;
float R_Up_Turn_Right_Point_Angle_1 = 0;
void Get_R_Up_Turn_Right_Point_1(uint8 Grade)
{
uint8 i = 0;
R_Up_Turn_Right_Point_Flag_1 = 0;
R_Up_Turn_Right_Point_1 = 0;
R_Up_Turn_Right_Point_1 = 0;
R_Up_Turn_Right_Point_Position_1 = 0;
R_Up_Turn_Right_Point_Angle_1 = 0;
switch(Grade)
{
case 1:
{
for (i = 4; i < (R_Statics - 4); i++)
{
if((R_Line >= (Y_Border_Min + 2)) && (R_Line <= (Y_Border_Max - 2)))
{
if ((R_Grow_Dir == -2 || R_Grow_Dir == 1) && (R_Grow_Dir == -2 || R_Grow_Dir == 1) &&
(R_Grow_Dir == 3) && (R_Grow_Dir == 3) && (R_Grow_Dir == 2 || R_Grow_Dir == 3 || R_Grow_Dir == 4))
{
R_Up_Turn_Right_Point_1 = R_Line;
R_Up_Turn_Right_Point_1 = R_Line;
R_Up_Turn_Right_Point_Flag_1 = 1;
break;
}
}
}
break;
}
case 2:
{
for (i = 4; i < (R_Statics - 4); i++)
{
if((R_Line >= (Y_Border_Min + 2)) && (R_Line <= (Y_Border_Max - 2)))
{
if ((R_Grow_Dir == -2 || R_Grow_Dir == 1) && (R_Grow_Dir == -2 || R_Grow_Dir == 1) &&
(R_Grow_Dir == 2 || R_Grow_Dir == 3) && (R_Grow_Dir == 2 || R_Grow_Dir == 3) &&
(R_Grow_Dir == 2 || R_Grow_Dir == 3 || R_Grow_Dir == 4))
{
R_Up_Turn_Right_Point_1 = R_Line;
R_Up_Turn_Right_Point_1 = R_Line;
R_Up_Turn_Right_Point_Flag_1 = 1;
break;
}
}
}
break;
}
case 3:
{
for (i = 4; i < (R_Statics - 4); i++)
{
if((R_Line >= (Y_Border_Min + 2)) && (R_Line <= (Y_Border_Max - 2)))
{
if ((R_Grow_Dir == -2 || R_Grow_Dir == 1 || R_Grow_Dir == 4) && (R_Grow_Dir == -2 || R_Grow_Dir == 1 || R_Grow_Dir == 4) &&
(R_Grow_Dir == 2 || R_Grow_Dir == 3 || R_Grow_Dir == 4) && (R_Grow_Dir == 2 || R_Grow_Dir == 3 || R_Grow_Dir == 4) &&
(R_Grow_Dir == 2 || R_Grow_Dir == 3 || R_Grow_Dir == 4))
{
R_Up_Turn_Right_Point_1 = R_Line;
R_Up_Turn_Right_Point_1 = R_Line;
R_Up_Turn_Right_Point_Flag_1 = 1;
break;
}
}
}
break;
}
}
if(R_Up_Turn_Right_Point_Flag_1 == 1)
{
R_Up_Turn_Right_Point_Position_1 = i;
R_Up_Turn_Right_Point_Angle_1 = Get_Turn_Point_Angle(R_Line, R_Line, R_Line, R_Line, R_Line, R_Line);
}
} 4. 右侧左转上拐点
宽松判断:前方几个点的生长方向为 -2 或 1或 4 ,后方几个点生长方向为 2或 3或 4;
中等判断:前方几个点的生长方向为 -2或 1,后方几个点生长方向为 2或 3;
严酷判断:前方几个点的生长方向为 -2或 1,后方几个点生长方向为 3 ;
/**
* 函数功能: 寻找左转上拐点
* 特殊说明: 分为三个严格等级,根据每个点的生长方向判断
* 寻找方式为遍历二维数组边线的每个点
* 形参: uint8 Grade 选择判定严格等级,常用2,即中等等级
*
* 示例: Get_R_Left_Turn_Up_Point_1(2);
* 返回值: 无
*/
uint8 R_Left_Turn_Up_Point_1 = {0};
uint8 R_Left_Turn_Up_Point_Flag_1 = 0;
uint8 R_Left_Turn_Up_Point_Position_1 = 0;
float R_Left_Turn_Up_Point_Angle_1 = 0;
void Get_R_Left_Turn_Up_Point_1(uint8 Grade)
{
uint8 i =0;
R_Left_Turn_Up_Point_Flag_1 = 0;
R_Left_Turn_Up_Point_1 = 0;
R_Left_Turn_Up_Point_1 = 0;
R_Left_Turn_Up_Point_Position_1 = 0;
R_Left_Turn_Up_Point_Angle_1 = 0;
switch(Grade)
{
case 1:
{
for (i = 5; i < (R_Statics - 4); i++)
{
if((R_Line >= (Y_Border_Min + 2)) && (R_Line <= (Y_Border_Max - 2)))
{
if ((R_Grow_Dir == -2 || R_Grow_Dir == 1) && (R_Grow_Dir == -2 || R_Grow_Dir == 1) && (R_Grow_Dir == -2 || R_Grow_Dir == 1) &&
(R_Grow_Dir == -3) && (R_Grow_Dir == -3) && (R_Grow_Dir == -2 || R_Grow_Dir == 1))
{
R_Left_Turn_Up_Point_1 = R_Line;
R_Left_Turn_Up_Point_1 = R_Line;
R_Left_Turn_Up_Point_Flag_1 = 1;
break;
}
}
}
break;
}
case 2:
{
for (i = 5; i < (R_Statics - 4); i++)
{
if((R_Line >= (Y_Border_Min + 2)) && (R_Line <= (Y_Border_Max - 2)))
{
if ((R_Grow_Dir == -2 || R_Grow_Dir == 1) && (R_Grow_Dir == -2 || R_Grow_Dir == 1) && (R_Grow_Dir == -2 || R_Grow_Dir == 1) &&
(R_Grow_Dir == -3 || R_Grow_Dir == -4) && (R_Grow_Dir == -3 || R_Grow_Dir == -4) && (R_Grow_Dir == -2 || R_Grow_Dir == 1))
{
R_Left_Turn_Up_Point_1 = R_Line;
R_Left_Turn_Up_Point_1 = R_Line;
R_Left_Turn_Up_Point_Flag_1 = 1;;
break;
}
}
}
break;
}
case 3:
{
for (i = 5; i < (R_Statics - 4); i++)
{
if((R_Line >= (Y_Border_Min + 2)) && (R_Line <= (Y_Border_Max - 2)))
{
if ((R_Grow_Dir == -2 || R_Grow_Dir == 1 || R_Grow_Dir == 4) && (R_Grow_Dir == -2 || R_Grow_Dir == 1 || R_Grow_Dir == 4) &&
(R_Grow_Dir == -2 || R_Grow_Dir == 1 || R_Grow_Dir == 4) &&
(R_Grow_Dir == -3 || R_Grow_Dir == -4) && (R_Grow_Dir == -3 || R_Grow_Dir == -4) && (R_Grow_Dir == -2 || R_Grow_Dir == 1))
{
R_Left_Turn_Up_Point_1 = R_Line;
R_Left_Turn_Up_Point_1 = R_Line;
R_Left_Turn_Up_Point_Flag_1 = 1;;
break;
}
}
}
break;
}
}
if(R_Left_Turn_Up_Point_Flag_1 == 1)
{
R_Left_Turn_Up_Point_Position_1 = i;
R_Left_Turn_Up_Point_Angle_1 = Get_Turn_Point_Angle(R_Line, R_Line, R_Line, R_Line, R_Line, R_Line);
}
}
2、根据X坐标跳变寻找拐点
X坐标跳变寻拐点,使用的是一维边线数组。对于一维边线数组来说,每行左右边线只会存储一个点,即每行只会有两个边线点,差别于二维数组每行图像可以记载多个点。当碰到十字路口时对比尤为明显。
比方在十字路口时,正常的二维数组应是如下环境:
https://i-blog.csdnimg.cn/direct/e4707bee83044d36b60f517a073343c6.jpeg
但一维数组存储的边线,应是如下环境:(画成蓝线更明显)
https://i-blog.csdnimg.cn/direct/dacf3f47f16240129f0be7527c7ebf0f.jpeg
可以对比上下两张图像,一维边线失去了横向的多个点,即每行每个边线只存储一个点,由此在四个拐点处,会出现当前拐点的X值与下一个点的(或前一个点的)X值出现较大变革,通过遍历每侧的一维边线每个点,可以找到四个拐点。同时需判断其与其前方(或后方)的几个点连续。下面上代码:
此中 L_Border 和 R_Border 是存储一维边线的数组。
/**
* 函数功能: 寻找上转左拐点
* 特殊说明: 通过检测X值的跳变判定拐点的存在
* 寻找方式为遍历一维数组边线的每个点
* 形参: uint8 Region_Start 起始行
* uint8 Region_End 截止行
*
* 示例: Get_L_Up_Turn_Left_Point_2(2, 57);
* 返回值: 无
*/
uint8 L_Up_Turn_Left_Point_2 = {0};
uint8 L_Up_Turn_Left_Point_Flag_2 = 0;
uint8 L_Up_Turn_Left_Point_Position_2 = 0;
void Get_L_Up_Turn_Left_Point_2(uint8 Region_Start, uint8 Region_End)
{
uint8 i = 0;
L_Up_Turn_Left_Point_2 = 0;
L_Up_Turn_Left_Point_2 = 0;
L_Up_Turn_Left_Point_Position_2 = 0;
if(Region_Start <= Region_End)
{
for(i = Region_Start; i <= Region_End; i++)
{
if(L_Border - L_Border >= 6)
{
if(L_Border - L_Border >= 6)
{
if(L_Border == 2 && L_Border == 2 && L_Border != 2)
{
L_Up_Turn_Left_Point_2 = L_Border;
L_Up_Turn_Left_Point_2 = i;
L_Up_Turn_Left_Point_Flag_2 = 1;
L_Up_Turn_Left_Point_Position_2 = i;
}
}
}
}
}
}
/**
* 函数功能: 寻找右转上拐点
* 特殊说明: 通过检测X值的跳变判定拐点的存在
* 寻找方式为遍历一维数组边线的每个点
* 形参: uint8 Region_Start 起始行
* uint8 Region_End 截止行
*
* 示例: Get_L_Right_Turn_Up_Point_2(2, 57);
* 返回值: 无
*/
uint8 L_Right_Turn_Up_Point_2 = {0};
uint8 L_Right_Turn_Up_Point_Flag_2 = 0;
uint8 L_Right_Turn_Up_Point_Position_2 = 0;
void Get_L_Right_Turn_Up_Point_2(uint8 Region_Start, uint8 Region_End)
{
uint8 i = 0;
L_Right_Turn_Up_Point_2 = 0;
L_Right_Turn_Up_Point_2 = 0;
L_Right_Turn_Up_Point_Position_2 = 0;
if(Region_Start <= Region_End)
{
for(i = Region_Start; i <= Region_End; i++)
{
if(L_Border - L_Border >= 6)
{
if(L_Border - L_Border >= 6)
{
if(L_Border == 2 && L_Border == 2 && L_Border != 2)
{
L_Right_Turn_Up_Point_2 = L_Border;
L_Right_Turn_Up_Point_2 = i;
L_Right_Turn_Up_Point_Flag_2 = 1;
L_Right_Turn_Up_Point_Position_2 = i;
}
}
}
}
}
}
/**
* 函数功能: 寻找上转右拐点
* 特殊说明: 通过检测X值的跳变判定拐点的存在
* 寻找方式为遍历一维数组边线的每个点
* 形参: uint8 Region_Start 起始行
* uint8 Region_End 截止行
*
* 示例: Get_R_Up_Turn_Right_Point_2(2, 57);
* 返回值: 无
*/
uint8 R_Up_Turn_Right_Point_2 = {0};
uint8 R_Up_Turn_Right_Point_Flag_2 = 0;
uint8 R_Up_Turn_Right_Point_Position_2 = 0;
void Get_R_Up_Turn_Right_Point_2(uint8 Region_Start, uint8 Region_End)
{
uint8 i = 0;
R_Up_Turn_Right_Point_2 = 0;
R_Up_Turn_Right_Point_2 = 0;
R_Up_Turn_Right_Point_Position_2 = 0;
if(Region_Start <= Region_End)
{
for(i = Region_Start; i <= Region_End; i++)
{
if((int8)R_Border - (int8)R_Border <= -6)
{
if((int8)R_Border - (int8)R_Border <= -6)
{
if(R_Border == 77 && R_Border == 77 && R_Border != 77)
{
R_Up_Turn_Right_Point_2 = R_Border;
R_Up_Turn_Right_Point_2 = i;
R_Up_Turn_Right_Point_Flag_2 = 1;
R_Up_Turn_Right_Point_Position_2= i;
}
}
}
}
}
}
/**
* 函数功能: 寻找左转上拐点
* 特殊说明: 通过检测X值的跳变判定拐点的存在
* 寻找方式为遍历一维数组边线的每个点
* 形参: uint8 Region_Start 起始行
* uint8 Region_End 截止行
*
* 示例: Get_R_Left_Turn_Up_Point_2(2, 57);
* 返回值: 无
*/
uint8 R_Left_Turn_Up_Point_2 = {0};
uint8 R_Left_Turn_Up_Point_Flag_2 = 0;
uint8 R_Left_Turn_Up_Point_Position_2 = 0;
void Get_R_Left_Turn_Up_Point_2(uint8 Region_Start, uint8 Region_End)
{
uint8 i = 0;
if(Region_Start <= Region_End)
{
for(i = Region_Start; i <= Region_End; i++)
{
if((int8)R_Border - (int8)R_Border <= -6)
{
if((int8)R_Border - (int8)R_Border <= -6)
{
if(R_Border == 77 && R_Border == 77 && R_Border != 77)
{
R_Left_Turn_Up_Point_2 = R_Border;
R_Left_Turn_Up_Point_2 = i;
R_Left_Turn_Up_Point_Flag_2 = 1;
R_Left_Turn_Up_Point_Position_2 = i;
}
}
}
}
}
} 3、组合判断拐点
可先用生长方向找拐点,再用X坐标跳变强化判断拐点,代码如下:
//组合寻拐点
uint8 L_Up_Turn_Left_Point = {0};
uint8 L_Up_Turn_Left_Point_Flag = 0;
void Get_L_Up_Turn_Left_Point(void)
{
L_Up_Turn_Left_Point_Flag = 0;
Get_L_Up_Turn_Left_Point_1(2);
if(L_Up_Turn_Left_Point_Flag_1 == 1)
{
L_Up_Turn_Left_Point_Flag_1 = 0;
Get_L_Up_Turn_Left_Point_2(L_Up_Turn_Left_Point_1 - 7, L_Up_Turn_Left_Point_1 + 7);
{
if(L_Up_Turn_Left_Point_Flag_2 == 1)
{
L_Up_Turn_Left_Point_Flag_2 = 0;
L_Up_Turn_Left_Point_Flag = 1;
L_Up_Turn_Left_Point = L_Up_Turn_Left_Point_1;
L_Up_Turn_Left_Point = L_Up_Turn_Left_Point_1;
}
}
}
}
uint8 L_Right_Turn_Up_Point = {0};
uint8 L_Right_Turn_Up_Point_Flag = 0;
void Get_L_Right_Turn_Up_Point(void)
{
L_Right_Turn_Up_Point_Flag = 0;
Get_L_Right_Turn_Up_Point_1(2);
if(L_Right_Turn_Up_Point_Flag_1 == 1)
{
L_Right_Turn_Up_Point_Flag_1 = 0;
Get_L_Right_Turn_Up_Point_2(L_Right_Turn_Up_Point_1 - 7, L_Right_Turn_Up_Point_1 + 7);
{
if(L_Right_Turn_Up_Point_Flag_2 == 1)
{
L_Right_Turn_Up_Point_Flag_2 = 0;
L_Right_Turn_Up_Point_Flag = 1;
L_Right_Turn_Up_Point = L_Right_Turn_Up_Point_1;
L_Right_Turn_Up_Point = L_Right_Turn_Up_Point_1;
}
}
}
}
uint8 R_Up_Turn_Right_Point = {0};
uint8 R_Up_Turn_Right_Point_Flag = 0;
void Get_R_Up_Turn_Right_Point(void)
{
R_Up_Turn_Right_Point_Flag = 0;
Get_R_Up_Turn_Right_Point_1(2);
if(R_Up_Turn_Right_Point_Flag_1 == 1)
{
R_Up_Turn_Right_Point_Flag_1 = 0;
Get_R_Up_Turn_Right_Point_2(R_Up_Turn_Right_Point_1 - 7, R_Up_Turn_Right_Point_1 + 7);
{
if(R_Up_Turn_Right_Point_Flag_2 == 1)
{
R_Up_Turn_Right_Point_Flag_2 = 0;
R_Up_Turn_Right_Point_Flag = 1;
R_Up_Turn_Right_Point = R_Up_Turn_Right_Point_1;
R_Up_Turn_Right_Point = R_Up_Turn_Right_Point_1;
}
}
}
}
uint8 R_Left_Turn_Up_Point = {0};
uint8 R_Left_Turn_Up_Point_Flag = 0;
void Get_R_Left_Turn_Up_Point(void)
{
R_Left_Turn_Up_Point_Flag = 0;
Get_R_Left_Turn_Up_Point_1(2);
if(R_Left_Turn_Up_Point_Flag_1 == 1)
{
R_Left_Turn_Up_Point_Flag_1 = 0;
Get_R_Left_Turn_Up_Point_2(R_Left_Turn_Up_Point_1 - 7, R_Left_Turn_Up_Point_1 + 7);
{
if(R_Left_Turn_Up_Point_Flag_2 == 1)
{
R_Left_Turn_Up_Point_Flag_2 = 0;
R_Left_Turn_Up_Point_Flag = 1;
R_Left_Turn_Up_Point = R_Left_Turn_Up_Point_1;
R_Left_Turn_Up_Point = R_Left_Turn_Up_Point_1;
}
}
}
}
4、逆透视求角度找拐点
使用此方法需先求得逆透视变动数组,再使用逆透视求取角度。逆透视实现方法可以参考之前的文章:智能车摄像头开源—5 逆透视处置惩罚
//-------------------------------------------------------------------------------------------------------------------
//@brief 逆透视知三点求形成的角度
//@param Ax,Ay 下边点
//@param Bx,By 要求角度的一点
//@param Cx,Cy 上边点
//@return
//@since v1.0
//Sample usage:
//注:使用时传入原图像的三个点,不可传入逆透视图像的三个点
//-------------------------------------------------------------------------------------------------------------------
float Get_Turn_Point_Angle(uint8 Ax, uint8 Ay, uint8 Bx, uint8 By, uint8 Cx, uint8 Cy)
{
float BA = 0.00;//向量BA的模
float BC = 0.00;
float SBA_BC = 0.00;//向量点乘的值
float Angle = 0.00;
uint8 AX = Back_Inverse_Matrix_Row;
uint8 AY = Back_Inverse_Matrix_Col;
uint8 BX = Back_Inverse_Matrix_Row;
uint8 BY = Back_Inverse_Matrix_Col;
uint8 CX = Back_Inverse_Matrix_Row;
uint8 CY = Back_Inverse_Matrix_Col;
BA = sqrt((float)((AX-BX)*(AX-BX)+(AY-BY)*(AY-BY)));
BC = sqrt((float)((CX-BX)*(CX-BX)+(CY-BY)*(CY-BY)));
SBA_BC = (float)((AX-BX)*(CX-BX)+(AY-BY)*(CY-BY));
Angle =acos(SBA_BC * 1.00/ (BA * BC));
return Angle * 57.3f;
}
//逆透视后的图像拐点求角度,传入已经逆透视后的三个点
float I_Get_Turn_Point_Angle(uint8 Ax, uint8 Ay, uint8 Bx, uint8 By, uint8 Cx, uint8 Cy)
{
float BA = 0.00;//向量BA的模
float BC = 0.00;
float SBA_BC = 0.00;//向量点乘的值
float Angle = 0.00;
BA = sqrt((float)((Ax-Bx)*(Ax-Bx)+(Ay-By)*(Ay-By)));
BC = sqrt((float)((Cx-Bx)*(Cx-Bx)+(Cy-By)*(Cy-By)));
SBA_BC = (float)((Ax-Bx)*(Cx-Bx)+(Ay-By)*(Cy-By));
Angle =acos(SBA_BC * 1.00/ (BA * BC));
return Angle * 57.3f;
} 寻找时对所有的二维边线点举行遍历寻找。至于三个点的输入顺序,已经知道中间点,别的两个点可以实验两次即可确定。经本人实验,此方法并不稳定,以是后续未使用,便不过多赘述,如有兴趣可自行研究。
三、边界点求取
由于使用爬线算法,提前在图像中补了黑框,以是在碰到十字、弯道等图像时,不可避免的会有部分边线落在黑框上,在此我称之为边界点,即位于左右两侧边界上的点。
找出左右两侧边界上的点的个数,可用于判断弯道(一侧有边界点,另一侧没有边界点)、判断十字(左右两侧都有边界点),当然还有环岛等,要根据详细环境去使用,以下给出代码:
/**
* 函数功能: 寻找左侧边线的边界点个数
* 特殊说明: 无
* 形参: 无
*
* 示例: Get_L_Border_Point_Num();
* 返回值: 无
*/
uint8 L_Border_Point_Num = 0;
uint8 L_UP_Border_Point_Num = 0; //记录位于顶部黑框上的边界点个数
void Get_L_Border_Point_Num(void)
{
uint8 i = 0;
L_Border_Point_Num = 0;
L_UP_Border_Point_Num = 0;
for(i = Y_Meet; i < L_Line; i++)
{
if(L_Border == 2) //左右两侧用一维边线即可,2是我的左侧边界
{
L_Border_Point_Num ++;
}
}
for(i = 40; i < L_Statics - 2; i++)
{
if(L_Line == 2) //顶部的边界点使用二维边线
{
L_UP_Border_Point_Num ++;
}
}
}
/**
* 函数功能: 寻找右侧边线的边界点个数
* 特殊说明: 无
* 形参: 无
*
* 示例: Get_R_Border_Point_Num();
* 返回值: 无
*/
uint8 R_Border_Point_Num = 0;
uint8 R_UP_Border_Point_Num = 0;
void Get_R_Border_Point_Num(void)
{
uint8 i = 0;
R_Border_Point_Num = 0;
R_UP_Border_Point_Num = 0;
for(i = Y_Meet; i < R_Line; i++)
{
if(R_Border == 77) //77是我的右侧边界
{
R_Border_Point_Num ++;
}
}
for(i = 40; i < R_Statics - 2; i++)
{
if(R_Line == 2)
{
R_UP_Border_Point_Num ++;
}
}
}
四、对位边界行
当图像的某一行,它的左侧边线点位于左侧边界上的同时,右侧边线点也位于右侧边界上,那我称之为对位边界行,可以用来辅助判断十字路口,正常环境下只有十字路口才会出现数个对位边界行。
/**
* 函数功能: 寻找对位边界行的个数
* 特殊说明: 无
* 形参: 无
*
* 示例: Get_Relative_Border_Point_Num();
* 返回值: 无
*/
uint8 Relative_Border_Point_Num = 0; //存储对位边界行的个数
void Get_Relative_Border_Point_Num(void)
{
uint8 i = 0;
Relative_Border_Point_Num = 0;//函数每张图像边线都调用一次,每调用一次都要清零
for(i = Y_Meet; i < L_Start_Point; i++)
{
if(L_Border == 2 && R_Border == 77) //2为左侧边界的X坐标,77为右侧边界
{
Relative_Border_Point_Num ++;
}
}
}
五、圆弧拐点求取
圆弧拐点常见于小S弯道以及环岛,可以用于元素判断。判断直角拐点的时候轻易与圆弧拐点误判,但有差别的需求场景,详细本身真正纯熟使用后就可理解。
圆弧拐点的判断原理为找一段连续点中最突出的那个点,比方左侧内凹的圆弧拐点,其前方的数个点X坐标一直在增大,其之后的数个点X坐标值一直在减小,同时使用生长方向举行辅助判断,生长方向使用的是本人八向迷宫算法中的方向,可查看之前文章去了解:
智能车摄像头开源—1.2核心算法:自适应八向迷宫(下)
/**
* 函数功能: 寻找左侧圆弧拐点
* 特殊说明: 无
* 形参: 无
*
* 示例: Get_L_Arc_Turn_Point();
* 返回值: 无
*/
uint8 L_Arc_Turn_Point = {{0}}; //最多找三个就足够使用
uint8 L_Arc_Turn_Point_Flag = 0; //找到一个就挂出标志位
uint8 L_Arc_Turn_Point_Num = 0; //记录找到的个数
void Get_L_Arc_Turn_Point(void)
{
uint8 i = 0;
L_Arc_Turn_Point_Flag = 0;
for(i = 0; i < 3; i++) //每次调用先清零
{
L_Arc_Turn_Point_Num = 0;
L_Arc_Turn_Point = 0;
L_Arc_Turn_Point = 0;
}
for(i =7; i < (L_Statics - 5); i++)
{
if((L_Line >= (Y_Border_Min + 2)) && (L_Line <= (Y_Border_Max - 7)))
{
if((L_Line > L_Line) && (L_Line > L_Line) && (L_Line >= L_Line) &&
(L_Line >= L_Line) && (L_Line > L_Line) && (L_Line > L_Line) &&
(L_Grow_Dir == -2 || L_Grow_Dir == 1) && (L_Grow_Dir == -2 || L_Grow_Dir == 1) &&
(L_Grow_Dir == 1 || L_Grow_Dir == 4) && (L_Grow_Dir == 1 || L_Grow_Dir == 4)) //左侧内凹
{
L_Arc_Turn_Point = L_Line;
L_Arc_Turn_Point = L_Line;
L_Arc_Turn_Point_Flag = 1;
L_Arc_Turn_Point_Num ++;
i += 15; //每个圆弧拐点前后几个点可能都满足圆弧拐点的条件,所以找到后加15个点
if(L_Arc_Turn_Point_Num == 3)
{
break;
}
}
}
}
for(i =7; i < (L_Statics - 5); i++)
{
if((L_Line >= (Y_Border_Min + 2)) && (L_Line <= (Y_Border_Max - 7)))
{
if((L_Line < L_Line) && (L_Line < L_Line) && (L_Line <= L_Line) &&
(L_Line <= L_Line) && (L_Line < L_Line) && (L_Line < L_Line) &&
(L_Grow_Dir == 4 || L_Grow_Dir == 1) && (L_Grow_Dir == 4 || L_Grow_Dir == 1) &&
(L_Grow_Dir == 1 || L_Grow_Dir == -2) && (L_Grow_Dir == 1 || L_Grow_Dir == -2)) //左侧外凸
{
L_Arc_Turn_Point = L_Line;
L_Arc_Turn_Point = L_Line;
L_Arc_Turn_Point_Flag = 1;
L_Arc_Turn_Point_Num ++;
i += 15;
if(L_Arc_Turn_Point_Num == 3)
{
break;
}
}
}
}
}
/**
* 函数功能: 寻找右侧圆弧拐点
* 特殊说明: 无
* 形参: 无
*
* 示例: Get_R_Arc_Turn_Point();
* 返回值: 无
*/
uint8 R_Arc_Turn_Point = {{0}};
uint8 R_Arc_Turn_Point_Flag = 0;
uint8 R_Arc_Turn_Point_Num = 0;
void Get_R_Arc_Turn_Point(void)
{
uint8 i = 0;
R_Arc_Turn_Point_Flag = 0;
for(i = 0; i < 3; i++)
{
R_Arc_Turn_Point_Num = 0;
R_Arc_Turn_Point = 0;
R_Arc_Turn_Point = 0;
}
for(i = 7; i < (R_Statics - 5); i++)
{
if((R_Line >= (Y_Border_Min + 2)) && (R_Line <= (Y_Border_Max - 2)))
{
if((R_Line < R_Line) && (R_Line < R_Line) && (R_Line <= R_Line) &&
(R_Line <= R_Line) && (R_Line < R_Line) && (R_Line < R_Line) &&
(R_Grow_Dir == 4 || R_Grow_Dir == 1) && (R_Grow_Dir == 4 || R_Grow_Dir == 1) &&
(R_Grow_Dir == 1 || R_Grow_Dir == -2) && (R_Grow_Dir == 1 || R_Grow_Dir == -2)) //右侧内凹
{
R_Arc_Turn_Point = R_Line;
R_Arc_Turn_Point = R_Line;
R_Arc_Turn_Point_Flag = 1;
R_Arc_Turn_Point_Num ++;
i += 15;
if(R_Arc_Turn_Point_Num == 3)
{
break;
}
}
}
}
for(i = 7; i < (R_Statics - 5); i++)
{
if((R_Line >= (Y_Border_Min + 2)) && (R_Line <= (Y_Border_Max - 2)))
{
if((R_Line > R_Line) && (R_Line > R_Line) && (R_Line >= R_Line) &&
(R_Line >= R_Line) && (R_Line > R_Line) && (R_Line > R_Line) &&
(R_Grow_Dir == -2 || R_Grow_Dir == 1) && (R_Grow_Dir == -2 || R_Grow_Dir == 1) &&
(R_Grow_Dir == 1 || R_Grow_Dir == 4) && (R_Grow_Dir == 1 || R_Grow_Dir == 4)) //右侧外凸
{
R_Arc_Turn_Point = R_Line;
R_Arc_Turn_Point = R_Line;
R_Arc_Turn_Point_Flag = 1;
R_Arc_Turn_Point_Num ++;
i += 15;
if(R_Arc_Turn_Point_Num == 3)
{
break;
}
}
}
}
}
六、求每侧边线斜率及截距
将每侧边线由中间点分割为两段(后称为一段和二段),分别计算一段、二段和整条边线的斜率以及截距(一维边线),然后将三者的斜率举行比对。当一段斜率近似等于二段斜率,一段斜率近似等于整段斜率,二段斜率近似等于整段斜率时,可判断为该侧边线为直线,用于直道的判断。
/**
* 函数功能: 最小二乘法计算斜率
* 特殊说明: 无
* 形参: uint8 begin 输入起点
* uint8 end 输入终点
* uint8 *border 输入需要计算斜率的一维边线数组
* 示例: Slope_Calculate(start, end, border);
* 返回值: Result 计算出的斜率
*/
float Slope_Calculate(uint8 begin, uint8 end, uint8 *border) //注:begin 必须小于 end,一般 begin 位于图像上方, end 位于图像下方
{
float X_Sum = 0, Y_Sum = 0, XY_Sum = 0, X2_Sum = 0;
int16 i = 0;
float Result = 0;
static float Result_Last;
for(i = begin; i < end ; i++)
{
X_Sum += (float)i;
Y_Sum += (float)border;
XY_Sum += (float)i * (border);
X2_Sum += (float)i * i;
}
if((end - begin) * X2_Sum - X_Sum * X_Sum) //防止为0的情况出现
{
Result = ((float)(end - begin) * XY_Sum - X_Sum * Y_Sum) / ((float)(end - begin) * X2_Sum - X_Sum * X_Sum);
Result_Last = Result;
}
else
{
Result = Result_Last;
}
return Result;
}
/**
* 函数功能: 计算斜率截距
* 特殊说明: 调用最小二乘法计算斜率
* 形参: uint8 start 输入起点
* uint8 end 输入终点
* uint8 *border 输入需要计算斜率的一维边线数组
* float *slope_rate 存储斜率的变量地址
* float *intercept 存储截距的变量地址
* 示例: Calculate_Slope_Intercept(start, end, L_Border, &L_Straightaway_Lope_Rate_C, &L_Intercept);
* 返回值: 无
*/
void Calculate_Slope_Intercept(uint8 start, uint8 end, uint8 *border, float *slope_rate, float *intercept)
{
uint16 i, Num = 0;
uint16 X_Sum = 0, Y_Sum = 0;
float Y_Average = 0, X_Average = 0;
for(i = start; i < end; i++)
{
X_Sum += i;
Y_Sum += border;
Num ++;
}
if(Num)
{
X_Average = (float)(X_Sum / Num);
Y_Average = (float)(Y_Sum / Num);
}
*slope_rate = Slope_Calculate(start, end, border);
*intercept = (float)(Y_Average - (*slope_rate) * X_Average);
}
float Max_Slope_Dif_Thre = 0.6; //实测得出,并未使用
float Min_Slope_Dif_Thre = 0.15; //实测得出,当两段线段的斜率低于这个阈值时,可认为斜率相等
//斜率和截距
float R_Straightaway_Lope_Rate_A = 0; //第一段的斜率
float R_Straightaway_Lope_Rate_B = 0; //第二段的斜率
float R_Straightaway_Lope_Rate_C = 0; //整段边线的斜率
float R_Intercept = 0; //整段边线的截距
uint8 R_Straight_Flag = 0; //右侧边线是否为直线标志位
/**
* 函数功能: 求右侧边线斜率截距,并判断是否为直线
* 特殊说明: 无
* 形参: uint8 start //右侧边线起始点Y坐标
* uint8 end //右侧边线终止点Y坐标
*
* 示例: Get_R_Intercept_And_Slope(R_Line + 5, R_Line);
* 返回值: 无
*/
void Get_R_Intercept_And_Slope(uint8 start, uint8 end)
{
R_Straightaway_Lope_Rate_A = 0;
R_Straightaway_Lope_Rate_B = 0;
R_Straightaway_Lope_Rate_C = 0;
R_Intercept = 0;
R_Straight_Flag = 0; //所有值先清零
R_Straightaway_Lope_Rate_A = Slope_Calculate(start, ((end - start) / 2) + start, R_Border);
R_Straightaway_Lope_Rate_B = Slope_Calculate(((end - start) / 2) + start, end, R_Border); //计算两段斜率
Calculate_Slope_Intercept(start, end, R_Border, &R_Straightaway_Lope_Rate_C, &R_Intercept); //计算整段边线斜率和截距
if(My_ABS_F(R_Straightaway_Lope_Rate_A - R_Straightaway_Lope_Rate_B) <= Min_Slope_Dif_Thre &&
My_ABS_F(R_Straightaway_Lope_Rate_B - R_Straightaway_Lope_Rate_C) <= Min_Slope_Dif_Thre &&
My_ABS_F(R_Straightaway_Lope_Rate_A - R_Straightaway_Lope_Rate_C) <= Min_Slope_Dif_Thre) //判断是否为直线
{
R_Straight_Flag = 1;
}
}
float L_Straightaway_Lope_Rate_A = 0;
float L_Straightaway_Lope_Rate_B = 0;
float L_Straightaway_Lope_Rate_C = 0;
float L_Intercept = 0;
uint8 L_Straight_Flag = 0;
/**
* 函数功能: 求左侧边线斜率截距,并判断是否为直线
* 特殊说明: 无
* 形参: uint8 start //左侧边线起始点Y坐标
* uint8 end //左侧边线终止点Y坐标
*
* 示例: Get_L_Intercept_And_Slope(L_Line + 5, L_Line);
* 返回值: 无
*/
void Get_L_Intercept_And_Slope(uint8 start, uint8 end)
{
L_Straightaway_Lope_Rate_A = 0;
L_Straightaway_Lope_Rate_B = 0;
L_Straightaway_Lope_Rate_C = 0;
L_Intercept = 0;
L_Straight_Flag = 0;
L_Straightaway_Lope_Rate_A = Slope_Calculate(start, ((end - start) / 2) + start, L_Border);
L_Straightaway_Lope_Rate_B = Slope_Calculate(((end - start) / 2) + start, end, L_Border);
Calculate_Slope_Intercept(start, end, L_Border, &L_Straightaway_Lope_Rate_C, &L_Intercept);
if(My_ABS_F(L_Straightaway_Lope_Rate_A - L_Straightaway_Lope_Rate_B) <= Min_Slope_Dif_Thre &&
My_ABS_F(L_Straightaway_Lope_Rate_B - L_Straightaway_Lope_Rate_C) <= Min_Slope_Dif_Thre &&
My_ABS_F(L_Straightaway_Lope_Rate_A - L_Straightaway_Lope_Rate_C) <= Min_Slope_Dif_Thre)
{
L_Straight_Flag = 1;
}
}
七、拟合曲线计算方差
当我们求出左右两侧一维边线的斜率和截距以后,可以用斜率和截距在图像中画出一条直线,然后将实际的赛道边线的每个点(一维边线)与拟合直线的对应行的点计算方差,整条边线的方差的大小反应了赛道边线的弯曲程度。
但实际验证此方法时,效果不甚理想,会碰到各种特别环境,同时以下代码存在些许bug,作者到最终都没修复,以是更多是在此处给出一个思路,想用的话可以实验修修下列代码的bug:
//拟合曲线
int16 L_Fitting_Line[] = {0};
int16 R_Fitting_Line[] = {0};
//方差
float L_Variance = 0;
float R_Variance = 0;
/**
* 函数功能: 计算左右两侧边线的方差
* 特殊说明: 每三行计算一次,减少计算量,但获取的信息不那么精确,可以选择计算每一行
* 形参: 无
*
* 示例: Get_Fitting_Line_And_Variance();
* 返回值: 无
*/
void Get_Fitting_Line_And_Variance(void)
{
uint8 i = 0;
for(i = 1; i <= 19; i ++)
{
L_Fitting_Line = (int16)(L_Straightaway_Lope_Rate_C * (float)(i * 3) + L_Intercept); //y = kx+b(把XY轴交换下就能想通了)
L_Fitting_Line = Limit_16(L_Fitting_Line, X_Border_Min + 2, X_Border_Max - 2);
R_Fitting_Line = (int16)(R_Straightaway_Lope_Rate_C * (float)(i * 3) + R_Intercept);
R_Fitting_Line = Limit_16(R_Fitting_Line, X_Border_Min + 2, X_Border_Max - 2);
}
Get_Variance(L_Line + 1, L_Line, L_Fitting_Line, L_Border, &L_Variance, 3);
Get_Variance(R_Line + 1, R_Line, R_Fitting_Line, R_Border, &R_Variance, 3);
}
八、求取左右侧边线差值
用每行右侧边线点的X坐标值减去对应行左侧边线点的X坐标值(一维边线),可以得到每行的差值(大概说每行赛道的宽度),由此可以用来判断十字等元素。比方十字:图像从下往上的边线差值先变小,后突然变大,后又变小,呈现出十字的趋势。
以下代码每隔两行计算一行,并没有计算全部行的差值,但是计算每一行完全可行。
int8 Row_Difference = {0};
uint8 Max_Row_Dif_Line_Num = 0; //当某一行左侧边线点位于左侧边界上,右侧边线点位于右侧边线上,与对位边界行同理,可以记录下来
/**
* 函数功能: 寻找对位边界行的个数
* 特殊说明: 无
* 形参: 无
*
* 示例: Get_Relative_Border_Point_Num();
* 返回值: 无
*/
void Get_Row_Difference(uint8 *l_border, uint8 *r_border)
{
uint8 i = 0;
Max_Row_Dif_Line_Num = 0;
for(i = 0; i < 20; i ++)
{
Row_Difference = (int8)(r_border - l_border);
if(Row_Difference == 75)
{
Max_Row_Dif_Line_Num ++;
}
}
}
九、TOF测距
部分组别使用TOF测距模块检测停滞、坡道等元素,那么就必要调用对应函数获取距离。逐飞官方手册中说明:测量间隔不可低于10ms。因此我们直接使用图像计数(收罗十张图像间隔绝对大于10ms,而且时间相对固定,可根据本身帧率恰当调整)
if(Image_Num % 10 == 0) //其中Image_Num 需在复制图像时加一,即没采集一张图像加一一次
{
Barrier_Distance = TOF_Get_Distance_mm();
}
十、总函数调用
将上述所有处置惩罚举行整合,放至一个函数内,每张图像调用一次函数便可获取所有信息。对于部分元素内部环境(比方环岛、十字等特别元素会被对应的循环接管),只需调用对应的函数获取所需的信息即可。顺序可随意
/**
* 函数功能: 获取所有信息
* 特殊说明: 无
* 形参: 无
*
* 示例: Get_Information();
* 返回值: 无
*/
void Get_Information(void)
{
Get_L_Border_Point_Num();
Get_R_Border_Point_Num();
Get_Relative_Border_Point_Num();
Get_L_Up_Turn_Left_Point_1(2);
Get_L_Right_Turn_Up_Point_1(2);
Get_R_Up_Turn_Right_Point_1(2);
Get_R_Left_Turn_Up_Point_1(2);
Get_L_Arc_Turn_Point();
Get_R_Arc_Turn_Point();
Get_L_Intercept_And_Slope(L_Line + 5, L_Line);
Get_R_Intercept_And_Slope(R_Line + 5, R_Line);
Get_Fitting_Line_And_Variance();
Get_Row_Difference(L_Border, R_Border);
}
十一、总结
以上处置惩罚后就完成了对于边线信息的提取,加上之前的处置惩罚,我们可以获取一张图像赛道的以下信息:
1、左右两侧边线的二维数组
[*]每个点的X,Y坐标;
[*]每个点的生长方向;
[*]每个点的阈值;
2、每侧二维边线点的个数
3、左右两侧边线的一维数组
[*]每个点的X,Y(数组下标)坐标;
4、每侧一维边线点的个数
5、所有二维边线点的均值阈值
6、左右两侧爬线相遇点的X,Y坐标
7、左右两侧边界点的个数
8、顶部边界点的个数
9、对位边界行的个数
10、上转左、上转右、右转上、左转上四个拐点坐标及标记位
11、每侧最多三个圆弧拐点及标记位
12、每侧边线的斜率、截距以及是否为直线标记位
13、每侧边线的方差(弯曲程度)
14、左右两侧边线的差值
15、停滞物(坡道)距离
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]