项目编号X504183| 主控 STM32F103C8T6 核心板 | 视觉采集 ESP32-CAM + OV2640 | 称重 HX711 + 1 kg 传感器 | 分拣执行 SG90 舵机 | 本地显示 OLED12864(I2C) | 通信 HTTP POST + TCP Socket | AI 识别 服务端经火山引擎调用豆包视觉模型 | 服务端 jeesite + SpringBoot + MySQL摘要:本文介绍一套以视觉大模型做判定的水果分拣系统。硬件侧以 STM32F103C8T6 为核心板,红外对管检测到水果到位后触发拍照,ESP32-CAM 把拍到的 JPEG 以 HTTP POST 直接上传服务端,图像不经过主控;服务端经火山引擎调用豆包视觉模型,返回种类、颜色、品质、伤病与分析理由五个字段;主控把视觉结论与 HX711 测得的重量合并,按品种分档的质量阈值判定等级,最后由 SG90 舵机转到 0 / 45 / 135 / 180 四个角度完成分拣。服务端基于 jeesite 平台与 SpringBoot 框架构建,以 MySQL 完成数据持久化,网页端提供用户管理、分拣机管理、分拣参数维护、实时数据查询与分拣记录查询。
一、项目概述
传统分拣方案的瓶颈在「识别」这一步:颜色传感器只认颜色、重量传感器只认重量,两个苹果一个新鲜一个有伤,靠这两类传感器根本分不开。本项目把识别交给视觉大模型:一张照片进去,种类、颜色、品质、伤病四项结论加一条分析理由出来,再结合重量判级,就能把「按品质分通道」这件原本很难的事做扎实。
本文所述系统以 STM32F103C8T6 为核心板,外接 ESP32-CAM 摄像头模组、HX711 称重模块与 1 kg 称重传感器、SG90 舵机、红外对管与 OLED12864 显示屏;物体到位由红外对管触发拍照,图像由相机模组自己经 Wi-Fi 上传服务端,服务端经火山引擎调用豆包视觉模型给出种类、颜色、品质、伤病与分析理由,主控再把视觉结论与 HX711 的重量读数合并,按品种分档的质量阈值判定等级,最后由舵机转到 0 / 45 / 135 / 180 四个角度把水果送进对应通道;服务端基于 jeesite 平台与 SpringBoot 框架构建并以 MySQL 持久化,网页端负责用户管理、分拣机管理、分拣参数维护、实时数据查询与分拣记录查询,把视觉识别、称重判级与分拣执行整合进同一套可远程查看的闭环。
系统在 2025 年 5 月 完成整机联调,分拣记录页存下的 32 条实测记录,就是这条链路真实跑过的痕迹。
1.1 系统技术架构
系统自上而下划分成应用层、服务层、网络层、控制层与感知执行层五个层次。
图 1 系统总体架构图
五层的关键分工是:相机只负责按快门与上传,主控只负责合并判定与驱动舵机,视觉识别始终在服务端经火山引擎调用豆包视觉模型完成。这也意味着换一版提示词、换一个模型,硬件侧一行代码都不用动。
1.2 系统功能结构
按部署位置划分为硬件系统、软件系统与交付服务三列。
图 2 系统功能结构图
三列之间只有一条边界:设备侧只做「触发 + 采集 + 执行」,图像识别、判级逻辑与记录留存全部放在服务端。
二、硬件系统设计
2.1 硬件系统原理框图
整块板子的中心是 STM32F103C8T6 核心板模块,一侧是红外对管与 ESP32-CAM 摄像头模组,另一侧是 HX711 称重模块、舵机、OLED 显示屏与蜂鸣器,整机由 TYPE-C 取电口单口供电。
图 3 硬件系统原理框图
从框图可以看出本项目在硬件上的一个关键取舍:图像数据完全不经过主控。红外对管的DO只给主控一个边沿事件,真正触发拍照与上传的是模组自己 —— 收到指令后取景,把 JPEG 经 Wi-Fi 以 HTTP POST 直达服务端。主控既不是数据通道,也不需要在固件里解析图像。
2.2 硬件原理图
原理图用嘉立创 EDA 绘制,同时提供 pdf、png、json 与 schdoc 四种格式。本文的接线描述以json源文件中的网络标号为准。
图 4 硬件原理图
提示:解析原理图时建议直接读嘉立创 EDA 的json源文件。导出的 PDF 文字层只会包含已经连线的部分,而源文件里能取出「引出但未连接」的悬空网络标号。提取方式是从schematics[0].dataStr.shape这组字符串里,用\^\^([\d.\-]+)~([\d.\-]+)\^\^(\S+?)~取网络标号坐标,用spiceSymbolName\([^\]*)\` 取元件型号。
2.3 硬件实物
实物装配以洞洞板(万用板)作底板、杜邦线互连,亚克力分拣盘下方贴装 1 kg 悬臂梁式称重传感器,SG90 舵机负责切换分拣通道,OLED12864 就地显示重量与分类结果,整机由一根 TYPE-C 线供电。
图 5 硬件实物总览
OLED 上实测显示标题行「水果智能分拣」,随后三行是重量: xx g、品种: --、分类: --。空载时品种与分类显示占位符,放上水果后由主控写入识别与判级结果。
图 6 称重机构特写与分拣演示
第二张是两组关键机构的特写:左边是贴在亚克力圆盘下方的金属悬臂梁传感器,丝印1 kg清晰可辨,旁边是绿色 HX711 模块与四色线束 —— 这是「重量采集」最直接的证据;右边是红苹果压在托盘上的分拣演示现场,OLED 显示着实时重量读数。
2.4 引脚分配
| 连接对象 | 信号 | 引脚 | 说明 |
|---|---|---|---|
| 舵机 SG90(U3) | PWM | PB5 | 正好是 TIM3_CH2,可直接用硬件 PWM 产生 50 Hz、脉宽 0.5~2.5 ms 的周期信号 |
| OLED12864(U1) | SCK / SDA | PB6 / PB7 | IIC 屏上的 SCK 就是 SCL;与硬件 I2C1 的默认分配完全一致 |
| HX711(U4) | SCK / DT | PB10 / PB11 | 走的是 HX711 自己那套两线时序,与 SPI 的模式、片选、时钟极性都对不上 |
| 红外对管(J1) | DO | PA0 | 即 EXTI0;一根信号线触发一次拍照,判定逻辑做在模块内部 |
| ESP32-CAM(U5) | IO1(TX) / IO3(RX) | PA10 / PA9 | 对应 USART1 的收发脚;模组其余引脚在图上没有任何网络 |
| 蜂鸣器(BUZZER1) | 信号端 | PB1 | 另一端接地,识别异常或分拣完成时由普通 GPIO 驱动发声 |
| TYPE-C 2P 取电口(U2) | 5V / GND | — | 正极接 5 V、负极接地,整板单口取电 |
| 串口接口(J4,4 针) | IO4 / IO2 / GND / 5V | — | 引出 ESP32-CAM 的下载与调试排针,四只脚在图上被自动命名为 32T / 32R / 32GND / 32V |
| 串口排针(J9,标「TTL」) | RX / TX | PA10 / PA9 | 与 ESP32-CAM 侧的 IO1 / IO3 同名对应,供串口调试使用 |
| 核心板(U16) | Micro-USB / ST-LINK | — | 板上自带 Micro-USB 取电口与 ST-LINK 下载口,固件烧录即从这里进行 |
把这几根线排开来看,落点并不随意:
· 舵机信号PB5恰好是TIM3_CH2,50 Hz、脉宽 0.5~2.5 ms 的周期信号可用硬件 PWM 直接产生
· 显示屏SCK/SDA落在PB6/PB7,正是硬件 I2C1 的默认分配
· HX711 的SCK/DT落在PB10/PB11,走它自己的两线时序,用通用 IO 位操作最合适
· 红外对管DO落在PA0,即EXTI0,一根信号线触发一次拍照
· 摄像头两线落在PA9/PA10,正是USART1 的收发脚
各路外设各归其位,主控这一侧的负担非常轻。
三、逐线核对原理图得到的四处判断
把嘉立创 EDA 源文件里的网络标号提取出来、与 PDF 文字层逐条对照后,有四处值得单独说明。
3.1 舵机的信号脚正好落在定时器通道上
SG90 的信号线接 PB5,而 PB5 正是 STM32F103 的 TIM3_CH2。舵机要的是 50 Hz、脉宽 0.5~2.5 ms 的周期信号,信号脚落在定时器通道上,意味着可以直接用硬件 PWM 产生这条波形,不必靠软件延时去凑节拍;四个分拣角度只要改比较值即可切换。对需要长时间稳定保持角度的舵机来说,这一点比步进电机那类器件更需要留意。
3.2 显示屏接线与硬件 I2C1 默认分配完全吻合
OLED12864 的 SCK 接 PB6、SDA 接 PB7。在 IIC 屏上 SCK 就是时钟线 SCL,而 STM32F103 的硬件 I2C1 默认功能分配正是 SCL = PB6、SDA = PB7,两者完全一致。这块屏因此具备直接调用片上 I2C1 外设的条件,不必再用通用 IO 去模拟时序。
3.3 称重模块走的是两线自定义时序,不是硬件 SPI
HX711 的 SCK 接 PB10、DT 接 PB11,总共两根信号线加电源。HX711 的时序是自己一套:DT 高电平常表示本次转换尚未完成,SCK 打 25~27 个脉冲才能把 24 位数据读出来,与 SPI 的模式、片选、时钟极性都对不上,所以在 PB10 / PB11 上用通用 IO 位操作最合适。顺带一提,这两个脚同时是 USART3 的默认 TX / RX,本设计把串口让给了别的用途。
3.4 触发拍照的红外对管只占一根线,且落在中断线上
红外对管的 DO 接 PA0,即 EXTI0。一根信号线就能触发一次拍照,说明「有物体遮挡即到达」的判定逻辑做在模块内部,主控只接收一个边沿事件,既不必轮询,也不用为它单独开一路外设。把它与图像通道放在一起看,整条采集链路在主控这一侧其实非常轻。
以上四条并非对设计的质疑,而是对既有连线的如实描述 —— 本文只记录原理图上真实存在的连接,不对作者的意图作推测。
四、通信链路与数据交互
4.1 通信参数
| 链路 | 协议 / 方式 | 说明 |
|---|---|---|
| 无线承载 | Wi-Fi 2.4 GHz(STA 模式) | 默认热点名 8266wifi、密码 123456789;说明文件里特别注明需使用电脑开热点 |
| 图像上传 | HTTP POST(REST 方式) | 相机端把拍到的 JPEG 直接 POST 到服务端上传接口,不经过主控转发 |
| 指令通道 | TCP Socket | 服务端与硬件之间以 TCP Socket 周期性同步状态、下发指令 |
| 服务端地址 | 192.168.137.250 | 上传接口端口 8889、Socket 端口 19214,两项在同一台内网主机上 |
| 上传接口路径 | 8889 端口的 /f/esp32/upload | 相机端 POST 的目标路径(按平台规范省略协议前缀,实际固件按完整地址解析) |
| AI 识别 | 服务端经火山引擎调用豆包视觉模型 | 后台「分拣参数管理」里落了 apiKey 与提示词两条配置,识别不在硬件侧进行 |
| 抓拍模式 | word mode: Periodic photo taking | 调试模式关闭(debug mode: false);也可由红外对管触发单次抓拍 |
相机端保存的配置原文如下(局域网地址,仅供同网段调试参考):
ssid:8266wifi pass:123456789 server IP:192.168.137.250 server port:19214 rest url:http://192.168.137.250:8889/f/esp32/upload debug mode:false word mode: Periodic photo taking
说明文件末尾另注:务必注册火山引擎账户、开通视觉模型服务。抓拍模式默认为周期拍照(Periodic photo taking),也可由红外对管触发单次抓拍。
4.2 数据交互时序
以「放一个水果上盘」为例,整个过程分成十二步:红外对管检测到位、边沿事件进主控、发出拍照指令、图像以 HTTP POST 直传服务端、服务端建立识别记录、经火山引擎调用豆包视觉模型、五字段结论写入记录、重量读数合并判级、结论经 TCP Socket 回传、主控驱动舵机转到对应角度、OLED 刷新显示、记录落库。
图 7 系统数据交互时序图
这张图里有两处值得展开。第一处是「主控全程不搬运图像数据」:JPEG 从模组直接进网络,主控只是事件的触发者,采集链路在主控这一侧非常轻。第二处是「识别与判级不在硬件上」:提示词与 apiKey 都落在后台「分拣参数管理」配置里,换提示词不必重烧固件;硬件侧既没有模型也没有阈值逻辑。
五、软件系统与界面功能
5.1 界面字段
| 界面分组 | 字段与说明 |
|---|---|
| 用户管理 | 姓名 / 电话 / 更新时间 / 操作 — 按姓名与电话检索,右上角另有「新增」;表格为分页列表 |
| 编辑用户 | 姓名 / 电话 / 密码 / 再输入一遍 — 页头写明「基本信息【手机号码将作为登录的账户信息】」,手机号既当登录账号又当联系方式 |
| 分拣机管理 | 终端编号 / 终端名称 / online / 更新时间 / 操作 — 登记每一台分拣终端的编号、名称与在线状态;查询条件为终端编号与终端名称 |
| 分拣参数管理 | 参数名称 / 类型 / 参数项 / 参数值 / 备注 / 更新时间 / 操作 — 共 14 条,前 12 条是按品种分档的质量阈值,第 13 条是「模型key」(apiKey),第 14 条是「模型提示词」 |
| 实时数据查询 | 品种 / 重量 / 颜色 / 分类 四张卡 + 实时图像 — 顶端给出更新时间与设备在线状态,下方按时间列出最近 5 张识别画面 |
| 分拣记录查询 | 终端编号 / 终端名称 / 照片 / 种类 / 颜色 / 伤病 / 重量(g) / 品质 / 分析理由 / 识别时间 / 操作 — 共 32 条,每页 20 条;视觉模型的五个输出字段与重量读数并列在一行里 |
侧边菜单共五项:用户管理 / 分拣机管理 / 分拣参数管理 / 实时数据查询 / 分拣记录查询。其中「分拣参数管理」是整套系统最值得看的一页 —— 视觉模型的 apiKey 与提示词就落在这里。
5.2 登录页与实时数据查询
登录页标题「水果分拣系统」,字段只有「登录账号」「登录密码」两项。
图 8 登录页与实时数据查询页
实时数据查询页顶端给出更新时间与设备在线状态,中部是品种、重量、颜色、分类四张卡,下方按时间列出最近 5 张识别画面。实测这一帧为:更新时间2025-05-01 21:14:46、设备在线、品种未知、重量0g、颜色未知、分类-。
必须强调:这是设备空闲时的快照,盘子上还没有放水果,数值为 0 与占位符是正常的,不是「系统没识别出来」。真实识别效果要看分拣记录页的 32 条实测记录。
5.3 用户管理与分拣机管理
图 9 用户管理页与分拣机管理页
用户管理页实测注册了 3 条记录,更新时间集中在 2025-04-30 至 2025-05-01。编辑用户弹层的页头写着基本信息【手机号码将作为登录的账户信息】,也就是说手机号同时承担账号与联系方式两个角色。
分拣机管理页实测只有 1 台:终端编号ZDBH01、终端名称智能分拣机、online 状态在线、更新时间2025-05-01 21:14:37。加装第二台分拣机时,后台不需要改代码,登记一条即可。
5.4 分拣参数管理与豆包提示词
图 10 分拣参数管理页与模型提示词编辑
分拣参数管理页共 14 条配置,逐条列出如下(第 13 条 apiKey 属凭据信息,交付稿已作遮蔽处理):
1 蓝莓质量划分 blueberry2 3 2 蓝莓质量划分 blueberry1 5 3 蓝莓质量划分 blueberry3 1 4 苹果质量划分 apple1 200 5 苹果质量划分 apple2 150 6 苹果质量划分 apple3 100 7 橘子质量划分 orange1 180 8 橘子质量划分 orange2 130 9 橘子质量划分 orange3 80 10 草莓质量划分 strawberry1 20 11 草莓质量划分 strawberry2 15 12 草莓质量划分 strawberry3 10 13 模型key apikey <已遮蔽> 14 模型提示词 mode doubao.tse <提示词全文>
前 12 条可以看出「三档质量阈值按品种分别设置」:蓝莓 1 / 3 / 5 g、苹果 200 / 150 / 100 g、橘子 180 / 130 / 80 g、草莓 20 / 15 / 10 g。一个草莓和一个苹果显然不该共用一套阈值,这个设计是对的。
第 14 条的提示词全文如下(逐字取自编辑弹层):
请帮我分析图片中的水果是种类,颜色和有无伤病,并输出种类名称(范围苹果、橘子、草莓、蓝莓、其他),颜色名称输出范围(范围红、蓝、黄、其他),品质输出范围(范围优、良、一般、差),伤病情况输出范围(无、轻微、严重、其他),分析理由(字数不超过10个字,包含疑似种类名称),请严格按照格式输出:品类,颜色,品质,伤病情况,分析理由,不要输出其他内容。
提示词严格约定了输出格式:品类,颜色,品质,伤病情况,分析理由五个字段、逗号分隔、不输出其他内容 —— 这正是分拣记录页能做成结构化表格的原因。
5.5 分拣记录查询
图 11 分拣记录查询页
实测该查询共 32 条记录,每页显示 20 条。页面前六条依次是:
ZDBH01 智能分拣机 苹果 红 无 226 g 优 疑似苹果密网套 2025-05-01 18:39 ZDBH01 智能分拣机 其他 黄 无 52 g 差 疑似非常见水果 2025-05-01 18:38 ZDBH01 智能分拣机 其他 黄 无 58 g 差 疑似生水果 2025-05-01 18:38 ZDBH01 智能分拣机 其他 其他 无法判断 59 g 差 画面模糊选种 2025-05-01 18:38 ZDBH01 智能分拣机 其他 红 无 90 g 差 疑似西红柿 2025-05-01 18:37 ZDBH01 智能分拣机 苹果 红 无 232 g 优 疑似红苹果 2025-05-01 18:37
这六条是整套系统最有说服力的证据:模型输出的五个字段与重量读数并列成行,第 4 条「画面模糊选种」还真实暴露了模型在图像不清晰时的退化表现 —— 它没有硬猜一个种类,而是把「无法判断」如实写进了结论。
说明:本项目界面截图含真实手机号与模型 apiKey,已在交付前统一作不可逆遮蔽(马赛克 + 高斯模糊)。遮蔽后经高频细节能量自校验,各遮蔽区域降至原值的 5% 以下。
六、固件编译与烧录
硬件侧程序用 Keil 5 开发,编译后经串口下载工具烧录。
图 12 Keil 编译输出与串口烧录记录
实测这一轮编译与烧录的输出是:
Build Target 'USER' Program Size: Code=7712 RO-data=5248 RW-data=328 ZI-data=1792 .\Obj\stm32.axf - 0 Error(s), 0 Warning(s) Build Time Elapsed: 00:00:04 flyMcu V0.188 端口 COM37 波特率 115200 芯片 ID: 0x0000412 STM32F103x8_low-density 芯片全片擦除成功 共写入 18749 字节,进度 100%,耗时 583 毫秒 从 08000000 开始运行
代码窗口里还能看到一处与参数页互相印证的注释:// nz 1苹果, 2橘子, 3草莓, 4蓝莓, 5其他—— 与参数页的apple1..3、orange1..3、strawberry1..3、blueberry1..3编号完全对得上。编译 0 错误 0 警告、烧录成功并开始运行,固件这一侧是实测通过的状态。
七、系统视频展示
演示视频完整记录了系统从登录、配置参数,到放上水果、红外触发拍照、服务端调用视觉模型、舵机转到对应通道的全过程。
图 13 演示视频封面
视频里可以重点看三处:一是水果到位到屏幕出现识别结果之间的停顿,这段时间正是图像上传与视觉模型推理在服务端跑;二是 OLED 上重量读数与分拣记录页数值的一致性;三是舵机切换角度的动作,四个角度对应四个分类通道。
八、交付内容
| 交付项 | 形式 |
|---|---|
| 硬件实物 | 已装配并完成联调的整机(洞洞板 + 杜邦线) |
| 程序源码 | STM32F103C8T6 固件工程与 Java 服务端 + 网页端工程 |
| 硬件原理图 | 嘉立创 EDA 源文件(json / schdoc)及导出的图像与文档 |
| 演示视频 | 系统完整运行的演示录像 |
| 技术支持 | 远程协助环境搭建、程序调试与小规模修改答疑 |
项目编号 X504183。本文所述系统已完成实测验证,相关技术问题可在评论区交流。