# -*- coding:utf-8 -*- # @Author len # @Create 2023/11/23 11:28 import math import sensor, image, time, lcd import binascii from Maix import GPIO from machine import Timer, PWM, UART, Timer from fpioa_manager import fm import KPU as kpu class Mainlen(): def __init__(self): '''初始化摄像头和 LCD 显示屏''' lcd.init() # lcd初始化 # 摄像头模块初始化 sensor.reset() # 复位和初始化摄像头 sensor.set_pixformat(sensor.RGB565) # 设置像素格式为彩色 RGB565 # sensor.set_pixformat(sensor.GRAYSCALE) # 设置像素格式为灰色 sensor.set_framesize(sensor.QVGA) # 设置帧大小为QVGA(320×240) sensor.set_vflip(1) # 后置模式 sensor.skip_frames(30) # # 跳过前30帧 # sensor.set_auto_gain(0, gain_db=17) # 设置摄像头的自动增益功能 # 映射串口引脚 fm.register(6, fm.fpioa.UART1_RX, force=True) fm.register(7, fm.fpioa.UART1_TX, force=True) # 初始化串口 self.uart = UART(UART.UART1, 115200, read_buf_len=4096) # 循迹 # --------------感光芯片配置 START ------------------- self.IMG_WIDTH = 240 self.IMG_HEIGHT = 320 # 直线灰度图颜色阈值 self.LINE_COLOR_THRESHOLD = [(0, 60)] # 找黑色 self.LINE_COLOR_BAISE = [(60, 255)] # 找白色 self.ROIS = { # 找黑色 # 'left': (0, 0, 320, 50), # 纵向取样-左侧 # 'right': (0, 190, 320, 50), # 纵向取样-右侧 'left': (0, 0, 180, 50), # 纵向取样-左侧 'right': (0, 190, 180, 50), # 纵向取样-右侧 'up': (240, 0, 80, 240), # 横向取样-上方 'middle_up': (160, 0, 80, 240), # 横向取样-中上 'middle_down': (80, 0, 80, 240), # 横向取样-中下 'down': (0, 0, 80, 240), # 横向取样-下方 } # 红黄绿的阈值 # 颜色识别阈值 (L Min, L Max, A Min, A Max, B Min, B Max) LAB模型 # 下列阈值元组用来识别 红、绿、蓝三种颜色,可通过调整数据阈值完成更多颜色的识别。 self.thresholds = [(59, 100, 40, 127, 5, 127), # 红色阈值 (90, 100, -5, 2, -4, 20), # 黄色阈值 (87, 100, -59, 127, -10, 127),] # 绿色阈值 self.is_need_send_data = False # 是否需要发送数据的信号标志 # 主函数 run def startMain(self): Flag_track = False # 循迹标识 Flag_qr = False # 二维码标识 Flag_light = False # 交通灯标识 while True: data = self.uart.read(8) # print(data) # print(len(data)) # if (len(data) >= 8): if data is not None: if (data[1] == 0x02) and (data[7] == 0xBB) and self.verify_checksum(data): # 巡线与控制舵机 if data[2] == 0x91: if data[3] == 0x01: # 启动循迹 sensor.set_pixformat(sensor.GRAYSCALE) # 设置像素格式为灰色 Flag_track = True print("开始循迹") pass elif data[3] == 0x02: # 停止循迹 sensor.set_pixformat(sensor.RGB565) # 设置像素格式为彩色 RGB565 Flag_track = False print("停止循迹") pass elif data[3] == 0x03: # 调整舵机 print("调整舵机") self.Servo(data) else: pass # 识别任务 elif data[2] == 0x92: if data[3] == 0x01: # 识别二维码 sensor.set_pixformat(sensor.RGB565) # 设置像素格式为彩色 RGB565 Flag_qr = True print("开始识别二维码") pass elif data[3] == 0x02: # 停止识别二维码 Flag_qr = False print("停止识别二维码") pass elif data[3] == 0x03: # 识别交通灯 sensor.set_pixformat(sensor.RGB565) # 设置像素格式为彩色 RGB565 Flag_light = True print("开始识别交通灯") pass elif data[3] == 0x04: # 停止识别交通灯 Flag_light = False print("停止识别交通灯") pass else: pass img = sensor.snapshot() # 获取图像 if Flag_track: # 循迹 print("循迹") self.tracking(img) pass elif Flag_qr: # 二维码 print("识别二维码") self.discem_QR(img) pass elif Flag_light: # 红绿灯 print("识别红绿灯") self.discem_light(img) pass lcd.display(img) # 在LCD显示 # 红绿灯 def discem_light(self, img): img = sensor.snapshot() # 获取图像 print(type(img)) width, height = img.size left = 2 * width // 3 top = 0 right = width bottom = height img = img.crop((left, top, right, bottom)) lcd.display(img) list_result_color = [] for color in self.thresholds: list_result_color.append(img.find_blobs([color], merge = True)) # int_result_color = max(list_result_color) print(list_result_color) for blobs in list_result_color: for b in blobs: img=img.draw_rectangle(b[0:4]) img=img.draw_cross(b[5], b[6]) lcd.display(img) int_result_color = list_result_color.index(max([len(i) for i in list_result_color])) print(bytes([int_result_color])) # 串口发送交通灯信息 self.uart.write(bytes([0x55])) self.uart.write(bytes([0x92])) self.uart.write(bytes([0x03])) self.uart.write(bytes([int_result_color])) self.uart.write(bytes([0x03])) self.uart.write(bytes([0x03])) self.uart.write(bytes([0x03])) self.uart.write(bytes([0xbb])) # 二维码 def discem_QR(self, img): self.Tise_servo(10) res_QR = img.find_qrcodes() # 寻找二维码 for i in range(9): img = sensor.snapshot() # 获取图像 TS_QR = img.find_qrcodes() # 再次寻找二维码 for qr in TS_QR: if all(qr.payload() != existing_qr.payload() for existing_qr in res_QR): res_QR.append(qr) print(len(res_QR)) if len(res_QR): # 识别到 for res in res_QR: img.draw_rectangle(res.rect()) img.draw_string(2, 2, res.payload(), color=(0, 128, 0), scale=2) result = res.payload() print(result) result = self.remove_chinese_chars(result) # 剔除中文 print(result) # 串口发送二维码信息 self.uart.write(bytes([0x55])) self.uart.write(bytes([0x02])) self.uart.write(bytes([0x92])) self.uart.write(bytes([0x04])) self.uart.write(bytes([len(result)])) for qr_data in result: self.uart.write(bytes([ord(qr_data)])) self.uart.write(bytes([0xbb])) else: # 未识别 self.uart.write(bytes([0x55])) self.uart.write(bytes([0x02])) self.uart.write(bytes([0x92])) self.uart.write(bytes([0x02])) self.uart.write(bytes([0x00])) self.uart.write(bytes([0x00])) self.uart.write(bytes([0xbb])) # 移除字符串内的中文 def remove_chinese_chars(self, text): """移除字符串中的中文字符,并返回新的字符串""" # 定义中文字符的Unicode范围 chinese_char_ranges = [ ('\u4e00', '\u9fff'), # 基本汉字 ('\u3400', '\u4dbf'), # 扩展A ('\u20000', '\u2a6df'), # 扩展B # 可以根据需要添加更多的中文范围 ] # 移除中文字符 return ''.join(char for char in text if not any(start <= char <= end for start, end in chinese_char_ranges)) # 循迹 def tracking(self, img): print("循迹") roi_blobs_result = self.find_blobs_in_rois(img) down_center, state_crossing, deflection_angle = self.state_deflection_angle(roi_blobs_result) dsd = self.data_format_wrapper(down_center, state_crossing, deflection_angle) self.UsartSend(dsd) print("下发指令:", dsd) # 寻找色块 在ROIS中寻找色块,获取ROI中色块的中心区域与是否有色块的信息 def find_blobs_in_rois(self, img): canvas = img.copy() roi_blobs_result = {} # 在各个ROI中寻找色块的结果记录 for roi_direct in self.ROIS.keys(): roi_blobs_result[roi_direct] = { 'cx': 0, 'cy': 0, 'w': 0, 'blob_flag': False } for roi_direct, roi in self.ROIS.items(): blobs = canvas.find_blobs(self.LINE_COLOR_THRESHOLD, roi=roi, merge=True) if len(blobs) != 0: largest_blob = max(blobs, key=lambda b: b.pixels()) if largest_blob.area() > 1000: roi_blobs_result[roi_direct]['cx'] = largest_blob.cy() roi_blobs_result[roi_direct]['cy'] = largest_blob.cx() roi_blobs_result[roi_direct]['w'] = largest_blob.h() roi_blobs_result[roi_direct]['blob_flag'] = True else: # blobs=canvas.find_blobs(LINE_COLOR_THRESHOLD, roi=roi, merge=True, pixels_area=10) continue blobs_baise = canvas.find_blobs(self.LINE_COLOR_BAISE, roi=(0, 0, 60, 240), merge=True) # 车载摄像头屏幕下部找白色#宽度200修改为240#用途寻卡,y示例中40修改为0 blobs_dixing = canvas.find_blobs(self.LINE_COLOR_BAISE, roi=(125, 0, 60, 240), merge=True) # 车载摄像头屏幕中间找白色 #宽度200修改为240,y示例中40修改为0 blobs_zuo = canvas.find_blobs(self.LINE_COLOR_THRESHOLD, roi=(0, 0, 180, 50), merge=True) # 车载摄像头屏幕左下部找黑色 blobs_you = canvas.find_blobs(self.LINE_COLOR_THRESHOLD, roi=(0, 190, 180, 50), merge=True) # 车载摄像头屏幕右下部找黑色 if len(blobs_baise) != 0: print("*********进入循环第1步*******") largest_baise = max(blobs_baise, key=lambda b: b.pixels()) wx, wy, wwidth, wwheight = largest_baise[:4] arc = wwidth * wwheight if arc >= 11000: print("*********进入循环第2步*******") if len(blobs_dixing) != 0: print("*********进入循环第3步*******") largest_dixing = max(blobs_dixing, key=lambda b: b.pixels()) wx, wy, wwidth, wwheight = largest_dixing[:4] arc = wwidth * wwheight if arc >= 11000: print('kapian') # 首先中部区域识别到白色进入判断地形还是卡片,接着判断下部,如果为白色判断为卡片。 self.UsartSend(self.data_format_wrapper(0, 1, 0)) # 地形停止命令 if len(blobs_zuo) != 0 and len(blobs_you) != 0: print("ka十字路口") self.UsartSend(self.data_format_wrapper(1, 1, 0)) # 地形停止命令 else: print('dixing') print(roi_blobs_result) # 返回的是黑色色块,各区域中心位置 self.UsartSend(self.data_format_wrapper(0, 1, 0)) # 地形停止命令 return roi_blobs_result # 返回的是黑色色块,各区域中心位置 # 计算偏转状态值 def state_deflection_angle(self, roi_blobs_result): ''' 说明:偏转状态值返回 ''' # ROI区域权重值 # ROIS_WEIGHT = [1, 1, 1, 1] ROIS_WEIGHT = [1, 0, 0, 1] state_crossing = False deflection_angle = 0 # 偏转角 down_center = 0 # 中下值 center_num = 0 # 中间值 # print(roi_blobs_result) # 偏转值计算,ROI中心区域X值 centroid_sum = roi_blobs_result['up']['cx'] * ROIS_WEIGHT[0] + roi_blobs_result['middle_up']['cx'] * \ ROIS_WEIGHT[1] \ + roi_blobs_result['middle_down']['cx'] * ROIS_WEIGHT[2] + roi_blobs_result['down']['cx'] * \ ROIS_WEIGHT[3] if roi_blobs_result['up']['blob_flag']: center_num += ROIS_WEIGHT[0] if roi_blobs_result['middle_up']['blob_flag']: center_num += ROIS_WEIGHT[1] if roi_blobs_result['middle_down']['blob_flag']: center_num += ROIS_WEIGHT[2] if roi_blobs_result['down']['blob_flag']: center_num += ROIS_WEIGHT[3] center_pos = centroid_sum / (ROIS_WEIGHT[0] + ROIS_WEIGHT[1] + ROIS_WEIGHT[2] + ROIS_WEIGHT[3]) deflection_angle = (self.IMG_WIDTH / 2) - center_pos # 判断两侧ROI区域检测到黑色线 if roi_blobs_result['left']['blob_flag'] and roi_blobs_result['right']['blob_flag']: # 判断两侧ROI区域检测到黑色线处于图像下方1/3处 if roi_blobs_result['left']['cy'] <= ((self.IMG_HEIGHT / 3)) or roi_blobs_result['right']['cy'] <= ( (self.IMG_HEIGHT / 3)): # 当最下方ROI区域的黑线宽度大于140像素(检测到路口) if roi_blobs_result['down']['w'] > 140: down_center = 1 # 自行修改处 判断识别到十字路口 print("输出了十字路口标识") return down_center, state_crossing, deflection_angle # 控制舵机 def Tise_servo(self, angle): # 判断舵机控制方向 if angle < 0: # 限制舵机角度,防止过大损坏舵机 if angle > 80: angle = 80 angle = -angle elif angle > 0: # 限制舵机角度,防止过大损坏舵机 if angle > 35: angle = 35 angle = angle # PWM通过定时器配置,接到IO17引脚 tim_pwm = Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_PWM) S1 = PWM(tim_pwm, freq=50, duty=0, pin=17) S1.duty((angle + 90) / 180 * 10 + 2.5) def Servo(self, data): ''' 功能:180度舵机:angle:-90至90 表示相应的角度 360连续旋转度舵机:angle:-90至90 旋转方向和速度值。 【duty】占空比值:0-100 ''' angle = data[5] # 判断舵机控制方向 if data[4] == ord('-'): # 限制舵机角度,防止过大损坏舵机 if angle > 80: angle = 80 angle = -angle elif data[4] == ord('+'): # 限制舵机角度,防止过大损坏舵机 if angle > 35: angle = 35 angle = angle # PWM通过定时器配置,接到IO17引脚 tim_pwm = Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_PWM) S1 = PWM(tim_pwm, freq=50, duty=0, pin=17) S1.duty((angle + 90) / 180 * 10 + 2.5) # 检验校验和 def verify_checksum(self, data): if len(data) != 8: return False # 计算校验和:data[2]、data[3]、data[4] 和 data[5] 的和,然后对256取模 calculated_checksum = (data[2] + data[3] + data[4] + data[5]) % 256 # 比较计算出的校验和与data[6]是否相等 if calculated_checksum == data[6]: return True else: return False # 串口发送 def UsartSend(self, str_data): ''' 串口发送函数 ''' self.uart.write(str_data) # 判断符号 def get_symbol(self, num): ''' 根据数值正负,返回数值对应的符号 正数: ‘+’, 负数‘-’ 主要为了方便C语言解析待符号的数值。 ''' print("num = ", num) if num >= 0: return ord('+') else: return ord('-') # 封装数据 def data_format_wrapper(self, down_center, state_crossing, deflection_angle): ''' 根据通信协议封装循迹数据 TODO 重新编写通信协议 与配套C解析代码 ''' send_data = [ 0x55, 0x02, 0x91, down_center, # 底部色块中心是否在中点附近#底部色块十字路口 1 if state_crossing else 0, # 是否越障 无用 self.get_symbol(deflection_angle), # 偏航角符号 print输出是43 + 45- +向左转调整 -向右转调整 abs(int(deflection_angle)), # 偏航角 0xbb] return bytes(send_data) # 运行程序 myMain = Mainlen() myMain.startMain()