目录

    • 系列文章
    • 更换扫线方式
    • 获取车的轮廓
    • 车屁股所在行数确定
    • 白色球台导致的问题
    • 5.21思考
      • 1、 关于会车地点确定
      • 如何判断会车状态

系列文章

智能车复工日记【1】——菜单索引回顾
智能车复工日记【2】——普通PID、变结构PID、微分先行PID、模糊PID、专家PID
智能车复工日记【3】:图像处理——基本扫线和基本特征提取和十字补线
智能车复工日记【4】:关于图像的上下位机的调整问题总结

智能车复工日记【5】:起跑线的识别与车库入库
智能车复工日记【6】:有bug的模糊PID记录

由于现在板子上没有UWB引脚,所以这里先假设一个flag;
当距离很近的时候,该flag置1,表示进入会车状态。

注意:当距离很近的时候,后车应检查图像,观察视野中是否出现车辆,如果没有出现,则将不进入会车状态,如下图所示

这种情况以后再处理,现在主要讨论正确进入会车状态的后车图像问题。

更换扫线方式

由于原先的扫线方式是继承性扫线,且是从中间往两边扫线,所以当道路中间有车辆的时候,扫出来的左右线必定是错误的。
如图所示:

如图所示
所以现在更换扫线方式:

选的扫线方式:
1、8邻域扫线法(zcc提供思路)2、根据下面几行的正确的左右边线,拟合出两条预测线,然后通过人为平移或调整斜率达到最后的预测线,然后沿着左预测线往左扫得到左线,沿着右预测线往右扫得到右线。
3、根据下面几行正确的左右边线,获取一个正确的左线点和右线点。然后预测上一行(从下往上获取扫线)的左线为当前左线值右边几个像素,上一行的右线为当前右线值左边几个像素。然后顺着预测的两个点,一个往左扫获取正确左线,一个往右扫获取正确右线。(dudo提供思路)
由于8邻域算法相比于其他的方法比较繁琐,第二种方法的鲁棒性不好,这里选择第三种扫线方法。

注意:扫线的时候注意获取车屁股的位置(车轮廓的最下面的一行),当车屁股小于5行时,我们将上一帧的正确的第0行左右线作为新的起点,来进行预测。
代码:
代码

 //会车变量
 byte huiche_flag = 0;
 //车的初步左右轮廓
 public byte[] car_left_contour = new byte[70];
 public byte[] car_right_contour = new byte[70];
 //车的准确的左右轮廓         为1:表示是正确的   为0:表示是错误的
 public byte[] car_left_contour_flag = new byte[70];
 public byte[] car_right_contour_flag = new byte[70];
 byte lowest_contour_hang = 0;
 //当车距离非常近的时候,所需要用到的变量:
 byte last_status_leftline = 185;
 byte last_status_rightline = 0;
 byte lock_car_ass = 0;      //为0的时候表示没有进入锁     1表示锁扫线方法
