2021全国大学生电子设计竞赛F题智能送药小车
前提:本篇文章重在分享自己的心得与感悟,我们把最重要的部分,摄像头循迹,摄像头数字识别问题都解决了,有两种方案一种是openARTmini摄像头进行数字识别加寻迹,即融合代码。另一种是使用openmv4进行数字识别(使用的是模板匹配),然后利用灰度传感器进行寻迹。因为当时python用得不算很熟,最终我们选择了第二种方案使open MV4实现数字识别,灰度传感器寻迹,在控制智能车运动调试的过程中更加简单。当然赛后我们也尝试了使用open ARTmini的方案,同样操作容易。其次我们下来也做了方案三K210数字识别,数字识别率可达97.8%,使用openmv寻迹。
人们常常希望向成功者获取经验,可是生活中哪有那么多成功者,我想只有失败者才最有发言权,最有经验可以分享吧。因为某些原因我们无缘国奖,但我不甘是失败者,我们为此付出了多少,坚持了多少,只有我们自己知道,只要全力以赴就无所谓失败,也许多年后再回过头来看,想起来这些热血我依然能够热泪盈眶,那就无悔了。
追求卓越,成功才会在不经意见追上你!
目录:
一、题目分析
二、分工以及小车的搭建
三、摄像头部分
四、控制部分
五、联调
六、随谈
七、工程代码
备注:
只需要工程代码的同学(如下是此次电赛过程中所有的代码),建议先看完正文
openMV4模板匹配数字识别方案,本次电赛所有工程代码包括论文报告
https://download.csdn.net/download/cubejava/79012510
openmv巡线代码:
https://download.csdn.net/download/cubejava/41873305
stm32HAL库keil工程(配置freeRTOS,巡线,自动返回,定点停车):
https://download.csdn.net/download/cubejava/41871669
openARTmini巡线和数字识别的融合代码:
https://download.csdn.net/download/cubejava/79012645
小车底板AD原理图和pcb工程:
https://download.csdn.net/download/cubejava/42512528
(可私聊提供技术支持,代码注释完善,若有不懂的可手把手教学)
正文
一、题目
我们是做测控方向的,当时在4号早上7:30左右题目就出来了,随即就有人发在了群里。
看了一下测控方向除了无人机能选的也就D题和F题了,D题是基于互联网的摄像测量系统(D题),我们对这方面的了解不多,没有什么把握,就没敢选,后来听说选D题的在GitHub上能够找到源码,做到后面车调不动时倒有点小后悔,哈哈。
F题经典小车题目,但与往年不同的是今年识别和控制都是重点,必须要先能做到识别,你才能谈接下来的循迹和运动控制。
谈到识别就需要用到摄像头,但之前我们对于摄像头并没有太重视,循迹一般用的都是红外对管,灰度传感器,或者线性CCD就足够了,但是这次的赛到元素存在十字,黑白色块,还要求能自动返回,使用普通的循迹模块就比较吃力了。在电赛备赛期间就做过线性CCD寻迹的智能车,使用的stm32f4,HAL库,cube MX配置的。采取了二值化,动态阈值算法,将CCD采集的值滤波,导入PID,再加入速度环,实现串级PID。也算是练了练手。
工程链接:https://download.csdn.net/download/cubejava/79012959
二、分工以及小车的搭建
我们组三人都没有玩过摄像头,要立马开始现学,我们一开始准备使用openmv进行循迹和数字识别,后来发现我们的openmv的版本不能训练神经网络,必须要openmvplus(可是没有,买又来不急送到),就很难受。
于是我们想采用openARTMiNi进行数字识别加循迹,但由于python用得不熟,当时没有足够的把握能够实现。所以我们采取了折中的办法,即文章开头说的,openMV使用模板匹配识别数字,寻迹使用灰度传感器。
其中一位学长着手完成openMV模板匹配识别数字,另和我则负责stm32f4的工程创建小车的底层代码和运动控制,使用的HAL库。
由于之前备赛准备的东西比较充分,所以当天上午就把车给搭建好了,使用平衡小车,加了一个万向轮,转弯差速控制。
小车硬件:
STM32F411CEU6,TB6612,车模(自带霍尔编码器减速电机),LM2596,MPU6050,航模电池,openMV,K210
使用AD画小车底板。原理图和PCB如下
小车软件:
使用的HAL库建立工程,在cubeMX中配置freeRTOS操作系统,
KEIL工程程序代码:
串口重定向printf
/*串口重定向printf*/int fputc(int ch, FILE *f){ HAL_UART_Transmit(&huart6, (uint8_t *)&ch, 1, 1000); return ch;}
编码器模式取值
/*编码器模式取值*/int Read_Encoder(uint8_t TIMX) { int Encoder_TIM; switch(TIMX) { case 1: Encoder_TIM= (short)TIM1 -> CNT; TIM1 -> CNT=0;break; case 2: Encoder_TIM= (short)TIM2 -> CNT; TIM2 -> CNT=0;break; default: Encoder_TIM=0; }return Encoder_TIM;}
判断mpu6050初始化是否成功
void Mpu6050_Init(){while(w_mpu_init() != mpu_ok) {printf("0x%x (ID_ERROR)\r\n", w_mpu_init());HAL_Delay(10);}/*DMP初始化*/dmp_init(); }
初始化程序
int main(void){/*开启编码器计数*/HAL_TIM_Encoder_Start(&htim1, TIM_CHANNEL_ALL);HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);/*开启电机PWM输出*/HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);/*开启舵机的PWM输出*/HAL_TIM_PWM_Start(&htim5,TIM_CHANNEL_3);HAL_TIM_PWM_Start(&htim5,TIM_CHANNEL_4);/*Enable the USART1 Interrupt*/HAL_UART_Receive_IT(&huart1,(uint8_t *)&usart6_rxbuff,1);/*临时*/Temporary();Mpu6050_Init();/*使能定时器4中断*/HAL_TIM_Base_Start_IT(&htim4);HAL_UART_Transmit(&huart6, (uint8_t *)&cc, 1,0xFFF);//TIM3 ->CCR1 = 400;//TIM3 ->CCR2 = 400; /* USER CODE END 2 */ /* Call init function for freertos objects (in freertos.c) */ MX_FREERTOS_Init();HAL_UART_Transmit(&huart6, (uint8_t *)&cc, 1,0xFFF); /* Start scheduler */ osKernelStart(); while (1) { /* USER CODE END WHILE *///printf("-------------\r\n");//HAL_Delay(500); /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */}
freeRTOS.C文件中的代码
巡线和功能实现代码均放在freeRTOS.C中
三、摄像头部分
有关摄像头部分,我们在第一天尝试过单使用openMV寻迹,但神经网络训练不了就弃置了。
在openmv中处理好数据,可以寻红线,识别十字,识别黑白色块,
传回来的数据:
左右线传回为模拟量,传入单片机,进行pid控制
openMV模板匹配识别数字:
可实现一排识别多个数字。
四、控制部分
控制部分,巡线使用的PD控制
首先速度闭环,PI控制,让小车保持在一个稳定的速度
在速度环上叠加一层方向环,PD控制,巡线
int track_control(float collect_openmv){static float last_bias,Bias,kp=0.75,Kd=0.25;//0.5的效果还行float Turn; Bias=-(collect_openmv-60);Turn=-Bias*kp-Kd*(Bias-last_bias);last_bias = Bias;//printf("\r\nt:%.1f\r\n",Turn);return Turn;}
小车的自动返回,和判定十字转弯,黑白色块停车使用了标志位,定义数组,根据出栈入栈原理,记录去回的方向。
五、联调
openmv和K210的数据传入单片机
打开串口中断,注意需要写好摄像头和单片机之间的通信协议,如下:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) //串口中断,遥控部分。{uint16_t tem;if(huart->Instance == USART1)// 判断是由哪个串口触发的中断{//printf("ok");tem = usart6_rxbuff;printf("%x\r\n",tem);if(RxState==0&&tem==0x79){RxState=1;RxBuffer1[RxCounter1++]=tem;}else if(RxState==1){RxBuffer1[RxCounter1++]=tem;if(RxCounter1>=10||tem == 0x85) //RxBuffer1接受满了,接收数据结束{RxState=2;} } else if(RxState==2)//检测是否接受到结束标志 {if(RxBuffer1[RxCounter1-1] == 0x85){if(RxBuffer1[1] == 0xaa)//识别{Flag = Flag + 1;JG=RxBuffer1[RxCounter1-2];Turn = RxBuffer1[RxCounter1-3];if(Flag>3){if(JG>0&&FS==0) {FS = JG;}printf("\r\n Turn:%d FS:%d \r\n",Turn,FS);}}if(RxBuffer1[1] == 0xbb)//搜线{printf("\r\n___sou xian___\r\n");printf("\r\n Cx:%d, STOP:%d \r\n",Cx,STOP);Cx=RxBuffer1[RxCounter1-2];STOP=RxBuffer1[RxCounter1-3];}RxCounter1 = 0;RxState = 0;}else //接收错误{RxState = 0;RxCounter1=0;for(i=0;i<10;i++){RxBuffer1[i]=0x00; //将存放数据数组清零}printf("接收错误\r\n");}} if(state==2) { if(STOP == 0) { state = 0; } } if(state==4) { if(STOP == 0) { state = 0; } }}HAL_UART_Receive_IT(&huart1, (uint8_t *)&usart6_rxbuff, 1); }
六、随谈
我们是在结束的前一天晚上完成好的数字识别,离调车整个程序的逻辑就只有一天的时间,说真的,程序写少了,这个程序逻辑真的很难想出来,尤其是在当时已经连续三天每天只睡2个小时左右,头脑非常混沌,呆滞。
还是要多写程序,练习自己写程序的逻辑思维。
结尾:第一次参加国赛,也是第一次参加电赛,本来应该是大一结束就开始了的,电赛延期拖到了大二开学后的一段时间,自己需要学的内容还有很多,好好学习,提升自己的技术,明年省赛继续冲!
七、 工程代码:
openMV4模板匹配数字识别方案,本次电赛所有工程代码包括论文报告
https://download.csdn.net/download/cubejava/79012510
openmv巡线代码:
https://download.csdn.net/download/cubejava/41873305
stm32HAL库keil工程(配置freeRTOS,巡线,自动返回,定点停车):
https://download.csdn.net/download/cubejava/41871669
openARTmini巡线和数字识别的融合代码:
https://download.csdn.net/download/cubejava/79012645
小车底板AD原理图和pcb工程:
https://download.csdn.net/download/cubejava/42512528
备注:这些都是我们没日没夜熬出来的程序代码,花费了很多精力,所以没有免费开源(望理解),设置了付费,不过在博客里也写了相关的思路,可以参考借鉴一些经验,来年省赛加油!!!
现在电赛越来越趋向于视觉和人工智能了,一定要好好学习摄像头,视觉,神经网络。