(第4天)4.3 多种条件下的控制:if~elif~else~

本文深入解析Python中的elif关键字及条件嵌套的使用方法,通过实例演示如何根据多个条件进行逻辑判断,适用于编程初学者掌握复杂条件控制流程。

 

image.png


(1)elif 关键字

编程中有多个条件需要判断时,可以在 if~else 句式的基础上再添加 elif 关键字。这个 elif 关键字后面需要编写条件表达式。如果表达式的值为True的话,则会执行“:”后面的代码。如下图所示:

1.png

下面举个例子,看一下如何使用 elif 关键字。

1.png

上面的代码解读如下:

1.png

在云编辑中,输入上面的代码,结果如下:

1.png


(2)条件内部中的条件(嵌套组合)

 

有时根据实际业务情况,还会出现在条件内部,再次进行条件控制的情况。比如说,体温发烧37.5°,去了医院之后,医生会问,是否连续发烧4天以上呢?如果不是的话,就不能做病毒检测。所以这样的情况,可以在 if 关键字内部,再次使用 if 关键字,如下图所示。

1.png

在云编辑中,输入上面的代码,执行结果如下:


挑战一下

 

如果你学完了今天的知识内容,动手试试下面的挑战任务吧!


电影观看等级

设想一个条件控制程序,根据年龄判断一个人的电影观看等级,设置一个变量age,用来存储年龄。

以下是判断的条件:

年龄

观看等级

18岁以上

R级

13岁到18岁之间

PG13级

未到13岁

PG普通级

 

【源代码:资源包\第4天\4-3-挑战.py】

image.png



 

此处为语雀文档,点击链接查看:https://www.yuque.com/codeclub/gnec1v/ihgl72

