1. 项目概述:一个用手机姿态控制的实体迷宫
几年前,我在一个创客展上看到一个用摇杆控制的平衡球迷宫,觉得很有意思,但总觉得少了点“人机合一”的沉浸感。后来玩手机游戏时,发现很多赛车游戏用重力感应控制方向,手感非常直接。我就想,能不能把这种体验搬到实体世界?让手机直接变成操控杆,通过倾斜手机来控制一个真实的迷宫平台,让里面的小球滚动。
这就是“BlueMaze”项目的起源。它本质上是一个嵌入式系统综合应用:以ESP32微控制器为大脑,通过蓝牙接收来自Android手机加速度计的实时数据,再驱动两个伺服电机(舵机),分别控制一个木质迷宫平台在X轴和Y轴上的倾斜角度。你向左倾斜手机,迷宫就向左倾斜,小球随之滚动,目标就是引导小球走出迷宫。
这个项目麻雀虽小,五脏俱全。它串联了传感器数据采集(手机加速度计)、短距离无线通信(蓝牙)、微控制器编程(ESP32/Arduino)、执行器控制(伺服电机)以及快速应用开发(MIT App Inventor)。无论你是想入门物联网、学习无线控制,还是单纯想做一个有趣的互动玩具,它都是一个绝佳的练手项目。下面,我就把自己从电路连接、代码调试到迷宫制作、避坑优化的全过程经验拆解开来,手把手带你复现这个项目。
2. 核心硬件选型与电路设计解析
硬件是整个系统的骨架,选型和连接决定了项目的稳定性和扩展性。我的核心思路是:主控要集成无线、执行器要独立供电、结构要稳固且易于加工。
2.1 主控芯片:为什么是ESP32?
市面上常见的单片机很多,比如经典的Arduino Uno、功能强大的STM32,但我最终选择了ESP32-DevKitC开发板。原因有三点:
- 集成双模蓝牙:这是最关键的一点。ESP32集成了蓝牙4.2(包括经典蓝牙和低功耗蓝牙),我们本项目使用经典蓝牙(SPP协议)进行数据传输。这意味着你不需要额外购买和连接HC-05、HC-06这类蓝牙模块,简化了电路,也降低了成本和连接出错的概率。
- 性能与资源充足:ESP32是双核处理器,主频高达240MHz,内存也足够,运行我们这种需要实时解析蓝牙数据并产生PWM信号控制舵机的程序绰绰有余。其丰富的GPIO口也为我们连接其他传感器(如后续想增加声音反馈、灯光效果)留下了余地。
- Arduino生态友好:虽然ESP-IDF是乐鑫官方的开发框架,功能更强大,但对于大多数爱好者和快速原型开发来说,Arduino IDE的库支持和社区资源更为丰富,上手更快。本项目就完全基于Arduino框架开发。
注意:ESP32开发板型号众多,建议选择引脚引出较多的型号(如30+ GPIO),并确认其USB转串口芯片稳定(如CP2102、CH340),这关系到后续代码上传的可靠性。
2.2 执行机构:伺服电机的选择与控制
迷宫平台需要两个自由度的倾斜,因此需要两个舵机。我选择了最常见的SG90微型舵机。选择它是因为它价格低廉、扭矩适中(1.8kg/cm)、控制简单(标准PWM信号)。但这里有几个关键细节:
- 工作电压:SG90标称工作电压为4.8V-6V。虽然ESP32的VIN引脚或某些板载的5V引脚可以输出5V,但强烈不建议直接从ESP32取电给舵机供电。舵机在启动和堵转时会产生很大的瞬时电流(可能超过1A),这极易导致ESP32的电源不稳定,引发复位或损坏。必须为舵机准备独立的电源。
- PWM信号:舵机控制线(通常是橙色或白色)连接至ESP32的GPIO。ESP32的LEDC(LED PWM控制器)可以生成非常稳定的PWM信号。我们需要选择支持硬件PWM输出的引脚,例如GPIO12、13、14、15、16、17、18、19、21、22、23、25、26、27等。
- 机械安装:舵机需要牢固固定。我使用了热熔胶,但对于长期使用或频繁操作,建议使用螺丝固定。舵机自带的塑料舵盘需要与迷宫结构进行可靠连接,我通过3D打印了一个连接件,也可以用轻木片加工。
2.3 电源方案:双路供电与共地
如前所述,电源分离是关键。我采用了一个带有双USB输出口的移动电源。
- 一路USB(5V/1A):通过Micro-USB或Type-C线给ESP32开发板供电。
- 另一路USB(5V/1A或2.4A):我剪断了一根旧的USB线,引出红(+5V)黑(GND)两根线,专门用于给两个舵机供电。
- 共地操作:ESP32的GND和舵机电源的GND必须连接在一起,这是确保PWM信号参考电位一致的基础,否则舵机可能无法工作或乱转。即使两路电源来自同一个移动电源的不同接口,也务必用导线将两者的GND引脚物理连接。
2.4 电路连接图与实操要点
下图清晰地展示了所有部件的连接关系:
[电源]移动电源 ├── USB口1 ──> ESP32开发板 (VCC, GND) └── USB口2 ──> 舵机电源线 (+5V, GND) ├──> 舵机1 (红线:+5V, 棕线:GND, 橙线:信号) └──> 舵机2 (红线:+5V, 棕线:GND, 橙线:信号) [信号]ESP32 GPIO ──> 舵机信号线 (例如:GPIO13 -> 舵机1信号线) (例如:GPIO14 -> 舵机2信号线) [地线]ESP32 GND引脚 ──> 舵机电源GND线 (必须连接!)实操连接步骤与心得:
- 准备杜邦线:准备足够数量的公对公、公对母杜邦线。舵机线通常是母头,所以连接ESP32时需要用公对公线,连接电源排针时用公对母线。
- 使用面包板或PCB:对于初次测试,强烈建议使用面包板。将ESP32、电源排针插在面包板上,再用杜邦线连接,非常便于修改和调试。确认一切工作正常后,可以考虑焊接在洞洞板(万用板)上,或者像我一样自己腐蚀一块简单的PCB,以获得更稳定的连接。
- 上电顺序:建议先连接好所有线路,检查无误后,最后再插入移动电源。避免带电插拔信号线。
- 信号线防干扰:如果舵机线较长(超过20cm),PWM信号可能会受到干扰。如果发现舵机有抖动现象,可以尝试在信号线靠近舵机端,对地(GND)加一个0.1uF的电容进行滤波。
3. ESP32端程序深度剖析与调试
代码是项目的大脑,负责解析蓝牙指令并精确控制舵机。我们使用Arduino IDE进行开发。
3.1 开发环境搭建
- 安装Arduino IDE:从官网下载并安装最新版。
- 添加ESP32板支持:打开
文件 -> 首选项,在“附加开发板管理器网址”中输入:https://espressif.github.io/arduino-esp32/package_esp32_index.json。然后打开工具 -> 开发板 -> 开发板管理器,搜索“esp32”,安装“Espressif Systems”的包。 - 安装蓝牙库:ESP32的Arduino核心已包含蓝牙库(
BluetoothSerial),无需额外安装。 - 选择开发板与端口:连接ESP32到电脑,在
工具菜单中选择正确的开发板型号(如ESP32 Dev Module)和对应的串口端口。
3.2 核心代码解读
以下是代码的核心逻辑,我添加了详细注释:
#include <BluetoothSerial.h> #include <ESP32Servo.h> // 使用专门的ESP32舵机库,兼容性更好 // 蓝牙串口对象 BluetoothSerial SerialBT; // 创建两个舵机对象 Servo servoX; Servo servoY; // 定义舵机信号引脚 const int servoXPin = 13; const int servoYPin = 14; // 存储从手机接收到的原始数据 int xValue = 0; int yValue = 0; // 舵机角度范围限制(根据你的机械结构调整) const int minAngle = 30; const int maxAngle = 150; // 数据解析相关变量 String inputString = ""; // 存储接收到的字符串 bool stringComplete = false; // 标志是否收到完整数据包 void setup() { Serial.begin(115200); // 启动硬件串口,用于调试输出 SerialBT.begin("BlueMaze"); // 启动蓝牙,设备名为“BlueMaze” Serial.println("蓝牙设备已启动,等待连接..."); // 允许ESP32的GPIO用于舵机控制(必须调用) ESP32PWM::allocateTimer(0); ESP32PWM::allocateTimer(1); ESP32PWM::allocateTimer(2); ESP32PWM::allocateTimer(3); // 关联舵机对象与引脚,并设置PWM频率(SG90通常为50Hz) servoX.setPeriodHertz(50); servoX.attach(servoXPin, 500, 2400); // 最小500us,最大2400us脉冲宽度,可微调 servoY.setPeriodHertz(50); servoY.attach(servoYPin, 500, 2400); // 初始化舵机到中间位置 servoX.write(90); servoY.write(90); delay(1000); // 给舵机时间运动到初始位置 } void loop() { // 1. 检查蓝牙是否连接,并读取数据 if (SerialBT.connected()) { while (SerialBT.available()) { char inChar = (char)SerialBT.read(); // 读取一个字符 if (inChar == '\n') { // 如果收到换行符,说明一个数据包结束 stringComplete = true; } else { inputString += inChar; // 否则将字符添加到字符串 } } } // 2. 如果收到完整数据包,开始解析 if (stringComplete) { // 预期数据格式:"X123Y98\n",即X和Y值以字母分隔 int xIndex = inputString.indexOf('X'); int yIndex = inputString.indexOf('Y'); if (xIndex != -1 && yIndex != -1) { // 提取X值:从'X'后一位开始,到'Y'之前结束 String xStr = inputString.substring(xIndex + 1, yIndex); // 提取Y值:从'Y'后一位开始,到字符串末尾 String yStr = inputString.substring(yIndex + 1); // 转换为整数 xValue = xStr.toInt(); yValue = yStr.toInt(); // 调试输出(可通过串口监视器查看) Serial.print("收到数据 - X: "); Serial.print(xValue); Serial.print(", Y: "); Serial.println(yValue); // 3. 数据映射与舵机控制 // 手机发来的数据范围假设为0-200(App中加了100),我们需要映射到舵机角度范围 // 注意:手机坐标系与迷宫坐标系可能需要转换(例如,手机前后倾对应迷宫左右倾) int angleX = map(yValue, 0, 200, minAngle, maxAngle); // 注意X/Y的对应关系 int angleY = map(xValue, 0, 200, minAngle, maxAngle); // 限制角度在安全范围内 angleX = constrain(angleX, minAngle, maxAngle); angleY = constrain(angleY, minAngle, maxAngle); // 写入角度,控制舵机 servoX.write(angleX); servoY.write(angleY); } // 处理完毕,清空字符串和标志,准备接收下一个数据包 inputString = ""; stringComplete = false; } // 短暂延迟,避免循环过快 delay(10); }代码关键点解析:
- 数据协议:我定义了简单的字符串协议
"X[value]Y[value]\n"。例如,手机发送"X145Y167\n"。这种格式易于在Arduino端用indexOf和substring函数解析,比传输二进制数据更直观、易调试。 map()函数:这是Arduino的核心函数,用于将一个范围内的值线性映射到另一个范围。这里将手机发来的0-200映射到舵机的30-150度。务必根据你实际迷宫结构的机械限位来调整minAngle和maxAngle,否则可能卡住或损坏结构。constrain()函数:安全锁。确保计算出的角度值不会超出预设的安全范围,是保护舵机和机械结构的重要一步。- 坐标系转换:
angleX = map(yValue,...)和angleY = map(xValue,...)这行代码非常关键!因为手机平放时,其X轴(左右)通常对应迷宫的Y轴(前后倾斜),Y轴(前后)对应迷宫的X轴(左右倾斜)。这个对应关系需要根据你安装手机的方向和舵机的安装方向来调整,可能需要多次测试。
3.3 上传代码与调试技巧
- 上传:在Arduino IDE中点击上传按钮。如果遇到“上传失败”或“串口打不开”,请检查:
- 是否安装了正确的USB驱动(CP210x或CH340)。
- 是否选择了正确的端口。
- 尝试按住ESP32板上的
BOOT按钮,再点击上传,待出现“上传中...”提示时松开。
- 串口监视器:上传成功后,打开
工具 -> 串口监视器,设置波特率为115200。重启ESP32,你会看到“蓝牙设备已启动,等待连接...”的提示。这是最重要的调试工具。 - 舵机测试:可以先注释掉蓝牙数据解析部分,在
setup()函数最后或loop()里写死几个角度值(如servoX.write(30); delay(1000); servoX.write(150);),测试舵机是否能正常运动到极限位置,并确认机械结构运行顺畅无阻碍。
4. Android应用开发:MIT App Inventor 2 实战
对于不熟悉Java/Kotlin的开发者,MIT App Inventor 2(AI2)是快速构建原型App的神器。它采用图形化“积木块”编程,直观易懂。
4.1 界面设计(Designer)
我们需要以下核心组件:
AccelerometerSensor(加速度传感器):非可视组件,用于读取手机加速度数据。BluetoothClient(蓝牙客户端):非可视组件,负责与ESP32通信。ListPicker(列表选择器):用户点击后弹出已配对的蓝牙设备列表。Clock(时钟):非可视组件,用于定时发送数据,这是实现流畅控制的关键。Label(标签):用于显示状态(如“未连接”、“已连接”)和当前的X、Y加速度值(用于调试)。Button(按钮):可选,用于手动连接/断开。HorizontalArrangement(水平布局):用于排列组件,使界面美观。
布局要点:将ListPicker的文本设为“点击连接蓝牙”,并放置一个显示连接状态的Label。再放置两个Label分别显示“X: 0”和“Y: 0”。最后,将Clock组件的TimerInterval属性设置为50毫秒(即每秒发送20次数据),这是一个在流畅度和功耗之间比较平衡的值。
4.2 逻辑编程(Blocks)
这才是核心。主要逻辑分为三块:
1. 初始化与蓝牙设备列表加载:
当 Screen1 初始化时 执行 设置 AccelerometerSensor1.Enabled 为 true 设置 ListPicker1.Elements 为 BluetoothClient1.AddressesAndNames解释:屏幕一打开就启用加速度计,并将已配对设备的列表赋值给ListPicker。
2. 蓝牙连接:
当 ListPicker1.AfterPicking 时 执行 调用 BluetoothClient1.Connect 地址为 ListPicker1.Selection 如果 BluetoothClient1.IsConnected 成立 那么 设置 Label_Status.Text 为 “已连接” 设置 Label_Status.BackgroundColor 为 绿色 设置 Clock1.TimerEnabled 为 true //启动定时发送 否则 设置 Label_Status.Text 为 “连接失败” 设置 Label_Status.BackgroundColor 为 红色解释:用户从列表中选择一个设备(我们的ESP32“BlueMaze”)后,尝试连接。连接成功则更新状态并启动定时器;失败则提示。
3. 定时读取加速度计并发送数据:
当 Clock1.Timer 时 执行 如果 BluetoothClient1.IsConnected 成立 那么 // 获取加速度值(单位:m/s²) 设 xRaw 为 AccelerometerSensor1.XAccel 设 yRaw 为 AccelerometerSensor1.YAccel // 数据缩放与偏移(关键步骤!) // 1. 放大:乘以一个系数(如10),增加控制灵敏度 // 2. 取整:转换为整数 // 3. 偏移:加上一个固定值(如100),确保所有数据为正数,方便ESP32解析 设 xValue 为 向下取整(xRaw * 10) + 100 设 yValue 为 向下取整(yRaw * 10) + 100 // 可选:限制数值范围在安全区间,例如0-200 设 xValue 为 限制数值范围(xValue, 0, 200) 设 yValue 为 限制数值范围(yValue, 0, 200) // 更新界面显示(调试用) 设置 Label_X.Text 为 连接字符串(“X: ”, xValue) 设置 Label_Y.Text 为 连接字符串(“Y: ”, yValue) // 按照协议格式发送数据:”X[value]Y[value]\n” 调用 BluetoothClient1.SendText 文本为 连接字符串(“X”, xValue, “Y”, yValue, “\n”)这里是整个App的灵魂,有几个经验点:
- 为什么用定时器发送,而不是加速度变化时发送?我最初尝试在
AccelerometerSensor.AccelerationChanged事件中发送,发现数据流不稳定,有时会丢包或延迟,导致迷宫控制卡顿。改为固定频率(50ms一次)的定时发送后,控制变得非常平滑。这是因为事件触发频率可能过高且不均匀,而定时器提供了稳定、可控的数据流。 - 数据缩放与偏移:原始加速度值通常在-10到+10之间(单位m/s²),变化幅度小且包含负数。
xRaw * 10将其放大,使控制更灵敏。+100是为了将所有值变为正数(大致在0-200范围),避免了在ESP32端解析负号字符串的麻烦。限制数值范围是另一道安全锁,防止意外值导致舵机角度越界。 - 协议格式:必须与ESP32端代码的解析格式严格匹配。这里发送
"X123Y156\n",ESP32就等待这个格式。
4.3 打包与安装
在AI2中点击构建 -> 安卓应用 (.apk),可以选择下载到电脑再传到手机安装,或生成二维码直接扫码安装。首次安装非市场应用,需要在手机设置中允许“安装未知来源应用”。
5. 迷宫结构设计与制作详解
稳定的机械结构是良好体验的保障。我的设计目标是:轻质、稳固、低摩擦。
5.1 材料与工具清单
- 核心结构:
- 底板:一块25cm x 30cm的MDF板(中密度纤维板)或亚克力板,厚度约5mm。MDF易于加工,亚克力更美观。
- 支撑柱:4根截面1cm x 1cm的轻木条(Balsa),长度分别为23cm(2根)和19cm(2根),用于制作底层框架。
- 侧立板:2根轻木条,长度13cm和12cm,用于支撑整个可动框架。
- 可动平台(迷宫托盘):
- 托盘底板:16cm x 20cm的硬卡纸或薄木板(如椴木板)。
- 迷宫墙壁:使用1.5cm宽的硬卡纸条制作。
- 转轴连接件:可以用3D打印(推荐),也可以用厚塑料片或小木块加工。需要两个:一个连接X轴舵机与迷宫托盘,一个连接Y轴舵机与底层框架。
- 从动轴:两根竹签或直径2-3mm的钢棍,作为迷宫托盘另一侧的支撑旋转点。
- 连接与固定:
- 热熔胶枪与胶棒(快速固定,适合原型)。
- 小螺丝(M2或M3,用于加固关键连接点,如舵机与木条的连接)。
- 手工刀、尺子、铅笔、手钻。
5.2 制作步骤与技巧
第一步:制作迷宫托盘
- 在16x20cm的卡纸上,用铅笔画出迷宫路径设计。路径宽度建议在2cm以上,确保小球(我用铝箔揉成的球)能顺利通过。
- 用尺子和手工刀,将1.5cm宽的卡纸条按设计图切割成相应长度的“墙壁”。
- 关键技巧:切割两条2cm宽的卡纸条作为“定位规”。在粘贴墙壁时,用它们卡在两条平行墙壁之间,可以确保所有路径宽度一致。
- 使用少量热熔胶,将墙壁垂直粘贴在底板对应位置上。动作要快,胶量要少,避免胶体凸起影响小球滚动。
第二步:组装可动框架
- 将23cm和19cm的轻木条用热熔胶粘成一个外框(23cm边为长边,19cm边为宽边)。确保四个角是直角,可以借助直角尺或利用书本角。
- 在这个外框的一条19cm边的中心位置,粘贴或螺丝固定一个舵盘连接件(连接Y轴舵机)。
- 在对边的中心位置,粘贴一小段竹签或安装一个轴承,作为从动旋转轴。
- 在迷宫托盘的一条16cm边的中心,粘贴另一个舵盘连接件(连接X轴舵机)。
- 在迷宫托盘的对边中心,同样粘贴一小段竹签作为从动轴。
- 将迷宫托盘通过其舵盘连接件,安装到Y轴舵机的舵盘上。同时,托盘另一侧的竹签轴,应能轻松搭在底层框架对应的支撑点上(可以在支撑点粘一个带凹槽的小木块)。
第三步:搭建整体支撑结构
- 在MDF底板上,确定两个侧立板的位置。它们应平行,间距略大于可动框架的宽度(19cm),例如25cm。
- 将13cm和12cm长的侧立板垂直固定在MDF底板上。可以用胶粘,但更稳固的方法是在底板和木条上预钻孔,然后用螺丝从底板下方拧紧。
- 关键步骤:将Y轴舵机固定在12cm高的侧立板顶端,确保其转轴与可动框架上的连接件同心。同样,在13cm高的侧立板顶端,制作一个支撑凹槽,用于承载可动框架另一侧的从动轴。
- 将组装好的可动框架(带着迷宫托盘)架到两个侧立板上:Y轴舵机与框架连接,另一侧的从动轴放入支撑凹槽。此时,用手拨动框架,它应该在Y轴方向顺畅摆动。
- 最后,将X轴舵机固定在可动框架的侧面,并将其舵盘与迷宫托盘上的连接件接好。现在,迷宫托盘应能在X轴方向顺畅摆动。
第四步:总装与布线
- 在MDF底板边缘钻一个直径6mm的孔,用于穿过舵机和ESP32的电源线、信号线。
- 将ESP32、移动电源固定在底板下方,用扎带整理好线缆,避免缠绕到运动部件。
- 进行通电测试。先不装小球,通过手机App控制,观察两个舵机是否带动迷宫平台在两个方向上平滑、完整地运动,且没有卡顿或异响。
6. 系统联调、校准与玩法优化
所有部分制作完成后,进入最有趣的联调与优化阶段。
6.1 首次连接与基础测试
- 给整个系统上电。
- 打开手机蓝牙,在设置中搜索并配对名为“BlueMaze”的设备(ESP32首次启动后可见)。
- 打开我们开发的App,点击“点击连接蓝牙”,选择“BlueMaze”。状态栏应变为绿色“已连接”。
- 此时,App已经开始发送数据。将手机平放在桌面上,这应该是迷宫的“水平”位置。
- 观察迷宫平台。它很可能没有水平,或者倾斜方向与手机动作相反。这很正常,需要校准。
6.2 软件校准:修改映射参数
校准的核心是调整ESP32代码中的map()函数参数和手机App中的偏移量。
- 确定中位值:在手机水平放置时,查看App界面显示的X和Y的数值(例如“X: 102, Y: 98”)。这个值就是“零位”对应的原始数据。假设是 (102, 98)。
- 修改App代码:在AI2的发送数据逻辑中,将偏移计算修改为:
这样,当手机水平时,发送出的设 xValue 为 向下取整(xRaw * 10) + (100 - 102) // 实际计算:100 - 零位值 设 yValue 为 向下取整(yRaw * 10) + (100 - 98)xValue和yValue理论上应接近100。重新打包安装App。 - 调整ESP32映射:在Arduino代码中,
map()函数将0-200映射到舵机角度。我们需要让“100”这个值映射到舵机的物理水平位置角度。这个角度不一定是90度,需要实测。- 注释掉蓝牙控制部分,手动编写测试代码,让舵机慢慢从30度运动到150度,观察迷宫平台何时水平。假设X轴舵机在115度时平台水平。
- 则将
map(yValue, 0, 200, minAngle, maxAngle)改为map(yValue, 0, 200, 115-(幅度), 115+(幅度))。例如,map(yValue, 0, 200, 75, 155),这样100就映射到115度,即水平位置。Y轴同理。
- 方向修正:如果平台倾斜方向与手机相反,只需在App端或ESP32端交换X/Y的映射关系,或在某个数值前乘以-1即可。
6.3 机械调校与润滑
- 重心调整:确保迷宫托盘(尤其是装上小球后)的重心大致在其几何中心。如果重心偏了,舵机负载会不均匀,可能产生抖动或无力。可以在托盘底部轻的位置粘贴配重(如硬币)来调整。
- 减少摩擦:所有旋转支点(舵机轴、从动轴与支撑凹槽)的摩擦要尽量小。可以在竹签轴上涂一点润滑油(如凡士林或硅脂),或使用微型轴承。
- 限位保护:在代码中设定的
minAngle和maxAngle一定要小于机械结构的物理极限角度,留出几度的余量,防止堵转烧毁舵机。
6.4 玩法扩展与优化建议
- 增加难度与趣味性:
- 多球模式:在迷宫中放置多个颜色不同的铝箔球,挑战同时控制多个球。
- 计时挑战:在App端增加计时器功能,记录走出迷宫的时间。
- 迷宫地图切换:设计多个可替换的迷宫底板,用磁吸或卡扣方式固定。
- 硬件升级:
- 金属舵机:如果觉得SG90扭矩不足或有抖动,可以升级为金属齿轮的MG90S舵机。
- 电池集成:使用18650电池盒和降压模块替代移动电源,让作品更一体化。
- 增加反馈:在迷宫终点安装一个触碰开关或光电传感器,当小球到达时,让ESP32控制一个蜂鸣器播放胜利音效,或通过蓝牙通知手机App。
- 软件优化:
- 数据平滑滤波:在ESP32端,可以对接收到的X、Y值进行滑动平均滤波,消除手机微小抖动带来的平台抖动。
- 控制曲线:将线性映射改为指数或自定义曲线,使得手机在小角度倾斜时平台移动平缓,大角度倾斜时移动迅速,操控感更跟手。
这个项目从构思到实现,最深的体会是“软硬结合”的魅力。每一个环节——电路的一个虚焊、代码的一个参数、结构的一毫米偏差——都会直接影响最终体验。调试过程就是不断在手机屏幕、串口监视器、和晃动的迷宫平台之间来回切换、观察、思考、修改的过程。当最终小球随着手机倾斜而流畅滚动,并成功走出迷宫时,那种成就感远超单纯完成一个软件或硬件任务。它完整地呈现了一个物联网控制系统的闭环,希望这个详细的分享能帮你顺利搭建出自己的“BlueMaze”,并在此基础上玩出更多花样。