文章目录
概要
本文分析的代码是基于立创·庐山派K230CanMV开发板的目标追踪系统实现,主要功能是通过GC2093传感器采集图像,识别特定"黑框白心"目标(如带黑色边框的白色标记物),计算目标位置偏差并通过UART串口发送数据,同时在显示器(虚拟窗口、LCD或HDMI)上实时展示追踪过程及调试信息。
整体架构流程
代码整体遵循"初始化-循环处理-资源释放"的经典嵌入式系统流程,具体步骤如下:
-
系统初始化
- 硬件配置:初始化FPIOA引脚映射,配置UART2(波特率115200)用于数据传输。
- 传感器初始化:启动GC2093传感器,设置分辨率(400x240)和灰度模式,开始采集图像。
- 显示器初始化:根据
DISPLAY_MODE
(VIRT/LCD/HDMI)初始化对应显示设备,设置显示分辨率。 - 媒体管理:初始化
MediaManager
协调传感器与显示器资源。
-
主循环处理
- 图像采集:通过
sensor.snapshot()
获取实时图像。 - 目标识别:调用
find_target_blob()
在当前ROI内搜索"黑框白心"目标,结合多阈值和形态筛选规则验证目标。 - 动态ROI调整:若锁定目标,缩小ROI至目标周围(提高效率);若目标丢失,逐步扩大ROI至全视野(重新搜索)。
- 数据处理:计算目标与画面中心的偏差,通过
smooth_target()
平滑坐标,减少抖动。 - 状态管理:通过
is_locked
标记目标状态,lost_frames
计数连续丢失帧数,超过阈值则重置状态。 - 数据发送与显示:通过UART发送目标状态及坐标,在图像上绘制辅助线、ROI和调试信息,最终通过显示器展示。
- 图像采集:通过
-
资源释放
- 异常处理:捕获键盘中断或其他异常时,停止传感器、关闭显示器、释放媒体资源,确保系统安全退出。
技术名词解释
-
HSV阈值(thr_white/thr_black)
用于颜色(灰度)识别的范围参数。代码中通过灰度值范围定义"白色"和"黑色"区域,多阈值配置(列表形式)可适应不同光照下的颜色偏差(如强光/弱光下白色的灰度值范围变化)。 -
ROI(Region of Interest,感兴趣区域)
图像中需要重点处理的区域。通过限制处理范围减少计算量:目标锁定时缩小ROI(围绕目标),目标丢失时扩大ROI(逐步恢复全视野)。 -
Blob分析
对图像中连通的像素区域(色块)进行分析的技术。代码中通过find_blobs()
识别黑色边框和白色中心的色块,结合面积、长宽比等特征筛选有效目标。 -
实心度(Solidity)
色块实际像素数与外接矩形面积的比值,用于判断色块的"填充程度"。代码中利用实心度筛选空心黑色边框(实心度低),排除实心噪声区域。 -
UART(通用异步收发传输器)
一种串行通信协议,代码中用于将目标状态、坐标等数据发送到外部设备(如控制器),波特率115200确保数据传输稳定。 -
帧率(FPS)
每秒处理的图像帧数,反映系统实时性。代码中通过time.ticks_ms()
计算帧率,用于评估系统性能。
技术细节
1. 多阈值适配与目标识别逻辑
代码通过多阈值解决光照适应性问题,核心在于"黑框优先+白心验证"的双层识别策略:
# 多阈值配置(适应不同光照下的黑白识别)
thr_white = [(200, 255)] # 白色中心的灰度范围(可扩展多组)
thr_black = [(0, 60)] # 黑色边框的灰度范围(可扩展多组)
def find_target_blob(img, roi):
# 1. 先找黑色边框(多阈值遍历+合并相邻色块)
black_blobs = []
for t in thr_black:
blobs = img.find_blobs([t], roi=roi, merge=True) # merge=True合并相邻色块
if blobs:
black_blobs.extend(blobs)
# 2. 对每个黑色边框验证是否包含白色中心
for b_blob in black_blobs:
# 筛选黑色边框(面积、长宽比、实心度)
if not (MIN_RECT_AREA < b_blob.area() < MAX_RECT_AREA and
0.5 < b_blob.w()/b_blob.h() < 2.0 and
0.1 < b_blob.solidity() < 0.35):
continue
# 在黑色边框内部10%边距内搜索白色中心
inner_roi = b_blob.rect()
inner_margin = int(min(inner_roi[2], inner_roi[3]) * 0.1)
inner_roi = (
inner_roi[0] + inner_margin,
inner_roi[1] + inner_margin,
inner_roi[2] - 2*inner_margin,
inner_roi[3] - 2*inner_margin
)
# 验证白色中心
white_blobs = []
for t in thr_white:
w_blobs = img.find_blobs([t], roi=inner_roi)
if w_blobs:
white_blobs.extend(w_blobs)
if white_blobs:
w_blob = max(white_blobs, key=lambda b: b.area()) # 取最大白色块
# 检查白心是否在黑框中心区域
dx = abs(w_blob.cx() - b_blob.cx())
dy = abs(w_blob.cy() - b_blob.cy())
if dx < b_blob.w()*0.15 and dy < b_blob.h()*0.15:
return (b_blob, w_blob) # 返回匹配的黑框白心
return (None, None)
代码解析:
- 多阈值遍历:通过
thr_black
和thr_white
列表支持多组灰度范围,解决单一阈值在强光/弱光下失效的问题。 - 合并相邻色块:
merge=True
确保断裂的黑色边框被识别为完整区域(如边框有缺口时仍能正常识别)。 - 内边距ROI:在黑色边框内部10%区域搜索白色中心,避免背景干扰,确保"心在框内"的特征匹配。
2. 动态ROI与状态管理机制
通过ROI自适应调整平衡搜索效率与范围,结合状态标记实现稳定跟踪:
# 初始化全视野ROI
current_roi = (0, 0, sensor.width(), sensor.height())
is_locked = False
lost_frames = 0
MAX_LOST_FRAMES = 8 # 连续丢失8帧则重置状态
while True:
img = sensor.snapshot()
b_blob, w_blob = find_target_blob(img, current_roi)
if b_blob and w_blob:
# 目标锁定:缩小ROI至目标周围
is_locked = True
lost_frames = 0
# 计算锁定状态下的ROI(目标周围90%范围)
lock_roi = (
max(0, b_blob.x() - int(b_blob.w()*0.05)),
max(0, b_blob.y() - int(b_blob.h()*0.05)),
min(sensor.width(), b_blob.w()*1.1),
min(sensor.height(), b_blob.h()*1.1)
)
current_roi = lock_roi
else:
# 目标丢失:逐步扩大ROI
lost_frames += 1
if lost_frames > MAX_LOST_FRAMES:
is_locked = False
# 每次扩大100像素(限制在全视野内)
new_x = max(0, current_roi[0] - 50)
new_y = max(0, current_roi[1] - 50)
new_w = min(sensor.width(), current_roi[2] + 100)
new_h = min(sensor.height(), current_roi[3] + 100)
current_roi = (new_x, new_y, new_w, new_h)
代码解析:
- 锁定状态优化:目标锁定时ROI缩小为目标尺寸的1.1倍,减少背景处理量(如400x240图像中,ROI缩小后计算量可降低60%以上)。
- 丢失恢复策略:目标丢失后,ROI每次向外扩展50像素(横向/纵向),逐步恢复全视野,避免瞬间扩大导致的计算量激增。
- 状态容错机制:
MAX_LOST_FRAMES=8
允许短时间遮挡(约0.2~0.3秒,按30FPS计算),防止误判丢失。
3. 数据平滑与偏差计算
通过指数平滑减少抖动,结合视角补偿提升跟踪稳定性:
# 数据平滑与偏差计算
prev_x, prev_y = 0