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

资讯详情

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

基于VL53L0X激光雷达与Arduino的猫砂使用计数器DIY指南

基于VL53L0X激光雷达与Arduino的猫砂使用计数器DIY指南 1. 项目缘起一个养猫人的“懒人”执念养猫的朋友们不知道你们有没有过这样的经历每天下班回家第一件事就是冲到猫砂盆前弯下腰眯着眼试图通过观察猫砂表面的“地形”来判断今天主子到底“临幸”了几次。是两次还是三次昨天铲过了吗有时候工作一忙或者出差一两天回来面对猫砂盆心里更是没底。猫砂用得太久不换不仅气味感人对猫咪的健康也不好换得太勤又觉得浪费。这种“薛定谔的猫砂使用量”问题一直是我这个科技宅兼猫奴心头的一个小疙瘩。于是一个想法冒了出来能不能做一个简单、低成本的小装置自动统计猫咪使用猫砂盆的次数并且在猫砂需要更换或补充时给出明确的提示这就是“猫砂使用计数器与指示器”项目的初衷。它不是什么复杂的智能家居核心目标就两个数数和提醒。数清楚猫咪一天去了几次厕所再根据这个数据结合一些简单的逻辑告诉我“嘿该干活了”。这个项目非常适合喜欢动手的铲屎官用一些常见的电子模块就能实现成本可以控制在百元以内。今天我就把自己从构思、选型到动手实现的全过程以及中间踩过的坑和总结的经验毫无保留地分享出来。2. 核心方案设计如何“感知”猫咪的如厕行为要实现计数首先得解决最根本的问题如何可靠地检测到猫咪进入了猫砂盆这是一个典型的传感器选型问题需要综合考虑可靠性、抗干扰性、安装便利性以及对猫咪的友好度无感或低干扰。2.1 传感器方案对比与选型我最初构思了几个方案并进行了详细的对比和实测方案一超声波测距/红外对射传感器原理在猫砂盆入口两侧安装一对红外对射管或者在上方安装超声波模块检测是否有物体阻断或靠近。优点检测准确理论上可以区分进入和离开。缺点安装复杂需要精准对齐且猫砂盆形状各异固定是个问题。易受干扰猫砂粉尘可能附着在传感器表面影响性能。猫咪如果从侧面跳入可能无法触发。对猫不友好虽然红外不可见但物理上存在障碍物猫咪可能会警惕。实测结果我尝试用HC-SR04超声波模块发现猫咪动作太快有时检测不到且猫砂盆边缘的弧形结构导致安装不稳容易误触发。否决。方案二振动传感器原理将压电陶瓷片或SW-420这类模块贴在猫砂盆底部或侧面检测猫咪进出、刨砂时产生的振动。优点非接触式安装相对隐蔽。缺点误报极高楼板震动、人走路、甚至关门都可能触发。需要非常复杂的软件滤波算法来区分“猫振动”和“环境振动”对新手极不友好。信号处理复杂振动信号是模拟量需要AD转换和阈值判断稳定性差。实测结果误报多到计数器几分钟就飙到99完全无法使用。否决。方案三热释电红外传感器原理检测生物体的红外热辐射变化。常见的人体感应模块就是这种。优点非接触只对移动的热源敏感。缺点检测范围大标准模块的扇形检测区域可能覆盖猫砂盆周边区域人经过也会触发。存在盲区对静止或缓慢移动的目标不敏感猫咪如果进去后安静地“办事”可能中途丢失目标导致一次如厕被误判为多次进出。实测结果需要精细调整安装角度和灵敏度但仍难以避免人在附近活动的干扰。可用但不够优雅。作为备选。方案四激光雷达模块原理使用如VL53L0X这类小型ToF激光测距传感器垂直向下安装在猫砂盆正上方测量到猫砂表面的距离。优点精准毫米级精度可以准确感知是否有物体猫咪进入了测量区域。抗干扰强不受环境光、粉尘除非极厚影响。安装简单只需一个吊装点无需在盆体上打孔或粘贴。逻辑清晰当测量距离持续小于“空盆距离”一定时间即可判定为一次有效使用。缺点成本略高于普通红外传感器。实测结果稳定、可靠、误报率极低。猫咪无论以何种姿势进入都能被稳定检测到。最终选定。注意选择VL53L0X还有一个重要原因它通过I2C通信编程简单且Arduino社区支持库非常成熟。相比其他方案它提供了“距离”这个明确的数字量而非“有/无”的开关量这让我们后续做状态判断如区分猫咪是在刨砂还是已离开有了更多数据依据。2.2 系统架构与组件清单确定了核心传感器整个系统的架构就清晰了感知层VL53L0X激光雷达负责采集距离数据。控制层一块单片机如Arduino Nano或ESP8266负责读取传感器数据、运行计数逻辑、驱动显示设备。指示层用于显示计数和状态。我选择了OLED屏幕显示实时次数并用一个RGB LED灯来提供无需查看屏幕的直观状态提示如绿色-正常黄色-使用较频繁红色-建议清理。供电采用USB供电或锂电池确保长时间稳定运行。完整物料清单主控Arduino Nano 或 NodeMCU (ESP8266) *1选型理由Nano接口简单成本低ESP8266自带Wi-Fi为未来联网上报数据留有余地。本项目先以离线为主故选用Nano。测距传感器VL53L0X激光测距模块 *1显示模块0.96寸 I2C OLED屏幕 (128x64) *1状态指示共阴RGB LED灯 *1 220Ω电阻 *3其他杜邦线若干面包板或PCBUSB数据线/电源安装用支架或3D打印外壳。3. 硬件连接与安装要点硬件连接的核心是I2C总线。VL53L0X和OLED屏幕都使用I2C通信可以共享时钟线(SCL)和数据线(SDA)这大大简化了布线。3.1 电路连接示意图Arduino Nano引脚 - 外设模块 5V/VIN - VL53L0X VCC, OLED VCC GND - VL53L0X GND, OLED GND, RGB LED阴极 A4 (SDA) - VL53L0X SDA, OLED SDA A5 (SCL) - VL53L0X SCL, OLED SCL D9 - RGB LED 红色阳极 (通过220Ω电阻) D10 - RGB LED 绿色阳极 (通过220Ω电阻) D11 - RGB LED 蓝色阳极 (通过220Ω电阻)连接细节与避坑指南I2C地址冲突VL53L0X的默认I2C地址是0x29而大部分OLED屏幕的默认地址是0x3C。两者不同因此可以直接并联在I2C总线上无需担心地址冲突。这是最省事的方案。电源去耦如果条件允许在VL53L0X的VCC和GND之间并联一个0.1uF的陶瓷电容可以滤除电源噪声提高测距稳定性尤其是在长导线连接时。RGB LED限流电阻必须串联直接连接IO口到LED会因电流过大烧毁IO口或LED。220Ω电阻在5V系统下能将电流限制在安全范围约15mA。导线选择传感器到Arduino的连线如果超过20cm建议使用屏蔽线或双绞线以减少干扰。I2C通信对线路噪声比较敏感。3.2 机械安装与调试安装方式直接决定了检测的可靠性。推荐安装方案将整个电路板或核心的VL53L0X模块固定在一个轻质支架上垂直悬挂于猫砂盆正上方。测量点应选择在猫砂盆中央区域避开边缘。安装高度H的确定首先清空猫砂盆。将传感器固定在你预设的安装位置上电。通过串口监视器读取传感器到盆底的原始距离值D_empty。放入你日常使用的、平整后的猫砂深度记为L_litter。再次读取距离值D_litter。理论安装高度H D_empty。而D_litter将是系统判断“盆内无猫”的基准距离。关键阈值设定触发距离阈值通常设为D_litter - 10cm。例如空砂面距离为50cm当传感器读数持续小于40cm时认为有物体猫进入。判定为一次使用的持续时长这个很重要用于防止猫咪在盆边探头探脑或快速经过导致的误计数。我通过观察发现猫咪一次如厕包括刨坑、排泄、掩埋最少也需要20秒以上。因此设置一个持续触发时间阈值比如30秒。只有当“距离小于触发阈值”的状态连续保持超过30秒才被记录为一次有效使用。这能过滤掉90%以上的误触发。4. 软件逻辑与代码实现详解软件部分是项目的大脑负责将原始的毫米数据转化为可靠的“一次如厕”事件。逻辑清晰与否直接决定计数器的准确性。4.1 核心状态机设计我们不能简单地认为“距离一小于阈值就计数”那样会重复计数。必须引入一个状态机来跟踪猫砂盆的状态。我定义了三个状态空闲状态盆内无猫。此时持续监测距离当距离小于触发阈值时启动一个计时器并进入“预触发状态”。预触发状态可能是有猫进入了。在这个状态里持续检查距离。如果距离在设定的“判定时长”如30秒内一直保持在触发阈值以内则判定为一次有效使用计数器加1并进入“使用中状态”。如果在判定时长内距离又回到了阈值以上猫走了则重置计时器回到“空闲状态”。使用中状态确认猫咪正在使用。此时不再重复计数。持续监测距离直到距离恢复到阈值以上猫离开并保持一段时间如5秒稳定才切换回“空闲状态”准备检测下一次。这个状态机有效地解决了“短暂干扰”和“单次使用内多次触发”的问题。4.2 Arduino代码核心片段解析这里给出最核心的逻辑判断部分代码省略了库初始化和显示代码。#include Wire.h #include VL53L0X.h #include Adafruit_SSD1306.h VL53L0X sensor; Adafruit_SSD1306 display(128, 64, Wire, -1); // 参数配置 const int TRIGGER_DISTANCE_MM 400; // 触发阈值单位毫米需根据实际安装调整 const unsigned long USE_DURATION_MS 30000; // 判定为一次使用的最短持续时长30秒 const unsigned long DEBOUNCE_MS 5000; // 离开后防抖延时5秒 // 状态变量 enum State { IDLE, PENDING, IN_USE }; State currentState IDLE; unsigned long stateStartTime 0; int usageCount 0; // 引脚定义 const int LED_R 9; const int LED_G 10; const int LED_B 11; void setup() { Serial.begin(115200); Wire.begin(); sensor.init(); sensor.startContinuous(); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.clearDisplay(); // 初始化RGB LED引脚为输出 pinMode(LED_R, OUTPUT); pinMode(LED_G, OUTPUT); pinMode(LED_B, OUTPUT); setLEDColor(0, 255, 0); // 初始绿色 } void loop() { int distance sensor.readRangeContinuousMillimeters(); // 简单的错误过滤VL53L0X在超出量程或出错时会返回很大的值 if (distance 8000) distance TRIGGER_DISTANCE_MM 100; unsigned long currentTime millis(); switch (currentState) { case IDLE: if (distance TRIGGER_DISTANCE_MM) { // 检测到有物体进入进入预触发状态 currentState PENDING; stateStartTime currentTime; Serial.println(State: PENDING (Cat可能进来了)); } break; case PENDING: if (distance TRIGGER_DISTANCE_MM) { // 在判定时间内物体离开了认为是干扰回到空闲 currentState IDLE; Serial.println(State: Back to IDLE (干扰排除)); } else if (currentTime - stateStartTime USE_DURATION_MS) { // 物体持续存在超过设定时间判定为一次有效使用 usageCount; currentState IN_USE; stateStartTime currentTime; // 重置计时器开始记录使用时长 Serial.print(Usage Count: ); Serial.println(usageCount); updateDisplay(); // 根据使用次数更新LED提示 updateLEDIndicator(); } break; case IN_USE: if (distance TRIGGER_DISTANCE_MM) { // 物体似乎离开了启动一个防抖延时 if (currentTime - stateStartTime DEBOUNCE_MS) { // 确认离开回到空闲状态 currentState IDLE; Serial.println(State: IDLE (Cat已离开)); } // 如果未达到防抖时间仍保持在IN_USE状态 } else { // 物体仍然在重置离开计时起点 stateStartTime currentTime; } break; } // 更新OLED显示 display.clearDisplay(); display.setTextSize(2); display.setTextColor(SSD1306_WHITE); display.setCursor(0,0); display.print(Count:); display.println(usageCount); display.setTextSize(1); display.print(Dist:); display.print(distance); display.println( mm); display.print(State:); display.println(stateToString(currentState)); display.display(); delay(200); // 主循环延时避免过于频繁读取传感器 } String stateToString(State s) { switch(s) { case IDLE: return IDLE; case PENDING: return PENDING; case IN_USE: return IN_USE; default: return UNKNOWN; } } void updateLEDIndicator() { if (usageCount 3) { setLEDColor(0, 255, 0); // 绿色使用很少 } else if (usageCount 6) { setLEDColor(255, 255, 0); // 黄色使用中等 } else { setLEDColor(255, 0, 0); // 红色使用频繁建议检查/清理 } } void setLEDColor(int r, int g, int b) { // 共阴LEDPWM值越高越亮 analogWrite(LED_R, 255 - r); analogWrite(LED_G, 255 - g); analogWrite(LED_B, 255 - b); }代码关键点解读readRangeContinuousMillimeters()这是VL53L0X库提供的连续测量模式比单次测量模式更稳定、快速。错误值过滤传感器在测距失败时会返回一个极大值如8190代码中将其视为“无物体”避免干扰状态判断。防抖机制在IN_USE状态中只有当物体离开并持续DEBOUNCE_MS时间后才确认离开。这防止了猫咪在掩埋过程中短暂起身导致的状态误切换。状态可视化通过串口打印状态转换这在调试阶段至关重要能帮你清晰地看到逻辑是否按预期运行。5. 指示策略与数据利用计数只是第一步如何将数据转化为对铲屎官有用的信息是这个项目的价值所在。5.1 多级视觉与听觉提示我设计了三级提示系统一级提示绿色当日使用次数 3。一切正常无需关注。二级提示黄色当日使用次数3 count 6。猫咪如厕频率正常或略高可能是饮水增多。OLED屏幕显示具体次数提醒主人留意。三级提示红色闪烁当日使用次数 6。这是一个需要警惕的信号。猫咪可能在24小时内频繁上厕所这可能与泌尿系统健康问题如膀胱炎、结石或消化问题有关。此时除了RGB LED变为红色并缓慢闪烁还可以考虑增加一个蜂鸣器短鸣一声避免持续噪音吓到猫强烈建议主人检查猫砂盆情况并观察猫咪状态。关于“每日清零”代码中需要在每天固定时间例如凌晨0点将usageCount重置为0。对于Arduino Nano需要外接一个DS3231这样的高精度实时时钟模块来获取准确日历时间。一个更简单的替代方案是使用ESP8266它可以通过网络同步时间NTP实现起来更方便也为后续联网功能打下基础。5.2 从“次数”到“猫砂状态”的进阶算法单纯的次数统计还不够智能。我们还可以结合“单次使用时长”和“历史数据”来更精准地判断猫砂状态。猫砂污染度模型假设每次如厕都会对固定区域的猫砂造成一定“污染值”。排尿和排便的“污染值”权重可以不同例如排尿权重为1.0排便权重为1.5。通过监测单次使用的时长IN_USE状态的持续时间可以做一个非常粗略的区分时间极短的可能是排尿时间较长的可能是排便。系统可以维护一个“总污染度”变量当该值超过某个阈值时触发“建议全部更换猫砂”的提示。猫砂余量估算这是一个更有挑战但有趣的功能。原理是猫咪每次掩埋排泄物都会带走或覆盖一部分猫砂。我们可以假设每次使用导致猫砂平面平均下降一个微小的高度Δh。通过VL53L0X持续监测D_litter砂面距离的变化趋势。如果发现D_litter在多次使用后持续、稳定地增大即砂面距离传感器越来越远就意味着猫砂在减少。当D_litter比初始值增大了某个阈值例如3cm就可以触发“建议添加猫砂”的提示。实现难点猫砂表面本身是不平整的每次猫咪刨动都会改变局部高度。因此需要采用滑动平均滤波。不是用单次测量值而是记录最近N次比如最近10次“盆内无猫”时的距离读数求其平均值作为当前的“有效砂面高度”。这样可以平滑掉单次刨砂造成的波动看到整体的下降趋势。// 简单的滑动平均滤波示例概念代码 const int SAMPLE_SIZE 10; int distanceSamples[SAMPLE_SIZE]; int sampleIndex 0; int getFilteredDistance(int currentRawDistance) { distanceSamples[sampleIndex] currentRawDistance; sampleIndex (sampleIndex 1) % SAMPLE_SIZE; long sum 0; for (int i 0; i SAMPLE_SIZE; i) { sum distanceSamples[i]; } return sum / SAMPLE_SIZE; } // 在IDLE状态时调用此函数获取滤波后的砂面距离用于余量判断。6. 实地部署、校准与长期维护硬件和代码完成后真正的考验在于部署到真实的、充满变量的猫咪生活环境中。6.1 部署流程与初始校准安全第一确保所有线路绝缘良好固定牢固避免被猫咪啃咬或拉扯。可以将整个电路板装入一个带散热孔的小塑料盒中。上电观察在无猫状态下让系统运行几个小时。通过串口监视器观察D_litter的读数是否稳定。正常情况应在小范围内波动±5mm。如果跳动剧烈检查电源稳定性或传感器固定是否松动。触发阈值微调让家人或朋友模拟猫咪以不同速度经过、靠近猫砂盆。观察串口日志确保不会轻易触发PENDING状态。同时亲自进入猫砂盆是的你需要亲自上阵蹲下保持30秒以上确保能稳定触发计数。根据测试结果微调TRIGGER_DISTANCE_MM和USE_DURATION_MS这两个参数。基线距离确认在猫砂平整且干燥时记录下稳定的D_litter值作为“满砂”基线。这个值会随着猫砂消耗而缓慢变化。6.2 常见问题排查与优化问题计数器偶尔跳数多计。排查检查串口日志看是否在短时间内有多次IDLE - PENDING - IDLE的快速切换。这通常是环境干扰如飘过的塑料袋、强烈的光影变化对早期版本传感器的干扰或传感器抖动造成的。解决软件滤波在读取距离值后增加一个软件滤波。除了之前的滑动平均还可以采用“中值滤波”即连续采样5次去掉最大最小值取中间3次的平均值。这能有效抵抗突发毛刺。优化状态机增加PENDING状态进入的“门槛”。例如要求连续2次检测间隔100ms距离都小于阈值才进入PENDING可以过滤掉瞬间的干扰。物理加固确保传感器模块本身固定绝对牢固任何微小的晃动都会导致测距值跳动。问题猫咪使用但未计数漏计。排查观察猫咪如厕全过程。是否因为猫咪体型较小或姿势特殊紧贴盆边导致其身体最高点未能持续低于触发阈值解决适当调低TRIGGER_DISTANCE_MM或者略微降低传感器的安装高度。确保测量光束能覆盖猫咪的常见活动区域。问题OLED显示乱码或闪烁。排查通常是电源问题或I2C通信受干扰。解决为OLED模块单独并联一个100uF的电解电容提供瞬间大电流。检查I2C总线上的上拉电阻。Arduino的A4、A5内部有弱上拉但长导线时可能不够。可以在SDA和SCL线上各外接一个4.7kΩ电阻上拉到5V。降低I2C通信速率。在Wire.begin()后尝试Wire.setClock(100000);设置为标准100kHz模式提高稳定性。6.3 长期维护与数据记录定期清洁传感器每隔一两周用干燥的软布轻轻擦拭VL53L0X传感器的玻璃窗口防止积灰影响测距精度。猫砂类型的影响不同猫砂矿砂、豆腐砂、膨润土砂的颗粒大小、反光特性不同但对激光测距的影响微乎其微主要影响的是基于重量的方案。本项目方案基本不受猫砂类型影响。数据持久化使用EEPROM存储usageCount。这样即使意外断电数据也不会丢失。在每次更新计数后将数据写入EEPROM。#include EEPROM.h void saveCount() { EEPROM.put(0, usageCount); // 将计数存入EEPROM地址0 } void loadCount() { EEPROM.get(0, usageCount); }进阶联网与可视化如果将主控换为ESP8266则可以轻松接入家庭Wi-Fi。你可以将每日的使用次数、单次时长等信息上报到私有的服务器如用Node-RED搭建、或物联网平台如ThingsBoard甚至简单地发送到Telegram Bot给自己推消息。这样就可以在手机上随时查看历史曲线远程了解猫咪的如厕健康情况。这个“猫砂使用计数器与指示器”项目从构思到实现花了我几个周末的时间。它不完美但足够可靠和实用。最关键的是它让我从日复一日的“猜测”中解放出来用数据来了解猫咪的习惯。当那个RGB灯从绿色变成黄色时我知道该去铲屎了如果它罕见地变成了红色我会格外留意猫咪那几天的状态。这种用技术解决细小生活痛点的过程以及最终带来的确定性和安心感正是DIY最大的乐趣所在。如果你也受类似问题困扰不妨动手试一试可以根据你家猫砂盆的具体形状和猫咪的习惯对安装方式和判断阈值进行微调打造一个专属的智能养猫小助手。
返回列表