3、MicroPython基础:变量、数据类型、条件判断、循环、函数等核心语法快速回顾
在开始使用OpenMV进行视觉AI开发之前,我们需要先建立MicroPython的语法基础。MicroPython是Python 3的一个精简实现,专为微控制器设计,保留了Python的核心语法和动态特性,同时增加了对硬件寄存器和外设的直接访问能力。本章将聚焦于变量与数据类型、条件判断、循环、函数四大核心模块,并结合OpenMV摄像头场景进行实战演练。理解这些语法是编写高效、可维护视觉代码的前提,也是后续学习颜色识别、二维码检测、神经网络部署等高级功能的基石。
3.1 变量与数据类型:从内存到图像帧
核心原理:MicroPython中的变量是对象的引用,不需要显式声明类型。OpenMV的RAM通常只有512KB~1MB,理解数据类型的内存占用对视觉应用至关重要。整数(int)占用4字节,浮点数(float)占用4字节,字符串(str)按字符数动态分配,而图像对象(Image)则直接映射到帧缓冲区。
判断方法:使用type()函数检查变量类型,使用sys.getsizeof()查看内存占用。OpenMV IDE的“帧缓冲区”窗口可以直观看到图像数据的内存布局。
# OpenMV 中的变量类型示例
import sensor, image, time
# 整数类型 - 用于计数和阈值
frame_count = 0
threshold = (30, 100, 15, 127, 15, 127) # LAB颜色阈值元组
# 浮点数 - 用于计算比例
fps = 0.0
exposure_time = 20.5 # 毫秒
# 布尔类型 - 用于状态标志
is_detected = False
# 元组 - 用于坐标和颜色
center_coord = (160, 120) # 图像中心
rgb_value = (255, 0, 0) # 红色
# 列表 - 用于存储多个目标
blobs_list = []
# 字典 - 用于配置参数
config = {'threshold': threshold, 'pixels_threshold': 100, 'area_threshold': 100}
# 图像对象 - 直接引用帧缓冲区
img = sensor.snapshot() # 获取一帧图像
print("图像尺寸:", img.width(), "x", img.height())
print("图像类型:", type(img))
sensor.snapshot()返回的图像对象是帧缓冲区的引用,不是拷贝。频繁创建新图像对象会导致内存碎片化,建议复用变量名。
| 数据类型 | 内存占用 | OpenMV典型应用 | 注意事项 |
|---|---|---|---|
| int | 4字节 | 像素坐标、帧计数 | 避免大整数运算 |
| float | 4字节 | FPS计算、比例换算 | 浮点运算较慢 |
| tuple | 按元素累加 | 颜色阈值、坐标对 | 不可变,适合常量 |
| list | 动态增长 | 多目标存储 | 注意内存碎片 |
| dict | 键值对存储 | 配置参数 | 查找速度快 |
| Image | 帧缓冲区 | 图像处理 | 复用变量,避免拷贝 |
常见误区:初学者常将Python桌面版的无限内存假设带入MicroPython。在OpenMV中,一个QVGA(320x240)的RGB565图像需要150KB内存,如果同时保存多帧图像,很容易触发MemoryError。建议使用gc.collect()手动触发垃圾回收,并尽量在循环中复用变量。
3.2 条件判断:让摄像头“思考”
核心原理:条件判断通过比较运算符(==, !=, >, <, >=, <=)和逻辑运算符(and, or, not)组合条件表达式,控制程序执行路径。在视觉应用中,条件判断用于决定是否执行目标检测、是否触发拍照、是否发送数据等。
操作流程:
- 明确判断目标(如:检测红色物体)
- 设定判断阈值(如:面积大于100像素)
- 组合条件表达式(如:颜色匹配且面积足够)
- 执行分支操作(如:发送坐标或点亮LED)
# 条件判断在目标检测中的应用
import sensor, image, time
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time=2000)
red_threshold = (30, 100, 15, 127, 15, 127) # 红色LAB阈值
while True:
img = sensor.snapshot()
blobs = img.find_blobs([red_threshold], pixels_threshold=200, area_threshold=200)
if blobs:
# 找到至少一个红色目标
largest_blob = max(blobs, key=lambda b: b.area())
if largest_blob.area() > 500:
# 目标足够大,执行精确操作
print("检测到大目标,面积:", largest_blob.area())
img.draw_rectangle(largest_blob.rect(), color=(0, 255, 0))
if largest_blob.cx() < 120:
print("目标在左侧")
elif largest_blob.cx() > 200:
print("目标在右侧")
else:
print("目标在中央")
else:
# 目标太小,可能是噪声
print("目标过小,忽略")
else:
# 未检测到目标
print("未检测到红色物体")
time.sleep_ms(100)
blobs是否为空,再计算面积。
案例拆解:假设我们需要设计一个智能门禁系统,当检测到红色标签且标签面积大于阈值时,才允许开门。初始现象是系统频繁误触发,原因是未设置面积下限。通过添加area_threshold=200参数,并增加if largest_blob.area() > 500的二次判断,误触发率降低了90%。关键参数是面积阈值和颜色阈值的配合,实际操作中需要根据环境光线调整LAB阈值。
3.3 循环结构:连续帧处理的核心
核心原理:循环允许重复执行代码块,是视频流处理的基础。OpenMV的主循环通常使用while True持续获取图像帧。for循环适合遍历已知集合(如色块列表),while循环适合条件驱动的持续处理。
判断方法:
- 需要遍历所有元素 → 使用for循环
- 需要持续运行直到条件改变 → 使用while循环
- 需要提前退出 → 使用break
- 需要跳过当前迭代 → 使用continue
# 循环结构在OpenMV中的应用
import sensor, image, time
# for循环遍历色块
def process_blobs(blobs):
for i, blob in enumerate(blobs):
print(f"色块{i+1}: 位置({blob.cx()}, {blob.cy()}), 面积{blob.area()}")
if blob.area() < 50:
continue # 跳过小色块
# 处理有效色块
process_large_blob(blob)
# while循环实现帧率控制
frame_count = 0
start_time = time.ticks_ms()
while True:
img = sensor.snapshot()
frame_count += 1
# 每秒打印一次帧率
if time.ticks_diff(time.ticks_ms(), start_time) > 1000:
fps = frame_count * 1000 / time.ticks_diff(time.ticks_ms(), start_time)
print(f"当前帧率: {fps:.1f} FPS")
frame_count = 0
start_time = time.ticks_ms()
# 检测到特定条件时退出循环
if sensor.get_id() == 0: # 示例条件
break
for循环遍历色块时,enumerate()比手动索引更快。
常见误区:无限循环中忘记添加延时(time.sleep_ms())会导致CPU占用100%,使摄像头帧率不稳定。另外,在循环中频繁创建大对象(如图像拷贝)会导致内存碎片,建议使用gc.collect()定期清理。
3.4 函数封装:模块化视觉逻辑
核心原理:函数将可复用的代码块封装,支持参数传递和返回值。在OpenMV中,函数用于封装图像处理流程、传感器配置、通信协议等。合理的函数设计能显著提高代码可读性和可维护性。
操作流程:
- 识别可复用的代码块
- 定义函数签名(参数和返回值)
- 实现函数体逻辑
- 测试函数边界条件
- 在主循环中调用
# 函数封装示例:完整的颜色检测模块
import sensor, image, time
# 传感器初始化函数
def init_sensor():
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_auto_whitebal(False) # 关闭自动白平衡
sensor.skip_frames(time=2000)
print("传感器初始化完成")
# 颜色检测函数
def detect_color(img, threshold, min_area=100):
"""检测指定颜色的最大色块
参数:
img: 图像对象
threshold: LAB颜色阈值元组
min_area: 最小面积阈值
返回:
(cx, cy, area) 或 None
"""
blobs = img.find_blobs([threshold], pixels_threshold=min_area, area_threshold=min_area)
if blobs:
largest = max(blobs, key=lambda b: b.area())
return (largest.cx(), largest.cy(), largest.area())
return None
# 主程序
init_sensor()
red_threshold = (30, 100, 15, 127, 15, 127)
while True:
img = sensor.snapshot()
result = detect_color(img, red_threshold, min_area=200)
if result:
cx, cy, area = result
print(f"目标位置: ({cx}, {cy}), 面积: {area}")
img.draw_cross(cx, cy, color=(0, 255, 0))
else:
print("未检测到目标")
time.sleep_ms(50)
| 函数设计原则 | OpenMV实践建议 | 示例 |
|---|---|---|
| 单一职责 | 每个函数只做一件事 | detect_color只负责检测 |
| 参数默认值 | 提供合理的默认参数 | min_area=100 |
| 返回值明确 | 使用None表示无结果 | return None |
| 避免全局变量 | 通过参数传递数据 | 传入img对象 |
| 错误处理 | 使用try/except捕获异常 | try: sensor.snapshot() except: |
案例复盘:在一个物流分拣项目中,我们需要检测不同颜色的包裹。初始代码将所有逻辑写在主循环中,导致代码冗长且难以调试。通过重构为三个函数:init_sensor()、detect_color()、send_result(),代码量减少了40%,调试时间缩短了60%。关键改进是使用函数参数传递颜色阈值,使得切换检测颜色只需修改一行调用代码。风险在于函数调用有轻微性能开销,但在OpenMV的帧率范围内(30FPS)影响可忽略。
import导入,方便多项目复用。