半岛·综合体育官网入口 第18届全国大学生智能汽车竞赛四轮车开源讲解【9】--出入库、三叉、T字

下面写开源概要。第十八届全国大学生智能汽车大赛四轮车开源讲解_Joshua.X的博客-CSDN博客开源链接写在下面

这应该是关于赛道元素的最后一章了。其实三叉形只出现在16、17场比赛中。T形也只出现在17场比赛中。车库比它们稍微老一点,出现在15场比赛中。让我来和大家分享一下如何处理这些元素。

注意:以下方案可能使用以下一个或多个变量,所有这些变量都在开源[3]中有关边缘提取的章节中进行了讨论。

const uint8  Standard_Road_Wide[MT9V03X_H];//标准赛宽数组
volatile int Left_Line[MT9V03X_H]; //左边线数组
volatile int Right_Line[MT9V03X_H];//右边线数组
volatile int Mid_Line[MT9V03X_H];  //中线数组
volatile int Road_Wide[MT9V03X_H]; //实际赛宽数组
volatile int White_Column[MT9V03X_W];//每列白列长度
volatile int Search_Stop_Line;     //搜索截止行,只记录长度,想要坐标需要用视野高度减去该值
volatile int Boundry_Start_Left;   //左右边界起始点
volatile int Boundry_Start_Right;  //第一个非丢线点,常规边界起始点
volatile int Left_Lost_Time;       //边界丢线数
volatile int Right_Lost_Time;
volatile int Both_Lost_Time;//两边同时丢线数
int Longest_White_Column_Left[2]; //最长白列,[0]是最长白列的长度,也就是Search_Stop_Line搜索截止行,[1】是第某列
int Longest_White_Column_Right[2];//最长白列,[0]是最长白列的长度,也就是Search_Stop_Line搜索截止行,[1】是第某列
int Left_Lost_Flag[MT9V03X_H] ; //左丢线数组,丢线置1,没丢线置0
int Right_Lost_Flag[MT9V03X_H]; //右丢线数组,丢线置1,没丢线置0

注:文中所有参数及角点范围仅供参考,实际参数请根据需求调整!!!!!!!!!

其实智能小车的一切参数都是需要根据你的实际情况进行调整的,千万不要一模一样的照搬!!!

1. 车库 1.1 出车库

比赛中没有明确的规则,但一般约定俗成,当车手准备起步时,举手示意裁判,按下设定的起步按钮,双手离开车体,车模自行起步前最好有1~2秒的时间间隔,这样可以避免用推车辅助起步。

说实话,四轮车出车库时半岛·综合体育官网入口,你不需要记下指示。你可以将车辆稍微倾斜地放在车库里,让它自由地找到方向。不需要特殊处理。

bd84cb5f0358486aba57e0889d8ec9f4.jpeg

无需特殊处理,就这样放着就可以自行出库

为了保险起见,这里提供几种出仓的方法。

方法 1:

汽车放置于车库内,启动阶段,汽车模型前轮左转/右转,速度设置为给定速度,当编码器积分超过一定阈值时,切换自由循迹。

方法 2:

汽车放置于车库内,起步阶段将汽车模型前轮左/右转向至极限,速度设置为给定速度,利用陀螺仪积分将航向角积分到90度左右(正90度或负90度,取决于汽车是从左侧还是右侧起步),然后切换自由循迹。

2f5290b117074d29a7f2ca0b19da53a0.png

车库与轨道垂直,陀螺仪积分90度即为退出成功。

方法 3:

汽车放置于车库内,出发阶段,汽车模型前轮左转/右转,速度设定为给定速度,通过图像判断长于视野、边界起点较低等条件,若满足条件则认为出发成功,切换自由跟踪。

1.2 仓储

汽车模型的存储是一个值得关注的问题。

因为你可以完成比赛,但是如果你没能进入停车位,你就会受到惩罚。有些情况下,如果你没能进入停车位,计时装置就不会被触发,导致没有成绩。我在比赛直播期间经常遇到这种情况,很遗憾。

不知道别人怎么存东西,我都是走斑马线的。

1.2.1 斑马线

