目录

须知

寻边线

寻拐点

补线

最后


须知

讲解代码使用的摄像头为总钻风摄像头,图像像素为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<188;x++){if(Emo_imag[y][x]==EmoBlack)  //黑黑黑即判断为边线{if(Emo_imag[y][x+1]==EmoBlack && Emo_imag[y][x+2]==EmoBlack){if(y < Endline )Right_Black_Point[y] = 187;elseRight_Black_Point[y]=x;//left=star;break;}}if(x==186)                      //有点问题,返校复查{Right_Black_Point[y]=187;//left=star;if(y > 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<=(star+18);y++){if(Right_Black_Point[y]<184 && Emo_abs(Right_Black_Point[y+1]-Right_Black_Point[y])<3&& Emo_abs(Right_Black_Point[y+2]-Right_Black_Point[y])<4){findr_x=Right_Black_Point[y];findr_y=y;for(uint8 dircount = 0;dircount<5;dircount++){examr_x=findr_x+directionrd[dircount][0];examr_y=findr_y+directionrd[dircount][1];if(Emo_imag[examr_y][examr_x]==255){examcount++;}}if(examcount >= 4){examcount=0;Right_Down_Point[0]=findr_x;Right_Down_Point[1]=findr_y;
//                            if(Last_Right_Point[0] == 0)
//                            {
//                                Last_Right_Point[0] = Right_Down_Point[1];
//                            }
//                            if(Emo_Uint8_dec(Right_Down_Point[1],Last_Right_Point[0]) < -15)
//                            {
//                                Right_Down_Point_finish_flag = 0;
//                            }
//                            else
//                            {Right_Down_Point_finish_flag = 1;
//                                Last_Right_Point[0] = Right_Down_Point[1];
//                            }//                        findrcount = (int)y-(int)star;break;}else{Right_Down_Point_finish_flag = 0;examcount=0;}}if(y==100){Right_Down_Point_finish_flag=0;}}}}if(Left_Down_Point_finish_flag == 0){if(y > Endline && Left_Black_Point[y-1]==0 && Left_Black_Point[y-2]==0 && Emo_imag[y][Left_Black_Point[y]+6] == EmoWhite&& y > Endline && Emo_imag[y-2][Left_Black_Point[y]] == EmoWhite && Emo_imag[y-5][Left_Black_Point[y]] == EmoWhite)     //左下拐点{star=y;for(uint8 y=star;y<=(star+18);y++){if(Left_Black_Point[y]>4 && Emo_abs(Left_Black_Point[y+1]-Left_Black_Point[y])<3&& Emo_abs(Left_Black_Point[y+2]-Left_Black_Point[y])<4){findl_x=Left_Black_Point[y];findl_y=y;for(uint8 dircount = 0;dircount<5;dircount++){examl_x=findl_x+directionld[dircount][0];examl_y=findl_y+directionld[dircount][1];if(Emo_imag[examl_y][examl_x]==255){examcount++;}}if(examcount>=4 ){examcount=0;Left_Down_Point[0]=findl_x;Left_Down_Point[1]=findl_y;
//                            if(Last_Left_Point[0] == 0)
//                            {
//                                Last_Left_Point[0] = Left_Down_Point[1];
//                            }
//                            if(Emo_Uint8_dec(Left_Down_Point[1],Last_Left_Point[0]) < -15)
//                            {
//                                Left_Down_Point_finish_flag = 0;
//                            }
//                            else
//                            {Left_Down_Point_finish_flag = 1;
//                                Last_Left_Point[0] = Left_Down_Point[1];
//                            }//                        findlcount = y-star;break;}else{Left_Down_Point_finish_flag = 0;examcount=0;}}if(y==100){Left_Down_Point_finish_flag=0;}}}}
//        if(CrossLeft_Down_Point_finish_flag==1 && CrossRight_Down_Point_finish_flag==1)
//        {
//            break;
//        }}if(Left_Down_Point_finish_flag==1 && Right_Down_Point_finish_flag==1)end=Right_Down_Point[1];else if(Left_Down_Point_finish_flag==1)end=Left_Down_Point[1];else if(Right_Down_Point_finish_flag==1)end=Right_Down_Point[1];elseend=94;for(uint8 y=20;y<=end;y++){if(Right_Up_Point_finish_flag == 0){if(y > Endline && Right_Black_Point[y+1]==187 && Right_Black_Point[y+2]==187 && Right_Black_Point[y+3]==187)   //右上拐点{star=y;for(uint8 y=star;y>=(star-22);y--){if(Right_Black_Point[y]<180 && Emo_abs(Right_Black_Point[y-1]-Right_Black_Point[y])<4&& Emo_abs(Right_Black_Point[y-2]-Right_Black_Point[y])<4 && Emo_imag[y][Right_Black_Point[y]-6] == EmoWhite&& Emo_imag[y-1][Right_Black_Point[y-1]-5] == EmoWhite && Emo_imag[y-2][Right_Black_Point[y]-5] == EmoWhite&& Emo_imag[y-3][Right_Black_Point[y]-5] == EmoWhite && Right_Black_Point[y] > Middle_Black_Point[y] && Emo_imag[y+3][Right_Black_Point[y]] == EmoWhite&& Emo_imag[y+5][Right_Black_Point[y]] == EmoWhite && Emo_imag[y+7][Right_Black_Point[y]] == EmoWhite){findr_x=Right_Black_Point[y];findr_y=y;for(uint8 dircount = 0;dircount<5;dircount++){examr_x=findr_x+directionru[dircount][0];examr_y=findr_y+directionru[dircount][1];if(Emo_imag[examr_y][examr_x]==255){examcount++;}}if(examcount>=4 && findr_y >Endline){examcount=0;Right_Up_Point[0]=findr_x;Right_Up_Point[1]=findr_y;if(Last_Right_Point[1] == 0){Last_Right_Point[1] = Right_Up_Point[1];}if(Right_Up_Point[1] < 16){Right_Up_Point[1] = Last_Right_Point[1];Right_Up_Point_finish_flag = 0;}else{Right_Up_Point_finish_flag = 1;Last_Right_Point[1] = Right_Up_Point[1];}findrcount=(int)star-(int)findr_y;break;}else{Right_Up_Point_finish_flag = 0;examcount=0;}}if(y==16){Right_Up_Point_finish_flag=0;break;}if(y == 1){break;}}}}if(Left_Up_Point_finish_flag == 0){if(y > Endline && Left_Black_Point[y+1]==0 && Left_Black_Point[y+2]==0 && Left_Black_Point[y+3]==0)     //左上拐点{star=y;for(uint8 y=star;y>=(star-22);y--){if(Left_Black_Point[y]>8 && Emo_abs(Left_Black_Point[y-1]-Left_Black_Point[y])<4&& Emo_abs(Left_Black_Point[y-2]-Left_Black_Point[y])<4 && Emo_imag[y][Left_Black_Point[y]+6] == EmoWhite&& Emo_imag[y-1][Left_Black_Point[y-1]+5] == EmoWhite && Emo_imag[y-2][Left_Black_Point[y]+5] == EmoWhite&& Emo_imag[y-3][Left_Black_Point[y]+5] == EmoWhite && Left_Black_Point[y] < Middle_Black_Point[y] && Emo_imag[y+3][Left_Black_Point[y]] == EmoWhite&& Emo_imag[y+5][Left_Black_Point[y]] == EmoWhite && Emo_imag[y+7][Left_Black_Point[y]] == EmoWhite){findl_x=Left_Black_Point[y];findl_y=y;for(uint8 dircount = 0;dircount<5;dircount++){examl_x=findl_x+directionlu[dircount][0];examl_y=findl_y+directionlu[dircount][1];if(Emo_imag[examl_y][examl_x]==255){examcount++;}}if(examcount>=4 && findl_y > Endline){examcount=0;Left_Up_Point[0]=findl_x;Left_Up_Point[1]=findl_y;if(Last_Left_Point[1] == 0){Last_Left_Point[1] = Left_Up_Point[1];}if(Left_Up_Point[1] < 16){Left_Up_Point[1] = Last_Left_Point[1];Left_Up_Point_finish_flag = 0;}else{Left_Up_Point_finish_flag = 1;Last_Left_Point[1] = Left_Up_Point[1];}findlcount=(int)star-(int)y;break;}else{Left_Up_Point_finish_flag = 0;examcount=0;}}if(y==16){Left_Up_Point_finish_flag=0;break;}if(y == 1){break;}}}}}}

圆环的类似拐点距离丢线行比较远,于是我选择放开上拐点的距离限制,转而记录下扫描的拐点距离丢线的行数,正好发现可以用来区分17届新加的元素P字 。

当然因为如果距离圆环太远的话,圆环的特征是不太明显的,所以我会在识别到上下拐点时进入元素前摇的函数,在到达百分比区别P字和圆环前,将由此函数接管。

//元素前摇
//-------------------------------------------------------------------------------------------------------------------
//  @brief      元素前摇
//  @param
//  @param
//  @param
//  @param
//  @return     void
//  @note       在扫描到两个符合的上下两个拐点后,针对先形拐点进行判断是什么元素,判断包括圆环、十字、P字
//-------------------------------------------------------------------------------------------------------------------
void Windup(void)
{if(WindupL_flag == 1){//Beepindex = Beepon;Link_Left_One_Point[0] = Left_Down_Point[0];Link_Left_One_Point[1] = Left_Down_Point[1];Link_Left_Two_Point[0] = Left_Up_Point[0];Link_Left_Two_Point[1] = Left_Up_Point[1];PaddingR = 0;PaddingL = 1;if(Right_Up_Point_finish_flag == 1 && Right_Down_Point_finish_flag == 1 && Left_Down_Point[1] > 42 && findlcount < 6 && Left_Up_Point[0] > 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] <= 105 && findlcount < 4 && Left_Up_Point[0] > 30&& (Left_Up_Point[0] - Left_Down_Point[0]) <= 42 && (Left_Down_Point[1] - Left_Up_Point[1]) >= 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]) <= 78 && Emo_imag[Left_Up_Point[1]][Left_Up_Point[0] - 5] != EmoWhite&& 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]]){if(findlcount >= 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] <= 110&& findlcount <= 17){GarageL_Find();if(GarageL_Findfinish_flag == 1){GarageL_Findfinish_flag = 0;GarageL_flag = 1;WindupL_flag = 0;}}else if(Left_Down_Point_finish_flag == 0){WindupL_flag = 0;PaddingR = 0;PaddingL = 0;Beepindex = 0;}}else if(WindupR_flag == 1){//Beepindex = Beepon;Link_Right_One_Point[0] = Right_Down_Point[0];Link_Right_One_Point[1] = Right_Down_Point[1];Link_Right_Two_Point[0] = Right_Up_Point[0];Link_Right_Two_Point[1] = Right_Up_Point[1];PaddingR = 1;PaddingL = 0;if(Left_Up_Point_finish_flag == 1 && Left_Down_Point_finish_flag == 1 && Right_Down_Point[1] > 42 && findrcount < 6 && Right_Up_Point[0] < 158){Cross_flag = 1;WindupR_flag = 0;Beepindex = 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 && Right_Down_Point[1] <= 105 && findrcount < 4 && Right_Up_Point[0] < 158&& (Right_Down_Point[0] - Right_Up_Point[0]) <= 42 && Right_Down_Point[1] - Right_Up_Point[1] >= 45&& Right_Down_Point[1] - Right_Up_Point[1] <= 78 && Emo_imag[Right_Up_Point[1]][Right_Up_Point[0] + 5] != EmoWhite&& 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]]){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] <= 110 && findrcount <= 17){GarageR_Find();if(GarageR_Findfinish_flag == 1){GarageR_Findfinish_flag = 0;GarageR_flag = 1;WindupR_flag = 0;}}else if(Right_Down_Point_finish_flag == 0){WindupR_flag = 0;PaddingR = 0;PaddingL = 0;Beepindex = 0;}}
}

因为上下拐点之间是有丢线的,所以我们需要做补线处理。

补线

对于十字或者刚开始的P字和圆环,我们只要补直线就好。

/*******************补线**********************/
//先找到要补充的两条线,通过两点计算斜率得到两点组成的一次方程,得到剩余x的位置,将线换做左右边线
//-------------------------------------------------------------------------------------------------------------------
//  @brief      直线补线函数
//  @param      xone  第一个补线点x坐标
//  @param      yone  第一个补线点y坐标
//  @param      xtwo  第二个补线点x坐标
//  @param      ytwo  第二个补线点y坐标
//  @return     void
//  @note  Left_Black_Point[y]为我的左边线数组 Right_Black_Point[y]为我的右边线数组
//-------------------------------------------------------------------------------------------------------------------
void Padding_LineR(uint8 xone,uint8 yone,uint8 xtwo,uint8 ytwo)
{float k=0;  //斜率float b=0;  //截距//uint8 xstar=0;//uint8 xend=0;uint8 ystar=0;uint8 yend=0;k=((float)ytwo - (float)yone)/((float)xtwo - (float)xone);//k=((float)xtwo - (float)xone)/((float)ytwo - (float)yone);b=(float)yone - ((float)xone*k);if(yone>ytwo){ystar=ytwo;yend=yone;}else{ystar=yone;yend=ytwo;}for(uint8 y=ystar;y<=yend;y++){Right_Black_Point[y]=(uint8)(((float)y-b)/k);  //两点之间补线}
}

而对于入环和出环这类的控制,我们希望车子运行能够更加顺滑一些,于是我们选择补曲线。当然还需注意开始和结束补线的时机。

//补曲线,利用弯道进行补
//Cx曲线上点x,Cy曲线上点y
//-------------------------------------------------------------------------------------------------------------------
//  @brief      曲线补线函数
//  @param      Ux  上补线点x坐标
//  @param      Uy  上补线点y坐标
//  @param      Dx  下补线点x坐标
//  @param      Dx  下补线点y坐标
//  @return     void
//  @note       利用拉个朗日插值法,上下两个点由图像决定,中间点为固定点,如果补线效果不好,需要重新校准中间点
//  Left_Black_Point[y]为我的左边线数组 Right_Black_Point[y]为我的右边线数组
//-------------------------------------------------------------------------------------------------------------------
void Padding_CurveL(uint8 Ux,uint8 Uy,uint8 Dx,uint8 Dy)
{int x0 = 0,x1 = 0,x2 = 0;int y0 = 0,y1 = 0,y2 = 0;//0.0096   -2.1047  138.5182//0.0103   -2.2049  141.8985//x=x0(y-y1)(y-y2)/(y0-y1)(y0-y2)+x1(y-y0)(y-y2)/(y1-y0)(y1-y2)+x2(y-y0)(y-y1)/(y2-y0)(y2-y1)x0 = (int)Dx;x1 = (int)(Dx + (float)3*(Ux-Dx)/5);x2 = (int)Ux;y0 = (int)Dy;y1 = (int)(Dy - (float)2*(Dy-Uy)/3);y2 = (int)Uy;if(Ux == 0 && Ux == 0 && Dx == 0 && Dy == 0){}else{if(Dy > 110){Dy = 110;}//Re = Left_Black_Point[Dy];
//        Left_Black_Point[Dy] = (uint8)(a*(float)(Dy*Dy)+b*(float)Dy+c);//error = (int)Re - (int)Left_Black_Point[Dy];for(uint8 y = Uy ;y <= Dy;y++){
//            Re = y - Uy + 25;
//            Left_Black_Point[y]=(uint8)(a*(float)(Re*Re)+b*(float)Re+c);Left_Black_Point[y]=(uint8)((x0*(y-y1)*(y-y2))/((y0-y1)*(y0-y2)))+((x1*(y-y0)*(y-y2))/((y1-y0)*(y1-y2)))+((x2*(y-y0)*(y-y1))/((y2-y0)*(y2-y1)));}}}void Padding_CurveR(uint8 Ux,uint8 Uy,uint8 Dx,uint8 Dy)
{int x0 = 0,x1 = 0,x2 = 0;int y0 = 0,y1 = 0,y2 = 0;//0.0096   -2.1047  138.5182//0.0103   -2.2049  141.8985//x=x0(y-y1)(y-y2)/(y0-y1)(y0-y2)+x1(y-y0)(y-y2)/(y1-y0)(y1-y2)+x2(y-y0)(y-y1)/(y2-y0)(y2-y1)x0 = (int)Dx;x1 = (int)(Dx - (float)3*(Dx-Ux)/5);x2 = (int)Ux;y0 = (int)Dy;y1 = (int)(Dy - (float)2*(Dy-Uy)/3);y2 = (int)Uy;if(Ux == 0 && Ux == 0 && Dx == 0 && Dy == 0){}else{if(Dy > 110){Dy = 110;}for(uint8 y = Uy ;y <= Dy;y++){Right_Black_Point[y]=(uint8)((x0*(y-y1)*(y-y2))/((y0-y1)*(y0-y2)))+((x1*(y-y0)*(y-y2))/((y1-y0)*(y1-y2)))+((x2*(y-y0)*(y-y1))/((y2-y0)*(y2-y1)));}}}

补线的点的确定就需要同学自己去定了,也可以参考我的源码上边的点。为了不让元素的代码很乱,所以我选择在需要补线的地方选定补线的点,并打开补线标志,这样当独列出一个函数用来补线,就不会造成元素的代码里边充斥着补线的代码。不然更改补线方式后,需要对每个补线的程序进行更改。

//-------------------------------------------------------------------------------------------------------------------
//  @brief      补线函数
//  @param      PaddingR  右补线标志  0:不补线 1:要补线
//  @param      PaddingL  左补线标志
//  @param      Padding_CurveR  右曲线补线标志 0:补直线 1:补曲线
//  @param      Padding_CurveL  左曲线补线标志
//  @return     void
//  @note       补线的方式都通过此函数进行,外部只需要做到决定补线点的位置,和是否左右补线
//  Left_Black_Point[y]为我的左边线数组 Right_Black_Point[y]为我的右边线数组
//-------------------------------------------------------------------------------------------------------------------
void Padding_Line(void)
{if(PaddingL == 1 && PaddingR == 1 && Paddingcurve == 0){Padding_LineR(Link_Right_One_Point[0],Link_Right_One_Point[1],Link_Right_Two_Point[0],Link_Right_Two_Point[1]);Padding_LineL(Link_Left_One_Point[0],Link_Left_One_Point[1],Link_Left_Two_Point[0],Link_Left_Two_Point[1]);}else if(PaddingL == 1 && PaddingR == 0 && Paddingcurve == 0){Padding_LineL(Link_Left_One_Point[0],Link_Left_One_Point[1],Link_Left_Two_Point[0],Link_Left_Two_Point[1]);Padding_LineR(0,0,0,0);}else if(PaddingL == 0 && PaddingR == 1 && Paddingcurve == 0){Padding_LineR(Link_Right_One_Point[0],Link_Right_One_Point[1],Link_Right_Two_Point[0],Link_Right_Two_Point[1]);Padding_LineL(0,0,0,0);}else if(PaddingL == 0 && PaddingR == 1 && Paddingcurve == 1){Padding_CurveR(Link_Right_One_Point[0],Link_Right_One_Point[1],Link_Right_Two_Point[0],Link_Right_Two_Point[1]);Padding_CurveL(0,0,0,0);}else if(PaddingL == 1 && PaddingR == 0 && Paddingcurve == 1){Padding_CurveL(Link_Left_One_Point[0],Link_Left_One_Point[1],Link_Left_Two_Point[0],Link_Left_Two_Point[1]);Padding_CurveR(0,0,0,0);}else{Padding_LineL(0,0,0,0);Padding_LineR(0,0,0,0);}
}

最后

如果有对本文章有建议或者看法的话,可以评论或者私信我,希望可以和各位多多交流。本文章中的代码已经开源,开源地址:GitHub - StingeMoZGD/17th-SmartCar: 17届智能车竞赛,无线充电组

17届智能车图像处理部分讲解相关推荐

  1. 17届智能车:使用编码器计算实际路程

    系列文章目录 17届智能车:浅析总转风摄像头的图像处理_ssfight1的博客-CSDN博客_智能车摄像头图像处理 文章目录 目录 系列文章目录 文章目录 前言 一.编码器脉冲数获取 二.路程换算 1 ...

  2. 17届智能车-多车编队寻光测距

    1.多车规则 对于17届智能车的多车编队组来说,想要使得三车有序不相撞,可以通过超声波模块来完成速度闭环保持距离,或是开环控制速度使得三车匀速跑完全程. 超声波水太深,我比较菜,把持不住.受前摄像头四 ...

  3. 17届智能车-湖科逐梦四队硬件开源

    写在前面的话 时间很快,25号17届智能车湖南赛点就落下了帷幕,由于比赛时候驱动损坏导致无法完赛,特此开源硬件,和大家一同学习交流进步.(充电LCC大概3s,线上赛道26s左右完赛) 充电部分 LCC ...

  4. 17届智能车竞赛技术报告 | 乐师逐飞二队 - 基础四轮组

    学 校:乐山师范学院       队伍名称:乐师逐飞二队     参赛队员:唐冰奇 罗文祝 唐玉琴 带队教师:杨济豪 李富钢     简 介: 本文以第十七届全国大学生智能车竞赛为背景,以大赛组委会指 ...

  5. 17届智能车竞赛技术报告 | 北京科技大学无线充电组

    学 校:北京科技大学        队伍名称:北京科技大学无线充电组  参赛队员:杨凯宁. 屈琛涛.王冠尧 指导教师:赵鑫鑫.李 勇      #mermaid-svg-vSKeNucI52JD3EF ...

  6. 17届智能车竞赛技术报告-极速越野 | 南昌大学-闪电小飞侠

    学 校:南昌大学      队伍名称:闪电小飞侠    参赛队员:龚浩宇 .曹禹源 带队教师:黄玉水 .胡奕涛 #mermaid-svg-lWYOzh4FrFCtIYQJ {font-family:& ...

  7. 17届智能车竞赛-极速越野 | 山东大学(威海)-越野三队

    学校:山东大学(威海)     队伍名称:越野三队       参赛队员:郑睿.茅陈昕.余海波 带队教师:王小利刘萍萍     01 引  言   第十七届全国大学生智能车竞赛将于 2022年七至八月 ...

  8. 17届智能车-电磁组比赛心得一

    目录 前言 一.电磁车的电感排布方案 二.如何调整电感的ADC数值+滤波方法 三.电磁四轮车的一些基础控制算法 1.差比和算法+方向环 2.电机的速度环控制算法 小结 前言 2022年由于疫情的缘故, ...

  9. 17届智能车:浅析总转风摄像头的图像处理

    目录 前言 一.图像的采集 二.图像处理 1.赛道提取 1.二值化 2.利用两两像素点灰度值的相关性 3.归一化处理 2.寻找边线 1.从图像中心线开始,向左右分别寻找黑色像素点,找到黑色像素点并加以 ...

最新文章

  1. 港大徐爱民组研究助理招聘-内分泌代谢方向
  2. mamp设置mysql密码_MAMP中设置Mysql root密码
  3. Unity3d中使用自带动画系统制作下雨效果(一)
  4. python性能分析工具模块_python——关于Python Profilers性能分析器
  5. html视频鼠标移除不播放,html - 在Mouseover上播放Gif并在鼠标移除时暂停Gif而不替换图像? - 堆栈内存溢出...
  6. html2canvas截图地图和看到的不一样_认知高度不同的人,看到的世界是不一样的...
  7. (原创)我也玩万能表单(自定义表单)(一)
  8. 9.1 正则介绍_grep(上)
  9. 《球球大作战》源码解析——(1)运行起来
  10. apple pay扫银联二维码原理
  11. vscode背景的更换_vscode自定义背景颜色
  12. 形式逻辑(07)性质判断 和 推理
  13. Nature综述: 关键物种对于微生物菌群结构和功能的驱动作用
  14. python倒序排列方法梳理
  15. 线性回归-简单线性回归
  16. 评计算机中级职称上海积分,上海落户积分,这个中级职称(国家职业资格)性价比最高!...
  17. 男子因微信被封从腾讯公司坠亡 前快播CEO王欣说出一个值得深思的问题
  18. 照片美妆---基于Haar特征的Adaboost级联人脸检测分类器
  19. Postman RSA+Bate64签名
  20. vue3 watch onTrack onRrigger

热门文章

  1. 医学图像处理——入门篇(二)
  2. 笔记本更换西数1T硬盘慢
  3. shift后门制作和禁止
  4. java jcf查看_Java 集合系列之一:JCF集合框架概述
  5. 天池赛:工业蒸汽量预测
  6. python中求阶乘的代码_python如何求阶乘
  7. 竟然卖了几百万,故宫、豆瓣、果壳们如何将日历做成生意?
  8. 请善待老公,其实男人不容易!
  9. Web前端-jQuery
  10. 城建税计算机代码6,【城建税的计算方法与城建税税率】- 环球网校