比赛终于结束了,终于可以好好睡一觉了。比赛从四月份延到了七月份,但这丝毫没有影响我把这比赛硬生生打成电赛,前前后后制作的时间加起来可能也就只有完完整整一星期吧,还能拿到省三我已经很满足了。就开个贴给今后要参加比赛的同学们讲讲这段经历,讲讲我觉得比较重要的几点。
队友
有没有好队友非常的关键,有两个好队友就成功了一半。我做的是超市购物组,相对来说比较好的搭配是一个机械的一个信电的一个计算的。不管是从车的结构还是底层驱动还是图像识别上都能够考虑到。由于我们组没有学机械的同学,对于一些车的结构来说虽然我们可以想到,但是要是做出来非常困难,AutoCAD这些画图建模软件也都不会用,丧失了一些竞争力。现场看到的一些车的结构真的是令人感到震撼。
队友的关系也要好,最好是自己比较熟的,交流起来也比较方便。这次比赛过去心态崩了两天,队友硬是给我心态整回来了,这点非常感谢我的队友。总之,队友最好不要是那种带来划水的,没啥时间观念的,否则真的会很累,除非是大佬,轻松一带二。
训练场地
可以说这次是吃了场地的大亏了,学校里的场地出发区并没有涂成红色的,而实际的比赛场地变成了红色的。赛场上调试的时候,巡线传感器直接懵逼了,连出发区都走不出去,心态直接爆炸了一点点。抓取货物的时候,发现学校里测试的场地和实际比赛的场地高度还是有个2~3cm的差别,直接导致了机械臂根本抓不到东西,心态又炸了一次。场地还是得严格按照规则来搭,否则真的到了场上心态要崩。要么机械臂程序的容错率要够高,才能避免场地制作带来的误差。
赛前准备
说实话这个比赛的制作准备时间还是非常充裕的,千万不要像我一样把这个比赛做成电赛,连续肝几天几夜,一天只睡两三个小时。一定不要拖到最后几天才做。早做早调试早发现问题早解决。要用到的模块一定要多准备一些,至少完完全全的准备额外的一套。能带多少去就带多少!!!!不然场上坏了想换都换不了。我们的破机器人可以说是从头换到了尾,第二天舵机坏了,找隔壁组借了一个,连夜换了调试,舵机驱动板烧了一块,比赛开始前半小时,降压模块还莫名其妙地烧掉了,电压表还找不到借了一个,赶紧换了一个模块。多带一套还是非常有必要的。
硬件
说真的,硬件真的是一分钱一分货,还是得买性能好的,该花的钱还是得花,不然赛场上就会出现各种模块爆炸的情况。我们一开始电机驱动用的是那种最便宜的L298N,起初重量没上去还是比较正常的,但是搭完车,电机的电流就开始增加了,小车旋转30s驱动就滚烫。后来换了一块70+的驱动,承载电流大了很多,也不是很烫了。隔壁做运输对抗的他们用两百一块的驱动,峰值电流还可以到200A,这驱动想烧了都难。
再说说机械臂,由于我们并没有什么机械制作的基础,也就只能想到用机械臂了。大家买的时候一定要买那种结构比较稳定的!!!!!我们从上上届的车上拆了一条机械臂,动的时候晃的不行,每抓一次都捏一把汗。舵机也得全新的,本来还想着废物利用,后来全换成新的了。扭矩一定要够大!!!!https://item.taobao.com/item.htm?spm=a1z09.2.0.0.299e2e8dl9HVl5&id=578661255198&_u=d2jd14b827f9这家店应该是淘宝上比较便宜的了,质量也还行,但是我调的时候可能堵转太久了,还是烧了一个。
机械臂的调试一直是非常麻烦的事情,本来想直接用arduino写程序调,但是后来觉得实在是太麻烦了,搞了一块舵机调试板子,还有上位机,拖拖拽拽就能调,还是比较方便的。尽管如此,机械臂的调试还是非常花时间,如果有什么东西可以把机械臂用手调好的状态记录下来那就太好了!(我在想peach)
巡线传感器我看到很多人用的16路传感器,网上一搜价格劝退,但是前面用单个单个拼在一起又感觉太容易松动了,而且间距不太好控制。前期准备的时候我索性自己做了一个六路和单路的传感器,前面还有灯,再也不怕光线影响了,夜车都能开嘿嘿。但是这个电位器好像没啥用,调节不了灵敏度,在红色上还是认为是白的。可能这个地方大家需要自己再改进改进。这两个传感器我也都开源了网址附上:
https://lceda.cn/wywy/tcrt5000
https://lceda.cn/wywy/tcrt5000-6-lu
降压模块也要多准备一些,主要还是要看电流的大小,给舵机、电机用电流一定得大,太小了一下子就很烫烧掉了,接线的时候一定要看清楚正负!!!!上次再实验室头昏了,接反了,一通电,一路火花带闪电,30块没了,实验室的小伙伴人都傻了,这也是我这么久来炸掉的第一个电容,人生圆满了。。。。附上降压模块遗照一张。
最后我也不得不说一句,蓝牙模块拿来调车是真的好用,各种数据打回手机就行了,根本不用连电脑串口看数据,方便的很,和板子的TX、RX一连,手机app一开数据都有,爽得很。HC-04 HC-08都可以,强推蓝牙模块调车。
软件实现
由于我只是做的底层控制,所以只能说说底层的软件怎么实现的。用的是比较简单的arduino mega2560,IO口也比较充足,串口也很多,完全足够连接各种模块。我也简单讲讲我都实现思路。下图是一开始没有考虑到出发区的红色,现场马上改了一下。
修改之后是这样
只走了外面的一大圈,但是拿奖还是够了,如果想冲省一,中间还是要拿的。
附上我小车所有底盘代码:
#define jidianqi 13
#define wheel_left_en 44
#define wheel_right_en 45
#define left_wheel_in1 46
#define left_wheel_in2 47
#define right_wheel_in3 48
#define right_wheel_in4 49
#define qti_left 53 //黑色返回1
#define qti_right 52
#define f_qti1 38
#define f_qti2 39
#define f_qti3 40
#define f_qti4 41
#define f_qti5 42
#define f_qti6 43
#define b_qti1 32
#define b_qti2 33
#define b_qti3 34
#define b_qti4 35
#define b_qti5 36
#define b_qti6 37
unsigned char action0[5] = {0xFF, 0x09, 0x00, 0x00, 0x00};
unsigned char action1[5] = {0xFF, 0x09, 0x00, 0x01, 0x00};
unsigned char action2[5] = {0xFF, 0x09, 0x00, 0x02, 0x00};
unsigned char action3[5] = {0xFF, 0x09, 0x00, 0x03, 0x00};
unsigned char action4[5] = {0xFF, 0x09, 0x00, 0x04, 0x00};
unsigned char action5[5] = {0xFF, 0x09, 0x00, 0x05, 0x00};
unsigned char action6[5] = {0xFF, 0x09, 0x00, 0x06, 0x00};
unsigned char f_QTIS = 0xff;
unsigned char b_QTIS = 0xff;
unsigned char s_QTIS = 0xff;
int crossing_counts = 0;
byte stop_flag = 0; //路口停止位,stop为1表示车停止,stop为0表示车未停止
int incomingByte = 0; // for incoming serial data
int result[7] = {};
int count = 0;
void setup() {
delay(13000);
car_init();
motor_motion(80,80);
delay(2000);
stop_car();
start_car();
}
void loop(){
while(!stop_flag)
Robot_hunting_judge_crossing();
if((crossing_counts == 1)||(crossing_counts==2))
{
turn_left();
start_car();
motor_motion(255,255);
delay(300);
}
else if(crossing_counts==3)
{
turn_right();
start_car();
motor_motion(255,255);
delay(300);
}
else if((crossing_counts==12)||(crossing_counts==10)||(crossing_counts==11)||(crossing_counts==21)||(crossing_counts==19)||(crossing_counts==20)||(crossing_counts==30)||(crossing_counts==28)||(crossing_counts==29))
{
start_car();
motor_motion(255,255);
delay(300);
}
else if(((crossing_counts>=4)&&(crossing_counts<=8))||((crossing_counts>=13)&&(crossing_counts<=17))||((crossing_counts>=22)&&(crossing_counts<=26))||((crossing_counts>=31)&&(crossing_counts<=35)))
{
send_recieve_smd();
how_carry_things();
start_car();
motor_motion(255,255);
delay(300);
}
else if((crossing_counts==9)||(crossing_counts==18)||(crossing_counts==27)||(crossing_counts==36))
{
send_recieve_smd();
how_carry_things();
turn_right();
start_car();
motor_motion(255,255);
delay(300);
}
else if(crossing_counts==37)
{
park_car();
}
stop_flag = 0;
}
void car_init() //小车所用各个引脚初始化
{
Serial.begin (115200);
Serial1.begin (115200);
Serial3.begin (9600);
//初始化各IO,模式为OUTPUT 输出模式
pinMode(jidianqi,OUTPUT);
digitalWrite(jidianqi,LOW);
pinMode(wheel_right_en,OUTPUT);
pinMode(wheel_left_en,OUTPUT);
pinMode(left_wheel_in1,OUTPUT);
pinMode(left_wheel_in2,OUTPUT);
pinMode(right_wheel_in3,OUTPUT);
pinMode(right_wheel_in4,OUTPUT);
digitalWrite(wheel_right_en,HIGH); //给高电平
digitalWrite(wheel_left_en,HIGH); //给高电平
pinMode(f_qti1,INPUT);
pinMode(f_qti2,INPUT);
pinMode(f_qti3,INPUT);
pinMode(f_qti4,INPUT);
pinMode(f_qti5,INPUT);
pinMode(f_qti6,INPUT);
pinMode(b_qti1,INPUT);
pinMode(b_qti2,INPUT);
pinMode(b_qti3,INPUT);
pinMode(b_qti4,INPUT);
pinMode(b_qti5,INPUT);
pinMode(b_qti6,INPUT);
pinMode(qti_left,INPUT);
pinMode(qti_right,INPUT);
digitalWrite(left_wheel_in1,HIGH); //给高电平
digitalWrite(left_wheel_in2,LOW); //给低电平
digitalWrite(right_wheel_in3,HIGH); //给高电平
digitalWrite(right_wheel_in4,LOW); //给低电平
send_arm_action(action0);
}
int f_readqti() //读取前方QTI的值
{
int f_qti,f_q1,f_q2,f_q3,f_q4,f_q5,f_q6;
f_q1=digitalRead(f_qti1);
f_q2=digitalRead(f_qti2);
f_q3=digitalRead(f_qti3);
f_q4=digitalRead(f_qti4);
f_q5=digitalRead(f_qti5);
f_q6=digitalRead(f_qti6);
f_qti=32*f_q1+16*f_q2+8*f_q3+4*f_q4+2*f_q5+f_q6;
f_qti=f_qti & 0x3f;
return f_qti;
}
int b_readqti() //读取后方QTI的值
{
int b_qti,b_q1,b_q2,b_q3,b_q4,b_q5,b_q6;
b_q1=digitalRead(b_qti1);
b_q2=digitalRead(b_qti2);
b_q3=digitalRead(b_qti3);
b_q4=digitalRead(b_qti4);
b_q5=digitalRead(b_qti5);
b_q6=digitalRead(b_qti6);
b_qti=32*b_q1+16*b_q2+8*b_q3+4*b_q4+2*b_q5+b_q6;
b_qti=b_qti & 0x3f;
return b_qti;
}
int side_readqti() //读取两边QTI的值
{
int s_qti,l_q,r_q;
l_q=digitalRead(qti_left);
r_q=digitalRead(qti_right);
s_qti=2*l_q+r_q;
s_qti=s_qti & 0x03;
return s_qti;
}
void Robot_hunting()
{
f_QTIS = f_readqti();
switch (f_QTIS)
{
case 51:motor_motion(150, 150);break; //1 110011 1,直行
case 31:motor_motion(0, 255);break; //011111,大幅左转
case 15:motor_motion(0, 200);break; //001111,大幅左转
case 7:motor_motion(0, 150);break; //000111,小幅左转
case 39:motor_motion(0, 100);break; //100111,小幅左转
case 35:motor_motion(0, 100);break; //100011,小幅左转
case 55:motor_motion(0, 100);break; //110111,小幅左转
case 57:motor_motion(100, 0);break; //111001,小幅右转
case 49:motor_motion(100, 0);break; //110001,小幅右转
case 59:motor_motion(100, 0);break; //111011,小幅右转
case 56:motor_motion(150, 0);break; //111000,小幅右转
case 60:motor_motion(200, 0);break; //111100,大幅右转
case 62:motor_motion(255, 0);break; //111110,大幅右转
default:motor_motion(150, 150);break;
}
delay(5);
}
void Robot_hunting_judge_crossing() //小车巡线加判断路口
{
f_QTIS = f_readqti();
s_QTIS = side_readqti();
//Serial.print("f_QTIS=");
//Serial.println(s_QTIS);
if(s_QTIS==0){
stop_car();
delay(20);
stop_flag = 1;
crossing_counts++;
}
switch (f_QTIS)
{
case 51:motor_motion(80, 80);break; //1 110011 1,直行
case 31:motor_motion(0, 255);break; //011111,大幅左转
case 15:motor_motion(0, 200);break; //001111,大幅左转
case 7:motor_motion(0, 150);break; //000111,小幅左转
case 39:motor_motion(0, 100);break; //100111,小幅左转
case 35:motor_motion(0, 100);break; //100011,小幅左转
case 55:motor_motion(0, 100);break; //110111,小幅左转
case 57:motor_motion(100, 0);break; //111001,小幅右转
case 49:motor_motion(100, 0);break; //110001,小幅右转
case 59:motor_motion(100, 0);break; //111011,小幅右转
case 56:motor_motion(150, 0);break; //111000,小幅右转
case 60:motor_motion(200, 0);break; //111100,大幅右转
case 62:motor_motion(255, 0);break; //111110,大幅右转
default:motor_motion(80, 80);break;
}
delay(5);
}
void motor_motion(unsigned int left_val, unsigned int right_val)
{
analogWrite(wheel_right_en,right_val);
analogWrite(wheel_left_en,left_val);
}
void turn_right()
{
digitalWrite(left_wheel_in1,HIGH); //给高电平
digitalWrite(left_wheel_in2,LOW); //给低电平
digitalWrite(right_wheel_in3,LOW);
digitalWrite(right_wheel_in4,HIGH);
motor_motion(255, 255);
delay(600);
while(1){
f_QTIS = f_readqti();
if((f_QTIS == 51)||(f_QTIS == 49)||(f_QTIS == 35)||(f_QTIS == 57)||(f_QTIS == 39))
{
stop_car();
break;
}
}
}
/*
void turn_left()
{
digitalWrite(left_wheel_in1,LOW); //给高电平
digitalWrite(left_wheel_in2,HIGH); //给低电平
digitalWrite(right_wheel_in3,HIGH);
digitalWrite(right_wheel_in4,LOW);
motor_motion(255, 255);
delay(1800);
stop_car();
}
void turn_right()
{
digitalWrite(left_wheel_in1,HIGH); //给高电平
digitalWrite(left_wheel_in2,LOW); //给低电平
digitalWrite(right_wheel_in3,LOW);
digitalWrite(right_wheel_in4,HIGH);
motor_motion(255, 255);
delay(1800);
stop_car();
}
*/
void turn_left()
{
digitalWrite(left_wheel_in1,LOW); //给高电平
digitalWrite(left_wheel_in2,HIGH); //给低电平
digitalWrite(right_wheel_in3,HIGH);
digitalWrite(right_wheel_in4,LOW);
motor_motion(255, 255);
delay(600);
while(1){
f_QTIS = f_readqti();
//b_QTIS = b_readqti();
if((f_QTIS == 51)||(f_QTIS == 49)||(f_QTIS == 35)||(f_QTIS == 57)||(f_QTIS == 39))
{
stop_car();
break;
}
}
}
void stop_car(){
digitalWrite(left_wheel_in1,LOW);
digitalWrite(left_wheel_in2,LOW);
digitalWrite(right_wheel_in3,LOW);
digitalWrite(right_wheel_in4,LOW);
}
void start_car()
{
digitalWrite(left_wheel_in1,HIGH); //给高电平
digitalWrite(left_wheel_in2,LOW); //给低电平
digitalWrite(right_wheel_in3,HIGH); //给高电平
digitalWrite(right_wheel_in4,LOW); //给低电平
}
void send_arm_action(unsigned char action[])
{
for(int i = 0; i < 5; i++)
{
Serial3.write(action[i]);
delay(50);
}
}
void send_recieve_smd()
{
delay(1000);
Serial1.write(49);
delay(20);
while(Serial1.read() >= 0){}//清空缓存,避免缓存的串口数据的影响
while(incomingByte != 55)
{
if (Serial1.available() > 0) {
// read the incoming byte:
incomingByte = Serial1.read();
result[count] = incomingByte;
count++;
// say what you got:
//Serial.print("I received: ");
//Serial.println(incomingByte, DEC);
}
}
incomingByte = 0;
}
void how_carry_things()
{
if((count == 1)||(count>7))
;
else
{
for(int i = 0; i < (count-1); i++)
{
if(result[i] == 49)
{
send_arm_action(action1);
delay(15000);
}
else if(result[i] == 50)
{
send_arm_action(action2);
delay(15000);
}
else if(result[i] == 51)
{
send_arm_action(action3);
delay(15000);
}
else if(result[i] == 52)
{
send_arm_action(action4);
delay(20000);
}
else if(result[i] == 53)
{
send_arm_action(action5);
delay(16000);
}
else if(result[i] == 54)
{
send_arm_action(action6);
delay(20000);
}
else
;
}
}
count = 0;
}
void park_car()
{
turn_right();
digitalWrite(jidianqi,HIGH);
}
代码其实并不多,也就三百行。loop里其实是一直在找路口,然后根据路口的计数来判断应该做什么,还是非常简洁的,和上面的图一起看还是很清楚的。但是就是有一个很大的问题,就是转弯按照我这么写其实很不好,但是由于时间有限,我也没有什么好想法,在一个由于车的重心没有完全在中心点,或多或少都有问题,只能靠前面的巡线传感器巡线把车身摆正。串口三和舵机控制板连,串口一和树莓派连。购物车靠电磁铁拉着。
暂且就写这么多了,以后想到有什么可以补充再补充。也欢迎大家下面评论区交流,虽然这是我的第一次机器人竞赛,也是我最后一次机器人竞赛了。希望大家在竞赛中都能有好运气好名次。