赞
踩
本在大二下的电设延延期了,有幸与车队队友半途加入,笔者之前没做过摄像头与视觉的代码(之前做的AI电磁),虽然最后有各种各样遗憾,但也临时猛学收获许多新知识。测评前顺利做完了基础和发挥(1),可惜封箱后换了场地识别出了问题。
做的途中出现过以下问题:
主控英飞凌TC377,摄像头Openmv循迹,Openart识别
import sensor, image, time, math from image import SEARCH_EX, SEARCH_DS from pyb import UART uart = UART(3, 115200) '变量预定义' global cnt_loss global track cnt_loss=0 track=0 THRESHOLD = (14, 65, -128, 127, 22, 127) def err_revise(): rho_err = int(abs(line.rho())-img.width()/2) if line.theta()>90: theta_err = line.theta()-180 else: theta_err = line.theta() img.draw_line(line.line(), color = (0,0,255)) theta_err=-theta_err err = theta_err + rho_err return err def track_judge(): global track global cnt_loss light = img.statistics().l_mean() print(light) 'track:2.岔路口,1.普通红线,0.红线丢失' if (light>=23): track=2 elif light<23 and light>3: track=1 else: track=0 'main部分' sensor.reset() sensor.set_vflip(True) sensor.set_hmirror(True) sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QQQVGA) sensor.skip_frames(time = 2000) clock = time.clock() while(True): clock.tick() img = sensor.snapshot().binary([THRESHOLD]) line = img.get_regression([(100,100)], robust = True) track_judge() if (line): err=err_revise() if track==2: uart.write( str(err)+'T' ) print('T cro') elif track==1: uart.write( str(err)+'F' ) print('F cro') else: uart.write( str(err)+'L' ) print('loss') print(str(err)+'\n') time.sleep_ms(30)
'''数字识别Openart代码''' import pyb import sensor, image, time, math import os, nncu from machine import UART uart = UART(1, baudrate=115200) sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) # we run out of memory if the resolution is much bigger... sensor.set_brightness(300) # 设置图像亮度 越大越亮 sensor.skip_frames(time = 2000) sensor.set_auto_gain(False) # must turn this off to prevent image washout... sensor.set_auto_whitebal(False,(0,0x80,0)) # must turn this off to prevent image washout... clock = time.clock() net_path = "_1s_model_17_1.0000_xxxx.nncu" # 定义模型的路径 labels = [line.rstrip() for line in open("/sd/labels_number.txt")] # 加载标签 net = nncu.load(net_path, load_to_fb=True) # 加载模型 first_num=0 is_send_num=0 while(True): img = sensor.snapshot() print("********** Start Detections **********") for r in img.find_rects(threshold = 30000): # 在图像中搜索矩形 img.draw_rectangle(r.rect(), color = (255, 0, 0)) # 绘制矩形外框,便于在IDE上查看识别到的矩形位置 img1 = img.copy(r.rect()) # 拷贝矩形框内的图像 print(r.rect()[3]) if r.rect()[1]>10 and r.rect()[1]<140 and r.rect()[3]>65 and r.rect()[3]<90: for obj in nncu.classify(net , img1, min_scale=1.0, scale_mul=0.5, x_overlap=0.0, y_overlap=0.0): sorted_list = sorted(zip(labels, obj.output()), key = lambda x: x[1], reverse = True) # 打印准确率最高的结果 for i in range(1): if first_num==0: first_num=sorted_list[i][0] ###发送数字,为避免与Openmv发送err混淆,Openart发送数字对应的字母####### if str(first_num)=='1' and is_send_num==0: uart.write('a') print('1') is_send_num=1 elif str(first_num)=='2' and is_send_num==0: uart.write('b') print('2') is_send_num=1 elif str(first_num)=='3' and is_send_num==0: uart.write('c') print('3') is_send_num=1 elif str(first_num)=='4' and is_send_num==0: uart.write('d') print('4') is_send_num=1 elif str(first_num)=='5' and is_send_num==0: uart.write('e') print('5') is_send_num=1 elif str(first_num)=='6' and is_send_num==0: uart.write('f') print('6') is_send_num=1 elif str(first_num)=='7' and is_send_num==0: uart.write('g') print('7') is_send_num=1 elif str(first_num)=='8' and is_send_num==0: uart.write('h') print('8') sensor.set_brightness(400) is_send_num=1 ################## print('sco'+str(first_num)) if sorted_list[i][0]==first_num: '识别到需求数字,判断位于左/右侧' if (r.rect()[0]<120): uart.write('l') print(str(first_num)+' on l') else: uart.write('r') print(str(first_num)+' on r') img.draw_string(r.rect()[0] + 20, r.rect()[1]-20, sorted_list[0][0],color = (0,0,255), scale = 2,mono_space=False)
标志 | 状态 |
---|---|
0 | 初始未放药 |
1 | 放上药品后 |
2 | 病房处停下(丢红线)等待取药 |
3 | 取下药品掉头的过程 |
4 | 掉头后循线到达最后一个路口前 |
5 | 转完最后一个路口的返回药房 |
标志 | 控制 |
---|---|
0 | 停车 |
1 | 发车并PID循线,遇到路口时获取Openart的信息来控制左右转 |
2 | 停车 |
3 | 原地旋转180度 |
4 | PID循线,遇路口时按来时的方向反向转弯 |
5 | 加速并按PID循线直到丢线停车 |
发挥1部分
车2理论上不需要识别,只需要记录车1的转弯来对应串口发送指令控制车2路线即可。
核心代码:
1.通信部分以及控制指令:
#include "headfile.h" struct OPENMV mv,art; char * p=mv.str; int i=0; /* 函数:openmv_get_str() 变量说明: UART1-与openmv通信 UART0-与openart通信 UART2-与车2通信 @mv.data:每次串口中断接收到的字符 @mv.str:将一次完整的数据储存到字符串,T,F,L为一次发送结尾标志,并分别对应T-路口,F-不是路口,L-丢线 @mv.err:将字符串偏差转化为浮点型的偏差 @mv.track:对应T,L,F,2-是路口,0-丢线,1-普通赛道 */ void openmv_get_str() { if (uart_query(UART_1,&mv.data)) { if (mv.data!='T' && mv.data!='F' && mv.data!='L' ) { mv.str[i]=mv.data; i++; } else if (mv.data=='T') { mv.str[i]='\0'; i=0; mv.err = atof(mv.str); memset(mv.str,0,sizeof(mv.str)); mv.track=2; } else if (mv.data=='F') { mv.str[i]='\0'; i=0; mv.err = atof(mv.str); memset(mv.str,0,sizeof(mv.str)); mv.track=1; } else if (mv.data=='L') { mv.str[i]='\0'; i=0; mv.err = atof(mv.str); memset(mv.str,0,sizeof(mv.str)); if (!art.con_left && !art.con_right) mv.track=0; } } } /* 控制指令函数——方向控制 函数:direc_control() 变量说明: @art.con_left/right 状态1:(0秒~cnt.turn_sec秒)期间按固定占空比转弯 状态2:(cnt.turn_sec秒~cnt.turn_sec*2秒)期间按PID自然回线且不判断丢线(避免判断为到达病房) @cnt.left/right:左转计时变量,1次中断5ms。 @cnt.turn_time:转弯固定差速持续的时间以及转弯后一段不判断丢线的时间。 @cnt.turn_num:转弯的次数(即遇到的路口数) @flag.drug:药品的状态 @flag.str:是否发车 @flag.is_judge:发挥1的相关变量,即告诉车2已识别中端病房路口 @flag.send_direc:发挥1的相关变量,即告诉车2要转弯的方向 @flag.turn_memory[]:记下来时的方向,回去遇到路口时则从数组末端开始来控制方向 */ void direc_control() { if (art.con_left) { cnt.left++; if (cnt.left>cnt.turn_time*1000/5) { art.con_left=2; if (cnt.left>cnt.turn_time*2000/5) { art.con_left=0; cnt.left=0; if (flag.drug<4&&flag.str) { if (!flag.is_judge) { flag.is_judge=1; flag.send_direc='Z'; } flag.turn_memory[cnt.turn_num]=1; cnt.turn_num++; } else if(flag.drug>3) { cnt.turn_num--; } } } } else if (art.con_right) { cnt.right++; if (cnt.right>cnt.turn_second*1000/5) { art.con_right=2; if (cnt.right>cnt.turn_second*2000/5) { /**该if中代码某次控制中只执行一次(最后一次5ms中断进入)**/ art.con_right=0; cnt.right=0; if (flag.drug<4&&flag.str) { /*发挥1部分*/ if (!flag.is_judge) { flag.is_judge=1; flag.send_direc='X'; } /**********/ flag.turn_memory[cnt.turn_num]=2; cnt.turn_num++; } else if(flag.drug>3) { cnt.turn_num--; } } } } } /* 控制指令函数——赛道判别 函数:track_control() 变量说明: @art.con_left/right:上面有介绍 @art.num_judge:识别到的数字 @flag.turn_memory[]:记下来时的方向,回去遇到路口时则从数组末端开始来控制方向 思路:分为三大种情况,1.如果识别的是1和2,则特殊处理。 */ void track_control() { if (mv.track==2 &&flag.drug!=5) { /*识别到1或者2*/ if (art.num_judge==1 || art.num_judge==2) { if (art.num_judge==1 && flag.drug!=4 && flag.drug!=5) art.con_left=1; else if (art.num_judge==1 && flag.drug==4) { art.con_right=1; flag.drug=5; } else if (art.num_judge==2 && flag.drug!=4 && flag.drug!=5) art.con_right=1; else if (art.num_judge==2 && flag.drug==4) { art.con_left=1; flag.drug=5; } } /****************************/ /**识别到其他数字*****/ else { /**如果是第一个路口(无两路口则直接下一个判断)**/ else if(flag.drug==4 && cnt.turn_num == 2) { if(flag.turn_memory[1]==1) art.con_right=1; else if (flag.turn_memory[1]==2) art.con_left=1; } /**如果是最后一个路口**/ else if(flag.drug==4 && cnt.turn_num == 1 ) { if(flag.turn_memory[0]==1) art.con_right=1; else if (flag.turn_memory[0]==2) art.con_left=1; flag.drug=5; } } } } /* 函数:openart_get_str() 变量说明: @art.data:接收的单个字符(由于三个串口底层都用较简单的阻塞式读取,所以除了openmv读取偏差外,其他两个串口只读取简单的单字符且仅对应某串口会发送此类字符,尽可能避免干扰openmv的通信) @art.num_judge:识别的数字 */ void openart_get_str() { if (uart_query(UART_0,&art.data)) { if (art.data=='l') { art.con_left=1; } else if (art.data=='r') { art.con_right=1; } else if (art.data=='a') { art.num_judge=1; } else if (art.data=='b') { art.num_judge=2; } else if (art.data=='c') { art.num_judge=3; } else if (art.data=='d') { art.num_judge=4; } else if (art.data=='e') { art.num_judge=5; } else if (art.data=='f') { art.num_judge=6; } else if (art.data=='g') { art.num_judge=7; } else if (art.data=='h') { art.num_judge=8; } } }
2.电机控制
/* 函数:motor_control() 变量说明: @enc.speed_contorl_out:速度环输出 @enc.dif:方向环输出差速 @enc.left_pwm/right:左右速度 @cnt.round_second:原地旋转持续时间 */ void motor_control() { if (flag.drug==5) { enc.left_pwm=enc.speed_control_out+2000-enc.dif;//steer.angle即电机差速 enc.right_pwm=enc.speed_control_out+2000+enc.dif; } else { enc.left_pwm=enc.speed_control_out+0-enc.dif;//steer.angle即电机差速 enc.right_pwm=enc.speed_control_out+0+enc.dif; } if (!flag.man_swi) { if (flag.drug==3) { cnt.turn_round++; if (cnt.turn_round>cnt.round_second*1000/5) { cnt.turn_round=0; flag.drug=4; } motor(3000); motor2(3000); } else if(art.con_left==1) { motor(1000); motor3(4000); } else if (art.con_right==1) { motor(4000); motor3(1000); } else if (mv.track==0 && (flag.drug==5 || flag.drug==2) ) { motor(0); motor3(0); motor2(0); motor1(0); } else { if (enc.left_pwm>0) { motor(enc.left_pwm); motor1(0); } else { motor(0); motor1(-enc.left_pwm); } if (enc.right_pwm>0) { motor2(0); motor3(enc.right_pwm*1.2); } else { motor2(-enc.right_pwm*1.2); motor3(0); } } } }
3.中断
IFX_INTERRUPT(cc61_pit_ch0_isr, 0, CCU6_1_CH0_ISR_PRIORITY) { enableInterrupts();//开启中断嵌套 PIT_CLEAR_FLAG(CCU6_1, PIT_CH0); //自动循迹 if (!flag.man_swi) { /***************PID*************/ /*向车2发送信号*/ Send_to_car2(); /*外设处理*/ reeds_get();//检测是否装载着药品 if_put_drug();//是否放上药物 /*亮红灯——病房丢线*/ if (flag.drug==1 && mv.track==0) { flag.drug=2; flag.led=1; } if_get_drug();//是否取下药物 /*亮绿灯——药房丢线*/ if (flag.drug==5 && mv.track==0) flag.led=2; led_control();//提示灯控制 /*摄像头处理*/ openmv_get_str(); if (!art.left_control && !art.right_control) { if (flag.drug<3) openart_get_str(); track_control() } direc_control(); PID_steer_trace(); /**准备、发车**/ if (flag.str) { if (flag.mot_con) motor_control(); // if (flag.ste_con) steer_control();四轮车不好原地转弯,搭车还浪费好久时间 } /**计数**/ Count(); } /************人工测试***********/ else { openmv_get_str(); if (flag.str) { if (flag.mot_con) motor_control(); // if (flag.ste_con) steer_control(); } /**计数**/ Count(); } }
4.车2代码
核心思路:笔者是让车2在车1到达中端病房后前往与车1同侧的近端病房,记录车1在路口时转的方向,车2只需按这个方向在遇到路口时先转1次即到达车1的同侧近端病房,然后等待车1返回到达药房发送启动指令,车2原地旋转,再在遇到路口时转2次(共俩路口)即可到达车1抵达的病房。
/**获取车1指令**/ void car2_get_str() { if (uart_query(UART_2,&getcar1.data)) { /*得知车1的方向是左转*/ if (getcar1.data=='Z' ) { flag.turn_direc=1; flag.str=1; } /*得知车1的方向是右转*/ else if (getcar1.data=='X') { flag.turn_direc=2; flag.str=1; } /*接收到原地旋转指令*/ else if (getcar1.data=='S') { flag.get_turn=1; } } } /**方向控制**/ void track_control() { if (mv.track==2 &&flag.drug!=5) { if (flag.turn_direc==1) { art.con_left=1; } else if (flag.turn_direc==2) { art.con_right=1; } } } void direc_control() { //同车1 } void motor_control() { //同车1 } /***中断部分***/ IFX_INTERRUPT(cc61_pit_ch0_isr, 0, CCU6_1_CH0_ISR_PRIORITY) { enableInterrupts();//开启中断嵌套 PIT_CLEAR_FLAG(CCU6_1, PIT_CH0); //自动循迹--AI/PID if (!flag.man_swi) { /***************PID*************/ /*接收主车通信*/ car2_get_str(); /*外设处理*/ led_control(); flag.drug=0; /*摄像头处理*/ openmv_get_str(); if (cnt.left==0 && cnt.right==0) { track_control(); } direc_control(); PID_steer_trace(); /**准备、发车**/ if (flag.str) { if (flag.mot_con) motor_control(); } /**计数**/ Count(); } }
交大大佬的识别(相见恨晚):
https://zhuanlan.zhihu.com/p/391216590
openmv中文手册
https://docs.singtown.com/micropython/zh/latest/openmvcam/index.html
openart开源教程:
http://mp.weixin.qq.com/s?__biz=MzAxMjQxNjEyMw==&mid=2247487040&idx=1&sn=4d47976b398b3c92811a29356ada34b9&chksm=9bb36b54acc4e242edf935986c97ee1f6363dffc86678faf64c65903476f8c27504e1a392120&mpshare=1&scene=23&srcid=1203gAB1ALoBvqjYxJd7qp5c&sharer_sharetime=1638461358289&sharer_shareid=b8ab17ea4161b8f0d5c69ddb66ea7563#rd
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。