斑马线位于车库中心与轨道中心之间,一般用黑色绝缘胶粘在轨道上。

详细参数见下图:

2a73e55ac73141d992d0e30dc5842686.png

斑马线详细参数

对于智能汽车来说,这就是汽车看到的图像。

db852bae783b43d983d6ada55a58e6da.jpeg

智能汽车视野中的斑马线

最大的区别就是中间多了一个黑条。

这时候就可以用最简单的方法进行判断。

方法 1:

在固定范围内寻找跳跃,因为太过简单,前期可以用,但后期建议替换。

定义一个区域(区域需要自己调整),(左边的像素点!=右边的像素点)计数,如果超过一定阈值(阈值手动调整)则认为是斑马线。

代码如下:

/*
 * 斑马线检测函数,检测屏幕黑白跳变数,阈值可调,也要注意判断范围,近车靠头还是远离车头
 */
void Zebra_Detect(void)
{
    int i,j;
    change=0;
    for(i=55;i>=40;i--)
    {
        for(j=15;j<=90;j++)
        {
            if(image_two_value[i][j+1]-image_two_value[i][j]!=0)
            {
                change++;
            }
        }
    }
   // lcd_showint16(100,6,change);
    if(change>=100)
    {
        zebra_flag=1;
    }
    else
    {
        zebra_flag=0;
    }
}

这里的减法和不等号意思是一样的,因为图像二值化之后只有0x00和0xff两个值。

方法 2:

它是上述方法的改进,用满足一定条件的活动区域代替固定区域。

这是我最后选择的解决方案

想法

元素是互斥的,斑马线需要和所有元素互斥。最长的白色柱比较长(因为斑马线前后都要有直路)。最长的白色柱位于中间(因为最长的白色柱会越过斑马线)。边界的起点比较低,不一定在最下面(因为斑马线前后都要有直路,斑马线会影响车子前进的方向,车子会抖动)。从下往上找到轨道宽度很窄的地方,超过一定阈值,在最后一个窄的地方记录行数(由于斑马线条纹的原因,轨道宽度会卡在两条条纹之间,比标准轨道宽度小很多)。找到轨道宽度太窄的地方,往上移一行,往下移一行,找到两行的左右边界,把这个区域圈起来,进行跳跃计数。

图解如下

显卡

首先,找到几条连续的车道非常窄的线。找到五条连续太窄的线。从这条线开始,向上找到 15 条线,向下找到 8 条线,并找到这两条线的左右边界。这将划定一个区域半岛·综合体育官网入口,并在该区域中寻找过渡。如果过渡大于某个阈值,则将其视为斑马线。

代码

