智能运输开源主题赛赛后总结
2024智能运输开源主题赛
一、硬件
1.主板
本次cpu采用的是arduino uno开发板。
-
主要功能介绍 :该芯片共有6个模拟输入引脚,分别是A0~A5,这6个模拟输入引脚本次设计全部用到。
A0~A3: 这4路用于灰度巡线
A4~A5: 这4路用于路口计数
13、12、11、10 引脚:舵机的控制
9、8引脚:按键输入控制
7、6、5、4: 电机控制
3、2: 超声波控制
-
遇到的问题及解决方案
可能会遇到板子损坏的情况,在正式组装之前,首先确保要使用的功能,每一步都可以正常使用,特别要注意板子部分区域过热或者发烫的问题.
2.巡线模块
- 主要功能介绍
本次设计的巡线模块使用的是灰度巡线,相较于普通的红外传感器,受光线影响较小,得到的是模拟值,非常的简单易用,不需要复杂的调试,输入电压在3.3~5V之间.注意接线时,不要接反.
- 遇到的问题及其解决方案
在测试的过程中可以会遇到某个传感器与其他传感器数值误差过大的情况,需要更换后使用.
3.超声波模块
- 主要功能介绍
该模块主要用于检测小车距离前方杯子之间的距离
- 遇到的问题及解决方案
1.会遇到超声波模块损坏,无法得到数值的情况,还可能会遇到,
2.能得到数值但是接收到的数值特别慢的情况,是因为线接错或者是接线不牢固的情况
4.舵机
- 主要功能介绍
在本设计中四个舵机有三种不同的用途
1.13引脚舵机主要用于抓起的杯子抬起和放下.
2.12引脚主要用于爪子的开合与关闭.
3.11和10号引脚主要用于扫除扣下来不要的杯子
- 遇到的问题及解决方案
1.舵机无法承受力受限–只能采用大扭矩舵机
2.更换后还是无法承受,会出现瞬间断电重启的情况,本次设计采用的解决方案是舵机独立供电,或者采用串电阻和并电容的方案.
3.在安装舵机之前提前设置好舵机的角度,防止舵机卡住.把舵机的角度设置在合理的范围之内.
5.电机
-
主要功能介绍
本次设计使用的是霍尔编码电机,输入电压是6-12V.
-
遇到的问题及解决方案
1.电机接线不稳定,采用普通的杜邦线会造成电机接触不良,电机不旋转的情况,可以采用下面这种线材.
2.注意霍尔编码电机的尾部霍尔盘不要被异物挡住,否则就会造成电机无法旋转的情况.
6.按键
- 主要功能介绍
两个按键只要实现A和B发车区的区别
- 遇到的问题及解决方案
在写程序之前记得测试两个按键的好坏
7.轮胎
本次使用的是普通轮胎下次可以采用防滑轮胎来获得更好的效果.如图所示.
二、软件
1.超声波测距函数
//超声波测距函数
float checkdistance_2_3() {
digitalWrite(2, LOW);
delayMicroseconds(2);
digitalWrite(2, HIGH);
delayMicroseconds(10);
digitalWrite(2, LOW);
float distance = pulseIn(3, HIGH) / 58.00;
delay(10);//这个值可修改
return distance;
}
2.杯子计数及计数
- 代码展示
volatile int beizi;//声名杯子变量,遇到杯子时,变量beizi+1
volatile boolean j1;//状态判断
void beizipanduan()
{
if (checkdistance_2_3() <= 7.3)//当距离杯子的距离小于7.3说明检测到杯子并计数器加1
{
if (!j1)
{
beizi = beizi + 1;
j1 = 1;
}
}
else//检测到杯子时,技术器只计数一次,知道杯子被移走,距离大于7.3
{
j1 = 0;
}
}
3.路口判断
- 代码展示
volatile int heixian1;//A4路口判断
volatile int heixian2;//A5路口判断
volatile int heixian;//最终路口计算值
void lukoujisuan() {
switch (state1) {//A4路口判断
case 0://只有进入黑线时才会计数
if (analogRead(A4) >= jishuyuzhi) {
state1 = 1;
heixian1 = heixian1 + 1;
}
break;
case 1:
if (analogRead(A4) <= jishuyuzhi) {
state1 = 0;
}
break;
}
switch (state2) {//A5路口判断
case 0://只有进入黑线时才会计数
if (analogRead(A5) >= jishuyuzhi) {
state2 = 1;
heixian2 = heixian2 + 1;
}
break;
case 1:
if (analogRead(A5) <= jishuyuzhi) {
state2 = 0;
}
break;
}
//比较heixian1的值和heixian2的值,取最大值,防止黑线漏记
if (heixian1 >= heixian2) {
heixian = heixian1;
} else {
heixian = heixian2;
}
}
-
遇到的问题及解决方案
为了保证数据的准确性,有时候需要给路口赋值,切记在赋值时,要给heixian1,heixian2,heixian,这三个值同时赋值.
4.巡线函数
-
代码展示
//根据A0,A1,A2,A3值的变化实时的调整电机转速的变化 void xunxian() { if ((analogRead(A0) <= xunxianyuzhi && analogRead(A1) >= xunxianyuzhi) && (analogRead(A2) <= xunxianyuzhi && analogRead(A3) <= xunxianyuzhi)) { analogWrite(5,255-(zmoto - analogRead(A1) * 0.07)); digitalWrite(4,1); analogWrite(6,255-(ymoto + analogRead(A1) * 0.07)); digitalWrite(7,1); } else if ((analogRead(A0) <= xunxianyuzhi && analogRead(A1) <= xunxianyuzhi) && (analogRead(A2) >= xunxianyuzhi && analogRead(A3) <= xunxianyuzhi)) { analogWrite(5,255-(zmoto + analogRead(A2) * 0.07)); digitalWrite(4,1); analogWrite(6,255-(ymoto - analogRead(A2) * 0.07)); digitalWrite(7,1); } else if ((analogRead(A0) >= xunxianyuzhi && analogRead(A1) <= xunxianyuzhi) && (analogRead(A2) <= xunxianyuzhi && analogRead(A3) <= xunxianyuzhi)) { analogWrite(5,255-(zmoto - analogRead(A1) * 0.1)); digitalWrite(4,1); analogWrite(6,255-(ymoto + analogRead(A1) * 0.1)); digitalWrite(7,1); } else if ((analogRead(A0) <= xunxianyuzhi && analogRead(A1) <= xunxianyuzhi) && (analogRead(A2) <= xunxianyuzhi && analogRead(A3) >= xunxianyuzhi)) { analogWrite(5,255-(zmoto + analogRead(A3) * 0.1)); digitalWrite(4,1); analogWrite(6,255-(ymoto - analogRead(A3) * 0.1)); digitalWrite(7,1); } else if ((analogRead(A0) >= xunxianyuzhi && analogRead(A1) >= xunxianyuzhi) && (analogRead(A2) <= xunxianyuzhi && analogRead(A3) <= xunxianyuzhi)) { analogWrite(5,255-(zmoto - (analogRead(A0) + analogRead(A1)) * 0.1)); digitalWrite(4,1); analogWrite(6,255-(ymoto + (analogRead(A0) + analogRead(A1)) * 0.1)); digitalWrite(7,1); } else if ((analogRead(A0) <= xunxianyuzhi && analogRead(A1) <= xunxianyuzhi) && (analogRead(A2) >= xunxianyuzhi && analogRead(A3) >= xunxianyuzhi)) { analogWrite(5,255-(zmoto + (analogRead(A2) + analogRead(A3)) * 0.1)); digitalWrite(4,1); analogWrite(6,255-(ymoto - (analogRead(A2) + analogRead(A3)) * 0.1)); digitalWrite(7,1); } else { analogWrite(5,255-zmoto); digitalWrite(4,1); analogWrite(6,255-ymoto); digitalWrite(7,1); } }
-
可以优化的点
为了防止电机改变的变化值超过最大值255,本设计采用的是得到的值乘以一个小数,可采用映射的方法,不需要采用乘以一个小数的方法,既解决运算的麻烦,又可以不让值超限.如下所示
analogWrite(5,255-(zmoto + (map(analogRead(A0), 30, 800, 0, 50)))); digitalWrite(4,1);
5.修正函数
-
代码展示
//当小车转弯后,可能小车有部分的偏移,可以采用像巡线函数一样,进行车身的微调.只进行一次判读,车身只能能做到轻微调整不能保证,车声完全正. void panduan() { while (!((analogRead(A0) <= xunxianyuzhi && analogRead(A1) <= xunxianyuzhi) && (analogRead(A2) <= xunxianyuzhi && analogRead(A3) <= xunxianyuzhi))) { if ((analogRead(A0) <= xunxianyuzhi && analogRead(A1) >= xunxianyuzhi) && (analogRead(A2) <= xunxianyuzhi && analogRead(A3) <= xunxianyuzhi)) { zuozhuan(); delay(5); zuojisha(); delay(2); } else if ((analogRead(A0) <= xunxianyuzhi && analogRead(A1) <= xunxianyuzhi) && (analogRead(A2) >= xunxianyuzhi && analogRead(A3) <= xunxianyuzhi)) { youzhuan(); delay(5); youjisha(); delay(2); } else if ((analogRead(A0) >= xunxianyuzhi && analogRead(A1) <= xunxianyuzhi) && (analogRead(A2) <= xunxianyuzhi && analogRead(A3) <= xunxianyuzhi)) { zuozhuan(); delay(5); zuojisha(); delay(2); } else if ((analogRead(A0) <= xunxianyuzhi && analogRead(A1) <= xunxianyuzhi) && (analogRead(A2) <= xunxianyuzhi && analogRead(A3) >= xunxianyuzhi)) { youzhuan(); delay(5); youjisha(); delay(2); } else if ((analogRead(A0) >= xunxianyuzhi && analogRead(A1) >= xunxianyuzhi) && (analogRead(A2) <= xunxianyuzhi && analogRead(A3) <= xunxianyuzhi)) { zuozhuan(); delay(5); zuojisha(); delay(2); } else if ((analogRead(A0) <= xunxianyuzhi && analogRead(A1) <= xunxianyuzhi) && (analogRead(A2) >= xunxianyuzhi && analogRead(A3) >= xunxianyuzhi)) { youzhuan(); delay(5); youjisha(); delay(2); } else { break; } } qianjisha(); delay(100); }
-
遇到的问题及解决方案
在刚开始编写函数的时候条件写的是车身完全回到中心位置之后才停止调整,使用了while函数,导致小车车身状态一直调制无法跳出while循环,后来改成只有一个if判断,函数只执行一次.
6.急停函数
-
代码展示
//为了使前进的小车快速的停止,让电机停止的时候先反转一下,再停止,因为本设计代码没有采用电机pid控制,只能能采用这种方法. void jiting() { if (!ting) { analogWrite(5,(zmoto + 20)); digitalWrite(4,0); analogWrite(6,(ymoto + 20)); digitalWrite(7,0); delay(20); ting = 1; } digitalWrite(5,0); digitalWrite(4,0); digitalWrite(6,0); digitalWrite(7,0); } //因为电机只反转很少的时间所以,在执行玩急停函数之后要进行代码重置 void chongzhi() { ting = 0; }
-
遇到的问题
在遇到电机转速过低或者前进一点点距离就进行急停的话可能会造成电机后退.
7.抓杯子函数
- 代码展示
//如果舵机角度转变过快,可能会造成杯子脱落,所以在舵机角度变化的时候采用的是利用for循环逐渐变化的特点.防止电机变换过快.
void zhua() {
jiting();
delay(100);
panduan();
chongzhi();
if (checkdistance_2_3() >= 5) {//停的位置有点远,会进行二次调整
while (checkdistance_2_3() >= 5) {
xunxian();
}
}
jiting();
delay(100);
for (int i = (zuigao); i <= (zhongjian); i = i + (1)) {
servo_13.write(i);
delay(0);
delay(12);
}
delay(300);
for (int i = (zuijin); i <= (zhongjiansong); i = i + (1)) {
servo_12.write(i);
delay(0);
delay(12);
}
delay(50);
for (int i = (zhongjiansong); i <= (zuisong); i = i + (1)) {
servo_12.write(i);
delay(0);
delay(4);
}
delay(30);
for (int i = (zhongjian); i <= (zuidi); i = i + (1)) {
servo_13.write(i);
delay(0);
delay(4);
}
delay(300);
for (int i = (zuisong); i >= (zuijin); i = i + (-1)) {
servo_12.write(i);
delay(0);
delay(4);
}
delay(50);
for (int i = (zuidi); i >= (zhongjian); i = i + (-1)) {
servo_13.write(i);
delay(0);
delay(5);
}
for (int i = (zhongjian); i >= (zuigao); i = i + (-1)) {
servo_13.write(i);
delay(0);
delay(4);
}
chongzhi();
}
8.主函数总结
- 部分代码展示
if (anjian == 2) {
Serial.print("*****");
Serial.println(heixian);
beizipanduan();
if (k == 0) {//转弯的时候让k赋值为1,不进行路口计算,防止计数错乱.转弯完成后在进行强制赋值.
lukoujisuan();
} else {
}
switch (beizi) {
case 0:
xunxian();
break;
case 1:
diyicizhua();
beizi = 2;
fuzhi(0);
break;
case 3://每次抓完杯子之后,给杯子数赋值+1,保证数据的准确性,防止多加
zhua();
fuzhi(2);
beizi = 4;
break;
case 5:
zhua();
fuzhi(5);
beizi = 6;
break;
case 7:
zhua();
fuzhi(7);
beizi = 8;
break;
case 9:
zhua();
fuzhi(10);
beizi = 10;
break;
case 11:
diyicizhua();
fuzhi(22);
beizi = 12;
break;
case 13:
zhua();
fuzhi(24);
beizi = 14;
break;
case 15:
zhua();
fuzhi(29);
beizi = 16;
break;
case 17:
zhua();
fuzhi(30);
beizi = 18;
break;
case 19:
zhua();
fuzhi(41);
beizi = 20;
break;
default:
switch (heixian) {//路口判断
case 2://case函数每一种结果尽量都写上,放置数据错乱
xunxian();
break;
case 3:
xunxian();
break;
case 4:
zuozhuanwan(5, 0);
break;
case 5:
xunxian();
break;
case 6:
xunxian();
break;
case 7:
xunxian();
break;
case 8:
xunxian();
break;
case 9:
zuozhuanwan(10, 0);
break;
case 10:
xunxian();
break;
case 11:
zuozhuanwan(12, 0);
break;
case 12:
xunxian();
break;
case 13:
fang();
delay(200);
houtui();
delay(80);
youzhuanwan180();
fuzhi(18);
break;
case 18:
xunxian();
break;
case 19:
zuozhuanwan(20, 0);
break;
case 20:
xunxian();
break;
case 21:
zuozhuanwan(22, 0);
break;
case 22:
xunxian();
break;
case 23:
xunxian();
break;
case 24:
xunxian();
break;
case 25:
youzhuanwan(26, 0);
break;
case 26:
youzhuanwan(27, 0);
break;
case 27:
xunxian();
break;
case 28:
zuozhuanwan(29, 0);
break;
case 29:
xunxian();
break;
case 30:
xunxian();
break;
case 31:
youzhuanwan(32, 0);
break;
case 32:
youzhuanwan(33, 0);
break;
case 33:
xunxian();
break;
case 34:
xunxian();
break;
case 35:
k = 1;
while (checkdistance_2_3() >= 16.3) {
xunxian();
}
qianjisha();
delay(200);
fang();
for (int i = 1; i <= 400; i = i + (1)) {
houtui();
delay(1);
}
houjisha();
delay(200);
fuzhi(36);
break;
case 36:
tingzhi();
delay(3000);
anjian = 0;
break;
default:
xunxian();
chongzhi();
break;
}
break;
}