2021年全国大学生电子设计大赛F题——智能送药小车,全方位解决方案+程序代码(详细注释)山东赛区国奖

2021年全国大学生电子设计大赛F题——智能送药小车,全方位解决方案+程序代码(详细注释)山东赛区国奖 目录1.赛题及硬件方案分析2.用到的主要器件清单3.各部分思路及代码实现(1).小车舵机、马达驱动(2).蓝牙通信(3).单片机与OpenMV的串口通信(4).单片机与OpenMV的通信协议(5).单片机main文件中的函数(6).巡线(7).识别十字路口(8).数字识别(9).远端病房四个并排数字识别(10).返回时倒车4.总结感言1.赛题及硬件方案分析刚得到消息我们队被推荐为国奖国一国二还没定大概率是国二因为测评时发挥部分的小车二在转弯时因为电池原因急转弯失败发挥部分就没有成功验收。刚收到消息就更新了一下本方案的成本可以说是此赛题方案中成本最低的。通过题目可以很容易的看出来这道题有最难的两个点巡线数字识别。此题的巡线又不同于以往电赛题目的黑白巡线此题是巡红白线。这样就会带来一个问题以往巡黑白线我们最常用的是红外传感器但是此题中的红线和白色背景两者间的吸光能力差别很小一般的红外传感器根本难以分辨这就需要用到较为不常用的灰度传感器或者是颜色传感器。据我所知开赛前就已经准备好这两种传感器的小组还是较少的包括我们组就没有准备只能用摄像头代替用对应的算法进行巡线。数字识别用到的硬件就没什么好说的了肯定用到摄像头我们组用的是赛前已经准备好的OpenMVOpenMV上手还是很快的但是需要有一定的Python基础 。在比赛过程中也有很多大佬认为OpenMV同时进行巡线和数字识别的话其帧率会很低我们组当然也遇到了以上问题具体的解决方法在后面会讲到。另外题目中要求检测到200g药品的装载和卸载一般的话可以用压力传感器但为了方便我们直接用的红外对管以检测障碍物的方式判断药品是否装载。2.用到的主要器件清单STM32F407ZGT6单片机OpenMV4智能处理摄像头舵机转向四驱车锂电池、5V、12V降压模块L298N电机驱动模块红外传感器蓝牙模块HC-05LED指示灯以上器件都是一辆小车用到的如果要做发挥部分的话就需要两辆车了不过小车2用到的器件基本不变。我们采取的方案是一辆小车用一个摄像头我看网上有的大佬一辆小车左右两边各有一个摄像头这样的话写代码就会较为简便不过一般的OpenMV价格在300到500之间除非你的经费充足且赛前已经准备好否则还是老老实实用一个摄像头肝吧.....就像我们组当时只有一个OpenMV第二个还是临时找关系借的。3.各部分思路及代码实现(1).小车舵机、马达驱动这一部分的代码就不贴上了都是基本的PWM输出可以参照正点原子的相关例程并且两辆小车的代码都一样只不过舵机让车轮保持直行时的PWM脉宽不同到时候自己找到并在代码中改数字即可。(2).蓝牙通信蓝牙通信的代码说白了就是串口通信代码如下#include stm32f4xx.h#include bluetooth.h/*蓝牙通信:USART3,所用引脚为PC10,PC11*/void bluetooth_U3_Init(){GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); //使能GPIOA时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); //使能USART2时钟//串口1对应引脚复用映射GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_USART3); //GPIOA9复用为USART1GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_USART3); //GPIOA10复用为USART1//USART1端口配置GPIO_InitStructure.GPIO_Pin GPIO_Pin_10 | GPIO_Pin_11; //GPIOA9与GPIOA10GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF; //复用功能GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; //速度50MHzGPIO_InitStructure.GPIO_OType GPIO_OType_PP; //推挽复用输出GPIO_InitStructure.GPIO_PuPd GPIO_PuPd_DOWN; //上拉GPIO_Init(GPIOC, GPIO_InitStructure); //初始化PA9PA10//USART1 初始化设置USART_InitStructure.USART_BaudRate 9600;//波特率设置USART_InitStructure.USART_WordLength USART_WordLength_8b;//字长为8位数据格式USART_InitStructure.USART_StopBits USART_StopBits_1;//一个停止位USART_InitStructure.USART_Parity USART_Parity_No;//无奇偶校验位USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; //无硬件数据流控制USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx;//收发模式USART_Init(USART3, USART_InitStructure);//初始化串口1USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); //开启相关中断//Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel USART3_IRQn; //串口1中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 0; //抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority 2; //子优先级3NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; //IRQ通道使能NVIC_Init(NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、USART_Cmd(USART3, ENABLE); //使能串口1}/* 蓝牙使能引脚PA9 */void bluetooth_IO_Init(){GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); //??GPIOA??GPIO_InitStructure.GPIO_Pin GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode GPIO_Mode_OUT;GPIO_InitStructure.GPIO_Speed GPIO_Speed_100MHz; //??50MHzGPIO_InitStructure.GPIO_OType GPIO_OType_OD; //??????GPIO_InitStructure.GPIO_PuPd GPIO_PuPd_NOPULL; //??GPIO_Init(GPIOC, GPIO_InitStructure); //???PA9,PA10}注意以上代码中蓝牙使能引脚PA9可以不进行初始化这样就会一直使能。(3).单片机与OpenMV的串口通信此代码与蓝牙通信代码一样都是串口通信只不过我用的是USART5,所用引脚为PC12,PD2(4).单片机与OpenMV的通信协议因为OpenMV只能通过串口传输字符除非使用OpenMV的数据流打包但是我们传输的一般一次只有一个字符所以不用数据流打包)比如你要把 52 这个数传给单片机OpenMV会自动把 52 这个数转换为两个字符5和‘2’传输所以我们要在单片机和OpenMV之间定义一个统一的通信协议代码如下此代码中OpenMV传输的整数的范围在-99到99之间/* openmv发送过来的字符串转换为数据:帧头为 ,帧尾为.,转换后只保留整数中间用小数点判断,函数返回值为0则数据格式出错*/signed char CharCon(signed char c[],signed char x){int i 0;int j 0;while(c[i] ! .) {c[i] c[i] - 0;i;}j i;if(c[0] (--0)){i 4;c[0] 0;}switch(i){case 1:x c[i-1];break;case 2:x c[i-1] 10*c[i-2];break;case 3:x -(c[i-1] 10*c[i-2]);break;case 4:x -(c[j-1] 10*c[j-2]);break;default:return 0;break;} return x;}OpenMV发送的数据格式uart.write( )uart.write(str(int(deflection_angle)))uart.write(.)(5).单片机main文件中的函数main函数中需要用到状态机的编程逻辑在单片机收到OpenMV发送的数据时需要判断接收到的是数据帧还是命令比如收到的是f则接收到的是笔直向前的命令。// PB10是红外对管的输入引脚#include stm32f4xx.h#include sys.h#include delay.h#include duoji.h#include dianji.h#include bluetooth.h#include to_openmv.h#include hongwai.h#include usart.h#define Center 88 //小车前轮保持居中不偏时单片机输出的PWN脉宽#define Hongwai_IO PBin(10) //装载完药品hongwai_IO 0#define Red PAout(14)#define Green PEout(5)#define White PAout(13)//以下定义的是状态机的各种状态在最终的代码中有些并没有用到#define Start 0 //初始的等待状态#define Turn_left 1#define Turn_right 2#define Turn_no 3#define Stop 4#define Wait 5#define Slower 6#define Faster 7#define Forward 8#define Backward 9#define Back 10#define Change 11#define Stoping 12#define Changezuo 13#define Changeyou 14#define Fuwei 15#define Finish 16#define Nfuwei 17#define Zanwait 18int t 0;int speed 200; //马达速度signed char angle 0; //巡线时舵机转的角度signed char change_angle 30; //路口停下来识别数字时微调的角度uint16_t bluetooth 0; //接收到的蓝牙消息uint16_t temp 0;char state 0; //状态机中的当前状态char flag 0; //是否已经识别到要去的病房号signed char rec_flag 0; //单片机与OpenMV通信中的命令标志位signed char openmv_rec[4] {0}; //OpenMV发送的数据signed char i 0;signed char task 0; //当前执行的是任务几/* 指示灯引脚初始化 */void LED_Init(){ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); GPIO_InitTypeDef GPIO_Instruct; GPIO_Instruct.GPIO_Pin GPIO_Pin_13 | GPIO_Pin_14; GPIO_Instruct.GPIO_Mode GPIO_Mode_OUT; GPIO_Instruct.GPIO_Speed GPIO_Speed_100MHz; GPIO_Instruct.GPIO_OType GPIO_OType_PP; GPIO_Instruct.GPIO_PuPd GPIO_PuPd_UP; GPIO_Init(GPIOA, GPIO_Instruct); GPIO_Instruct.GPIO_Pin GPIO_Pin_5; GPIO_Init(GPIOE, GPIO_Instruct);}void All_Init(){ LED_Init(); hongwai_IO_Init(); dianji_IO_Init(); dianji_PWM_Init(1000 - 1, 84 - 1); duoji_PWM_Init(2000 - 1, 840 - 1); //0 bluetooth_U3_Init(); //bluetooth_IO_Init(); openmv_U5_Init(); // computer_U1_Init(); uart_init(9600); delay_init(168);}void turn_r(int t){ TIM_SetCompare1(TIM3, 88); PFout(5) 0; PFout(7) 1; PCout(4) 1; PCout(5) 0; TIM_SetCompare1(TIM5, t); TIM_SetCompare1(TIM13, t); delay_ms(3000);}int main(void){ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); All_Init(); while (1) { switch (state) { case Start: turn(0); while (flag 0); //已经识别到病房号 Green 0; White 1; delay_ms(1000); White 0; while (flag 1) { if (Hongwai_IO 0) { delay_ms(1000); if (Hongwai_IO 0) { state Forward; break; } } } break; case Forward: turn(angle); zhengzhuan(speed); break; case Turn_right: zhengzhuan(speed); turn_right(); delay_ms(400); break; //假设急转弯1.5S已经转过去具体参数需要调试 case Turn_left: turn_left(); zhengzhuan(200); delay_ms(400); break; //假设急转弯1.5S已经转过去具体参数需要调试 case Stop: stop(); delay_ms(20); fanzhuan(speed); delay_ms(300); stop(); turn(0); state Stoping; break; case Wait: stop(); turn(0); Red 1; if (Hongwai_IO 1) { delay_ms(500); if (Hongwai_IO 1) { USART_SendData(UART5, s); Red 0; state Back; flag 0; } } break; case Zanwait: stop(); turn(0); Red 1; if (Hongwai_IO 1) { delay_ms(500); if (Hongwai_IO 1) { USART_SendData(USART3, o); delay_ms(10); USART_SendData(UART5, s); Red 0; state Back; flag 0; } } break; case Back: fanzhuan(speed - 50); // turn(-angle); delay_ms(400); state Backward; USART_SendData(UART5, s); break; case Backward: turn(3 - angle); fanzhuan(speed - 50); break; case Change: turn(angle); zhengzhuan(speed); state Stop; delay_ms(500); break; case Changezuo: fanzhuan(speed); delay_ms(400); stop(); turn(30); zhengzhuan(speed); change_angle 30; delay_ms(500); state Stoping; USART_SendData(UART5, s); break; case Changeyou: fanzhuan(speed); delay_ms(400); stop(); turn(-30); zhengzhuan(speed); change_angle -30; delay_ms(500); state Stoping; USART_SendData(UART5, s); break; case Fuwei: turn(change_angle); fanzhuan(speed); delay_ms(500); stop(); turn(0); zhengzhuan(speed); delay_ms(400); stop(); break; case Nfuwei: turn(change_angle); fanzhuan(speed); delay_ms(500); stop(); turn(0); break; case Stoping: stop(); turn(0); break; case Finish: stop(); turn(0); Green 1; flag 0; state Start; break; default: break; } }}/* 蓝牙接收 */void USART3_IRQHandler(){ if (USART_GetITStatus(USART3, USART_IT_RXNE)) { bluetooth USART_ReceiveData(USART3); } USART_ClearITPendingBit(USART3, USART_IT_RXNE);}/* openmv接收 */void UART5_IRQHandler(){ if (USART_GetITStatus(UART5, USART_IT_RXNE)) { char j 0; signed char temp USART_ReceiveData(UART5); if (temp ) { rec_flag 1; i 0; } else if (rec_flag 1) { if (temp .) { openmv_rec[i] .; angle CharCon(openmv_rec, angle); rec_flag 0; for (j 0; j 4; j) openmv_rec[j] 0; } else { openmv_rec[i] temp; i; } } else //rec_flag 0,接收的为指令 { switch (temp) { case s: state Stop; break; case l: state Turn_left; break; case r: state Turn_right; break; case f: state Forward; break; case z: state Wait; break; case b: state Backward; break; case d: state Finish; break; case e: state Zanwait; break; case o: flag 1; break; case c: state Change; break; case x: state Changezuo; break; case y: state Changeyou; break; case w: state Fuwei; break; case n: state Nfuwei; break; case t: state Stoping; break; default: break; } } } USART_ClearITPendingBit(UART5, USART_IT_RXNE);}一下部分是OpenMV部分的代码包括巡线和数字识别的算法以及赛题各任务的判断逻辑(6).巡线思路将摄像头一帧图片的上半部分划分为三个平行的感兴趣区在三个感兴趣区中分别寻找最大的红色色块获得三个中心坐标然后给予其不同的权重后计算平均质心坐标用此质心坐标计算得到巡线时的偏转角度。为什么只将一帧图片的上半部分因为下半部分容易受到小车的阻挡或者阴影干扰RED_THRESHOLD [(200,10,10),(255,50,50)]#色块阈值BLACK_THRESHOLD [(0, 48, -58, 49, -36, -1)]ROIS [(0, 15, 160, 25, 0.9), (0, 40, 160, 25, 0.6), (0, 65, 160, 30, 0.6)]#--------------------------------------- 判断直线角度 ------------------------------------------------ centroid_sum 0 dx 0 for r in ROIS: blobs img.find_blobs(RED_THRESHOLD, roir[0:4], mergeTrue) # r[0:4] is roi tuple. if blobs: # Find the blob with the most pixels. largest_blob max(blobs, keylambda b: b.pixels()) # 返回最大值像素点 #img.draw_rectangle(largest_blob.rect()) # 将此区域的像素数最大的颜色块画矩形和十字形标记出来 #img.draw_cross(largest_blob.cx(),largest_blob.cy()) #print(largest_blob.cx()) centroid_sum (80-largest_blob.cx())*r[4] # r[4] is the roi weight. weight_sum weight_sum r[4] #计算centroid_sumcentroid_sum等于每个区域的最大颜色块的中心点的x坐标值乘本区域的权值 if weight_sum: center_pos (centroid_sum / weight_sum) weight_sum 0#--------------------------------------- 什么也识别不到则认为是到达了虚线 --------------------------------#----------------------------------------- 求解直线角度----------------------------------------------- deflection_angle 0 ns center_pos/60 # QQVGA 160x120. deflection_angle -math.atan(ns) deflection_angle math.degrees(deflection_angle) deflection_angle 0 - int(deflection_angle) #print(str(deflection_angle))#------------------------------------------ 直行 ---------------------------------------------------- uart.write( ) uart.write(str(int(deflection_angle))) uart.write(.)(7).识别十字路口我们需要识别十字路口然后停车进行数字识别或者进行其他操作。思路感兴趣区域取左上角和右上角当两个区域同时检测到红色色块时就表示识别到了十字路口。ROI [(10,0,40,50),(110,0,50,50)] blobs1 img.find_blobs(RED_THRESHOLD, roiROI[0],area_threshold150, mergeTrue) if blobs1: #img.draw_rectangle(blobs1.arect()) blobs2 img.find_blobs(RED_THRESHOLD, roiROI[1], area_threshold150,mergeTrue) if blobs2:#表示已经识别到十字路口 #img.draw_rectangle(blobs2.rect()) print()(8).数字识别一开始我们组用的是特征点检测的方法写好代码以后发现效果并不好不仅速度慢而且识别准确率很低后来就改成了模板匹配的方法这种方法唯一的缺点就是要想识别准确率高就需要截取较多的模板不仅有多种大小还要有多种角度我们小组最终截取了将近200张模板如果逐一进行匹配的话速度会非常的慢但是根据任务要求我们可以灵活匹配比如基础任务一我们只需要匹配数字一和二的模板其他任务只有在一开始识别病房号时需要从3到8逐一匹配但识别到病房号以后在路口进行数字识别时只需要对病房号的模板进行匹配并判断其在小车的左侧还是右侧以此来判断应该做转还是右转。核心代码如下#------------------------------------ 初始时刻识别病房号 ---------------------------------------------- while(wait 0): if(task 0): img sensor.snapshot().lens_corr(strength 1.6, zoom 1.0) imgimg.to_grayscale() #histogram img.get_histogram() #Thresholds histogram.get_threshold() #v Thresholds.value() img.binary([(0,v)]) for t in templates12: template image.Image(t) #对每个模板遍历进行模板匹配 r img.find_template(template, 0.65,step4, searchSEARCH_EX) if r: #img.draw_rectangle(r, color255) print(t[10]) #打印模板名字 numberint(t[10]) uart.write(o) wait 1 break else:#初始时刻任务二三识别病房号 img sensor.snapshot().lens_corr(strength 1.6, zoom 1.0) imgimg.to_grayscale() img.binary([(0,v)]) for t in templates3: template image.Image(t) r img.find_template(template, 0.65,step4, searchSEARCH_EX) if r: shuziint(t[10]) array1[shuzi-1] array1[shuzi-1]1 for t in templates4: template image.Image(t) r img.find_template(template, 0.55,step4, searchSEARCH_EX) if r: shuziint(t[10]) array1[shuzi-1] array1[shuzi-1]1 for t in templates5: template image.Image(t) r img.find_template(template, 0.60,step4, searchSEARCH_EX) #, roi(10, 0, 60, 60)) if r: shuziint(t[10]) array1[shuzi-1] array1[shuzi-1]1 for t in templates6: template image.Image(t) r img.find_template(template, 0.61,step4, searchSEARCH_EX) #, roi(10, 0, 60, 60)) if r: shuziint(t[10]) array1[shuzi-1] array1[shuzi-1]1 for t in templates7: template image.Image(t) r img.find_template(template, 0.65,step4, searchSEARCH_EX) if r: shuziint(t[10]) array1[shuzi-1] array1[shuzi-1]1 for t in templates8: template image.Image(t) r img.find_template(template, 0.65,step4, searchSEARCH_EX) if r: shuziint(t[10]) array1[shuzi-1] array1[shuzi-1]1 if (max(array1) 1): m array1[0] for index in range(0,8): if array1[index] m: m array1[index] number index 1 print(str(number)) uart.write(o) wait 1 num task if(num 2): num num - 2 continue else:#没识别出来则重新循环 print(no) continue#--------------------------------------- 十字路口前识别数字-------------------------------------------- while(wait 2): if((task 2)and(last ! 3)): if(n12): for loop in range(0,3): img sensor.snapshot() img.lens_corr(1.6) imgimg.to_grayscale() img.binary([(0,v)]) if number3: for t in template3: template image.Image(t) r img.find_template(template, 0.60,step4, searchSEARCH_EX) #, roi(10, 0, 60, 60)) if r: #img.draw_rectangle(r,color 255) print(str(number)) n1n11 if r[0]60: zuozuo1 else: youyou1 if number5: for t in template5: template image.Image(t) r img.find_template(template, 0.60,step4, searchSEARCH_EX) #, roi(10, 0, 60, 60)) if r: #img.draw_rectangle(r,color 255) print(str(number)) n1n11 if r[0]60: zuozuo1 else: youyou1 if number4: for t in template4: template image.Image(t) r img.find_template(template, 0.55,step4, searchSEARCH_EX) #, roi(10, 0, 60, 60)) if r: img.draw_rectangle(r,color 255) print(str(number)) n1n11 if r[0]60: zuozuo1 else: youyou1 if number6: for t in template6: template image.Image(t) r img.find_template(template, 0.60,step4, searchSEARCH_EX) #, roi(10, 0, 60, 60)) if r: img.draw_rectangle(r,color 255) print(str(number)) n1n11 if r[0]60: zuozuo1 else: youyou1 if number7: for t in template7: template image.Image(t) r img.find_template(template, 0.65,step4, searchSEARCH_EX) #, roi(10, 0, 60, 60)) if r: img.draw_rectangle(r,color 255) print(str(number)) n1n11 if r[0]60: zuozuo1 else: youyou1 if number8: for t in template8: template image.Image(t) r img.find_template(template, 0.60,step4, searchSEARCH_EX) #, roi(10, 0, 60, 60)) if r: img.draw_rectangle(r,color 255) print(t) print(str(number)) n1n11 if r[0]60: zuozuo1 else: youyou1以上代码中为了防止出现个别识别错误的情况出现我们最少识别三帧图片并统计出现次数最多的数字。(9).远端病房四个并排数字识别用一个摄像头的话很难做到一下子把四个并排的数字都完整的拍下来这样大多数人的思路就是先让小车识别中间的两个数字识别不到再让其左右偏转角度识别最左/最右侧的数字但是这种思路的话跑完单程绝对会超过20s的时间超时就会扣分。我们为了解决这个问题最后采用的是排除法先直接让小车向左偏转识别左边的两个数字如果没有识别到对应的病房号的话就认为病房在右边直接让小车向右转这种方法比较依赖模板匹配的准确度推荐现场截取模板。核心代码如下#--------------------------------------- 任务三识别不到数字时右转 --------------------------------------- if(n11):#识别三次后还不能识别出真正地数字 print(右) #time.sleep_ms(200) uart.write(w)#小车姿态复位 time.sleep_ms(400) uart.write(f) time.sleep_ms(200) uart.write(r) time.sleep_ms(1000) wait 1 last 3#last 3证明已经经过了四字识别 n1 0 n 0 zuo 0 you 0 no 0 break#-------------------------------------- 任务三识别到了数字就左转 ---------------------------------------- if(last ! 3): print(左) #uart.write(t) #time.sleep_ms(100) #uart.write(n) #time.sleep_ms(500) uart.write(f) time.sleep_ms(300) uart.write(l) time.sleep_ms(1100)#任务二可能需要的更少 wait 1 last 3 zuo 0 you 0 n 0 n1 0 no 0 break(10).返回时倒车因为我们用的是舵机转向四驱车体型较大、转弯半径较大不能像差速万向三轮车一样做到原地掉头所以在返程时我们会先进行倒车操作。在进行倒车时用上述色块巡线的话会收到十字路口的横向色块干扰所以在倒车时我们用寻找线段的方法得到偏转角度并且为了消除十字路口的干扰我们只计算角度在135—180度和0—45度之间的线段。#------------------------------------------ 倒车巡线 ------------------------------------------------ if (done 1): if(now 0): #bls img.find_blobs(RED_THRESHOLD, (30,20,60,20), area_threshold100,mergeTrue) # r[0:4] is roi tuple. #if bls: #for b in bls: #dx 0 #break for l in img.find_lines(threshold 1000, x_stride2,theta_margin 25, rho_margin 25): if(l.theta()135): deflection_angle 180 - l.theta() uart.write(b) uart.write( ) uart.write(str(int(deflection_angle))) uart.write(.) continue if(l.theta()45): deflection_angle 0 - l.theta() uart.write(b) uart.write( ) uart.write(str(int(deflection_angle))) uart.write(.) continue continue else: if((task 2)and(now 0)): bls img.find_blobs(RED_THRESHOLD, (30,20,60,20), area_threshold100,mergeTrue) # r[0:4] is roi tuple. if bls: for b in bls: dx 0 break for l in img.find_lines(threshold 1000, x_stride2,theta_margin 25, rho_margin 25): if(l.theta()135): deflection_angle 1.3*(180 - l.theta()) uart.write(b) uart.write( ) uart.write(str(int(deflection_angle))) uart.write(.) continue if(l.theta()45): deflection_angle 1.3*(0 - l.theta()) uart.write(b) uart.write( ) uart.write(str(int(deflection_angle))) uart.write(.) continue continue else: uart.write(f) else: uart.write(f)以上就是各个主要部分的核心代码了完全的OpenMV工程代码我写了1500行主要是不同任务进行的操作并不一样比如对于第二个十字路口任务二需要停车识别数字而对于任务三则需要直行这就需要有一些状态变量进行记录因此在代码中写的最多的也是if.....else...嵌套逻辑判断。4.总结感言紧张刺激的电赛还是过去了期间熬夜通宵是不可避免的不得不说今年的F题确实较难很多省份只把基础题全做出来就可以获国奖我们山东赛区的成绩暂时没出来我们组把基础部分和发挥部分一都做了出来但最后测评的时候发挥部分一因为小车二速度太慢导致急转弯的时候直接被卡在了原地很遗憾的没有验收发挥部分一。一开始我们小组准备的是往年滚球控制这种纯测控题但很遗憾今年并没有出我们就只能选择F题因为事先并没有准备车模所以也是临时向老师借的车模因为没有灰度传感器巡线就只能用摄像头同时巡线和数字识别后来小车2的摄像头也是临时向学弟借的。总的来说这次电赛还是准备不够充分从八月电赛延期一直到十一月电赛开赛的几个月的时间我们小组三个人有两人在准备保研面试另一人也在全力进行考研备考从器件和时间两方面都准备不足导致赛初我们的进度几乎可以说是停滞不前后来还是靠大家的全力应对以及各种熬夜通宵才取得了不错的成果。因为第一次写博客排版还不熟练语言叙述得也不够详尽代码模块也不知道怎么改成VScode那种高亮的模式以后会坚持继续写慢慢改进。文章中有的要点可能没有涉及到如果有小伙伴遇到了什么问题可以在评论区留言到时候我会汇总可能会再写一篇博客作为补充。PS不知道为啥里面的代码突然变成只有一行的了。。。。。本来都是分行的。。。