/*-------------------------------------------------------------------------------------------------------------------
  @brief     斑马线检测
  @param     null
  @return    null
  Sample     Zebra_Stripes_Detect(void)
  @note      边界起始靠下,最长白列较长,赛道宽度过窄,且附近大量跳变
-------------------------------------------------------------------------------------------------------------------*/
void Zebra_Stripes_Detect(void)
{
    int i=0,j=0;
    int change_count=0;//跳变计数
    int start_line=0;
    int endl_ine=0;
    int narrow_road_count=0;
    if(Cross_Flag!=0||(1<=Ramp_Flag&&Ramp_Flag<=3)||Zebra_Stripes_Flag!=0||
       Stop_Flag!=0 ||Electromagnet_Flag!=0||Barricade_Flag!=0)//元素互斥,不是十字,不是,不是坡道,不是停车
    {
        return;
    }
    赛宽变化判斑马线
    if(Search_Stop_Line>=60&&
       30<=Longest_White_Column_Left[1]&&Longest_White_Column_Left[1]<=MT9V03X_W-30&&
       30<=Longest_White_Column_Right[1]&&Longest_White_Column_Right[1]<=MT9V03X_W-30&&
       Boundry_Start_Left>=MT9V03X_H-15&&Boundry_Start_Right>=MT9V03X_H-15)
    {//截止行长,.最长白列的位置在中心附近,边界起始点靠下
        for(i=65;i>=20;i--)//在靠下的区域进行寻找赛道宽度过窄的地方
        {
            if((Standard_Road_Wide[i]-Road_Wide[i])>10)
            {
                narrow_road_count++;//多组赛宽变窄,才认为是斑马线
                if(narrow_road_count>=5)
                {
                    start_line=i;//记录赛道宽度很窄的位置
                    break;
                }
            }
        }
    }
    if(start_line!=0)//多组赛宽变窄,,以赛道过窄的位置为中心,划定一个范围,进行跳变计数
    {
        start_line=start_line+8;
        endl_ine=start_line-15;
        if(start_line>=MT9V03X_H-1)//限幅保护,防止数组越界
        {
            start_line=MT9V03X_H-1;
        }
        if(endl_ine<=0)//限幅保护,防止数组越界
        {
            endl_ine=0;
        }
        for(i=start_line;i>=endl_ine;i--)//区域内跳变计数
        {
            for(j=Left_Line[i];j<=Right_Line[i];j++)
            {
                if(image_two_value[i][j+1]-image_two_value[i][j]!=0)
                {
                    change_count++;
                }
            }
        }
//        ips200_show_uint(0*16,100,change_count,5);//debug使用,查看跳变数,便于适应赛道
    }
 //画出区域,便于找bug,debug使用
//        Draw_Line( Left_Line[start_line], start_line, Left_Line[endl_ine], endl_ine);
//        Draw_Line( Left_Line[start_line], start_line, Right_Line[start_line], start_line);
//        Draw_Line(Right_Line[endl_ine], endl_ine, Right_Line[start_line], start_line);
//        Draw_Line(Right_Line[endl_ine], endl_ine, Left_Line[endl_ine], endl_ine);
//        ips200_draw_line ( Left_Line[start_line], start_line, Left_Line[endl_ine], endl_ine, RGB565_RED);
//        ips200_draw_line ( Left_Line[start_line], start_line, Right_Line[start_line], start_line, RGB565_RED);
//        ips200_draw_line (Right_Line[endl_ine], endl_ine, Right_Line[start_line], start_line, RGB565_RED);
//        ips200_draw_line (Right_Line[endl_ine], endl_ine, Left_Line[endl_ine], endl_ine, RGB565_RED);
    if(change_count>30)//跳变大于某一阈值,认为找到了斑马线
    {
        Zebra_Stripes_Flag=1;
    }
//相关参数显示。debug使用
//    ips200_show_uint(0*16,50,narrow_road_count,5);
//    ips200_show_uint(1*16,50,change_count,5);
}

实际使用效果不错,但是边界起点条件可以放宽一些。因为如上图所示,右边可以看到车库BOB半岛·体育在线登录,而且边界起点已经比较高了,所以斑马线可以看得清清楚楚。

调试的时候可以用方框把区域圈出来,实际使用效果如下:

实际使用效果很好

注意,某个地方肯定会有误判。

a84e2c922f4b4e7ba95512a0412fe77d.jpeg

大 S 型转弯误判

大S弯道会出现误判,因为右边的赛道很窄,如果其他条件都满足的话,可以将最长的白色柱子的位置稍微放的居中一点。

方法 3:

这是我后来想出来的一个算法,理论上是可行的,但是因为没时间去实验,所以不知道实际效果怎么样,我写在这里,大家可以自己去尝试一下。

我们在前面的线路检查中已经找到了一个叫最长白色柱的东西。并且把所有列的最长白色柱长度都存储在一个数组中。

我们来看看斑马线的实际照片吧。

469fd47b2f144bcca9f885eb66bfe705.jpeg

斑马线

我们是否可以将最长的白色柱子理解为从下到上的边界?

最长的白名单显示含义

1bbc95992ca3493d8e74ceb6203c2d56.png

垂直边界图

那么,过度的纵向撕裂能被认为是斑马线吗?

我觉得是可以的,要看具体情况,我只是提出一个想法。

代码如下

