比赛终于结束了,终于可以好好睡一觉了。比赛从四月份延到了七月份,但这丝毫没有影响我把这比赛硬生生打成电赛,前前后后制作的时间加起来可能也就只有完完整整一星期吧,还能拿到省三我已经很满足了。就开个贴给今后要参加比赛的同学们讲讲这段经历,讲讲我觉得比较重要的几点。


队友


  有没有好队友非常的关键,有两个好队友就成功了一半。我做的是超市购物组,相对来说比较好的搭配是一个机械的一个信电的一个计算的。不管是从车的结构还是底层驱动还是图像识别上都能够考虑到。由于我们组没有学机械的同学,对于一些车的结构来说虽然我们可以想到,但是要是做出来非常困难,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口也比较充足,串口也很多,完全足够连接各种模块。我也简单讲讲我都实现思路。下图是一开始没有考虑到出发区的红色,现场马上改了一下。



  修改之后是这样



  只走了外面的一大圈,但是拿奖还是够了,如果想冲省一,中间还是要拿的。


  附上我小车所有底盘代码:


  1. #define jidianqi 13
  2. #define wheel_left_en 44
  3. #define wheel_right_en 45
  4. #define left_wheel_in1 46
  5. #define left_wheel_in2 47
  6. #define right_wheel_in3 48
  7. #define right_wheel_in4 49
  8. #define qti_left 53 //黑色返回1
  9. #define qti_right 52
  10. #define f_qti1 38
  11. #define f_qti2 39
  12. #define f_qti3 40
  13. #define f_qti4 41
  14. #define f_qti5 42
  15. #define f_qti6 43
  16. #define b_qti1 32
  17. #define b_qti2 33
  18. #define b_qti3 34
  19. #define b_qti4 35
  20. #define b_qti5 36
  21. #define b_qti6 37
  22. unsigned char action0[5] = {0xFF, 0x09, 0x00, 0x00, 0x00};
  23. unsigned char action1[5] = {0xFF, 0x09, 0x00, 0x01, 0x00};
  24. unsigned char action2[5] = {0xFF, 0x09, 0x00, 0x02, 0x00};
  25. unsigned char action3[5] = {0xFF, 0x09, 0x00, 0x03, 0x00};
  26. unsigned char action4[5] = {0xFF, 0x09, 0x00, 0x04, 0x00};
  27. unsigned char action5[5] = {0xFF, 0x09, 0x00, 0x05, 0x00};
  28. unsigned char action6[5] = {0xFF, 0x09, 0x00, 0x06, 0x00};
  29. unsigned char f_QTIS = 0xff;
  30. unsigned char b_QTIS = 0xff;
  31. unsigned char s_QTIS = 0xff;
  32. int crossing_counts = 0;
  33. byte stop_flag = 0; //路口停止位,stop为1表示车停止,stop为0表示车未停止
  34. int incomingByte = 0; // for incoming serial data
  35. int result[7] = {};
  36. int count = 0;
  37. void setup() {
  38. delay(13000);
  39. car_init();
  40. motor_motion(80,80);
  41. delay(2000);
  42. stop_car();
  43. start_car();
  44. }
  45. void loop(){
  46. while(!stop_flag)
  47. Robot_hunting_judge_crossing();
  48. if((crossing_counts == 1)||(crossing_counts==2))
  49. {
  50. turn_left();
  51. start_car();
  52. motor_motion(255,255);
  53. delay(300);
  54. }
  55. else if(crossing_counts==3)
  56. {
  57. turn_right();
  58. start_car();
  59. motor_motion(255,255);
  60. delay(300);
  61. }
  62. 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))
  63. {
  64. start_car();
  65. motor_motion(255,255);
  66. delay(300);
  67. }
  68. 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)))
  69. {
  70. send_recieve_smd();
  71. how_carry_things();
  72. start_car();
  73. motor_motion(255,255);
  74. delay(300);
  75. }
  76. else if((crossing_counts==9)||(crossing_counts==18)||(crossing_counts==27)||(crossing_counts==36))
  77. {
  78. send_recieve_smd();
  79. how_carry_things();
  80. turn_right();
  81. start_car();
  82. motor_motion(255,255);
  83. delay(300);
  84. }
  85. else if(crossing_counts==37)
  86. {
  87. park_car();
  88. }
  89. stop_flag = 0;
  90. }
  91. void car_init() //小车所用各个引脚初始化
  92. {
  93. Serial.begin (115200);
  94. Serial1.begin (115200);
  95. Serial3.begin (9600);
  96. //初始化各IO,模式为OUTPUT 输出模式
  97. pinMode(jidianqi,OUTPUT);
  98. digitalWrite(jidianqi,LOW);
  99. pinMode(wheel_right_en,OUTPUT);
  100. pinMode(wheel_left_en,OUTPUT);
  101. pinMode(left_wheel_in1,OUTPUT);
  102. pinMode(left_wheel_in2,OUTPUT);
  103. pinMode(right_wheel_in3,OUTPUT);
  104. pinMode(right_wheel_in4,OUTPUT);
  105. digitalWrite(wheel_right_en,HIGH); //给高电平
  106. digitalWrite(wheel_left_en,HIGH); //给高电平
  107. pinMode(f_qti1,INPUT);
  108. pinMode(f_qti2,INPUT);
  109. pinMode(f_qti3,INPUT);
  110. pinMode(f_qti4,INPUT);
  111. pinMode(f_qti5,INPUT);
  112. pinMode(f_qti6,INPUT);
  113. pinMode(b_qti1,INPUT);
  114. pinMode(b_qti2,INPUT);
  115. pinMode(b_qti3,INPUT);
  116. pinMode(b_qti4,INPUT);
  117. pinMode(b_qti5,INPUT);
  118. pinMode(b_qti6,INPUT);
  119. pinMode(qti_left,INPUT);
  120. pinMode(qti_right,INPUT);
  121. digitalWrite(left_wheel_in1,HIGH); //给高电平
  122. digitalWrite(left_wheel_in2,LOW); //给低电平
  123. digitalWrite(right_wheel_in3,HIGH); //给高电平
  124. digitalWrite(right_wheel_in4,LOW); //给低电平
  125. send_arm_action(action0);
  126. }
  127. int f_readqti() //读取前方QTI的值
  128. {
  129. int f_qti,f_q1,f_q2,f_q3,f_q4,f_q5,f_q6;
  130. f_q1=digitalRead(f_qti1);
  131. f_q2=digitalRead(f_qti2);
  132. f_q3=digitalRead(f_qti3);
  133. f_q4=digitalRead(f_qti4);
  134. f_q5=digitalRead(f_qti5);
  135. f_q6=digitalRead(f_qti6);
  136. f_qti=32_f_q1+16_f_q2+8_f_q3+4_f_q4+2_f_q5+f_q6;
  137. f_qti=f_qti & 0x3f;
  138. return f_qti;
  139. }
  140. int b_readqti() //读取后方QTI的值
  141. {
  142. int b_qti,b_q1,b_q2,b_q3,b_q4,b_q5,b_q6;
  143. b_q1=digitalRead(b_qti1);
  144. b_q2=digitalRead(b_qti2);
  145. b_q3=digitalRead(b_qti3);
  146. b_q4=digitalRead(b_qti4);
  147. b_q5=digitalRead(b_qti5);
  148. b_q6=digitalRead(b_qti6);
  149. b_qti=32_b_q1+16_b_q2+8_b_q3+4_b_q4+2_b_q5+b_q6;
  150. b_qti=b_qti & 0x3f;
  151. return b_qti;
  152. }
  153. int side_readqti() //读取两边QTI的值
  154. {
  155. int s_qti,l_q,r_q;
  156. l_q=digitalRead(qti_left);
  157. r_q=digitalRead(qti_right);
  158. s_qti=2_l_q+r_q;
  159. s_qti=s_qti & 0x03;
  160. return s_qti;
  161. }
  162. void Robot_hunting()
  163. {
  164. f_QTIS = f_readqti();
  165. switch (f_QTIS)
  166. {
  167. case 51:motor_motion(150, 150);break; //1 110011 1,直行
  168. case 31:motor_motion(0, 255);break; //011111,大幅左转
  169. case 15:motor_motion(0, 200);break; //001111,大幅左转
  170. case 7:motor_motion(0, 150);break; //000111,小幅左转
  171. case 39:motor_motion(0, 100);break; //100111,小幅左转
  172. case 35:motor_motion(0, 100);break; //100011,小幅左转
  173. case 55:motor_motion(0, 100);break; //110111,小幅左转
  174. case 57:motor_motion(100, 0);break; //111001,小幅右转
  175. case 49:motor_motion(100, 0);break; //110001,小幅右转
  176. case 59:motor_motion(100, 0);break; //111011,小幅右转
  177. case 56:motor_motion(150, 0);break; //111000,小幅右转
  178. case 60:motor_motion(200, 0);break; //111100,大幅右转
  179. case 62:motor_motion(255, 0);break; //111110,大幅右转
  180. default:motor_motion(150, 150);break;
  181. }
  182. delay(5);
  183. }
  184. void Robot_hunting_judge_crossing() //小车巡线加判断路口
  185. {
  186. f_QTIS = f_readqti();
  187. s_QTIS = side_readqti();
  188. //Serial.print(“f_QTIS=”);
  189. //Serial.println(s_QTIS);
  190. if(s_QTIS==0){
  191. stop_car();
  192. delay(20);
  193. stop_flag = 1;
  194. crossing_counts++;
  195. }
  196. switch (f_QTIS)
  197. {
  198. case 51:motor_motion(80, 80);break; //1 110011 1,直行
  199. case 31:motor_motion(0, 255);break; //011111,大幅左转
  200. case 15:motor_motion(0, 200);break; //001111,大幅左转
  201. case 7:motor_motion(0, 150);break; //000111,小幅左转
  202. case 39:motor_motion(0, 100);break; //100111,小幅左转
  203. case 35:motor_motion(0, 100);break; //100011,小幅左转
  204. case 55:motor_motion(0, 100);break; //110111,小幅左转
  205. case 57:motor_motion(100, 0);break; //111001,小幅右转
  206. case 49:motor_motion(100, 0);break; //110001,小幅右转
  207. case 59:motor_motion(100, 0);break; //111011,小幅右转
  208. case 56:motor_motion(150, 0);break; //111000,小幅右转
  209. case 60:motor_motion(200, 0);break; //111100,大幅右转
  210. case 62:motor_motion(255, 0);break; //111110,大幅右转
  211. default:motor_motion(80, 80);break;
  212. }
  213. delay(5);
  214. }
  215. void motor_motion(unsigned int left_val, unsigned int right_val)
  216. {
  217. analogWrite(wheel_right_en,right_val);
  218. analogWrite(wheel_left_en,left_val);
  219. }
  220. void turn_right()
  221. {
  222. digitalWrite(left_wheel_in1,HIGH); //给高电平
  223. digitalWrite(left_wheel_in2,LOW); //给低电平
  224. digitalWrite(right_wheel_in3,LOW);
  225. digitalWrite(right_wheel_in4,HIGH);
  226. motor_motion(255, 255);
  227. delay(600);
  228. while(1){
  229. f_QTIS = f_readqti();
  230. if((f_QTIS == 51)||(f_QTIS == 49)||(f_QTIS == 35)||(f_QTIS == 57)||(f_QTIS == 39))
  231. {
  232. stop_car();
  233. break;
  234. }
  235. }
  236. }
  237. /_
  238. void turn_left()
  239. {
  240. digitalWrite(left_wheel_in1,LOW); //给高电平
  241. digitalWrite(left_wheel_in2,HIGH); //给低电平
  242. digitalWrite(right_wheel_in3,HIGH);
  243. digitalWrite(right_wheel_in4,LOW);
  244. motor_motion(255, 255);
  245. delay(1800);
  246. stop_car();
  247. }
  248. void turn_right()
  249. {
  250. digitalWrite(left_wheel_in1,HIGH); //给高电平
  251. digitalWrite(left_wheel_in2,LOW); //给低电平
  252. digitalWrite(right_wheel_in3,LOW);
  253. digitalWrite(right_wheel_in4,HIGH);
  254. motor_motion(255, 255);
  255. delay(1800);
  256. stop_car();
  257. }
  258. */
  259. void turn_left()
  260. {
  261. digitalWrite(left_wheel_in1,LOW); //给高电平
  262. digitalWrite(left_wheel_in2,HIGH); //给低电平
  263. digitalWrite(right_wheel_in3,HIGH);
  264. digitalWrite(right_wheel_in4,LOW);
  265. motor_motion(255, 255);
  266. delay(600);
  267. while(1){
  268. f_QTIS = f_readqti();
  269. //b_QTIS = b_readqti();
  270. if((f_QTIS == 51)||(f_QTIS == 49)||(f_QTIS == 35)||(f_QTIS == 57)||(f_QTIS == 39))
  271. {
  272. stop_car();
  273. break;
  274. }
  275. }
  276. }
  277. void stop_car(){
  278. digitalWrite(left_wheel_in1,LOW);
  279. digitalWrite(left_wheel_in2,LOW);
  280. digitalWrite(right_wheel_in3,LOW);
  281. digitalWrite(right_wheel_in4,LOW);
  282. }
  283. void start_car()
  284. {
  285. digitalWrite(left_wheel_in1,HIGH); //给高电平
  286. digitalWrite(left_wheel_in2,LOW); //给低电平
  287. digitalWrite(right_wheel_in3,HIGH); //给高电平
  288. digitalWrite(right_wheel_in4,LOW); //给低电平
  289. }
  290. void send_arm_action(unsigned char action[])
  291. {
  292. for(int i = 0; i < 5; i++)
  293. {
  294. Serial3.write(action[i]);
  295. delay(50);
  296. }
  297. }
  298. void send_recieve_smd()
  299. {
  300. delay(1000);
  301. Serial1.write(49);
  302. delay(20);
  303. while(Serial1.read() >= 0){}//清空缓存,避免缓存的串口数据的影响
  304. while(incomingByte != 55)
  305. {
  306. if (Serial1.available() > 0) {
  307. // read the incoming byte:
  308. incomingByte = Serial1.read();
  309. result[count] = incomingByte;
  310. count++;
  311. // say what you got:
  312. //Serial.print(“I received: “);
  313. //Serial.println(incomingByte, DEC);
  314. }
  315. }
  316. incomingByte = 0;
  317. }
  318. void how_carry_things()
  319. {
  320. if((count == 1)||(count>7))
  321. ;
  322. else
  323. {
  324. for(int i = 0; i < (count-1); i++)
  325. {
  326. if(result[i] == 49)
  327. {
  328. send_arm_action(action1);
  329. delay(15000);
  330. }
  331. else if(result[i] == 50)
  332. {
  333. send_arm_action(action2);
  334. delay(15000);
  335. }
  336. else if(result[i] == 51)
  337. {
  338. send_arm_action(action3);
  339. delay(15000);
  340. }
  341. else if(result[i] == 52)
  342. {
  343. send_arm_action(action4);
  344. delay(20000);
  345. }
  346. else if(result[i] == 53)
  347. {
  348. send_arm_action(action5);
  349. delay(16000);
  350. }
  351. else if(result[i] == 54)
  352. {
  353. send_arm_action(action6);
  354. delay(20000);
  355. }
  356. else
  357. ;
  358. }
  359. }
  360. count = 0;
  361. }
  362. void park_car()
  363. {
  364. turn_right();
  365. digitalWrite(jidianqi,HIGH);
  366. }



   代码其实并不多,也就三百行。loop里其实是一直在找路口,然后根据路口的计数来判断应该做什么,还是非常简洁的,和上面的图一起看还是很清楚的。但是就是有一个很大的问题,就是转弯按照我这么写其实很不好,但是由于时间有限,我也没有什么好想法,在一个由于车的重心没有完全在中心点,或多或少都有问题,只能靠前面的巡线传感器巡线把车身摆正。串口三和舵机控制板连,串口一和树莓派连。购物车靠电磁铁拉着。


 暂且就写这么多了,以后想到有什么可以补充再补充。也欢迎大家下面评论区交流,虽然这是我的第一次机器人竞赛,也是我最后一次机器人竞赛了。希望大家在竞赛中都能有好运气好名次。