文章目录
概要
本系统是针对2026年全国大学生电子设计竞赛H题(通常涉及"运动目标控制与追踪"或类似赛题)设计的视觉处理核心。其目标是在嵌入式AI平台(如K210/K230)上,实现对特定目标(如乒乓球、彩色小球)的实时检测、精准跟踪以及瞬时速度估算。
该系统集成了轻量化深度学习模型推理 、空间标定与坐标映射 、滑动窗口线性回归速度估计 以及低延迟串口通信四大模块。通过AI模型在特定ROI(感兴趣区域)内的目标检测,结合像素-物理空间的标定参数,实现了从"看见目标"到"输出控制指令"的闭环,为后续的机械臂、云台或小车控制提供了关键的视觉反馈数据。
整体架构流程
系统采用图像采集 -> AI推理 -> 后处理 -> 追踪滤波 -> 物理坐标映射与速度估计 -> 数据输出与显示的流水线架构。整体流程如下:
-
系统初始化:加载神经网络模型(KModel)、标定文件(像素-厘米映射系数)、配置串口参数及显示设备。
-
图像预处理与AI推理:采集640x360分辨率的RGB图像,经过Letterbox填充与缩放后,送入YOLO/SSD类Anchor-Based检测网络进行推理。
-
目标筛选与空间映射:
-
对网络输出的检测框进行非极大值抑制(NMS) 和ROI区域过滤(仅保留赛道或指定区域内的目标)。
-
利用卡尔曼滤波器对检测框进行平滑与跟踪,解决短暂遮挡或抖动问题。
-
-
坐标转换与速度估计:
-
利用线性标定系数(
a,b),将检测框中心的像素X坐标转换为物理空间中的相对偏移距离(cm)。 -
通过滑动窗口线性回归对过去10帧的历史位置数据进行拟合,实时计算目标在X轴方向的运动速度(cm/s)。
-
-
数据输出与可视化:
-
通过**OSD(屏幕显示)**在视频画面上叠加绘制目标框、十字准星、物理距离及速度值。
-
通过**串口(UART)**以10Hz(100ms间隔)的频率向主控MCU(如STM32)发送精简的"相对偏移-距离-速度"3字段数据帧。
-
技术名词解释
-
ROI(感兴趣区域):在图像中人为划定的特定矩形区域。本系统中该区域为画面底部中央的带状区域(640x60像素),用于将AI模型的注意力集中于目标可能出现的"地面赛道",过滤掉背景干扰,提高系统实时性。
-
Anchor-Based Detection :一种目标检测方法。通过在图像上预设不同尺寸和比例的锚点框(Anchor),作为回归检测框的参考基准。本代码使用的
aicube.anchorbasedet_post_process函数正是指定此解码方法。 -
卡尔曼滤波(Kalman Filter) :一种线性最优状态估计算法。在本系统中用于对目标框的中心位置和速度进行平滑和预测,即使在目标被短暂遮挡时也能维持较为稳定的位置估计(通过
Max Lost Frames参数控制)。 -
线性回归滑动窗口(Sliding Window Regression):一种计算速度的方法。维护一个包含过去N帧(代码中为10帧)时间戳和位置的数据队列,通过最小二乘法拟合出一条"位置-时间"直线,该直线的斜率即为目标的瞬时速度。这种方法比简单的差分法具有更好的抗噪声能力。
-
视场标定(FOV Calibration) :通过实验测量,建立"图像像素坐标"与"现实世界物理坐标(厘米)"之间的映射关系。本代码使用线性模型
px = a * cm + b,通过标定文件calibration.json中的a和b参数实现逆映射。 -
NMS(非极大值抑制):一种用于消除目标检测中冗余重叠检测框的算法。确保每个目标只被一个最准确的框所表示。
-
OSD(屏幕显示) :一种视频叠加技术,允许在不干扰底层视频流的情况下,在显示画面上实时绘制文本、图形和标记。本代码利用
image.Image对象在硬件加速层绘制图形。
技术细节
1. 模型推理加速与ROI策略
预处理优化 :利用硬件ai2d模块在AI核心上进行图像缩放与填充(Letterbox),避免CPU干预,降低延迟。核心初始化代码如下:
python
ai2d=nn.ai2d()
ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,nn.ai2d_format.NCHW_FMT,np.uint8,np.uint8)
ai2d.set_pad_param(True,[0,0,0,0,top,bottom,left,right],0,[114,114,114])
ai2d.set_resize_param(True,nn.interp_method.tf_bilinear,nn.interp_mode.half_pixel)
ai2d_builder=ai2d.build(
[1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH],
[1,3,kmodel_frame_size[1],kmodel_frame_size[0]])
ROI过滤前移:在解码出所有检测框后,立即进行基于空间坐标的过滤,大幅减少进入追踪器的目标数量。ROI定义与过滤函数如下:
python
# ROI区域定义(显示空间640×480)
ROI_X = 0
ROI_Y = 210
ROI_W = 640
ROI_H = 60
# ROI映射到AI空间(640×360)
ROI_AI_SCALE_X = float(OUT_RGB888P_WIDTH) / float(SENSOR_DISP_WIDTH)
ROI_AI_SCALE_Y = float(OUT_RGB888P_HEIGH) / float(SENSOR_DISP_HEIGHT)
ROI_AI_X = int(ROI_X * ROI_AI_SCALE_X)
ROI_AI_Y = int(ROI_Y * ROI_AI_SCALE_Y)
ROI_AI_W = int(ROI_W * ROI_AI_SCALE_X)
ROI_AI_H = int(ROI_H * ROI_AI_SCALE_Y)
def is_in_roi(box):
cx = (box[2] + box[4]) / 2.0
cy = (box[3] + box[5]) / 2.0
if ROI_AI_X <= cx <= ROI_AI_X2 and ROI_AI_Y <= cy <= ROI_AI_Y2:
return True
return False
本代码中ROI区域仅占全图面积的约1/8(640×60 / 640×480 = 12.5%),显著降低了追踪算法和串口数据的计算开销。
NMS重抑制:在原始NMS之后,增加了一次针对ROI内目标的二次NMS,进一步确保追踪器接收到的候选框质量:
python
dedup=re_nms_single_class(raw,iou_threshold=RE_NMS_IOU_THRESHOLD)
roi_boxes,roi_out=filter_boxes_by_roi(dedup)
2. 高精度速度估计算法(核心)
基本原理:速度是位置对时间的导数。本方案采用"局部加权回归"的简化版------固定窗口最小二乘拟合。
核心类实现 :VelocityEstimator维护buf_t(时间戳ms)和buf_x(物理坐标cm)两个列表,核心更新函数如下:
python
class VelocityEstimator:
def __init__(self, window=10, dead_zone=1.5):
self.buf_t = [] # 时间戳 (ms)
self.buf_x = [] # cm 值
self.window = window
self.dead_zone = dead_zone
self.vx = 0.0
def update(self, t_ms, cm_x):
self.buf_t.append(t_ms)
self.buf_x.append(cm_x)
if len(self.buf_t) > self.window:
self.buf_t.pop(0)
self.buf_x.pop(0)
n = len(self.buf_t)
if n < 4:
self.vx = 0.0
return 0.0
# 以首帧时间为基准,转换为秒
t0 = self.buf_t[0]
t_sec = [(t - t0) / 1000.0 for t in self.buf_t]
# 最小二乘拟合:cm = v*t + b
sum_t = sum(t_sec)
sum_x = sum(self.buf_x)
sum_tx = sum(ti * xi for ti, xi in zip(t_sec, self.buf_x))
sum_t2 = sum(ti * ti for ti in t_sec)
denom = n * sum_t2 - sum_t * sum_t
if abs(denom) < 1e-6:
self.vx = 0.0
return 0.0
v_raw = (n * sum_tx - sum_t * sum_x) / denom
# 死区过滤,防止微小抖动
if abs(v_raw) < self.dead_zone:
v_raw = 0.0
self.vx = v_raw
return v_raw
拟合公式推导 :
斜率 vv 的计算公式为:
v=n∑(ti⋅xi)−∑ti∑xin∑ti2−(∑ti)2v=n∑ti2−(∑ti)2n∑(ti⋅xi)−∑ti∑xi
关键参数配置:
-
窗口长度:设为10帧(版本12.8更新项),在100ms发送间隔下使用最近1秒数据进行拟合,在系统惯性较大的控制场景中取得响应速度与平滑性的平衡。
-
死区阈值 :
dead_zone=1.5cm/s,当计算速度绝对值低于此值时强制归零,有效抑制平台微小振动导致的无效速度输出。
3. 通信协议精简与实时性设计
协议精简:针对H题中控制核心(MCU)的实时性需求,设计了两种协议模式:
python
def build_uart_frame_compact(box, cal_a, cal_b, vel_est):
"""
★ 精简3字段帧 --- 仅保留控制必需数据
帧格式: $rel_x,cm_x,vx_cm_s
帧长约18字节,比完整帧(~35字节)缩短约50%
"""
ocx = int((box[2] + box[4]) / 2.0)
rel_x = ocx - CENTER_X
cm_x = px_to_cm(float(ocx), cal_a, cal_b)
vx_cm_s = vel_est.get()
return "$%d,%.2f,%.2f" % (rel_x, cm_x, vx_cm_s)
强烈推荐使用精简3字段模式 (UART_PROTO_MODE = 1),将数据帧从$cls,rel_x,rel_y,w,h,conf,cm_x,vx压缩为$rel_x,cm_x,vx。这减少了约50%的串口占用时间,降低了MCU侧中断解析的负载。
定时发送机制 :代码采用100ms定时发送(10Hz),通过时间戳控制发送间隔:
python
if uart is not None:
now = time.ticks_ms()
if now - uart_last >= UART_SEND_INTERVAL_MS:
try:
parts = []
for box in disp_boxes:
if UART_PROTO_MODE == 1:
parts.append(build_uart_frame_compact(box, cal_a, cal_b, vel_est))
else:
parts.append(build_uart_frame(box, cal_a, cal_b, vel_est))
uart.send(";".join(parts) + "\r\n")
uart_last = now
except Exception as e:
if debug_mode >= 1:
print(" [WARN] uart:", e)
该频率与人眼视觉暂留和常规电机PID控制周期(10-50ms)相匹配,确保控制指令的稳定性和连贯性。
字符串拼接优化 :串口数据打包采用%格式符直接拼接,避免使用.format()方法,结合GC(垃圾回收)间隔拉长至15帧,有效减少了循环中的微卡顿。
4. 追踪器(PrecisionTracker)的抗干扰设计
动态IOU阈值:追踪器在匹配检测框时,会根据当前卡尔曼滤波器估算的目标运动速度动态调整IOU匹配阈值:
python
def _eff_iou(self):
if self.kf.speed() > self.vel_fast:
return self.iou_thresh_fast # 高速时使用宽松阈值 0.08
return self.iou_thresh # 低速时使用标准阈值 0.15
当目标高速运动 时,使用更宽松的阈值(IOU_MATCH_THRESHOLD_FAST = 0.08),防止因帧间位移过大导致"跟丢"。
置信度增强:当检测框落入滤波器预测位置附近的一定半径内时,对该检测框的置信度进行小幅提升:
python
if self.kf.ok and self.active:
p = self.kf.pos()
if p is not None:
d = ((cx - p[0])**2 + (cy - p[1])**2)**0.5
if d < self.conf_r:
conf = min(conf + self.conf_b * (1.0 - d / self.conf_r), 1.0)
该机制使追踪器更倾向于选择空间位置连续的目标,而非突发的假阳性检测。
形状验证 :在validate_ball_shape函数中,对检测框的宽高比和面积变化率进行限制:
python
def validate_ball_shape(w, h, prev_w=None, prev_h=None):
if w>0 and h>0:
ar=w/h
if ar<ASPECT_RATIO_MIN or ar>ASPECT_RATIO_MAX:
return False,"ar({:.2f})".format(ar)
if prev_w is not None and prev_h is not None and prev_w>0 and prev_h>0:
pa=prev_w*prev_h
ca=w*h
if pa>0:
ch=abs(ca-pa)/pa
if ch>SIZE_CHANGE_MAX_RATIO:
return False,"sz({:.0f}%)".format(ch*100)
return True,"ok"
有效排除了"长条形"或"急剧膨胀/缩小"的非合理目标,强化了对球形目标的几何约束。
5. 坐标标定与双向转换
系统通过加载calibration.json标定文件实现像素坐标与物理坐标的双向转换:
python
def load_calibration(calib_path):
try:
with open(calib_path, "r") as f:
cal = ujson.load(f)
a = float(cal["a"])
b = float(cal["b"])
r2 = float(cal.get("r_squared", 0))
print(" [CALIB] u={:.4f}*cm+{:.2f} R²={:.4f}".format(a, b, r2))
return a, b
except Exception as e:
print(" [CALIB] 加载失败: {} (默认)".format(e))
return 26.045, 314.17
def px_to_cm(px, cal_a, cal_b):
if cal_a is None or cal_b is None:
return 0.0
return (px - cal_b) / cal_a
def cm_to_px(cm, cal_a, cal_b):
"""反向: cm → 像素X"""
if cal_a is None or cal_b is None:
return 0.0
return cal_a * cm + cal_b
标定参数通过实验测量获得,线性模型 px = a * cm + b 在ROI区域内具有足够的精度(R²通常>0.99),为后续速度估计提供了准确的物理量纲输入。
实际测试
基于深度学习的视觉目标检测与实时速度估计系统
小结
本系统是针对全国大学生电子设计竞赛H题量身定制的高效视觉解决方案。它通过深度模型轻量化部署、精准的ROI空间聚焦、基于线性回归的滑动窗口速度估算 以及精简的串口通信协议,实现了低延迟、高精度的目标检测与运动状态感知。
-
先进性:将深度学习检测与经典信号处理(卡尔曼滤波、最小二乘法)有机结合,实现了鲁棒的"检测-追踪-预测"链条。
-
实用性 :代码参数高度可配置(如
OSD_DRAW_EVERY_N、UART_SEND_INTERVAL_MS),方便在"显示调试"与"极限性能"模式间切换,适配不同调参阶段的硬件资源。 -
赛题针对性:直接输出"相对偏差(cm)"和"实时速度(cm/s)",为主控MCU提供了最直接的控制误差项,便于实现闭环PID控制,是视觉组与电控组高效协作的桥梁。
该系统展示了在有限算力的嵌入式平台上,通过精巧的算法设计和工程优化,依然能完成复杂的实时视觉任务,具备很高的竞赛实战价值。