目录
须知
寻边线
寻拐点
补线
最后
须知讲解代码使用的摄像头为总钻风摄像头,图像像素为188*120,图像进行了二值化,这里建议如果需要对光线有要求的同学使用灰度处理。没有使用过上位机,展示的图片都是直接拍摄2寸IPS的显示。
寻边线当我们采集到一帧图像并做完二值化后,我们就开始寻边线,我使用了简单方便的左右扫线,因为在某些情况顶部图像会是黑的,所以使用从图像靠近车头的一端扫线。此时我做了些许处理,如果第一次扫线就从中间开始扫,以后会从上一次计算的中线开始扫,这也需要每次扫完一行就得计算一下中线。为了更加符合赛道变化,下一行的扫描起始点是上一行的中线,当然某些特殊情况也会出问题,比如遇到斑马线,所以又必须对斑马线做处理。左右扫线简单方便,但是执行起来需要遍历的数据量比较多。
//-------------------------------------------------------------------------------------------------------------------// @brief 左右巡边线// @param BoundaryRight右丢线行数// @param BoundaryLeft左丢线行数 // @param// @param// @return void// @note采用左右扫线的方法,从图像最低端开始,每行扫完会计算一次中线,下一行扫描从上一行中线开始//-------------------------------------------------------------------------------------------------------------------static uint8 star = 93; //寻线初始点//需优化:有时其他赛道会干扰//如果当左边线寻到有边界则使以后边线都处于边界void Get_Line(void){//float k=0; //斜率//float b=0; //截距//uint8 left=0;//int kuan=0;//k=((float)xtwo - (float)xone)/((float)ytwo - (float)yone);//b=(float)yone - ((float)xone*k);BoundaryRight = 0;BoundaryLeft = 0;if(Middle_Black_Point[119] == 0 || Middle_Black_Point[119] == 187) //判断起始点是否在图像中间{star = 93;}else{star = Middle_Black_Point[119];}for(uint8 y=119;y>=0;y--){for(uint8 x=star;x Endline && y < 108)BoundaryRight++;break;}}for(uint8 x=star;x>=0;x--){if(Emo_imag[y][x]==EmoBlack){if(Emo_imag[y][x-1]==EmoBlack && Emo_imag[y][x-2]==EmoBlack){if(y < Endline)Left_Black_Point[y] = 0;elseLeft_Black_Point[y]=x;break;}}if(x==0){Left_Black_Point[y]=0;if(y > Endline && y < 108)BoundaryLeft++;break;}}if(Right_Black_Point[y]==187&&Left_Black_Point[y]==0)//两边都没找到线{Middle_Black_Point[y]=Middle_Black_Point[y+1];//star=Middle_Black_Point[y];star = 93;}else if(Right_Black_Point[y]==187&&Left_Black_Point[y]!=0)//左边找到线{Middle_Black_Point[y]=Left_Black_Point[y]+Straight[y];star=Middle_Black_Point[y];//if(CurvatureRight < -0.0045)//{//kuan = ((y-120)*(y-120)/(20)+93);//kuan = kuan > (187-Right_Black_Point[y]) ? (187-Right_Black_Point[y]) : kuan;//Middle_Black_Point[y]=Left_Black_Point[y]+kuan;//}}else if(Left_Black_Point[y]==0&&Right_Black_Point[y]!=187)//右边找到线{Middle_Black_Point[y]=Right_Black_Point[y]-Straight[y];star=Middle_Black_Point[y];//if(CurvatureRight < -0.0045)//{//kuan = ((y-120)*(y-120)/(20)+93);//kuan = kuan > Right_Black_Point[y] ? Right_Black_Point[y] : kuan;//Middle_Black_Point[y]=Right_Black_Point[y]-kuan;//}}else //两边都找到线{Middle_Black_Point[y]=(uint8)(((int)Right_Black_Point[y]+(int)Left_Black_Point[y])/2);star=Middle_Black_Point[y];}//Middle_Black_Point[y] = Middle_Black_Point[y] < 1 ? 1 : Middle_Black_Point[y];//Middle_Black_Point[y] = Middle_Black_Point[y] > 186 ? 186 : Middle_Black_Point[y];Middle_Black_Point[y] = Middle_Black_Point[y] < 1 ? 0 : Middle_Black_Point[y];Middle_Black_Point[y] = Middle_Black_Point[y] >186 ? 187 :Middle_Black_Point[y];if(y==0)break;}//star = Middle_Black_Point[119];} 寻拐点寻拐点呢我采用了类似于八邻域的原理,首先是下拐点,我们从底端向上寻找,因为拐点对应的一定是丢线的,所以向上一直寻找到丢线行,以丢线行再向下对一个个点进行判断。
以右下拐点为例,我们放大像素块,我们从红点像素块的左下开始以顺时针旋转,当扫描的点周边的八个像素块出现四个白色像素块时即满足拐点。但是需要注意,拐点是不会距离丢线行数太多的,为了防止拐点误判的出现,我们应限制以丢线行数进行扫描的次数。前边说过左右扫线会因为斑马线造成干扰,所以扫寻完下拐点后,上拐点选择从图像上端向下扫,同样是向下扫到丢线后向再向上寻点,当然,对于扫描点附近八个像素块的旋转就应该换个方向。
//-------------------------------------------------------------------------------------------------------------------// @brief 拐点寻找// @param findlcount拐点距离丢线的行数,用于判断大小圆环,和区分P字和圆环// @param// @param// @param// @return void// @note采用5邻域的原理寻找拐点,下拐点从图像低端往上扫,上拐点从图像上方向下扫,左右扫线会在斑马线出现问题,//-------------------------------------------------------------------------------------------------------------------void Identify(void){uint8 findr_x = 0;//右点uint8 findr_y = 0;uint8 examr_x = 0;uint8 examr_y = 0;uint8 findl_x = 0;//左点uint8 findl_y = 0;uint8 examl_x = 0;uint8 examl_y = 0;uint8 star = 0;uint8 end = 0;uint8 examcount = 0;//uint8 count;//uint8 examerror;//uint8 dircount;int directionrd[5][2] = {{-1,1}, {-1,0}, {-1,-1}, {0,1}, {1,1}}; //顺时针下方向数组先x再yint directionld[5][2] = {{1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}}; //逆时针下方向数组int directionru[5][2] = {{1,1}, {0,1}, {-1,1}, {-1,0}, {-1,-1}}; //逆时针上方向数组int directionlu[5][2] = {{-1,1}, {0,1}, {1,1}, {1,0}, {1,-1}}; //逆时针上方向数组//每次采集后都对拐点标志位清零if(Right_Up_Point_finish_flag == 1)Right_Up_Point_finish_flag = 0;if(Left_Up_Point_finish_flag == 1)Left_Up_Point_finish_flag = 0;if(Right_Down_Point_finish_flag == 1)Right_Down_Point_finish_flag = 0;if(Left_Down_Point_finish_flag == 1)Left_Down_Point_finish_flag = 0;for(uint8 y = 105 ; y >= 30 ; y--){if(Right_Down_Point_finish_flag == 0){if(y > Endline && Right_Black_Point[y-1]==187 && Right_Black_Point[y-2]==187 && Emo_imag[y][Right_Black_Point[y]-6] == EmoWhite&& y > Endline && Emo_imag[y-2][Right_Black_Point[y]] == EmoWhite && Emo_imag[y-5][Right_Black_Point[y]] == EmoWhite)//右下拐点{star=y;for(uint8 y=star;y 30){Cross_flag = 1;WindupL_flag = 0;Beepindex = 0;}else if(Left_Up_Point_finish_flag == 1 && Left_Down_Point_finish_flag == 1 &&Right_Up_Point_finish_flag == 0 && Right_Down_Point_finish_flag == 0&& Left_Down_Point[1] >= 75 && Left_Down_Point[1] 30&& (Left_Up_Point[0] - Left_Down_Point[0]) = 45&& Right_Black_Point[Left_Up_Point[1]] < Right_Black_Point[(Left_Up_Point[1]+Left_Down_Point[1])/2]&& Right_Black_Point[(Left_Up_Point[1]+Left_Down_Point[1])/2] < Right_Black_Point[Left_Down_Point[1]]&& Right_Black_Point[Left_Up_Point[1]] < Right_Black_Point[Emo_one_third(Left_Down_Point[1],Left_Up_Point[1])]&& Right_Black_Point[Emo_one_third(Left_Down_Point[1],Left_Up_Point[1])] < Right_Black_Point[Left_Down_Point[1]]&& Right_Black_Point[Left_Up_Point[1]] < Right_Black_Point[Emo_two_third(Left_Down_Point[1],Left_Up_Point[1])]&& Right_Black_Point[Emo_two_third(Left_Down_Point[1],Left_Up_Point[1])] < Right_Black_Point[Left_Down_Point[1]]&& Right_Black_Point[Left_Up_Point[1]] < Right_Black_Point[Left_Down_Point[1]]){SlalomLeft_flag = 1;Slalomcount = 1;WindupL_flag = 0;}else if(Left_Up_Point_finish_flag == 1 && Left_Down_Point_finish_flag == 1 &&Right_Up_Point_finish_flag == 0 && Right_Down_Point_finish_flag == 0&& Left_Down_Point[1] >= 75 && findlcount >= 8 && Left_Up_Point[0] > 30 && (Left_Down_Point[1] - Left_Up_Point[1]) >= 45&& (Left_Down_Point[1] - Left_Up_Point[1]) = 16){CircleBig = 1;}CircleLeft_flag = 1;Circlecount = 1;WindupL_flag = 0;}else if(Left_Up_Point_finish_flag == 1 && Left_Down_Point_finish_flag == 1 && Right_Up_Point_finish_flag == 0 && Right_Down_Point_finish_flag == 0 && Left_Down_Point[1] = 75 && Right_Down_Point[1]Left_Black_Point[Right_Down_Point[1]]&& Left_Black_Point[Right_Up_Point[1]] > Left_Black_Point[Emo_one_third(Right_Down_Point[1],Right_Up_Point[1])]&& Left_Black_Point[Emo_one_third(Right_Down_Point[1],Right_Up_Point[1])] > Left_Black_Point[Right_Down_Point[1]]&& Left_Black_Point[Right_Up_Point[1]] > Left_Black_Point[Emo_two_third(Right_Down_Point[1],Right_Up_Point[1])]&& Left_Black_Point[Emo_two_third(Right_Down_Point[1],Right_Up_Point[1])] > Left_Black_Point[Right_Down_Point[1]]&& Left_Black_Point[Right_Up_Point[1]] > Left_Black_Point[Right_Down_Point[1]]){SlalomRight_flag = 1;Slalomcount = 1;WindupR_flag = 0;}else if(Right_Up_Point_finish_flag == 1 && Right_Down_Point_finish_flag == 1 &&Left_Up_Point_finish_flag == 0 && Left_Down_Point_finish_flag == 0&& Right_Down_Point[1] >= 75 && findrcount >= 9 && Right_Up_Point[0] < 158 && Right_Down_Point[1] - Right_Up_Point[1] >= 45&& Left_Black_Point[Right_Up_Point[1]] > Left_Black_Point[(Right_Up_Point[1]+Right_Down_Point[1])/2]&& Left_Black_Point[(Right_Up_Point[1]+Right_Down_Point[1])/2] > Left_Black_Point[Right_Down_Point[1]]&& Left_Black_Point[Right_Up_Point[1]] > Left_Black_Point[Emo_one_third(Right_Down_Point[1],Right_Up_Point[1])]&& Left_Black_Point[Emo_one_third(Right_Down_Point[1],Right_Up_Point[1])] > Left_Black_Point[Right_Down_Point[1]]&& Left_Black_Point[Right_Up_Point[1]] > Left_Black_Point[Emo_two_third(Right_Down_Point[1],Right_Up_Point[1])]&& Left_Black_Point[Emo_two_third(Right_Down_Point[1],Right_Up_Point[1])] > Left_Black_Point[Right_Down_Point[1]]&& Left_Black_Point[Right_Up_Point[1]] > Left_Black_Point[Right_Down_Point[1]]){if(findrcount >= 16){CircleBig = 1;}CircleRight_flag = 1;Circlecount = 1;WindupR_flag = 0;}else if(Right_Up_Point_finish_flag == 1 && Right_Down_Point_finish_flag == 1 &&Left_Up_Point_finish_flag == 0 && Left_Down_Point_finish_flag == 0 && Right_Down_Point[1]