import sensor import time import random import math # 全局状态变量 state = 0 # 0: 等待启动, 1: 第一轮测量, 2: 第二轮测量, 3: 编号输入测量, 4: 角度测量 remaining_targets = [1, 2, 3, 4] # 剩余目标物编号 current_target = None # 当前目标物 指定编号 = 0 # 用户输入的指定编号 # 设置VGA画面,并裁剪中间的画面 sensor.reset() sensor.set_pixformat(sensor.GRAYSCALE) sensor.set_framesize(sensor.VGA) sensor.set_windowing((200,240)) sensor.skip_frames(time=2000) clock = time.clock() # Tracks FPS. CENTER_X = 200//2 CENTER_Y = 240//2 # 边框的真实长度,单位mm FRAME_WIDTH_MM = 176 # 校准的数据 # 例如: 当距离是1400mm时,边框宽度为82像素 # DISTANCE_MM_1 = 1100 # FRAME_WIDTH_PIXEL_1 = 104 DISTANCE_MM_2 = 1400 FRAME_WIDTH_PIXEL_2 = 82 # DISTANCE_MM_3 = 1800 # FRAME_WIDTH_PIXEL_3 = 64 # 目标物数据(假设) 目标物数据 = { 1: {"name": "目标物1", "color": (0, 150)}, 2: {"name": "目标物2", "color": (0, 150)}, 3: {"name": "目标物3", "color": (0, 150)}, 4: {"name": "目标物4", "color": (0, 150)} } # 数字模板(简化版,实际应用中可能需要更复杂的模板匹配) 数字模板 = { 1: "1", 2: "2", 3: "3", 4: "4" } def find_center_min_blob(blobs): # 找中间最小的色块 blob = None min_area = 100000 for b in blobs: if abs(b.cx()-CENTER_X)+ abs(b.cy()-CENTER_Y) > 50: continue if b.area() > min_area: continue blob = b min_area = b.area() return blob def find_center_max_blob(blobs): # 找中间最大的色块 blob = None max_area = 0 for b in blobs: if abs(b.cx()-CENTER_X)+ abs(b.cy()-CENTER_Y) > 50: continue if b.area() < max_area: continue blob = b max_area = b.area() return blob def find_min_square(blobs): # 找最小面积的正方形 min_square = None min_area = float(&#39;inf&#39;) # 初始化为无穷大 # 调试信息:显示找到的色块数量 print(f"找到的色块数量: {len(blobs)}") for b in blobs: # 对于黑色正方形,降低面积过滤阈值 if b.area() < 5: # 进一步降低最小面积阈值 continue # 假设正方形的宽高比接近1 aspect_ratio = max(b.w(), b.h()) / min(b.w(), b.h()) if min(b.w(), b.h()) > 0 else 0 # 对于小正方形,宽高比判断可以更宽松 if aspect_ratio > 1.3: # 稍微放宽宽高比限制 continue if b.area() < min_area: min_area = b.area() min_square = b print(f"临时最小正方形: 面积={b.area()}, 宽={b.w()}, 高={b.h()}, 宽高比={aspect_ratio:.2f}") # 调试信息 if min_square: print(f"最终最小正方形: 面积={min_square.area()}, 宽={min_square.w()}, 高={min_square.h()}") else: print("未找到符合条件的正方形") return min_square def recognize_number(img, roi): # 识别数字(简化版) # 实际应用中可能需要OCR或模板匹配 # 这里返回一个随机数作为示例 return random.randint(1, 4) def calculate_angle(blob): # 计算物体与轴线的夹角 # 假设通过宽高比来估算角度 aspect_ratio = blob.w() / blob.h() if blob.h() > 0 else 1 angle = math.atan(aspect_ratio) * 180 / math.pi return min(max(angle, 30), 60) # 限制在30-60度之间 def start_measurement(): global state, current_target, remaining_targets if state == 0: # 第一轮测量:随机取一个目标物 if remaining_targets: current_target = random.choice(remaining_targets) remaining_targets.remove(current_target) state = 1 print(f"第一轮测量:已选择目标物 {current_target}") else: print("没有可用目标物") elif state == 1: # 第二轮测量:从剩余目标物中再随机取一个 if remaining_targets: current_target = random.choice(remaining_targets) remaining_targets.remove(current_target) state = 2 print(f"第二轮测量:已选择目标物 {current_target}") else: print("没有可用目标物") elif state == 2: # 第三轮测量:等待输入编号 state = 3 print("请输入要测量的正方形编号(1-4):") elif state == 3: # 第三轮测量:等待输入编号后的执行 if 指定编号 in 目标物数据: current_target = 指定编号 state = 4 print(f"第三轮测量:已选择目标物 {current_target}") else: print("无效的编号,请重新输入") elif state == 4: # 第四轮测量:角度测量准备 state = 5 print("第四轮测量:准备角度测量") elif state == 5: # 第四轮测量:角度测量 state = 0 print("第四轮测量:角度测量完成") # 主循环 while True: clock.tick() img = sensor.snapshot() # 显示状态信息 img.draw_string(10, 10, f"状态: {state}", color=(255, 0, 0)) if current_target: img.draw_string(10, 25, f"当前目标: {current_target}", color=(255, 0, 0)) img.draw_string(10, 40, "按任意键启动测量", color=(255, 0, 0)) # 检测按键(假设通过串口或其他方式输入) # 这里简化为每5秒自动启动,实际应用中需替换为真实的输入检测 # if 检测到按键: # start_measurement() # 模拟按键输入(用于测试) if time.time() % 5 < 0.1 and state == 0: start_measurement() # 找白色色块,黑色边框内部 frames = img.find_blobs([(150,256)]) frame_blob = find_center_min_blob(frames) if not frame_blob: print("NO FRAME") continue # 计算距离 distance = DISTANCE_MM_2*FRAME_WIDTH_PIXEL_2/frame_blob.w() # 缩小roi,避免黑框的黑边 frame_roi = (frame_blob.x()+5, frame_blob.y()+5, frame_blob.w()-10, frame_blob.h()-10) if frame_roi[2] <= 0 or frame_roi[3] <= 0: print("ROI ERROR") continue # print(frame_roi) # 找黑色色块,目标物体 objs = img.find_blobs([(0,150)], roi=frame_roi) obj_blob = find_center_max_blob(objs) if not obj_blob: print("NO OBJS") continue # 根据状态执行不同的测量任务 if state == 1 or state == 2: # 任务1和2:找最小面积正方形 # 调整色块检测参数,针对黑色正方形优化 # (0, 100) 更适合检测黑色 squares = img.find_blobs([(0, 100)], roi=frame_roi, pixels_threshold=2, area_threshold=2) min_square = find_min_square(squares) if min_square: # 计算正方形的x(假设x为宽度) square_x_mm = min_square.w()/frame_blob.w()*FRAME_WIDTH_MM print(f"距离D: {distance:.2f}mm, 最小正方形x: {square_x_mm:.2f}mm") img.draw_string(10, 60, f"D: {distance:.2f}mm", color=(255, 0, 0)) img.draw_string(10, 75, f"最小正方形x: {square_x_mm:.2f}mm", color=(255, 0, 0)) img.draw_rectangle(min_square.rect(), color=(0, 255, 0)) else: # 如果没找到最小正方形,尝试极低阈值 squares = img.find_blobs([(0, 100)], roi=frame_roi, pixels_threshold=1, area_threshold=1) min_square = find_min_square(squares) if min_square: square_x_mm = min_square.w()/frame_blob.w()*FRAME_WIDTH_MM print(f"极低阈值下找到: 距离D: {distance:.2f}mm, 最小正方形x: {square_x_mm:.2f}mm") img.draw_string(10, 60, f"D: {distance:.2f}mm", color=(255, 0, 0)) img.draw_string(10, 75, f"最小正方形x: {square_x_mm:.2f}mm", color=(255, 0, 0)) img.draw_rectangle(min_square.rect(), color=(0, 255, 0)) else: print("未找到任何正方形色块") elif state == 4: # 任务3:指定编号正方形 # 使用优化的黑色正方形检测参数 squares = img.find_blobs([(0, 100)], roi=frame_roi, pixels_threshold=2, area_threshold=2) # 找最小正方形 min_square = find_min_square(squares) if min_square: # 识别数字 number = recognize_number(img, min_square.rect()) print(f"识别到的编号: {number}") if number == 指定编号: # 计算指定编号正方形的x square_x_mm = min_square.w()/frame_blob.w()*FRAME_WIDTH_MM print(f"距离D: {distance:.2f}mm, 编号{指定编号}正方形x: {square_x_mm:.2f}mm") img.draw_string(10, 60, f"D: {distance:.2f}mm", color=(255, 0, 0)) img.draw_string(10, 75, f"编号{指定编号}正方形x: {square_x_mm:.2f}mm", color=(255, 0, 0)) else: print(f"识别到的编号{number}与指定编号{指定编号}不匹配") else: print("未找到正方形色块,无法识别编号") elif state == 5: # 任务4:角度测量 # 先找到最小正方形 squares = img.find_blobs([(0, 100)], roi=frame_roi, pixels_threshold=2, area_threshold=2) min_square = find_min_square(squares) if min_square: angle = calculate_angle(min_square) # 计算倾斜状态下的正方形x square_x_mm = min_square.w()/frame_blob.w()*FRAME_WIDTH_MM / math.cos(math.radians(angle)) print(f"角度θ: {angle:.2f}度, 正方形x: {square_x_mm:.2f}mm") img.draw_string(10, 60, f"角度θ: {angle:.2f}度", color=(255, 0, 0)) img.draw_string(10, 75, f"正方形x: {square_x_mm:.2f}mm", color=(255, 0, 0)) else: print("未找到正方形色块,无法进行角度测量") # 计算物体边长(原有功能) obj_w_mm = obj_blob.w()/frame_blob.w()*FRAME_WIDTH_MM # 原有形状识别功能 # print(frame_blob.w()) # print(obj_blob.density()) # if 0.9 < obj_blob.density() : # print("矩形") # elif 0.6 < obj_blob.density(): # print("圆形") # elif 0.4 < obj_blob.density(): # print("三角形形") # else: # print("无法识别到形状") # 绘制边框和目标 img.draw_rectangle(frame_blob.rect()) img.draw_rectangle(obj_blob.rect()) # 检测用户输入(假设通过串口接收) # 这里简化处理,实际应用中需根据硬件情况实现 # 如果在状态3,接收用户输入的编号 if state == 3: # 模拟用户输入(实际应用中替换为真实输入检测) # 这里每2秒自动输入一个随机编号 if time.time() % 2 < 0.1: 指定编号 = random.randint(1, 4) print(f"用户输入编号: {指定编号}") start_measurement() 解释一下各个代码
最新发布
08-02
<think>我们有一个复杂的程序,它使用OpenMV相机进行多轮测量任务。程序有多个状态,每个状态执行不同的测量任务。我将逐步解释代码的各个部分。 ### 1. 导入模块和全局变量设置 ```python import sensor import time import random import math # 全局状态变量 state = 0 # 0: 等待启动, 1: 第一轮测量, 2: 第二轮测量, 3: 编号输入测量, 4: 角度测量 remaining_targets = [1, 2, 3, 4] # 剩余目标物编号 current_target = None # 当前目标物 指定编号 = 0 # 用户输入的指定编号 ``` - 导入必要的模块:sensor(摄像头控制)、time(时间)、random(随机数)、math(数学计算)。 - 定义全局状态变量: - `state`:表示当前程序状态,0为初始等待状态,1和2为前两轮测量,3为等待用户输入编号,4为角度测量。 - `remaining_targets`:剩余待测量的目标物编号列表(1-4)。 - `current_target`:当前选中的目标物编号。 - `指定编号`:用户输入的指定目标物编号(用于第三轮测量)。 ### 2. 摄像头初始化 ```python # 设置VGA画面,并裁剪中间的画面 sensor.reset() sensor.set_pixformat(sensor.GRAYSCALE) sensor.set_framesize(sensor.VGA) sensor.set_windowing((200,240)) sensor.skip_frames(time=2000) clock = time.clock() # Tracks FPS. CENTER_X = 200//2 CENTER_Y = 240//2 ``` - 初始化摄像头:设置为灰度图像,VGA分辨率(640x480),然后裁剪中间200x240的区域。 - `sensor.skip_frames`:等待摄像头稳定。 - `clock`:用于计算帧率。 - `CENTER_X`和`CENTER_Y`:定义画面中心坐标。 ### 3. 测量参数设置 ```python # 边框的真实长度,单位mm FRAME_WIDTH_MM = 176 # 校准的数据 # 例如: 当距离是1400mm时,边框像素宽度为82 DISTANCE_MM_2 = 1400 FRAME_WIDTH_PIXEL_2 = 82 # 目标物数据(假设) 目标物数据 = { 1: {"name": "目标物1", "color": (0, 150)}, 2: {"name": "目标物2", "color": (0, 150)}, 3: {"name": "目标物3", "color": (0, 150)}, 4: {"name": "目标物4", "color": (0, 150)} } # 数字模板(简化版,实际应用中可能需要更复杂的模板匹配) 数字模板 = { 1: "1", 2: "2", 3: "3", 4: "4" } ``` - `FRAME_WIDTH_MM`:A4纸边框的实际宽度(单位毫米)。 - 校准数据:在已知距离(1400mm)下,边框在图像中的像素宽度(82像素)。 - `目标物数据`:目标物编号对应的名称和颜色范围(这里都是黑色,灰度值0-150)。 - `数字模板`:用于识别正方形上数字的模板(这里只是示例,实际未使用)。 ### 4. 辅助函数 #### 4.1 寻找中心区域的最小色块 ```python def find_center_min_blob(blobs): # 找中间最小的色块 blob = None min_area = 100000 for b in blobs: # 排除距离中心太远的色块(曼哈顿距离>50) if abs(b.cx()-CENTER_X)+ abs(b.cy()-CENTER_Y) > 50: continue if b.area() > min_area: continue blob = b min_area = b.area() return blob ``` - 在给定的色块列表中,寻找距离画面中心最近且面积最小的色块(用于边框检测)。 #### 4.2 寻找中心区域的最大色块 ```python def find_center_max_blob(blobs): # 找中间最大的色块 blob = None max_area = 0 for b in blobs: # 排除距离中心太远的色块 if abs(b.cx()-CENTER_X)+ abs(b.cy()-CENTER_Y) > 50: continue if b.area() < max_area: continue blob = b max_area = b.area() return blob ``` - 在给定的色块列表中,寻找距离画面中心最近且面积最大的色块(用于目标物体检测)。 #### 4.3 寻找最小正方形色块 ```python def find_min_square(blobs): # 找最小面积的正方形 min_square = None min_area = float(&#39;inf&#39;) # 初始化为无穷大 # 调试信息:显示找到的色块数量 print(f"找到的色块数量: {len(blobs)}") for b in blobs: # 对于黑色正方形,降低面积过滤阈值 if b.area() < 5: # 进一步降低最小面积阈值 continue # 假设正方形的宽高比接近1 aspect_ratio = max(b.w(), b.h()) / min(b.w(), b.h()) if min(b.w(), b.h()) > 0 else 0 # 对于小正方形,宽高比判断可以更宽松 if aspect_ratio > 1.3: # 稍微放宽宽高比限制 continue if b.area() < min_area: min_area = b.area() min_square = b print(f"临时最小正方形: 面积={b.area()}, 宽={b.w()}, 高={b.h()}, 宽高比={aspect_ratio:.2f}") # 调试信息 if min_square: print(f"最终最小正方形: 面积={min_square.area()}, 宽={min_square.w()}, 高={min_square.h()}") else: print("未找到符合条件的正方形") return min_square ``` - 在色块列表中寻找最小面积的正方形。条件: - 面积大于5(过滤噪点)。 - 宽高比小于1.3(允许一定变形)。 - 面积最小。 #### 4.4 识别数字(简化版) ```python def recognize_number(img, roi): # 识别数字(简化版) # 实际应用中可能需要OCR或模板匹配 # 这里返回一个随机数作为示例 return random.randint(1, 4) ``` - 实际应用中应使用OCR或模板匹配,这里返回1-4的随机数。 #### 4.5 计算角度 ```python def calculate_angle(blob): # 计算物体与轴线的夹角 # 假设通过宽高比来估算角度 aspect_ratio = blob.w() / blob.h() if blob.h() > 0 else 1 angle = math.atan(aspect_ratio) * 180 / math.pi return min(max(angle, 30), 60) # 限制在30-60度之间 ``` - 通过色块的宽高比估算角度(这里做了简化,实际应更精确)。 #### 4.6 启动测量 ```python def start_measurement(): global state, current_target, remaining_targets if state == 0: # 第一轮测量:随机取一个目标物 if remaining_targets: current_target = random.choice(remaining_targets) remaining_targets.remove(current_target) state = 1 print(f"第一轮测量:已选择目标物 {current_target}") else: print("没有可用目标物") elif state == 1: # 第二轮测量:从剩余目标物中再随机取一个 if remaining_targets: current_target = random.choice(remaining_targets) remaining_targets.remove(current_target) state = 2 print(f"第二轮测量:已选择目标物 {current_target}") else: print("没有可用目标物") elif state == 2: # 第三轮测量:等待输入编号 state = 3 print("请输入要测量的正方形编号(1-4):") elif state == 3: # 第三轮测量:等待输入编号后的执行 if 指定编号 in 目标物数据: current_target = 指定编号 state = 4 print(f"第三轮测量:已选择目标物 {current_target}") else: print("无效的编号,请重新输入") elif state == 4: # 第四轮测量:角度测量准备 state = 5 print("第四轮测量:准备角度测量") elif state == 5: # 第四轮测量:角度测量 state = 0 print("第四轮测量:角度测量完成") ``` - 根据当前状态启动下一轮测量: - 状态0:随机选择一个目标物进入状态1。 - 状态1:从剩余目标物中随机选一个进入状态2。 - 状态2:进入状态3(等待用户输入)。 - 状态3:检查输入编号是否有效,有效则进入状态4。 - 状态4:进入状态5(角度测量准备)。 - 状态5:完成角度测量,回到状态0。 ### 5. 主循环 #### 5.1 状态显示和按键检测 ```python while True: clock.tick() img = sensor.snapshot() # 显示状态信息 img.draw_string(10, 10, f"状态: {state}", color=(255, 0, 0)) if current_target: img.draw_string(10, 25, f"当前目标: {current_target}", color=(255, 0, 0)) img.draw_string(10, 40, "按任意键启动测量", color=(255, 0, 0)) # 模拟按键输入(用于测试) if time.time() % 5 < 0.1 and state == 0: start_measurement() ``` - 显示当前状态、当前目标物和提示信息。 - 每5秒自动触发一次测量(模拟按键)。 #### 5.2 边框检测 ```python # 找白色色块,黑色边框内部 frames = img.find_blobs([(150,256)]) frame_blob = find_center_min_blob(frames) if not frame_blob: print("NO FRAME") continue # 计算距离 distance = DISTANCE_MM_2*FRAME_WIDTH_PIXEL_2/frame_blob.w() ``` - 检测白色边框(灰度值150-255),并找到中心区域的最小色块(即边框)。 - 利用校准数据计算摄像头到边框的距离(通过边框在图像中的宽度)。 #### 5.3 目标物体检测 ```python # 缩小roi,避免黑框的黑边 frame_roi = (frame_blob.x()+5, frame_blob.y()+5, frame_blob.w()-10, frame_blob.h()-10) if frame_roi[2] <= 0 or frame_roi[3] <= 0: print("ROI ERROR") continue # 找黑色色块,目标物体 objs = img.find_blobs([(0,150)], roi=frame_roi) obj_blob = find_center_max_blob(objs) if not obj_blob: print("NO OBJS") continue ``` - 在边框内部定义ROI(避免边框边缘干扰)。 - 在ROI内检测黑色目标物体(灰度值0-150),并找到中心区域的最大色块。 #### 5.4 根据状态执行测量任务 - **状态1和2(第一、二轮测量)**:寻找最小正方形并计算其宽度(毫米)。 ```python if state == 1 or state == 2: squares = img.find_blobs([(0, 100)], roi=frame_roi, pixels_threshold=2, area_threshold=2) min_square = find_min_square(squares) if min_square: square_x_mm = min_square.w()/frame_blob.w()*FRAME_WIDTH_MM # 显示结果 else: # 尝试更低阈值 ... ``` - **状态4(第三轮测量)**:寻找指定编号的正方形。 ```python elif state == 4: squares = img.find_blobs([(0, 100)], roi=frame_roi, pixels_threshold=2, area_threshold=2) min_square = find_min_square(squares) if min_square: number = recognize_number(img, min_square.rect()) if number == 指定编号: square_x_mm = min_square.w()/frame_blob.w()*FRAME_WIDTH_MM # 显示结果 else: ... ``` - **状态5(第四轮测量)**:角度测量。 ```python elif state == 5: squares = img.find_blobs([(0, 100)], roi=frame_roi, pixels_threshold=2, area_threshold=2) min_square = find_min_square(squares) if min_square: angle = calculate_angle(min_square) # 考虑倾斜,计算实际宽度 square_x_mm = min_square.w()/frame_blob.w()*FRAME_WIDTH_MM / math.cos(math.radians(angle)) # 显示结果 ``` #### 5.5 绘制结果和模拟用户输入 ```python # 计算物体边长(原有功能) obj_w_mm = obj_blob.w()/frame_blob.w()*FRAME_WIDTH_MM # 绘制边框和目标 img.draw_rectangle(frame_blob.rect()) img.draw_rectangle(obj_blob.rect()) # 模拟用户输入(状态3时,每2秒自动输入一个随机编号) if state == 3: if time.time() % 2 < 0.1: 指定编号 = random.randint(1, 4) print(f"用户输入编号: {指定编号}") start_measurement() ``` ### 总结 该程序通过状态机控制测量流程: 1. **状态0**:等待开始。 2. **状态1和2**:自动选择目标物,测量最小正方形的宽度。 3. **状态3**:等待用户输入目标编号。 4. **状态4**:测量指定编号的正方形宽度。 5. **状态5**:测量正方形的倾斜角度和实际宽度。 程序利用OpenMV的色块检测功能,结合简单的几何关系进行测量。实际应用中需注意: - 数字识别需要更精确的方法(如模板匹配或OCR)。 - 角度测量方法需改进(如使用最小外接矩形)。 - 用户输入需通过实际硬件(如串口)实现。 §§相关问题§§: 1. 如何改进数字识别功能以提高准确性? 2. 在状态3中,如何通过串口接收用户输入? 3. 如何优化角度测量算法? 4. 程序在多目标物同时出现时如何确保正确识别?</think>以下是针对您提供的 OpenMV 代码的详细解释。这段代码实现了一个多阶段测量系统,通过状态机控制不同的测量任务,包括目标物选择、距离测量、最小正方形检测和角度测量。 ### 1. 全局变量和初始化 ```python # 全局状态变量 state = 0 # 0: 等待启动, 1: 第一轮测量, 2: 第二轮测量, 3: 编号输入测量, 4: 角度测量 remaining_targets = [1, 2, 3, 4] # 剩余目标物编号 current_target = None # 当前目标物 指定编号 = 0 # 用户输入的指定编号 # 设置VGA画面,并裁剪中间的画面 sensor.reset() sensor.set_pixformat(sensor.GRAYSCALE) sensor.set_framesize(sensor.VGA) sensor.set_windowing((200,240)) sensor.skip_frames(time=2000) clock = time.clock() # Tracks FPS. CENTER_X = 200//2 CENTER_Y = 240//2 # 边框的真实长度,单位mm FRAME_WIDTH_MM = 176 # 校准的数据 DISTANCE_MM_2 = 1400 FRAME_WIDTH_PIXEL_2 = 82 # 目标物数据(假设) 目标物数据 = { 1: {"name": "目标物1", "color": (0, 150)}, 2: {"name": "目标物2", "color": (0, 150)}, 3: {"name": "目标物3", "color": (0, 150)}, 4: {"name": "目标物4", "color": (0, 150)} } # 数字模板(简化版) 数字模板 = { 1: "1", 2: "2", 3: "3", 4: "4" } ``` **解释**: - `state`:控制程序状态的状态机变量 - `remaining_targets`:存储待测量的目标物编号 - `current_target`:当前选中的目标物 - `指定编号`:用户输入的特定目标编号 - 摄像头初始化为灰度模式,VGA分辨率,裁剪中间200x240区域 - 校准数据:已知距离(1400mm)对应的边框像素宽度(82像素) - 目标物数据:存储4个目标物的名称和颜色范围 - 数字模板:用于识别目标物上的数字(简化版) ### 2. 辅助函数 #### 2.1 目标物定位函数 ```python def find_center_min_blob(blobs): # 找中间最小的色块 blob = None min_area = 100000 for b in blobs: if abs(b.cx()-CENTER_X)+ abs(b.cy()-CENTER_Y) > 50: continue if b.area() > min_area: continue blob = b min_area = b.area() return blob def find_center_max_blob(blobs): # 找中间最大的色块 blob = None max_area = 0 for b in blobs: if abs(b.cx()-CENTER_X)+ abs(b.cy()-CENTER_Y) > 50: continue if b.area() < max_area: continue blob = b max_area = b.area() return blob ``` **解释**: - `find_center_min_blob`:在画面中心区域(曼哈顿距离<50)寻找面积最小的色块(用于边框检测) - `find_center_max_blob`:在画面中心区域寻找面积最大的色块(用于目标物检测) #### 2.2 最小正方形检测 ```python def find_min_square(blobs): # 找最小面积的正方形 min_square = None min_area = float(&#39;inf&#39;) # 初始化为无穷大 print(f"找到的色块数量: {len(blobs)}") for b in blobs: # 对于黑色正方形,降低面积过滤阈值 if b.area() < 5: # 进一步降低最小面积阈值 continue # 假设正方形的宽高比接近1 aspect_ratio = max(b.w(), b.h()) / min(b.w(), b.h()) if min(b.w(), b.h()) > 0 else 0 # 对于小正方形,宽高比判断可以更宽松 if aspect_ratio > 1.3: # 稍微放宽宽高比限制 continue if b.area() < min_area: min_area = b.area() min_square = b print(f"临时最小正方形: 面积={b.area()}, 宽={b.w()}, 高={b.h()}, 宽高比={aspect_ratio:.2f}") if min_square: print(f"最终最小正方形: 面积={min_square.area()}, 宽={min_square.w()}, 高={min_square.h()}") else: print("未找到符合条件的正方形") return min_square ``` **解释**: - 在检测到的色块中寻找最小面积的正方形 - 过滤条件: 1. 面积大于5(避免噪点) 2. 宽高比小于1.3(允许一定变形) - 输出调试信息帮助理解检测过程 #### 2.3 数字识别和角度计算 ```python def recognize_number(img, roi): # 识别数字(简化版) # 实际应用中可能需要OCR或模板匹配 # 这里返回一个随机数作为示例 return random.randint(1, 4) def calculate_angle(blob): # 计算物体与轴线的夹角 # 假设通过宽高比来估算角度 aspect_ratio = blob.w() / blob.h() if blob.h() > 0 else 1 angle = math.atan(aspect_ratio) * 180 / math.pi return min(max(angle, 30), 60) # 限制在30-60度之间 ``` **解释**: - `recognize_number`:简化的数字识别函数(实际应用中应替换为OCR或模板匹配) - `calculate_angle`:通过宽高比估算物体倾斜角度(限制在30-60度之间) #### 2.4 状态机控制 ```python def start_measurement(): global state, current_target, remaining_targets if state == 0: # 第一轮测量:随机取一个目标物 if remaining_targets: current_target = random.choice(remaining_targets) remaining_targets.remove(current_target) state = 1 print(f"第一轮测量:已选择目标物 {current_target}") else: print("没有可用目标物") elif state == 1: # 第二轮测量:从剩余目标物中再随机取一个 if remaining_targets: current_target = random.choice(remaining_targets) remaining_targets.remove(current_target) state = 2 print(f"第二轮测量:已选择目标物 {current_target}") else: print("没有可用目标物") elif state == 2: # 第三轮测量:等待输入编号 state = 3 print("请输入要测量的正方形编号(1-4):") elif state == 3: # 第三轮测量:等待输入编号后的执行 if 指定编号 in 目标物数据: current_target = 指定编号 state = 4 print(f"第三轮测量:已选择目标物 {current_target}") else: print("无效的编号,请重新输入") elif state == 4: # 第四轮测量:角度测量准备 state = 5 print("第四轮测量:准备角度测量") elif state == 5: # 第四轮测量:角度测量 state = 0 print("第四轮测量:角度测量完成") ``` **解释**: - 状态机控制测量流程: 1. **状态0→1**:随机选择第一个目标物 2. **状态1→2**:随机选择第二个目标物 3. **状态2→3**:等待用户输入目标编号 4. **状态34**:处理用户输入的编号 5. **状态4→5**:准备角度测量 6. **状态5→0**:完成角度测量,重置状态 ### 3. 主循环处理 ```python while True: clock.tick() img = sensor.snapshot() # 显示状态信息 img.draw_string(10, 10, f"状态: {state}", color=(255, 0, 0)) if current_target: img.draw_string(10, 25, f"当前目标: {current_target}", color=(255, 0, 0)) img.draw_string(10, 40, "按任意键启动测量", color=(255, 0, 0)) # 模拟按键输入 if time.time() % 5 < 0.1 and state == 0: start_measurement() # 边框检测 frames = img.find_blobs([(150,256)]) frame_blob = find_center_min_blob(frames) if not frame_blob: print("NO FRAME") continue # 计算距离 distance = DISTANCE_MM_2*FRAME_WIDTH_PIXEL_2/frame_blob.w() # 设置目标检测区域 frame_roi = (frame_blob.x()+5, frame_blob.y()+5, frame_blob.w()-10, frame_blob.h()-10) if frame_roi[2] <= 0 or frame_roi[3] <= 0: print("ROI ERROR") continue # 目标物检测 objs = img.find_blobs([(0,150)], roi=frame_roi) obj_blob = find_center_max_blob(objs) if not obj_blob: print("NO OBJS") continue # 根据状态执行不同测量任务 if state == 1 or state == 2: # 任务1和2:找最小面积正方形 squares = img.find_blobs([(0, 100)], roi=frame_roi, pixels_threshold=2, area_threshold=2) min_square = find_min_square(squares) if min_square: square_x_mm = min_square.w()/frame_blob.w()*FRAME_WIDTH_MM print(f"距离D: {distance:.2f}mm, 最小正方形x: {square_x_mm:.2f}mm") img.draw_string(10, 60, f"D: {distance:.2f}mm", color=(255, 0, 0)) img.draw_string(10, 75, f"最小正方形x: {square_x_mm:.2f}mm", color=(255, 0, 0)) img.draw_rectangle(min_square.rect(), color=(0, 255, 0)) else: # 尝试更低阈值 ... elif state == 4: # 任务3:指定编号正方形 squares = img.find_blobs([(0, 100)], roi=frame_roi, pixels_threshold=2, area_threshold=2) min_square = find_min_square(squares) if min_square: number = recognize_number(img, min_square.rect()) print(f"识别到的编号: {number}") if number == 指定编号: square_x_mm = min_square.w()/frame_blob.w()*FRAME_WIDTH_MM print(f"距离D: {distance:.2f}mm, 编号{指定编号}正方形x: {square_x_mm:.2f}mm") img.draw_string(10, 60, f"D: {distance:.2f}mm", color=(255, 0, 0)) img.draw_string(10, 75, f"编号{指定编号}正方形x: {square_x_mm:.2f}mm", color=(255, 0, 0)) else: print(f"识别到的编号{number}与指定编号{指定编号}不匹配") elif state == 5: # 任务4:角度测量 squares = img.find_blobs([(0, 100)], roi=frame_roi, pixels_threshold=2, area_threshold=2) min_square = find_min_square(squares) if min_square: angle = calculate_angle(min_square) square_x_mm = min_square.w()/frame_blob.w()*FRAME_WIDTH_MM / math.cos(math.radians(angle)) print(f"角度θ: {angle:.2f}度, 正方形x: {square_x_mm:.2f}mm") img.draw_string(10, 60, f"角度θ: {angle:.2f}度", color=(255, 0, 0)) img.draw_string(10, 75, f"正方形x: {square_x_mm:.2f}mm", color=(255, 0, 0)) # 绘制边框和目标 img.draw_rectangle(frame_blob.rect()) img.draw_rectangle(obj_blob.rect()) # 模拟用户输入(状态3) if state == 3: if time.time() % 2 < 0.1: 指定编号 = random.randint(1, 4) print(f"用户输入编号: {指定编号}") start_measurement() ``` **解释**: 1. **状态显示**:在画面顶部显示当前状态和提示信息 2. **边框检测**:寻找白色边框(灰度150-255) 3. **距离计算**:使用校准数据计算摄像头到目标的距离 4. **目标检测**:在边框区域内寻找黑色目标(灰度0-150) 5. **状态处理**: - **状态1/2**:寻找最小正方形并计算其宽度 - **状态4**:识别指定编号的正方形并测量 - **状态5**:测量倾斜角度并计算实际宽度 6. **用户输入模拟**:在状态3时自动生成随机编号 ### 系统工作流程 1. **初始化**:等待启动状态(state=0) 2. **启动测量**:模拟按键触发状态转换 3. **目标选择**: - 随机选择两个目标物(状态1和2) - 等待用户输入指定目标(状态3) 4. **测量执行**: - 距离和最小正方形测量(状态1/2) - 指定目标测量(状态4) - 角度测量(状态5) 5. **结果显示**:在画面和串口输出测量结果 6. **状态重置**:完成所有测量后回到初始状态 ### 改进建议 1. **数字识别增强**:使用模板匹配或简单OCR ```python # 简单数字识别函数示例 def recognize_number(img, roi): # 裁剪ROI区域 number_img = img.copy(roi=roi) # 二值化处理 number_img.binary([(0, 100)]) # 简单模板匹配 best_match = 0 best_score = 0 for num, template in 数字模板.items(): # 这里应使用实际的模板匹配算法 score = number_img.find_template(template, 0.7) if score > best_score: best_score = score best_match = num return best_match if best_match > 0 else random.randint(1, 4) ``` 2. **角度计算改进**:使用最小外接矩形 ```python def calculate_angle(blob): # 获取最小外接矩形 rect = blob.min_rect() # 计算角度(OpenMV可能不支持直接获取角度) # 这里使用宽高比作为替代方案 width = rect[2] height = rect[3] aspect_ratio = max(width, height) / min(width, height) if min(width, height) > 0 else 1 angle = math.atan(aspect_ratio) * 180 / math.pi return min(max(angle, 0), 90) # 限制在0-90度之间 ``` 3. **多目标处理**:扩展支持同时检测多个目标物 ```python def process_multiple_targets(img, frame_blob): # 在边框内查找所有目标物 targets = [] for i in range(1, 5): color_range = 目标物数据[i]["color"] blobs = img.find_blobs([color_range], roi=frame_roi) if blobs: target = find_center_max_blob(blobs) if target: targets.append((i, target)) return targets ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值