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

资讯详情

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

ESP32桌面保龄球系统:传感器检测与状态机计分的完整实践

ESP32桌面保龄球系统:传感器检测与状态机计分的完整实践 最近看到有人把一套 ESP32 保龄球系统的技术说明公开出来。项目本身不是那种复杂到吓人的硬件工程但把传感器检测、机械复位、计分状态机、网络显示串在一起之后很值得照着做一遍。这里说的不是专业球馆里的正式设备而是适合桌面摆位、客厅娱乐、课程设计或作品展示的桌面级保龄球系统用 ESP32 读取传感器判断有没有球滚过、有没有瓶子被打倒再控制舵机或电机把瓶子复位同时用屏幕、网页或手机 App 查看比分。适合的读者很明确手上有 ESP32 开发板想从点灯、读传感器这类单点教程跨到完整小作品的开发者或者想给社团、学校、门店做一个互动游戏设备又不想直接买商用机的人。这套系统最值得关注的点不是某个传感器跑通了而是四个部分怎么配合检测逻辑什么时候采数、计分状态怎么流转、执行机构什么时候复位、网络面板怎样不干扰主流程。很多 ESP32 项目卡在“功能模块都能跑但组合起来不稳定”所以下面按实际搭建顺序拆一遍。你会看到环境配置、引脚规划、状态机设计、烧录调试、网络控制和排查思路基本覆盖从零到能玩一局的完整链路。1. 先确认它到底做什么再决定要不要动手1.1 保龄球系统的核心能力我理解中的桌面级 ESP32 保龄球系统至少包含四块能力检测球道内的球球从发球端滚向瓶区时要能产生一个明确信号。检测剩余立瓶数量球撞击之后系统要知道还有几个瓶子立着算出击倒几个。执行复位动作舵机或步进电机把倒下的瓶子拉回站立位置。展示和记录比分屏幕显示、串口输出或者通过 WiFi / 蓝牙把实时比分同步到手机或电脑。如果只做其中一块难度很低。但真正让它像“产品”而不是“模块”关键在计分状态机。因为保龄球有一轮两球、全中、补中、第十轮加罚球的规则家庭娱乐可以简化但状态切换的时序不能省。状态一旦混乱传感器就会读错时间点表现为“球还没停稳就开始计数”“复位还没完成就进入下一轮”。1.2 为什么控制核心选 ESP32很多人第一反应是用 Arduino Uno。单做传感器检测确实够但一旦要接十个瓶位传感器、两个舵机、一个电机、一个显示屏还要处理网络请求Uno 的引脚数量和内存都会紧张。尤其 Uno 本身不带 WiFi需要外接 ESP8266 模块通信又增加一层不稳定。ESP32 的优势在于自带 WiFi 和蓝牙局域网面板、手机 App 控制不需要额外模块。双核处理器可以一个核跑传感器循环一个核响应网络请求。实践上不一定需要这么分但至少 CPU 余量够。引脚数量多支持 ADC、PWM、I2C、UART、SPI和绝大多数传感器、舵机、屏幕都能直接连接。价格在同类开发板里不贵买两块备用也不心疼。不同型号的选择要注意ESP32 型号适合场景需要注意的点经典 ESP32 WROOM通用开发资料多教程好找体积偏大功耗相对高ESP32-C3轻量任务价格低GPIO 够用单核跑复杂状态机加网络会吃力ESP32-S3需要摄像头、屏幕动画、复杂交互时更合适引脚和经典版不完全一样要先看引脚图如果你只做单体保龄球机经典 ESP32 或 ESP32-C3 都可以。如果以后还想加摄像头视觉识别球瓶或者用 LVGL 做动画界面直接上 ESP32-S3 更稳妥。入门阶段先别纠结哪块板子“最好”。你先确认手上板卡的丝印和引脚定义比网上推荐的型号更关键。2. 系统拆解传感器、执行机构、状态机是怎么配合的2.1 瓶位检测和球道检测的方案选择保龄球系统的检测方式没有唯一标准。不同方案成本和稳定性差别很大我在搭建时会把它们拆成“球道检测”和“瓶位检测”两路分开设计。球道检测通常放在球进入瓶区之前。常见有两种做法红外对射模块发射端和接收端装在球道两侧球经过时遮挡光线输出电平跳变。超声波测距模块安装在球道末端上方检测是否有物体进入固定距离范围。我更推荐红外对射。它对光线遮挡反应直接响应速度快不容易因为角度问题误判。超声波也可以但反应时间和最小检测距离需要调球速较快时可能跟不上。瓶位检测核心是判断每个瓶是否还站立。比较实用的三种方案方案安装方式成本稳定性适合阶段微动开关 / 薄膜压力传感器每个瓶位底部放一个低稳定但走线和固定麻烦入门到进阶红外反射阵列每个瓶位旁边装反射传感器中容易受邻近瓶影响进阶调试摄像头视觉识别顶部装 OV2640 等摄像头用 S3 跑识别较高上限高但算法难度大做作品或比赛从工程成本看桌面级项目最常用的是薄膜压力传感器或微动开关。瓶倒下时压力变化或开关状态变化信号最直接。摄像头方案听着酷但实际调试要处理环境光线、角度、球瓶颜色我建议不要把视觉识别作为第一版功能。2.2 执行机构复位、回球和防卡死瓶子复位如果是迷你保龄球瓶身轻可以用舵机加细线拉倒下的瓶子。10 个瓶子如果每个都独立控制需要的 IO 和舵机数量太多。更常见的做法是分组控制前场、中场、后场分开或者把 10 个瓶子用两三个舵机联动复位。这样机械结构简单逻辑也轻松。球回传如果你希望球自动滚回发球端可以在球道末端加一个直流电机带动挡板或推杆。这个功能不是必需但它会让整套系统更像“成品”。这里要特别提醒执行机构的电源问题。舵机和直流电机启动瞬间电流很大如果直接用 ESP32 的 3.3V 或者电脑 USB 供电电压瞬间跌落就会导致 ESP32 重启。我见过很多“复位时板子重启”的故障最后发现不是代码问题是电源不够。下面给出一个参考系统组成推荐模块供电注意主控ESP32 开发板5V USB 供电或独立 5V 稳压瓶位检测薄膜压力传感器 x10 或微动开关 x10传感器本身耗电低共地即可球道检测红外对射模块 x15V 供电信号输出到 3.3V GPIO复位机构舵机 x2~3单独 5V/6V 电源与 ESP32 共地回球机构直流电机 驱动模块单独电源不直接接 ESP32显示0.96 寸 OLED 或 2.4 寸 TFTI2C 或 SPI 连接2.3 计分状态机为什么不能省没有状态机的代码也能跑比如循环读传感器检测到球就延迟一秒再读一下瓶位算个分数。但真实场景下传感器信号不是规规矩矩的球经过红外对射时可能抖动瓶倒下时也不会同时触发所有传感器。如果不用状态机你很难判断“当前系统处于什么阶段”。我建议至少定义这几个状态等待发球系统空闲屏幕显示当前轮次和分数。球通过检测区红外对射触发系统知道球已经滚向瓶区。等待稳定球撞击后可能会反弹、滚动给传感器一个稳定窗口比如延迟 1 到 2 秒再读立瓶数。更新计分根据本轮第一次投球和第二次投球的逻辑更新分数。等待复位或自动复位确认计分完成后舵机复位瓶子然后回到等待发球。状态机的好处是每个阶段只处理一个明确动作。串口日志只要打印“当前状态 触发原因”就能很快定位问题。否则多个传感器同时触发时日志会乱成一团。3. 开发环境准备Arduino IDE 和 PlatformIO 怎么选3.1 Arduino IDE 配置 ESP32 开发环境Arduino IDE 是两个编辑器里最容易上手的一个。你不需要理解复杂编译配置选对开发板就能烧录。但很多人在“下载 ESP32 开发板包”这一步卡住。原因很简单Arduino IDE 默认从国外服务器下载 ESP32 支持包网络不稳定时很容易失败。常见表现是 Arduino 下载 ESP32 库失败、进度条卡住、提示“无法解析主机名”或连接超时。解决办法分两类使用镜像源或离线安装包。在网上搜索对应版本的离线包手动放到 Arduino15 目录下比反复重试更省时间。直接在 Arduino IDE 开发板管理器里手动添加开发板管理网址然后选择 ESP32 相关包。如果网络很慢就换个时间再试或用代理式下载工具注意这里只讨论常规开发环境下载方式。我个人会同时把离线包下载好作为一个备用手段。如果是在实验室或教室环境多台电脑需要配置一样的开发环境离线包比逐台在线下载快得多。3.2 PlatformIO 和 VS Code 更适合工程化如果你只是几行代码验证传感器Arduino IDE 完全够用。但如果保龄球系统要写状态机、网页控制、蓝牙服务文件会明显变多。我建议从第一天就考虑 VS Code PlatformIO。PlatformIO 的好处是工程目录清晰依赖库按项目管理不用手动装一堆库到全局。而且它支持多个开发环境同一套代码可以切换不同 ESP32 型号编译。比如你手上有 ESP32-C3 和 ESP32-S3只要在platformio.ini里改一下board就能分别编译不用复制整个项目。不过 PlatformIO 第一次创建项目也要下载工具链网络环境差时同样可能卡住。常见做法是搭建离线环境或使用已下载好的包缓存。如果只是学习先跑通 Arduino IDE 示例再切 PlatformIO 也不迟。下面给一个对比对比项Arduino IDEvs Code PlatformIO上手速度快适合单文件 Demo稍慢适合做完整项目库管理手动安装容易版本冲突按项目配置依赖清晰多板卡切换每次手动选型号配置文件名即可切换调试体验只有串口输出有串口监视器也支持部分调试能力适合场景验证单个模块完整系统、接口、批量开发3.3 烧录流程和常见失败原因ESP32 烧录不像 Arduino Uno 那么无脑但只要理解了流程就不难。先把 USB 线连接开发板安装对应的 USB 转串口驱动。常见芯片是 CP2102 或 CH340不同开发板芯片不同。设备管理器里看不到 COM 口时先检查驱动而不是急着重装 Arduino IDE。选择板卡时要看你手上具体型号例如 ESP32 Dev Module、ESP32C3 Dev Module、ESP32S3 Dev Module。选错型号最常见的现象就是编译成功但烧录失败或者烧录后运行不正常。烧录失败的高频原因串口被其他程序占用比如串口监视器没有关闭。驱动没装好COM 口不存在。板子没有进入下载模式。大多数开发板会自动进入但有些需要按住 BOOT 键再插 USB或者烧录时按住 BOOT 键。连接线质量差或 USB 口供电不足。如果你遇到“ESP32 烧录后无法再烧录”先别怀疑板子坏了。通常做法是关闭串口监视器拔掉 USB按住 BOOT 键重新插线在 Arduino IDE 里再烧一次。多数情况能救回来。还有一个实用技巧如果你想备份开发板里面已有的固件可以用 esptool 读取 Flash。先确认 Flash 大小和起始地址再用read_flash导出完整镜像。但这一步属于备份操作不要在拿到别人固件时随意读取破解只对你自己的设备做备份是合理的。4. 核心代码怎么写从单传感器到完整对局4.1 先跑通一个传感器我建议第一步不是写完整保龄球逻辑而是先让一个传感器在串口里能看到稳定读数。代码可以很简单#define SENSOR_PIN 26 void setup() { Serial.begin(115200); pinMode(SENSOR_PIN, INPUT_PULLUP); } void loop() { int value digitalRead(SENSOR_PIN); Serial.printf(sensor%d\n, value); delay(50); }在串口监视器里用手遮挡传感器观察输出是否发生变化。如果没变化先看引脚定义对不对再确认模块供电和共地。这里最容易忽略的是共地传感器用外部 5V 供电但 GND 必须和 ESP32 的 GND 连在一起否则信号电平始终无效。这一步看起来简单但很难跳过。因为后续所有计分逻辑都建立在“传感器信号可靠”的基础上。如果连基础读数都不稳定就别急着调状态机。4.2 读取瓶位传感器并计算立瓶数瓶位传感器我建议用一个数组管理方便后续扩展。const int sensorPins[10] {13, 12, 14, 27, 26, 25, 33, 32, 35, 34}; int readStandingPins() { int count 0; for (int i 0; i 10; i) { if (digitalRead(sensorPins[i]) LOW) { count; } } return count; }注意LOW 还是 HIGH 要看你的传感器模块。有的模块挡光输出低电平有的输出高电平。先打印所有引脚状态再确认哪些引脚代表“瓶子站立”。不要直接抄网上代码因为模块型号可能不同。一个常见问题是瓶子倒下瞬间传感器信号会连续抖动。如果刚检测到一次变化就立即计分很可能误判。解决方法是加延时和连续采样int readStandingPinsStable(int delayMs) { delay(delayMs); return readStandingPins(); }在实际项目里我会在“球通过检测后”延迟 1 到 2 秒再稳定采样几次取最小值或众数作为最终立瓶数。这样能过滤掉球还没停稳造成的干扰。4.3 简化状态机的实现完整保龄球规则比较复杂第一版可以先做简化计分每一轮最多两球记录两球击倒总数不处理全中补中奖励。等这套能跑通再逐步加标准规则。状态机可以用枚举加 switch 实现enum GameState { WAIT_BALL, BALL_PASSED, DETECT_STABLE, UPDATE_SCORE, RESET_PINS }; GameState state WAIT_BALL; unsigned long stateStart 0; void loop() { switch (state) { case WAIT_BALL: if (digitalRead(BALL_SENSOR_PIN) LOW) { state BALL_PASSED; stateStart millis(); } break; case BALL_PASSED: { unsigned long delta millis() - stateStart; if (delta 1500) { int standingBefore readStandingPins(); // 记录第一球立瓶数再进入检测稳定 state UPDATE_SCORE; stateStart millis(); } break; } case UPDATE_SCORE: // 根据 standingBefore 计算击倒数更新比分 state RESET_PINS; break; case RESET_PINS: // 驱动舵机或电机复位瓶子 state WAIT_BALL; break; } }上面只是示意片段真实项目里还要把第一球和第二球分开处理。我的建议是不要把完整逻辑一次性写在loop()里而是拆成函数handleBallDetect()处理球道传感器。handleScoreUpdate()处理计分逻辑。handlePinReset()处理复位动作。这样串口日志可以清晰标出当前状态调试时会方便很多。4.4 复位机构的联调顺序复位机构最容易出问题。原因是机械动作有延迟和失败率。我先用手动按钮测试舵机再接入自动化。手动测试步骤单独烧录一个舵机测试代码让舵机转到复位位置。观察瓶子是否全部恢复站立有没有卡住。调整舵机角度值确保结构不扭曲、不堵转。确认没问题后再把它接到状态机里。如果你跳过手动测试直接接进主程序一旦卡住你会分不清是状态机逻辑错误、舵机角度错误还是机械结构卡脚。我觉得这个坑完全可以避免。5. 联网控制局域网网页和手机 App5.1 为什么先做局域网网页桌面级保龄球系统最常见的联网需求是手机或电脑上能看到实时比分不用每次都蹲在机器旁边盯屏幕。ESP32 自带 WiFi这种方式最直接。实现思路是 ESP32 启动后连接路由器然后启动一个 HTTP Server。手机和电脑在同一个局域网里访问 IP就能看到页面。核心接口可以返回 JSON 数据例如当前轮次、当前分数、剩余立瓶数、系统状态。#include WiFi.h #include WebServer.h WebServer server(80); void setup() { WiFi.begin(your_ssid, your_password); while (WiFi.status() ! WL_CONNECTED) { delay(500); } server.on(/api/state, HTTP_GET, []() { String json {; json \round\: String(currentRound) ,; json \score\: String(totalScore) ,; json \state\:\idle\; json }; server.send(200, application/json, json); }); server.begin(); } void loop() { server.handleClient(); }这里要注意handleClient()会占用主循环时间。在保龄球状态机里如果球飞过来时正好在处理网络请求可能造成传感器读取延迟。解决方法是把网络处理拆到另一个核心或至少保证handleClient()不阻塞。如果你用的是双核 ESP32可以把loop()里的网络任务放到第二个核运行。对新手来说更稳妥的做法是尽量缩短页面响应时间返回简单 JSON而不是在 ESP32 里渲染复杂 HTML。5.2 蓝牙 App 控制怎么接局域网网页适合“多人同时看”蓝牙则适合“一个人拿着手机近距离控制”。ESP32 支持经典蓝牙和 BLE。对于此类项目BLE 配一个简单命令协议就够。思路如下ESP32 作为 BLE Server提供读和写 characteristics。手机使用 nRF Connect 或自己写 App 连接。写入字符R表示复位瓶子写入N表示开始新一局写入Q表示查询当前分数。ESP32 收到字符后执行对应动作并把状态写到 characteristic 里手机端读取显示。联网和蓝牙可以同时存在。WiFi 负责页面展示和多人查看BLE 负责近距离操作。但如果第一版不想做过复杂我建议先做 WiFi 网页因为调试浏览器比写手机 App 快得多。5.3 离线优先断网也要能玩这套系统最怕的坑是“把网络当作核心依赖”。如果只是看个比分WiFi 断了游戏体验不应该中断。所有计分、检测、复位逻辑都应该在本地完成网络只是“额外展示”和“远程控制”的入口。我在项目里会这样设计本机状态机独立运行不依赖任何网络请求。网络模块只在状态更新后把数据写入全局变量由网页接口读取。断网时屏幕继续显示本地比分。重新联网后网页重新刷新依然能看到当前状态。如果你把比分只存在网页上断网后一局直接丢数据用户会非常沮丧。对于分数保存ESP32 可以用 Preferences 库把历史分数、对局数写进 Flash重启后不丢。这个功能的优先级比网页漂亮界面更高。6. 调试、避坑和排查链路6.1 先看现象再查输入再改参数保龄球系统最常见的故障现象我整理成一个排查顺序它比具体某个错误码更通用现象优先检查项常见处理传感器一直触发接线、共地、电源、模块灵敏度打印原始电平挡光测试传感器完全不触发引脚定义、供电、信号电平逻辑用 LED 或万用表测 GPIO 电压一复位 ESP32 就重启舵机或电机电源不足独立供电加共地别用 USB 带电机计分不稳定采样时机、延时长度、传感器抖动球撞击后延迟 1~2 秒再采样WiFi 连不上热点名、密码、路由器频段先连手机热点验证代码再换路由器烧录失败串口被占用、驱动、BOOT 模式关闭串口监视器按住 BOOT 重试网页打开很慢HTML 过大、接口响应慢返回 JSON页面简化先看现象再查输入这是一个很实用的原则。很多“功能不工作”根本不是功能有问题而是输入数据和预期不一致。比如传感器逻辑是低电平触发但你的模块是高电平触发这种问题查代码查不出来必须看串口原始数据才能发现。6.2 传感器误触发的物理处理软件去抖只能解决一部分问题还有一部分是物理环境造成的。红外对射传感器在日光直射下容易误触发因为强光会干扰接收端。解决办法是给发射和接收端加遮光罩或者调整安装位置尽量避免正对窗户和灯光。如果球道表面反光严重把传感器架高一点比单纯调代码有效。薄膜压力传感器误触发通常是安装平面不平整或固定不紧。瓶底和传感器之间如果有缝隙瓶倒下时压力传递不均匀。我建议在传感器表面加一层薄垫片让受力更均匀。6.3 执行机构卡死和复位失败舵机复位时卡住第一反应是看角度值是否超出机械允许范围。舵机角度过大会顶着结构持续堵转电流变大发热增加。正确做法是手动测试找到一个能完全立起瓶子且不过度压迫的角度区间并在这个区间两端再加一个安全余量。如果瓶子被绳子拉着还要注意绳子长度和松紧。绳子太松瓶子立不到位绳子太紧舵机可能转不动。换绳子时也要关注线材弹性不同会影响复位角度。更稳妥的设计是加限位开关或电流检测。当复位机构运行到限位位置时直接停止而不是靠固定延时。延时方案在机械结构顺畅时能用一旦结构变硬或电池电量降低固定延时就会导致中途停止。6.4 供电分区和共地是稳定性的根基我在前面反复提到独立供电和共地这里再展开说。ESP32 控制板、传感器、舵机电机、显示屏幕如果全部从一个 USB 口取电只要有一个执行机构启动电压就会波动。这种波动可能不至于让所有模块崩溃但会让 WiFi 偶尔掉线、传感器读数异常、屏幕闪烁。更合理的分区方式ESP32 和传感器使用 5V 稳压电源电流不小于 2A。舵机使用单独 5V/6V 电源如果是较大舵机最好单独供电。直流电机使用电机驱动模块驱动模块独立供电。所有模块的 GND 连接到同一个参考地。最后一步共地特别容易被忽略。不共地时设备之间没有统一电平参考信号可能时好时坏。看起来像代码问题实际上是电路问题。6.5 日志和实验记录调试这个项目时我一般会在串口打印下面这些信息当前状态机状态。球道传感器原始值。每个瓶位传感器原始状态。当前轮次、第几球、累计分数。复位动作开始和结束时间。WiFi 连接状态和客户端请求次数。日志不用写得很精致但信息要完整。遇到问题先看日志一般能定位到是传感器、状态机还是执行机构的哪一环节。如果连日志都没有就只能盲猜很难搞。我再强调一次不要一次性把完整功能写完再调试。先单传感器再单次投球再手动复位再自动复位最后加网页和蓝牙。每加一层都跑一次完整流程。这样即使出问题范围也小能在五分钟内定位。最后说说这个项目给我的经验。做 ESP32 保龄球系统真正需要盯住的事情不是“用了什么高级算法”或者“界面多炫酷”而是基础工程是否稳定传感器有没有共地、供电会不会掉压、状态机是否在所有时序下都能回到初始状态、复位机构会不会在半路卡死。把这几件事做好再考虑扩展摄像头识别、LVGL 动画、热敏打印机打小票这些加分项。如果只是学习默认配置、普通传感器、一个舵机组足够先把一局跑通比追求完整功能更能带来收获。
返回列表