if (huiche_flag == 1)
{
    if (lowest_contour_hang > 5 && lock_car_ass==0)        //当车屁股所在的行数大于5时
    {
        SetText("会车重新扫线!!!");
        //前5行,沿着old往两边扫
        old = 93;
        for (j = 0; j < 5; j++)
        {
            for (i = (byte)old; i >= 0 && i < 184; i++)
            {
                fiv_width[j]++;
                if (Pixels[j, i + 1] == 0 && Pixels[j, i] == 0)
                {
                    leftfindflag[j] = 1;
                    lefetline[j] = (byte)(i + 1);
                    break;
                }
            }
            for (i = (byte)old; i <= 185 && i > 1; i--)
            {
                fiv_width[j]++;
                if (Pixels[j, i - 1] == 0 && Pixels[j, i] == 0)
                {
                    rightfindflag[j] = 1;
                    rightline[j] = (byte)(i - 1);
                    break;
                }
            }
            old = centerline[j];
        }
        //根据下一行的左线来决定此时左线扫的起始点
        //根据拟合出来的左线找真实左线
        for (j = 5; j < 60; j++)
        {
            byte new_left_fore = (byte)(lefetline[j - 1] - 6);
            for (i = new_left_fore; i >= 0 && i < 184; i++)
            {
                if (Pixels[j, i + 1] == 0 && Pixels[j, i] == 0)
                {
                    leftfindflag[j] = 1;
                    lefetline[j] = (byte)(i + 1);
                    break;
                }
            }
            byte new_right_fore = (byte)(rightline[j - 1] + 6);
            for (i = new_right_fore; i <= 185 && i > 1; i--)
            {
                if (Pixels[j, i - 1] == 0 && Pixels[j, i] == 0)
                {
                    rightfindflag[j] = 1;
                    rightline[j] = (byte)(i - 1);
                    break;
                }
            }
        }
        SetText("第0行中线" + centerline[0]);
        SetText("第0行左线" + lefetline[0]);
        SetText("第0行右线" + rightline[0]);
        last_status_leftline = lefetline[0];
        last_status_rightline = rightline[0];
    }
    else //车屁股低于5行,此时根据上一帧的最低的两个边界找两线
    {
        lock_car_ass = 1;
        //根据拟合出来的左线找真实左线
        for (j = 0; j < 50; j++)
        {
            byte new_left_fore = (byte)(last_status_leftline - 6);
            for (i = new_left_fore; i >= 0 && i < 184; i++)
            {
                if (Pixels[j, i + 1] == 0 && Pixels[j, i] == 0)
                {
                    leftfindflag[j] = 1;
                    lefetline[j] = (byte)(i + 1);
                    break;
                }
            }
            byte new_right_fore = (byte)(last_status_rightline + 6);
            for (i = new_right_fore; i <= 185 && i > 1; i--)
            {
                if (Pixels[j, i - 1] == 0 && Pixels[j, i] == 0)
                {
                    rightfindflag[j] = 1;
                    rightline[j] = (byte)(i - 1);
                    break;
                }
            }
            last_status_leftline = lefetline[j];
            last_status_rightline = rightline[j];//继承上一行
        }
        SetText("第0行中线" + centerline[0]);
        SetText("第0行左线" + lefetline[0]);
        SetText("第0行右线" + rightline[0]);
        last_status_leftline = lefetline[0];//继承上一帧
        last_status_rightline = rightline[0];
    }

}

获取车的轮廓

根据所获取的正确的两线,往中间扫,第一次遇到的黑色像素就是车的轮廓。
定义新的数组,用于放置车的左右轮廓所在的列坐标:


在这里插入图片描述

这样扫出来的轮廓还进行进一步的处理:
处理
首先需要排除掉这些错误的轮廓点:

如果当前行的右轮廓与左线的距离小于3,则认为,该右轮廓不正确;
如果当前行的左轮廓与右线的距离小于3,则认为,该左轮廓不正确;处理

同时也需要排除掉这些错误的轮廓点:
如果当前行的右轮廓与右线的距离小于3,则认为,该右轮廓不正确;
如果当前行的左轮廓与左线的距离小于3,则认为,该左轮廓不正确;
如果右轮廓列坐标<=2,则认为,该右轮廓不正确;
如果左轮廓列坐标>=183,则认为,该左轮廓不正确;

flag
当获取完所有正确的左右轮廓后,直接左加右除以二,得到车的中心位置;
代码:

if (huiche_flag == 1)
{
    Preliminary_scanning();
    //扫出左右线之后,从左右线往中间扫,扫到的第一个黑的将列坐标赋值给车的数组
    //扫左轮廓,并且计算正确性
    for (j = 0; j < 70; j++)
    {
        //进行保护
        byte left_start_sao = 0;
        if (lefetline[j] - 1 <= 1) left_start_sao = 1;
        else left_start_sao =(byte)(lefetline[j] - 1);
        for (i = left_start_sao; i <= 185 && i > 1; i--)
        {
            if (Pixels[j, i - 1] == 0 && Pixels[j, i] == 0)
            {
                car_left_contour[j] = (byte)(i);
                //与左右线靠的太近 或者太靠近左边 都认为是不正确的!!!
                if (My_Abs(car_left_contour[j] - lefetline[j]) <= 3 || My_Abs(car_left_contour[j] - rightline[j]) <= 3 || car_left_contour[j] >= 183)
                {
                    car_left_contour_flag[j] = 0;
                }
                else
                {
                    car_left_contour_flag[j] = 1;
                }
                break;
            }
        }
    }
    //扫右轮廓
    for (j = 0; j < 70; j++)
    {
        //进行保护
        byte right_start_sao = 0;
        if (rightline[j] + 1 >= 184) right_start_sao = 184;
        else right_start_sao = (byte)(rightline[j] + 1);
        for (i = right_start_sao; i >= 0 && i < 184; i++)
        {
            if (Pixels[j, i + 1] == 0 && Pixels[j, i] == 0)
            {
                car_right_contour[j] = (byte)(i);
                //与左右线靠的太近 或者太靠近右边 都认为是不正确的!!!
                if (My_Abs(car_right_contour[j] - lefetline[j]) <= 3 || My_Abs(car_right_contour[j] - rightline[j]) <= 3 || car_right_contour[j] <= 2)
                {
                    car_right_contour_flag[j] = 0;
                }
                else
                {
                    car_right_contour_flag[j] = 1;
                }
                break;
            }
        }
    }
    int sum_line = 0;
    int all_right_times = 0;
    byte times_in_if = 0;
    //找出轮廓后计算前车的中心位置
    for (j = 0; j < 70; j++)
    {
        //R_Start[j] = forecast_rightline[j];
        //只有确认了左右轮廓同时正确,我们才进行绘制
        if (car_right_contour_flag[j] == 1 && car_left_contour_flag[j] == 1)    
        {
            times_in_if++;
            //当第一次进入这个if语句的时候记录下此时的行
            if (times_in_if == 1)
            {
                lowest_contour_hang = j;
            }
            all_right_times++;
            R_Start[j] = car_right_contour[j];
            L_Start[j] = car_left_contour[j];
            sum_line += (car_right_contour[j] + car_left_contour[j]) / 2;
        }
        //LCenter[j] = centerline[j];
        LCenter[j] = 0;
        L_black[j] = lefetline[j];
        R_black[j] = rightline[j];
    }
    sum_line = sum_line / all_right_times;
    for (j = 0; j < 70; j++)
    {
        LCenter[j] = (byte)sum_line;
    }
    SetText("车屁股所在的行数:"+ lowest_contour_hang);
}

车屁股所在行数确定

当车的左右轮廓第一次全部正确时,记录下此时的行数,此为车屁股;

白色球台导致的问题

由于球台是白色的,所以会出现这样一个问题:

在这里插入图片描述
此时车屁股在第二行,往前走几厘米后:车屁股所在行数提高
在这里插入图片描述

所以需要限制一下,设置一个flag,当车屁股小于5行时置1;这样扫线方式就不会更改了。

之后再更,今天实现的就是这些了。

5.21思考

1、 关于会车地点确定

【a】、在直道会车,分为三种状况:
1、直道初始位置:
传球车辆会遇到的问题:车身没有摆正,视角中并没有出现接球车辆
2、直道中段位置:
3、直道末端位置:
【b】、在弯道内会车
【c】、在环岛会车
1、在环岛入口会车
2、在环岛弯道内会车
3、在环岛出口会车
这几个情况传球与接球车肯定均需要手动道路标志。
【d】、在坡会车
1、在上坡前会车
2、在坡顶会车
3、在下坡处会车

如何判断会车状态

思路1:根据编码器与帧数,算出已经行驶的距离(可以直接使用编码器记录脉冲数)当距离与我们预测的会车地点距离相近(减少图像压力,防止之前不停判断视野中是否出现车辆),且UWB送回的距离相近时,认为即将会车,flag置1;

然后扫线方式修改,找车!!
这种方式的缺点:首先必须算出会车地点到出发点赛道距离。