背景
18年下半年,也就是我大三的上半学期,是第二年的全国大学生智能汽车竞赛的校内报名时间。同学来邀请我参加,我也想做点东西,就一起参加了。我们一组三人,自己主要负责相关代码的编写。
准备
报名后就临近放假了,而且当时课程还挺多的,就没怎么准备,最后放假前拿着参赛的摄像头去学校实验室跑道上采集了下赛道数据,一边自己在寒假的时候开始准备,写写如何处理。
采集回来的赛道信息就是这样的二进制黑白图,每一列是一部分的采集变化图,就在整个过程中随意截取了几段,(黑色:1、白色:0)。
过程
首先让小车跑起来
简单来说就是用单片机生成pwm给驱动板来驱动电机和舵机。最开始可以给一个固定的值让小车直接往前走。
提取赛道的中线,让小车巡中线前进
整个赛道在单片机看来就是上图所示的0和1,所以0和1的分界线就是赛道的边界。首先将整个边界提取出来,(左边界+右边界)/2 就基本上是赛道的中线了,然后小车巡中线前进即可。思路:给不同高度的中线上的不同点以不同的权值,加权计算出来后得到一个中点的位置,然后将这个终点与摄像头的中点比较,看偏差的位置和多少,然后再给舵机不同的打角方向和幅度。每次都是按照这个中点来前进,整个流程下来就是按照中线前进了。
十字路口
这是个老元素了,首先得判断出来这是个十字路口,过程中要注意和圆环元素的区别,我判断的依据是两边边界的边线都在某个高度丢失。然后需要对十字路口进行补线,先找到边界丢失的拐角处,对于丢线的部分可以按照未丢线部分的斜率延长边线。当然还有其他方法,我在这就不说了。
圆环
这也是个老元素了,同样首先要判断出来这是圆环,可以使用电磁信号辅助判断。然后进行各种补线使小车进去一次圆环后出来,并且不再进这个圆环。我的方法是一个阶段一个阶段的进行判断、补线、判断、 补线。。。直到完成整个元素,具体的我就不在这里详细描述了。
断路
这个简单,直接巡电磁线前进就好了,注意的的进短路前要减速,因为小车尺寸限制,前瞻不能很长,所以短路转弯速度快了容易冲出去。
路障
我们使用的超声波配合摄像头一起识别,识别到后可以固定打角出去,然后定时,再固定打角回来。这是一种比较简单而且速度快的方法,但是参数不太好调。另外可以在侧面装一个摄像头,识别到后,系统切换到侧面的摄像头,然后按照摄像头采集的赛道边线数据前进。这种方法可能速度比较慢,但是稳定性较好,我们是这两种都完成了,摄像头的方案留作备用,在省赛决赛时,路障前后直道的长度较短,备用方法很有效。
结束
各个元素处理完后,就开始了漫长的调参优化过程了,由于组员没有用过Git,所以我们代码都是以QQ群的方式共享的。
最后放一段主循环的代码吧。
while (1)
{
GearSelect();
CameraGetImg(&buff, img, cam_type);
LCD_Img_Binary_Z(site, size, buff, imgsize);
ReadADC();
stop = 0;
if (Stop() == 1 && road_type != OBSTALE && in_obs == 0) //(Stop() == 1 || distance < 20)
{
road_type = STOP;
}
else if (road_type == STOP && Stop() == 0)
{
if (LengthOfX(40, img) > 15)
{
road_type = NORMAL;
}
else
{
road_type = ELECTROMAGNETIC;
}
}
switch (road_type)
{
case NORMAL:
roundabout_flag = 0;
road_flag = GetMiddleLine(img, mid_line, left_line, right_line, &roundabout_flag);
stop = StartLine(img);
if (road_flag == 1)
{
distance1 = GetUltrasnoic();
distance = GetUltrasnoic();
if (speed_param.obs_pit == 0)
{
distance += 100;
}
if (distance < 1100 && distance > 500 && distance1 < 1150 && distance1 > 500)
{
if (speed_param.obs_pit == 1)
{
set_vector_handler(PIT3_VECTORn, PIT3_IRQHandler);
ave_speed = AveSpeed();
// 定时后的固定速度
duty = 725 + 0.09 * (ave_speed);
uint32 pit_time = 275 - 0.1 * ave_speed;
pit_init_ms(PIT3, pit_time);
enable_irq(PIT3_IRQn);
road_type = OBSTALE;
break;
}
else
{
cam_type = SUB_CAM;
delay = speed_param.switch_delay;
road_type = OBSTALE;
break;
}
}
}
else if (road_flag == 2)
{
delay = speed_param.switch_delay-10;
road_type = ELECTROMAGNETIC;
MotorLpid(0);
MotorRpid(0);
}
for (int q = 0; q < HEIGHT; q++)
{
if (mid_line[q] < WIDTH)
LCD_point((Site_t){(int)(mid_line[q] * 1.6), (int)(q * 1.6)}, RED);
}
for (int q = 0; q < HEIGHT; q++)
{
if (left_line[q] < WIDTH)
LCD_point((Site_t){(int)(left_line[q] * 1.6), (int)(q * 1.6)}, BLUE);
}
for (int q = 0; q < HEIGHT; q++)
{
if (right_line[q] < WIDTH)
LCD_point((Site_t){(int)(right_line[q] * 1.6), (int)(q * 1.6)}, BLUE);
}
error = Error(mid_line);
far_error = FarError(mid_line);
if (roundabout_flag > 0)
{
if (AveSpeed() > 580)
{
speed_param.speed -= 100;
}
}
if(speed_param.func_error_control!=NULL)
{
speed_param.func_error_control(error, far_error, mid_line);
}
Steerpid(error);
if (mid_line[HEIGHT] > HEIGHT - 5)
{
road_type = STOP;
break;
}
else if (stop == 1)
{
MotorStop();
}
else
{
if (error < 0)
{
MotorLpid(speed_param.speed + speed_param.speed_error * error);
MotorRpid(speed_param.speed);
}
else
{
MotorLpid(speed_param.speed);
MotorRpid(speed_param.speed - speed_param.speed_error * error);
}
}
break;
case ELECTROMAGNETIC:
stop = StartLine(img);
error_elect = ADCControl(delay);
if (delay > 0)
{
if(error_elect<0) //向右转弯
{
MotorLpid(100);
MotorRpid(100+200 * error_elect);
}else
{
MotorLpid(100- 200 * error_elect);
MotorRpid(100 );
}
delay--;
}
else if (stop == 1)
{
MotorStop();
}
else
{
if (speed_param.elec_speed > 300)
{
if (error_elect < 0)
{
MotorLpid(speed_param.elec_speed);
MotorRpid(speed_param.elec_speed + speed_param.speed_error * error_elect);
}
else
{
MotorLpid(speed_param.elec_speed - speed_param.speed_error * error_elect);
MotorRpid(speed_param.elec_speed);
}
}
else
{
MotorLpid(speed_param.elec_speed);
MotorRpid(speed_param.elec_speed);
}
}
if (LengthOfX(40, img) > 25)
{
road_type = NORMAL;
}
break;
case OBSTALE:
if (speed_param.obs_pit == 1)
{
if (in_obs++ > 60)
{
in_obs = 0;
road_type = STOP;
break;
}
SteerDuty(duty);
if (obs_delay > 0)
{
obs_delay--;
}
if (duty == 700 && obs_delay == 0)
{
duty = 685;
}
if (duty == 685 && RetUsual(img) == 1 && LengthOfX(66, img) > 15 && in_obs > 20)
{
in_obs = 0;
road_type = NORMAL;
}
//全固定
MotorLpid(500);
MotorRpid(500);
}
else
{
if (CamProsObstale(img, side_line) == 1)
{
road_type = NORMAL;
cam_type = MAIN_CAM;
}
if (delay > 0)
{
MotorLpid(50);
MotorRpid(50);
delay--;
}
else
{
MotorLpid(speed_param.obs_speed);
MotorRpid(speed_param.obs_speed);
}
}
break;
case STOP:
MotorStop();
break;
}
}
各个阶段调车记录
{% dplayer "url=https://slyli.cn/video/2020-07/school.mp4" %}
小车成品
安徽省赛
我们这次比赛在安徽省是由淮南联合大学承办的。说到这必须得吐槽一下了,7月份,选择铁皮房做比赛场地,一个大的比赛场地只有角落有几台空调之外也就算了,竟然还限制队员进场人数。
参赛证
决赛视频
从其他地方找到的我们车的视频。
{% dplayer "url=https://slyli.cn/video/2020-07/anhui.mp4" %}
成绩
国赛
这次国赛在山东大学(威海)举办的,不得不说威海的气候确实舒服啊,夏天夜晚海风迎面吹来,清凉却不潮湿。
参赛证
决赛视频
{% dplayer "url=https://slyli.cn/video/2020-07/weihai.mp4" %}
成绩
遇到的问题
- 有段时间调车的时候会偶尔遇到卡死的问题,小调试屏也是白屏。开始以为是硬件接触不好的问题,就没在意,后面出现情况多了之后才开始重视,查找问题原因,一直耽搁了几天才找出来,用的是注释大法 hhhh。最后原因是
while循环
的问题,跳出循环的条件有点漏洞,导致while循环可能运行到一个未定义的状况,导致循环一直跳不出来。
总结与其他
- 这次比赛对我来说确实是一次十分有用的经历,在准备的过程中学到了很多真实有用的东西,增加了自己的技能,不管是找工作还是什么,使没有多少内容的简历增加了点可写的项目。
- 遗憾的是,当时我们队伍里三人都是大三的,而且都是准备考研的,所以在省赛之后的一段时间内,一直是一边准备考研一边准备比赛的,没能全力以赴在国赛中获得一个令人满意的成绩。