/*-------------------------------------------------------------------------------------------------------------------
  @brief     斑马线判断
  @param     null
  @return    null
  Sample     Zebra_Detect();
  @note      一个新想法,利用最长白列,每一列的最长白列可以看成从下往上的赛道边界,
            当看到斑马线时,会有较大的纵向撕裂,一条斑马线会有两次撕裂
            那么我用这个跳变计数,是否能识别斑马线呢,代码如下,
            我就不去测试了,看各位的测试结果了
-------------------------------------------------------------------------------------------------------------------*/
void Zebra_Detect(void)
{
    int j=0;
    int count=0;
    //前面的元素互斥各位自行补全
    if(Search_Stop_Line>=60)//视野很长
    {
        for(j=10;j<=MT9V03X_W-10;j++)
        {
            if(abs(White_Column[j]-White_Column[j+1])>=30)
            {
                count++;
            }
        }
    }
    if(count>=8)//阈值手动调整
    {
        Zebra_Stripes_Flag=1;
    }
}

注:很多同学说车识别了斑马线但是不停,问我为什么。

说实话,我从未见过你的车、你的图片或你的代码。如果你问我为什么,我真的不知道。以下是一些故障排除思路。

元素之间有没有互相排斥?有没有可能识别到斑马线后,小车刹车,但是过了斑马线后看到直路又开始加速?刹车程序是否正常?pid写得好不好?可以试试把pid期望值设为0,把小车放到赛道上,用手推,小车会有明显的车轮抱死的感觉,阻力很大。如果不是,请考虑pid的问题。是不是控制上给pid期望值设为0,但是其他地方直接操作pwm输出,导致直接操作pwm的地方覆盖了pid输出,导致刹车失效。另外建议所有速度和pwm输出都使用统一接口,统一控制,否则这里判断,那里修改,pid看到斑马线刹车,但是其他地方控制加速,导致冲突。1.3注意事项

出货无需特殊处理,但进货需特殊处理。

进入停车位的时候看到斑马线后直接拐弯,然后积分停车即可。

有个技巧,就是在设定出口方向的时候,同时准备好入口方向。如果赛道是环形赛道,那么从左边出去就必须从左边进去。从右边出去也是一样。所以当我看到斑马线的时候,出口就在左边,所以我可以直接左转,而不用判断左边还是右边的车库。

出站和入站方向之间的关系

这样就只需要判断斑马线了,不需要判断左右仓库了。

上述方法还是有缺陷的,因为从看到斑马线到拐弯需要时间,中间的延迟需要考虑。最好的方法是先找到斑马线区域,以斑马线区域为参考,找到车库的拐弯点,然后添加线进入车库。

在第18届赛车比赛之前,汽车不需要停车,只要停在斑马线后面1米以内就可以了。我没有采取下一步行动,我希望继续我的研究。

二、三叉

三重叉是一个非常有意思的元素,它的出现直接让16号比赛从一圈变成了两圈,圈数的增加直接导致失误率的急剧上升,对车模稳定性的要求也大大提升。

三叉戟的理论图如下

af32bdfac62d4487ba6fe78eb46a8e8c.png

三叉戟

三叉智能车实拍照片如下

82f0c959be4c4c36835a2c999fb18da9.jpeg

从智能汽车角度看 Trident

由于第18节中没有这个元素,所以我就不分享代码了

仅进行思想分析

边界起点较低,左右两侧有单调转折点,单调转折点纵坐标几乎相同,最长白色柱经历了V形过程,先减小后增大,同时找到最长白色柱的最低点,用它填充线条,从左侧或右侧单调转折点连线到顶部V形的底边

9f476dece90c459e995a4f74339b5256.png

三叉戟中最长的白色柱

c350b8013b78432ab3c32e68811944c6.jpeg

进入三管齐下的路线

进入三叉戟和退出三叉戟的图形基本相同,判断条件也相同。

由于历史太久远,我没有任何图片,所以我就不展示那三个岔路了。

建议三岔可以创建一个状态机。

状态 1:第一次看到这种情况时,你认为自己正在进入三叉路。状态 2:尖角消失,你认为自己在三叉路内。状态 3:再次看到相同的尖角,你退出三叉路。

字母“T”就更不吉利了,在十七次会话中只出现了一次。

a1d1ee016a54493fb48aa50f79199bec.png

T形图

实际图片如下

7397af5e11f046358589fd61101d9167.jpeg

T型实拍

关键词:

客户评论

我要评论