尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于Maixcam2的钢珠识别与视觉巡线系统实战指南

基于Maixcam2的钢珠识别与视觉巡线系统实战指南 最近在准备2026年电赛发现视觉识别类题目比如钢珠/金属球识别、视觉巡线的热度越来越高。这类题目往往要求快速、稳定地处理图像并将结果通过串口发送给主控MCU。传统的方案如OpenMVSTM32虽然成熟但在处理复杂算法或高帧率时可能力不从心。Sipeed的Maixcam2K230芯片凭借其强大的算力和内置的AI加速单元成为了一个非常有竞争力的新选择。本文将手把手带你基于Maixcam2从零搭建一套完整的钢珠/金属球识别与视觉巡线系统涵盖环境搭建、算法原理、代码实现、串口通信以及工程优化全流程。无论你是电赛新手还是想寻找更优视觉方案的开发者都能从中获得可直接复用的代码和避坑经验。1. 项目背景与核心概念全国大学生电子设计竞赛电赛的控制类与视觉类题目近年来对实时图像处理的要求日益提高。例如需要识别并定位滚动的小球钢珠/金属球或者让小车沿着特定颜色的轨迹线巡线行进。这类任务的核心挑战在于实时性、鲁棒性和准确性。Maixcam2是什么它是一款基于嘉楠Kendryte K230芯片的AIoT开发板。其核心优势在于强大算力双核RISC-V CPU 内置NPU神经网络处理单元能流畅运行YOLO等目标检测模型。丰富接口自带DVP摄像头接口、LCD显示屏、麦克风、扬声器以及多个UART串口非常适合作为视觉感知模块。易用生态官方提供了MaixPy3开发环境基于MicroPython大大降低了嵌入式AI应用的开发门槛。钢珠/金属球识别与视觉巡线是两个典型的机器视觉任务目标识别从图像中找出特定物体如银色小球并输出其位置中心坐标、大小等信息。难点在于光照变化、反光、背景干扰。视觉巡线从图像中提取出特定颜色的轨迹线并计算出小车当前相对于轨迹线的偏移量如中线偏差。难点在于赛道交叉、断线、颜色干扰。本文将这两个任务整合展示如何利用Maixcam2同时或分时处理它们并通过串口将结构化数据发送给下位机如STM32构成一个完整的感知-决策-控制闭环。2. 开发环境准备与硬件连接工欲善其事必先利其器。在开始编码前我们需要搭建好软硬件环境。2.1 硬件清单Sipeed Maixcam2 开发板核心USB Type-C 数据线用于供电和通信一台电脑Windows, macOS, Linux均可下位机调试设备可选如STM32开发板、USB转TTL模块用于测试串口通信。2.2 软件环境搭建Maixcam2的主要开发方式是通过MaixPy3 IDE。以下是详细步骤步骤一安装MaixPy3 IDE访问Sipeed官网或GitHub仓库下载对应你操作系统的MaixPy3 IDE安装包。按照提示完成安装。安装过程会自动安装必要的串口驱动。步骤二连接Maixcam2并更新固件用USB线将Maixcam2连接到电脑。首次连接系统可能会自动安装驱动请等待完成。打开MaixPy3 IDE。在右下角选择正确的串口通常显示为COMx或/dev/ttyUSBx。点击连接按钮。如果连接成功右侧的“文件”区域会显示板载文件系统。重要建议检查并更新到最新固件。在IDE的“工具”或“帮助”菜单中通常有“一键更新固件”功能。更新固件能确保获得最新的功能和稳定性修复。步骤三硬件连接示意图与下位机Maixcam2与下位机如STM32的典型连接方式是通过UART串口。Maixcam2的UART1(TX, RX) 或UART2引脚。STM32的任意一个USART/UART引脚。连接原则TX - RX, RX - TX并且共地(GND)。Maixcam2 (UART1) STM32 (USART1) TX ------------ RX (PA10) RX ------------ TX (PA9) GND ------------ GND注意电平Maixcam2的UART引脚通常是3.3V电平与STM32的3.3V UART直接兼容。如果使用5V系统的单片机需要电平转换。3. 核心视觉算法原理与MaixPy3基础在动手写代码前理解我们将要使用的核心算法和MaixPy3的基本图像操作至关重要。3.1 颜色识别与二值化这是视觉巡线和简单颜色物体识别的基石。其核心思想是在HSV颜色空间中进行阈值分割。RGB vs HSVRGB对光照敏感。HSV色相、饱和度、明度能更好地区分颜色本身受光照影响较小。流程获取一帧BGR图像。转换为HSV图像。根据目标颜色如红色轨迹线的H、S、V范围使用inRange函数生成一个二值化掩膜Mask。白色区域255代表目标黑色区域0代表背景。对这个二值图像进行形态学操作如开运算、闭运算去除噪点连接断线。3.2 轮廓查找与中心计算从二值化图像中提取出我们关心的“形状”。findContours函数可以找到图像中所有白色区域的轮廓。轮廓筛选通过面积、周长、外接矩形等属性过滤掉小的噪点轮廓找到我们需要的“球”或“线”的轮廓。中心计算对于球可以使用轮廓的矩(moments)来计算质心坐标(cx, cy)。对于线一种常见方法是“扫描线法”在图像下半部分画几条水平线计算每条水平线与白色轨迹线交点的中心位置再用这些中心点拟合出一条中线或直接计算偏差。3.3 基于AI的目标检测进阶对于更复杂、要求更高的钢珠识别例如需要区分不同金属、多目标、严重遮挡可以使用YOLO等目标检测模型。Maixcam2的优势其NPU可以加速YOLOv5/v8等模型的推理。流程使用PC训练一个针对“金属球”的小型YOLO模型数据集需包含各种光照、角度下的球。将模型转换为Maixcam2支持的格式如.kmodel。在MaixPy3中加载模型对每帧图像进行推理直接获取球的边界框和置信度。本文将重点讲解基于传统视觉颜色轮廓的实现方案因为它响应更快、代码简单足够应对许多电赛场景。文末会简要介绍AI方案的接入方式。3.4 MaixPy3图像处理基础代码框架先看一个最简单的图像捕获与显示框架# main.py import sensor, image, time, lcd # 1. 初始化摄像头 sensor.reset() # 复位摄像头 sensor.set_pixformat(sensor.RGB565) # 设置像素格式 sensor.set_framesize(sensor.QVGA) # 设置分辨率 QVGA: 320x240 sensor.skip_frames(time 2000) # 跳过一些帧等待摄像头稳定 sensor.set_auto_gain(False) # 关闭自动增益颜色识别时建议关闭 sensor.set_auto_whitebal(False) # 关闭自动白平衡颜色识别时建议关闭 # 2. 初始化LCD如果板子有屏幕 lcd.init() clock time.clock() # 创建一个时钟对象来跟踪FPS while(True): clock.tick() # 开始计时 img sensor.snapshot() # 从摄像头抓取一帧图像 # 你的图像处理算法将在这里编写 # 在图像上画一些信息 img.draw_string(0, 0, FPS:%.2f % clock.fps(), color(255,0,0)) # 将处理后的图像显示到LCD上 lcd.display(img)这个框架是所有视觉应用的基础。后续的识别算法都将写在# 你的图像处理算法那个位置。4. 完整实战案例钢珠识别与视觉巡线我们将创建一个综合项目它有两种工作模式模式1识别钢珠并上报坐标模式2进行视觉巡线并计算偏差。通过串口发送指令可以切换模式。4.1 项目文件结构规划在MaixPy3 IDE的文件系统中创建如下文件/ ├── main.py # 主程序入口 ├── ball_detector.py # 钢珠识别模块 ├── line_follower.py # 巡线模块 ├── uart_comm.py # 串口通信模块 └── config.py # 参数配置颜色阈值等4.2 参数配置 (config.py)将颜色阈值、串口参数等可调变量集中管理方便调试。# config.py # 颜色阈值 (HSV格式) - 需要根据实际环境用IDE的阈值编辑器调整 # 例如识别银色/灰色小球通常饱和度S较低明度V较高 BALL_COLOR_THRESHOLD [(0, 50, 150, 255, 200, 255)] # (L_H, L_S, L_V, H_H, H_S, H_V) # 红色轨迹线阈值示例 LINE_COLOR_THRESHOLD [(0, 100, 50, 10, 255, 255), (160, 100, 50, 180, 255, 255)] # 红色有两个区间 # 轮廓筛选参数 BALL_MIN_AREA 500 # 钢珠轮廓的最小像素面积 LINE_ROI (0, 120, 320, 120) # 巡线感兴趣区域 (x, y, w, h)只关注图像下半部分 # 串口配置 UART_PORT 1 # 使用UART1对应板子上的TX1/RX1引脚 UART_BAUDRATE 115200 UART_TIMEOUT 1000 # 工作模式 MODE_BALL_DETECT 1 MODE_LINE_FOLLOW 24.3 串口通信模块 (uart_comm.py)负责与下位机进行数据收发。我们定义简单的通信协议。# uart_comm.py from machine import UART import time import json # 使用JSON格式传输结构化数据 class UartCommunicator: def __init__(self, port1, baudrate115200): self.uart UART(port, baudrate, timeout1000) self.uart.init(baudrate, bits8, parityNone, stop1) def send_data(self, data_type, **kwargs): 发送数据。data_type: ball, line, error packet { type: data_type, data: kwargs, timestamp: time.ticks_ms() } try: # 将字典转换为JSON字符串并发送末尾加换行符作为帧分隔 packet_str json.dumps(packet) \n self.uart.write(packet_str.encode(utf-8)) except Exception as e: print([UART Send Error], e) def read_command(self): 读取下位机发来的指令 if self.uart.any(): try: cmd self.uart.readline().decode(utf-8).strip() return cmd except: pass return None # 示例数据包格式 # 识别到球: {type: ball, data: {x: 150, y: 100, r: 15, found: 1}, timestamp: 123456} # 巡线数据: {type: line, data: {deviation: -25, angle: 5, found: 1}, timestamp: 123457} # 错误: {type: error, data: {msg: color threshold error}, timestamp: 123458}4.4 钢珠识别模块 (ball_detector.py)实现基于颜色和轮廓的钢珠检测。# ball_detector.py import image from config import BALL_COLOR_THRESHOLD, BALL_MIN_AREA class BallDetector: def __init__(self): self.threshold BALL_COLOR_THRESHOLD def detect(self, img): 在图像中检测钢珠。 返回: (found, x, y, radius) found: 是否检测到 (0/1) x, y: 中心坐标 radius: 估算半径 # 1. 颜色二值化 mask img.binary(self.threshold, invertFalse) # 2. 形态学操作先腐蚀再膨胀闭运算去除小噪点连接可能断裂的区域 mask.morph(1, [0, 1, 0, 1, 1, 1, 0, 1, 0]) # 腐蚀 mask.morph(0, [0, 1, 0, 1, 1, 1, 0, 1, 0]) # 膨胀 # 3. 查找轮廓 contours mask.find_contours(threshold1000) ball_contour None max_area BALL_MIN_AREA # 4. 筛选轮廓找面积最大且接近圆形的 for cnt in contours: area cnt.area() if area max_area: # 简单圆形度判断面积与边界框面积之比 rect cnt.min_rect() rect_area rect[2] * rect[3] # w * h if rect_area 0: circularity area / rect_area if circularity 0.6: # 圆形度阈值可调整 max_area area ball_contour cnt if ball_contour: # 5. 计算最小外接圆获取圆心和半径 (x, y), radius ball_contour.min_circle() return 1, int(x), int(y), int(radius) else: return 0, 0, 0, 0 def draw_result(self, img, result): 在图像上绘制检测结果 found, x, y, r result if found: img.draw_circle(x, y, r, color(0, 255, 0), thickness2) img.draw_cross(x, y, color(0, 255, 0), size10) img.draw_string(x10, y-10, Ball(%d,%d)%(x,y), color(0,255,0))4.5 视觉巡线模块 (line_follower.py)实现基于扫描线的巡线算法。# line_follower.py import image, math from config import LINE_COLOR_THRESHOLD, LINE_ROI class LineFollower: def __init__(self): self.threshold LINE_COLOR_THRESHOLD self.roi LINE_ROI def detect(self, img): 检测轨迹线并计算偏差。 返回: (found, deviation, angle) found: 是否检测到线 (0/1) deviation: 横向偏差像素中心为0左负右正 angle: 线角度度 # 1. 只在ROI区域内处理提升速度 img_roi img.copy(self.roi) roi_x, roi_y, roi_w, roi_h self.roi # 2. 颜色二值化针对红色有两个区间 mask1 img_roi.binary([self.threshold[0]], invertFalse) mask2 img_roi.binary([self.threshold[1]], invertFalse) mask mask1.or_(mask2) # 合并两个红色区域 # 3. 形态学处理 mask.morph(1, [0,1,0,1,1,1,0,1,0]) mask.morph(0, [0,1,0,1,1,1,0,1,0]) # 4. 扫描线法在ROI内取几条水平线计算中线点 scan_lines 8 line_height roi_h // (scan_lines 1) points [] for i in range(scan_lines): scan_y line_height * (i 1) # 获取这一行的像素数据 line_data mask.get_pixel(0, scan_y, roi_w, 1) # 找出白色像素的列索引 white_indices [idx for idx, pixel in enumerate(line_data) if pixel 200] if white_indices: # 计算这一行白色区域的中心 center_x sum(white_indices) // len(white_indices) points.append((center_x, scan_y)) if len(points) 3: # 5. 使用最小二乘法拟合直线 y kx b sum_x sum_y sum_xy sum_xx 0 n len(points) for (px, py) in points: sum_x px sum_y py sum_xy px * py sum_xx px * px denominator n * sum_xx - sum_x * sum_x if abs(denominator) 1e-6: # 防止除零 k (n * sum_xy - sum_x * sum_y) / denominator b (sum_y - k * sum_x) / n # 6. 计算偏差在图像底部中心xroi_w/2处拟合线的x坐标与中心的差值 bottom_y roi_h - 1 line_x_at_bottom (bottom_y - b) / k if k ! 0 else roi_w/2 deviation line_x_at_bottom - (roi_w / 2.0) # 7. 计算角度转换为度 angle math.degrees(math.atan(k)) found 1 else: found, deviation, angle 0, 0, 0 else: found, deviation, angle 0, 0, 0 return found, int(deviation), angle def draw_result(self, img, result): 在图像上绘制巡线结果 found, deviation, angle result roi_x, roi_y, roi_w, roi_h self.roi img.draw_rectangle(self.roi, color(255, 0, 0)) # 画出ROI区域 if found: # 在ROI底部中心画一个参考点 center_x roi_x roi_w // 2 bottom_y roi_y roi_h - 1 img.draw_cross(center_x, bottom_y, color(0, 0, 255), size10) # 画出拟合的线在ROI坐标系下计算两个点再映射回原图 # 简单起见这里只画一条从ROI顶部到底部的竖线表示偏差 target_x int(center_x deviation) img.draw_line(center_x, bottom_y, target_x, bottom_y, color(0, 255, 0), thickness3) img.draw_string(10, 50, Dev:%d Ang:%.1f%(deviation, angle), color(0,255,0))4.6 主程序集成 (main.py)将所有模块组合起来并实现模式切换。# main.py import sensor, image, time, lcd from config import * from ball_detector import BallDetector from line_follower import LineFollower from uart_comm import UartCommunicator # 初始化硬件 sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) sensor.skip_frames(time2000) sensor.set_auto_gain(False) sensor.set_auto_whitebal(False) lcd.init() # 初始化功能模块 ball_detector BallDetector() line_follower LineFollower() uart UartCommunicator(portUART_PORT, baudrateUART_BAUDRATE) # 初始模式 current_mode MODE_BALL_DETECT print(System Start. Current Mode: Ball Detection) clock time.clock() while True: clock.tick() img sensor.snapshot() # 检查串口指令 cmd uart.read_command() if cmd: cmd cmd.lower() if cmd mode1: current_mode MODE_BALL_DETECT print(Switch to Mode: Ball Detection) elif cmd mode2: current_mode MODE_LINE_FOLLOW print(Switch to Mode: Line Following) elif cmd get_status: uart.send_data(status, modecurrent_mode, fpsclock.fps()) # 根据模式执行不同的视觉任务 if current_mode MODE_BALL_DETECT: result ball_detector.detect(img) ball_detector.draw_result(img, result) found, x, y, r result # 通过串口发送结果 uart.send_data(ball, xx, yy, radiusr, foundfound) elif current_mode MODE_LINE_FOLLOW: result line_follower.detect(img) line_follower.draw_result(img, result) found, deviation, angle result # 通过串口发送结果 uart.send_data(line, deviationdeviation, angleangle, foundfound) # 显示FPS和当前模式 mode_text Ball if current_mode MODE_BALL_DETECT else Line img.draw_string(0, 0, M:%s FPS:%.1f % (mode_text, clock.fps()), color(255,0,0)) lcd.display(img)4.7 运行与验证在MaixPy3 IDE中将以上5个文件main.py,ball_detector.py,line_follower.py,uart_comm.py,config.py上传到Maixcam2的文件系统根目录。点击IDE的“运行”按钮或者通过串口发送execfile(‘/main.py’)命令。观察LCD屏幕如果连接应该能看到摄像头画面并实时显示识别结果绿色的圆圈或线条。串口测试打开一个串口调试助手如Putty、SecureCRT或IDE自带的终端连接到Maixcam2的串口注意不是用于IDE通信的那个端口通常是另一个虚拟串口或者你可以用UART1外接USB转TTL到电脑。设置波特率为115200。你应该会看到不断打印的JSON格式数据包。向Maixcam2发送mode1或mode2字符串记得加换行符观察屏幕上的模式切换和数据包变化。5. 常见问题与排查思路在实际调试中你肯定会遇到各种问题。下面是一个快速排查指南。问题现象可能原因排查步骤与解决方案摄像头无图像/全黑1. 摄像头初始化失败。2. 摄像头排线接触不良。3. 环境光线太暗。1. 检查sensor.reset()是否报错。2. 重新插拔摄像头排线。3. 增加光照或尝试sensor.set_auto_exposure()。颜色识别不稳定时有时无1. 颜色阈值不准确。2. 环境光照变化。3. 自动白平衡/自动增益影响。1.关键步骤使用MaixPy3 IDE的“工具”-“机器视觉”-“阈值编辑器”实时调整HSV值找到稳定的阈值范围。2. 尽量在光照稳定的环境下测试。3. 确保代码中已关闭sensor.set_auto_gain(False)和sensor.set_auto_whitebal(False)。识别到的钢珠位置跳动大1. 轮廓面积过滤太小包含了噪点。2. 形态学操作参数不合适。3. 帧率过低。1. 增大BALL_MIN_AREA。2. 调整mask.morph的参数或尝试不同的形态学核。3. 降低分辨率如从QVGA降到QQVGA或优化代码逻辑提升FPS。巡线时在交叉口或断线处误判1. 扫描线法在复杂情况下失效。2. ROI区域设置不合理。1. 考虑更复杂的算法如提取最大连通域、预测轨迹。2. 调整LINE_ROI可能需要在弯道时使用更宽的ROI。串口收不到数据1. 串口号或波特率错误。2. 接线错误TX/RX接反。3. 未共地。4. 代码中UART初始化失败。1. 确认UART_PORT和UART_BAUDRATE与硬件连接一致。2. 检查TX、RX是否交叉连接。3. 务必连接GND。4. 在代码开头加print(‘UART init…’)调试。程序运行卡顿FPS很低1. 图像处理算法过于复杂。2. 分辨率太高。3. 循环内有耗时操作如打印大量信息。1. 优化算法在ROI内处理减少不必要的循环。2. 尝试sensor.set_framesize(sensor.QQVGA)(160x120)。3. 减少print和lcd.display的频率可每N帧显示一次。LCD显示方向不对或颜色异常LCD初始化参数与屏幕型号不匹配。查阅Maixcam2文档调整lcd.init()的参数如lcd.init(type1, freq15000000, invert1)等。6. 工程优化与进阶方向一个能用于比赛的系统不仅需要“能跑”还需要“跑得稳、跑得快”。以下是一些优化建议和进阶思路。6.1 性能优化固定曝光与增益在固定光照场景下手动设置曝光时间和增益值可以极大提高颜色识别的稳定性。使用sensor.set_auto_exposure(False, exposure_us数值)和sensor.set_gainceiling(数值)进行调试。降低分辨率QVGA(320x240)处理一帧的数据量是QQVGA(160x120)的4倍。在满足识别精度的前提下优先使用低分辨率。优化ROI不要处理整幅图像。例如巡线只关心图像下方1/3区域钢珠识别可以根据上一帧的位置预测一个小的搜索区域。算法简化在二值化后使用blob类方法如img.find_blobs可能比find_contours更快。可以对比测试。6.2 稳定性提升数据滤波对于识别到的坐标、偏差等数据不要直接使用单次结果。可以采用滑动平均滤波或卡尔曼滤波平滑输出减少抖动。# 简单的滑动平均滤波示例针对x坐标 history_size 5 x_history [] def filter_x(new_x): x_history.append(new_x) if len(x_history) history_size: x_history.pop(0) return sum(x_history) // len(x_history)状态机管理增加简单的状态机。例如在巡线时如果连续5帧找不到线则进入“寻线”状态扩大搜索范围或原地旋转。异常恢复在串口通信中增加应答和超时重发机制。下位机收到数据后回复ACK如果Maixcam2一段时间没收到ACK则重发上一包数据。6.3 进阶接入AI模型如果比赛任务非常复杂如识别多种特定形状的金属件传统方法可能不够用。这时可以启用Maixcam2的NPU。模型训练在PC上使用YOLOv5/v8等框架标注并训练一个轻量级模型如yolov5s。模型转换使用Sipeed提供的模型转换工具如nncase将.pt或.onnx模型转换为Maixcam2支持的.kmodel格式。模型部署将.kmodel文件放入板载文件系统使用MaixPy3的nn模块加载和推理。import nn # 加载模型 m nn.load(/path/to/your_model.kmodel) # 预处理图像缩放、归一化等 input_tensor img.resize(224, 224).tobytes() # 示例需按模型要求处理 # 运行推理 outputs m.forward(input_tensor) # 解析输出边界框、类别、置信度这将大幅提升复杂目标的识别准确率但会牺牲一些帧率。6.4 与下位机STM32的协作协议优化本文使用的JSON格式可读性好但并非最精简。在带宽紧张或要求极低延迟时可以定义更紧凑的二进制协议。协议帧示例自定义二进制[帧头 0xAA 0xBB] [数据类型 1字节] [数据长度 1字节] [数据载荷 N字节] [校验和 1字节] [帧尾 0x0D 0x0A]数据类型0x01球坐标0x02线偏差。数据载荷例如球坐标可以用4个uint16x, y, w, h表示。校验和简单累加和取低8位。分工明确Maixcam2只负责“感知”输出坐标、偏差等原始信息。所有的决策逻辑如PID控制、运动规划应放在下位机STM32中完成这样系统更灵活便于调试。7. 总结与备赛建议通过本文的实践我们完成了一个基于Maixcam2的、可同时处理钢珠识别和视觉巡线的嵌入式视觉系统。从环境搭建、算法原理、模块化编程到串口通信覆盖了电赛视觉类题目所需的核心技能链。关键要点回顾环境是基础正确安装IDE、更新固件、连接硬件是第一步。颜色阈值是灵魂必须花时间用阈值编辑器在现场光照下精细调整这是传统方法成功的关键。模块化编程将识别、通信、逻辑控制分离代码清晰易调试也便于团队协作。数据要滤波原始识别结果噪声大简单的滑动平均就能极大提升控制稳定性。通信要可靠定义清晰、带校验的通信协议是上下位机稳定协作的保障。给2026年电赛备赛同学的建议尽早熟悉平台不要等到赛题发布才学Maixcam2。现在就用它做一些基础练习比如颜色追踪、人脸检测、二维码识别熟悉其API和性能边界。积累代码模块将本文的串口通信、图像处理框架保存为模板。比赛时可以直接修改参数和算法逻辑节省大量时间。准备调试工具准备好串口调试助手、逻辑分析仪用于分析通信时序、阈值编辑器。良好的调试工具能帮你快速定位问题。关注官方动态密切关注Sipeed官方和电赛组委会的通知获取最新的固件、工具和例程。进行联调测试将Maixcam2与你准备的主控板如STM32、ESP32进行长时间、多场景的联调测试暴露并解决协作中的问题。希望这份详尽的教程能成为你备赛路上的得力助手。在实际项目中多思考、多调试、多总结将这套系统打磨得更加鲁棒和高效。如果在实践中遇到新的问题不妨回头看看“常见问题”章节或者尝试调整算法参数和优化策略。
返回列表