
简介这是一份面向高校本科毕业设计场景的《基于OpenMV的常见单垃圾自动分类系统》开题报告适合自动化、电子信息、物联网工程等专业学生参考选题与撰写开题文档。报告围绕城市生活垃圾智能分类需求展开梳理了研究背景与意义、图像采集与识别方案、系统硬件构成、预期目标、技术路线、设备选型及详细进度规划。内容中明确采用OpenMV4 H7 Plus摄像头模组采集图像结合深度学习网络进行四类垃圾识别并通过STM32F103C8T6单片机控制舵机、减速电机与红外传感器实现自动投放、满载检测和紫外线消毒。压缩包内共1个docx文件约104KB正文包含图表、参考文献及毕业设计时间节点结构完整可直接作为开题报告写作框架和方案论证的参考。已有661人学习下载。1. 基于OpenMV的常见单垃圾自动分类系统从开题到样机只隔一个闭环大部分挂在开题阶段的自动分拣项目并不是死在算法选型而是死在上位机下发的分类标签没有变成舵机的转角。基于OpenMV的常见单垃圾自动分类系统核心价值在于用一颗自带摄像头和MicroPython解释器的视觉模组完成识别再用串口把结果交给STM32控制机械结构形成“触发—识别—决策—执行”的完整闭环。这套方案在校园实训、小型回收站预分拣和课程设计里最常见真正的难点不在OpenMV本身而在模板注册、阈值标定和通信协议这三件事上。下文按开题报告会涉及到的落地顺序讲清楚先让OpenMV认得垃圾再让它说得清最后让机械执行变得准。2. OpenMV端图像识别落地模板匹配给常见单类垃圾建模2.1 为什么单类垃圾分类不首选大模型OpenMV的算力与内存收支常见单垃圾场景里识别对象通常只有两类到三类塑料瓶、易拉罐、纸盒。类内差异不小换角度、换光照、压扁程度不同但相对校园垃圾桶这个固定工位来说背景和距离是可控的。此时在OpenMV上跑大模型并不划算。H7系列的主频和帧缓冲决定了YOLO类权重需要编译成专用固件普通MicroPython环境下很难维持可用帧率而且一旦引入模型推理框架开题阶段的联调成本会翻倍。模板匹配基于归一化互相关在OpenMV SDK里有现成实现image.find_template()。灰度图QVGA分辨率下这个函数能跑到10~20 FPS足够驱动一个舵机分拣机构。对瓶、罐、盒这类纹理简单、轮廓清晰的目标NCC模板匹配不需要训练样本也不需要标注数据集开题阶段最看重的是“几天内能跑出第一版”这一点模板匹配比CNN更有优势。2.2 用OpenMV IDE跑通最小模板匹配脚本OpenMV IDE连接板卡后先确认三件事像素格式设为灰度、分辨率设在QVGA以下、自动增益关闭。原因很简单模板匹配对亮度和对比度敏感如果摄像头自己不断调整曝光同一个模板在不同时刻的匹配得分会产生明显漂移。下面这个脚本是整机最小可运行版本import sensor, image, time sensor.reset() sensor.set_pixformat(sensor.GRAYSCALE) sensor.set_framesize(sensor.QVGA) sensor.set_auto_gain(False) # 关闭自动增益固定亮度 sensor.set_auto_whitebal(False) # 灰度模式下也固定白平衡 sensor.skip_frames(20) ROI (40, 60, 240, 150) # 只检测投放口内部区域 MATCH_THRESHOLD 0.80 # NCC最低得分低于此值判unknown tpl_bottle image.Image(/tpl_bottle.pgm) tpl_can image.Image(/tpl_can.pgm) tpl_box image.Image(/tpl_box.pgm) def classify(img): for name, tpl in ((bottle, tpl_bottle), (can, tpl_can), (box, tpl_box)): r img.find_template(tpl, MATCH_THRESHOLD, roiROI, step2, searchimage.SEARCH_EX) if r: return name, r return unknown, None while True: img sensor.snapshot() label, r classify(img) if r: img.draw_rectangle(r, color200) print(label)代码逻辑每次抓帧后依次拿三个模板在ROI区域里做穷举模板匹配。step2让搜索步长减半以提升命中率image.SEARCH_EX保证不跳过边界。只要某个模板的NCC得分超过MATCH_THRESHOLD就返回该类别并画出矩形。匹配顺序有优先级实测中瓶子和易拉罐很少同时命中因此放在同一个列表里遍历是安全的。如果某两类经常同时命中说明模板截取区域太接近需要回到采集环节重新裁剪模板而不是继续调阈值。参数取值参考影响ROI覆盖投放口两侧挡板之间的区域越大误匹配越多越小越容易漏检MATCH_THRESHOLD0.75~0.85低于0.75背景误报明显高于0.85常见姿态漏检step1或2step1最稳但帧率下降step2是速度和命中的折中2.3 模板匹配的三个必调参数ROI、匹配阈值与step接着上一节的代码说最容易引发“识别飘忽”的是ROI设置。ROI框得太大把背景里的高光贴纸、反光地面都放进来NCC容易在背景的强纹理上产生假阳性框得太小垃圾投放时稍有偏移就滑出视野。我一般先用IDE的img.draw_rectangle()把ROI画出来连续投放几次样本调整到投放轨迹都能落在框内再锁定这个数值。匹配阈值不是一次设死的应该用一个“最不像的样本”来标定。把纸盒折叠成不同形状、把瓶子压扁、把易拉罐斜放逐帧观察得分变化取最低可接受得分的下浮0.02作为阈值。实际项目里我会临时把当前匹配得分打到串口跑50帧自然场景记录最大值再决定最终阈值。顺便说一句模板文件用.pgm格式保存OpenMV的image.Image()可以直接从SD卡读取模板尺寸不要太大QVGA的ROI裁剪图通常在几十KB级别。3. 给OpenMV喂干净数据样本采集、光照控制与模板阈值标定3.1 样本覆盖策略每种垃圾拍多少张、转多少角度才够模板匹配的“模型”就是模板图本身所以模板数量直接决定泛化能力。常见做法是每种垃圾先拍5张覆盖主要姿态正面、旋转90度、旋转180度、侧倾15度、压扁状态。如果投放角度完全随机就加到10张以上每张之间至少转30度。拍摄时不要开着自动曝光连续抓帧那样得到的模板平均亮度各不相同匹配分数会被拉低。固定曝光、固定补光之后再采集模板之间的一致性会好很多。这里要区分“模板”和“样本”两个概念。find_template在运行时只拿一张模板图和当前帧做互相关不会对同类的10张模板做平均多张模板是作为一个列表逐个比对的。所以每类存10张模板运行时要遍历10次帧率会按比例下降。如果发现帧率不够把同类模板按“正对、左倾、右倾、压扁”四类各留一张共4张而不是盲目叠加数量。3.2 用ROI裁剪脚本批量生成模板并做误匹配校验模板图不应该是整帧画面而应该和运行时一样用ROI裁剪。这样做的好处是模板尺寸小find_template在相同ROI上搜索时加载和计算都更快。采集时用VGA分辨率保留细节保存时只存ROI子图import sensor, image, time sensor.reset() sensor.set_pixformat(sensor.GRAYSCALE) sensor.set_framesize(sensor.VGA) sensor.set_auto_gain(False) sensor.set_auto_exposure(False, exposure_us30000) sensor.skip_frames(30) ROI (120, 180, 360, 220) # VGA分辨率下的投放口 LABEL can # 每类一个文件夹手动改 total 10 for i in range(total): img sensor.snapshot() img.draw_rectangle(ROI, color200) img.copy(roiROI).save(/capture/%s_%02d.pgm % (LABEL, i)) time.sleep_ms(500) # 给手动转动物体留出时间这段脚本先固定曝光为30毫秒然后按LABEL_序号.pgm命名把ROI内的小图存到SD卡。保存后把VGA改为QVGA在识别代码里直接加载这些PGM文件作为模板。模板注册完成后要跑一遍误匹配校验不投放垃圾连续采集300帧统计有多少帧误报成某一类误报率超过1%就下调阈值或缩小ROI。校验脚本不需要单独写把classify()放在for i in range(300)里跑统计返回结果不是unknown的帧数即可。样本组织方式覆盖情况误匹配经验范围每类1张固定角度只能覆盖正对视角5%~15%每类5张每45度一张覆盖常见投放姿态1%~3%每类10张以上含压扁、侧倾覆盖极端姿态1%以下前提光照固定3.3 光照与背景干扰标定固定增益、自动曝光与回退帧夜间或者窗边光线变化大的环境里set_auto_exposure(False)是最省心的选择但要重新标定曝光时间。做法是在最高亮度时段把曝光时间调到画面不过曝之后在最低亮度时段检查该曝光下画面是否还能看清纹理。如果最低亮度下模板特征消失就要加补光灯而不是继续延长曝光时间。延长曝光会拖低帧率垃圾下落时画面会拖影。补光更重要的是让ROI区域光照均匀避免出现反光高光点。易拉罐的弧形表面最容易在固定光源下产生一条高光带这条高光带在NCC计算里是强特征一旦垃圾旋转角度变一点点高光位置偏移匹配分数就会暴跌。解决方法是把光源从正上方移到斜前方45度或者用磨砂亚克力板扩散光线。室内光干扰严重时给ROI区域加一圈遮光挡板比换任何参数都管用。4. OpenMV与STM32串口通信让识别结果可靠驱动舵机分拣4.1 自定帧协议帧头、分类ID与校验位怎么设计识别结果如果不能稳定送到执行机构前面一切都白费。OpenMV与STM32通信最简单、最省MCU资源的通道是UART波特率115200。帧格式固定4字节0xAA 0x55 CMD CHK。0xAA 0x55是帧头CMD是分类IDCHK是前三个字节的异或值。别只发一个字节。二进制串口里一旦丢字节或对齐出错后续结果会全部错位加上帧头和校验就能从错误中恢复接收端只要发现帧头不对就丢弃当前字节重新等0xAA不会影响下一帧。这种设计在调试阶段尤其省心逻辑分析仪都不用接串口助手看十六进制就能判断通信是否正常。4.2 OpenMV UART发送与STM32中断接收的配套代码OpenMV端将分类结果映射成字节流代码如下import sensor, image, time from pyb import UART uart UART(3, 115200, timeout_char200) CMD_BOTTLE 0x01 CMD_CAN 0x02 CMD_BOX 0x03 CMD_UNKNOWN 0xFF def send_class_id(cmd): frame bytes([0xAA, 0x55, cmd, (0xAA ^ 0x55 ^ cmd) 0xFF]) uart.write(frame)UART(3)在OpenMV H7系列上对应固定的一组TX/RX引脚接线时注意TX接STM32的RX、RX接STM32的TX共地不能省。异或校验写在发送端接收端用同样的异或计算比对不一致就丢弃帧等待下一个0xAA。在识别主循环里连续3帧识别结果一致才调用一次send_class_id()这个“连续确认”机制能避免舵机在置信度临界值附近反复抖动比单纯提高匹配阈值更有效。STM32端用串口中断逐字节接收状态机解析#define FRAME_LEN 4 uint8_t rx_buf[FRAME_LEN]; uint8_t rx_cnt 0; uint8_t classify_id 0; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { uint8_t b; if (huart-Instance ! USART1) return; b rx_buf[0]; if (rx_cnt 0 b ! 0xAA) { /* 不在帧头位置丢掉 */ } else if (rx_cnt 1 b ! 0x55) { rx_cnt 0; } else { rx_buf[rx_cnt] b; if (rx_cnt FRAME_LEN) { uint8_t chk rx_buf[0] ^ rx_buf[1] ^ rx_buf[2]; if (chk rx_buf[3]) { classify_id rx_buf[2]; // 校验通过保存分类ID } rx_cnt 0; } } HAL_UART_Receive_IT(huart, rx_buf, 1); }这段状态机没有阻塞等待每次中断只处理一个字节第一个字节不是0xAA就丢弃第二个字节不是0x55就回到等帧头状态收满4字节后做异或比对。注意回调函数末尾一定要重新调用HAL_UART_Receive_IT否则只收一帧就停了。classify_id在整机运行时作为全局变量主循环里直接读取并映射到舵机动作即可。4.3 舵机分拣时序连续确认、到达时间与机械回程拿到classify_id后STM32按表格映射到舵机PWM比较值分类ID含义目标PWM比较值(参考)说明0x01塑料瓶320舵机摆向左侧回收口0x02易拉罐500舵机摆向中间回收口0x03纸盒680舵机摆向右侧回收口0xFF未知保持上一位置不做动作等待下一个结果PWM比较值需要根据实际舵机和机械臂标定不能照抄。标定方法先手动让舵机转到三个机械限位点记录对应的比较值再写入表格。如果系统有传送带OpenMV巡线功能可以复用来做传送带纠偏但识别帧率会被挤占我一般宁愿用两个对射传感器做触发和纠偏让OpenMV专职识别。5. 拿串口日志校准整机分类成功率与三个优先调试点5.1 串口日志统计分类成功率的标准做法整机装好后先别急着调参数用日志跑一次量化评估。给OpenMV端的识别输出加一个GT标记测试程序里按实际投放顺序写一个GT_SEQUENCE列表每完成一次识别就打印一行GTxxx RESULTyyy。电脑上用Python脚本收串口并统计import serial, re from collections import Counter with serial.Serial(COM10, 115200, timeout1) as ser: gt, pred [], [] while len(pred) 60: line ser.readline().decode(utf-8, ignore).strip() m re.match(rGT(\w)\sRESULT(\w), line) if m: gt.append(m.group(1)) pred.append(m.group(2)) cm Counter(zip(gt, pred)) for cls in sorted(set(gt)): total gt.count(cls) ok cm[(cls, cls)] print(%s: acc%.1f%% total%d % (cls, 100 * ok / total, total))统计口径要统一分母是实际投放次数分子是识别正确且机械执行到位次数。机械没有执行或执行错位都算失败。电脑和OpenMV之间不要经过USB Hub丢帧会导致GT和RESULT错位。这个脚本跑完之后会产出一个三类各自的准确率比肉眼观察靠谱得多。5.2 优先调三点室内光干扰、识别节流与舵机死区拿到统计结果后如果准确率低于90%依次检查三件事。第一是室内光干扰靠窗的工位下午和上午的光照差异非常大模板是早上拍的下午可能全部匹配不上。解决方法是给ROI区域加遮光挡板然后重新标定曝光时间。第二是识别节流OpenMV如果每帧都打印和发送帧率波动会直接影响NCC稳定性建议在识别循环里加入time.sleep_ms(50)把识别节流到每秒10次左右。第三是舵机死区。长期运行的舵机在同一个限位点会累积误差连续多次分拣后可能卡在挡板边缘。给执行动作加一个“回退-重新到位”的小段时序比调高PWM力度更可靠。每次把误判样本单独归档到SD卡的/capture/fp/目录再回到第3章重新跑模板注册流程模板是在一次次误判样本里养出来的不要为省事无限下调匹配阈值。本文还有配套的精品资源点击获取