电子设计大赛【摄像头循迹】讲解
目录
系统概述与基础原理
1.循迹定义
2.核心原理
硬件系统的搭建
1.摄像头模块
(1)机器视觉能力
(2)开发支持
2.主控芯片
3.电机驱动模块
(1)芯片组合定位与分工
(2)关键芯片能力解析
1. STM32F103VCT6(ARM Cortex-M3 MCU)
2. Cyclone IV EP4CE6E22C8N(Intel FPGA)
(3)电机驱动架构
软件代码讲解
一、直线循迹核心流程
二、关键代码解析
1. 图像预处理
2. ROI区域定义
3. 直线检测核心函数
4. 偏转角计算
5. 数据封装与发送
6. 主循环中的循迹处理
三、关键技术点
四、数据处理流程
效果展示
系统概述与基础原理
1.循迹定义
智能小车沿指定(黑线)路径循迹。
2.核心原理
摄像头采集路面图像 → 图像处理识别路径 → 生成控制信号 → 驱动执行机构(电机/舵机)
硬件系统的搭建
1.摄像头模块
搭载 Kendryte K210 芯片的 AI 摄像头模组。
(1)机器视觉能力
- 实时目标检测:识别物体/人脸的位置、大小及类型,输出坐标信息。
- 颜色/形状追踪:通过 HSV 空间过滤与连通域分析,定位最大色块并反馈中心坐标。
- 神经网络模型支持:可部署 YOLOv2 Tiny 等轻量模型,适用于边缘端 AI 推理。
(2)开发支持
-
编程环境:支持 MicroPython(MaixPy 框架),代码简洁易上手,提供图像处理库(如
find_blobs
色块检测)。 -
通信接口:集成 UART/I2C/SPI,可连接 STM32 等主控,通过串口传输坐标数据。
-
扩展性:支持 TF 卡存储模型文件,GROVE 兼容接口便于连接传感器。
2.主控芯片
采用ATMEGA2560-16AU这款芯片是 Arduino Mega 2560 开发板的核心微控制器芯片。
特性 | 参数 |
---|---|
架构 | 8 位 AVR RISC 处理器 |
主频 | 16MHz(-16AU 后缀表示 16MHz 版本) |
Flash 程序存储器 | 256KB |
SRAM | 8KB |
EEPROM | 4KB |
PWM 通道 | 15 路(12 位精度,支持相位校正/快速 PWM) |
数字 I/O 引脚 | 54 个(含 PWM 引脚) |
串口 | 4 个 UART |
外部中断 | 所有 I/O 引脚均支持 |
3.电机驱动模块
(1)芯片组合定位与分工
芯片 | 核心角色 | 电机驱动中的核心任务 |
---|---|---|
STM32F103VCT6 | 主控决策层 (MCU) | 系统调度、通信协议、高级算法(PID/FOC) |
Cyclone IV EP4CE6E22C8N | 实时执行层 (FPGA) | 超高速PWM生成、编码器捕获、硬件保护逻辑 |
💡 协作逻辑:
STM32 计算电机控制量 → FPGA 执行精确的功率器件驱动 → 实时反馈信号由 FPGA 捕获后送回 STM32
(2)关键芯片能力解析
1. STM32F103VCT6(ARM Cortex-M3 MCU)
-
核心参数:
-
72MHz 主频,256KB Flash,48KB RAM
-
高级定时器 × 3(支持6路互补PWM,死区时间可编程)
-
12位ADC × 3(21通道,1μs转换速度)
-
-
电机控制专长:
-
原生支持无感FOC算法(配合ST MotorControl SDK)
-
3路霍尔/编码器接口(用于电机位置检测)
-
硬件过流保护触发(BKIN引脚直连FPGA)
-
2. Cyclone IV EP4CE6E22C8N(Intel FPGA)
-
核心参数:
-
6272 逻辑单元(LEs),270Kb 嵌入式存储器
-
15个18×18乘法器(DSP模块)
-
最大182个用户I/O
-
-
电机控制专长:
-
纳秒级PWM响应:可生成分辨率<10ns的PWM(远优于MCU的百纳秒级)
-
硬实时保护:过流/过压保护延迟<500ns(比软件中断快100倍)
-
多路编码器并行处理(支持1024线增量式编码器@10万RPM)
-
(3)电机驱动架构
软件代码讲解
一、直线循迹核心流程
二、关键代码解析
1. 图像预处理
# 拍摄原始图像
img = sensor.snapshot()# 转换为灰度图(巡线模式下)
if img_color_type == 0:img = img.to_grayscale(copy=False)# 动态调整灰度阈值
get_mean_gray(img,320,240)
2. ROI区域定义
ROIS = {'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), # 下方横向检测区
}
3. 直线检测核心函数
def find_blobs_in_rois(img):'''在预定义ROI区域中检测黑线'''roi_blobs_result = {}for roi_direct in ROIS.keys():# 初始化结果字典roi_blobs_result[roi_direct] = {'cx':0, 'cy':0, 'w':0, 'blob_flag': False}for roi_direct, roi in ROIS.items():# 在ROI区域内寻找符合阈值的色块blobs = img.find_blobs(LINE_COLOR_THRESHOLD, roi=roi, merge=True)if blobs:# 找到最大色块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# 调试模式下绘制检测框if is_debug:x,y,width,height = largest_blob[:4]img.draw_rectangle((x,y,width, height), color=(255))return roi_blobs_result
4. 偏转角计算
def state_deflection_angle(roi_blobs_result):'''计算车辆偏转角度和路口状态'''# ROI区域权重配置(重点关注上下区域)ROIS_WEIGHT = [1, 0, 0, 1] # [上, 中上, 中下, 下]state_crossing = Falsedeflection_angle = 0down_center = 0center_num = 0# 1. 计算加权中心位置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])# 2. 统计有效检测区域数量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]# ... 其他区域类似# 3. 计算中心位置(避免除零错误)if center_num > 0:center_pos = centroid_sum / sum(ROIS_WEIGHT)else:center_pos = IMG_WIDTH / 2 # 默认中心位置# 4. 计算偏转角(图像中心 - 检测中心)deflection_angle = (IMG_WIDTH / 2) - center_pos# 5. 路口检测逻辑# 检测左右两侧是否有黑线(可能进入十字路口)if roi_blobs_result['left']['blob_flag'] or roi_blobs_result['right']['blob_flag']:# 检查是否在图像下方1/3区域内if (roi_blobs_result['left']['cy'] <= (IMG_HEIGHT/3) or roi_blobs_result['right']['cy'] <= (IMG_HEIGHT/3)):# 检查下方黑线宽度是否超过阈值(十字路口特征)if roi_blobs_result['down']['w'] > 140:state_crossing = True# 如果两侧同时检测到黑线,确定为十字路口if (roi_blobs_result['left']['blob_flag'] and roi_blobs_result['right']['blob_flag'] androi_blobs_result['left']['cy'] <= (IMG_HEIGHT/3) and roi_blobs_result['right']['cy'] <= (IMG_HEIGHT/3)):state_crossing = Truereturn down_center, state_crossing, deflection_angle
5. 数据封装与发送
def data_format_wrapper(down_center, state_crossing, deflection_angle):'''封装循迹数据为通信协议格式'''send_data = [0x55, # 帧头0x02, # 数据长度0x91, # 指令类型(循迹)down_center, # 底部中心标志(未使用)1 if state_crossing else 0, # 路口标志get_symbol(deflection_angle), # 偏转角符号(+/-)abs(int(deflection_angle)), # 偏转角绝对值0xbb # 帧尾]return bytes(send_data)def UsartSend(str_data):'''通过串口发送数据'''uart.write(str_data)
6. 主循环中的循迹处理
while True:# ... 其他代码# 巡线模式激活if Flag_track:# 1. 在ROI区域检测黑线roi_blobs_result = find_blobs_in_rois(img)# 2. 计算偏转角和路口状态down_center, state_crossing, deflection_angle = state_deflection_angle(roi_blobs_result)# 3. 定时发送数据(50ms周期)if is_need_send_data:UsartSend(data_format_wrapper(down_center, state_crossing, deflection_angle))
三、关键技术点
-
动态阈值调整:
-
使用
get_mean_gray()
函数实时计算图像平均灰度 -
基于平均灰度动态调整黑线检测阈值,适应不同光照条件
-
-
多区域检测策略:
-
将图像分为6个关键区域
-
横向区域(上、中上、中下、下)用于计算偏转角
-
纵向区域(左、右)用于十字路口检测
-
-
加权中心计算:
-
使用
[1, 0, 0, 1]
权重配置,重点考虑图像顶部和底部的检测结果 -
有效减少中间区域的干扰
-
-
十字路口识别:
-
同时检测左右两侧出现黑线
-
要求黑线位置在图像下方1/3区域内
-
下方黑线宽度超过140像素作为辅助判断
-
四、数据处理流程
-
主控STM32通过串口发送
0x55 0x02 0x91 0x01 ...
启动循迹 -
K210进入循迹模式,开始处理图像
-
每50ms计算一次偏转角并通过串口发送
-
发送数据格式:
[帧头, 长度, 指令, 底部标志, 路口标志, 符号, 偏转值, 帧尾]
-
STM32根据偏转值调整电机PWM输出,实现方向控制
效果展示
摄像头循迹1.0