基于ESP32与ADS1115的智能抗干扰测量系统设计与实现

发布时间:2026/7/29 4:50:03

基于ESP32与ADS1115的智能抗干扰测量系统设计与实现 1. 项目缘起从“复古机器人”到“免单扰神器”的DIY心路最近在整理工作室的旧物翻出来一堆学生时代玩过的老式机器人套件和几块吃灰的万用表。看着这些“老伙计”一个念头突然冒出来能不能把这些看似过时的东西结合现在流行的开源硬件和软件捣鼓出点既实用又有趣的新玩意儿比如一个能自动检测电路参数、还能帮你“免单扰”的桌面小助手这个想法听起来有点天马行空但恰恰是这种跨界组合往往能碰撞出意想不到的火花。我所说的“复古机器人”核心可能是一块老旧的Arduino主板、几个步履蹒跚的伺服电机或者是一个基于BASIC Stamp的控制器。它们代表了上一个时代的嵌入式启蒙。而“多功能开源数字万用表”则指向了像“OpenDMM”或基于ESP32/STM32的开源万用表项目这类项目将精密的测量功能与可编程性结合打破了传统仪器的黑盒。至于“免单扰神器”这更像是一个功能愿景——如何让一个设备智能地识别并规避环境中的电磁干扰或者在复杂的电路调试中自动过滤掉无关的噪声信号让测量结果更“干净”。所以这个项目的核心就是一次软硬件结合的再创造赋予一个复古的机器人平台以现代化的“感官”开源万用表模块和“大脑”新的控制逻辑让它能执行一些自动化的检测任务并在过程中实现一定程度的智能抗干扰。这不仅仅是怀旧更是对现有技术模块的创造性复用非常适合有一定电子基础和编程兴趣的爱好者来尝试。无论你是想重温经典机器人编程还是深入理解数据采集与信号处理这个项目都能提供一条有趣的实践路径。2. 核心模块选型与系统架构设计要实现上述构想我们首先需要拆解系统进行合理的模块选型与架构设计。整个系统可以看作一个典型的“感知-决策-执行”闭环但每个环节我们都将采用一种混合新旧技术的思路。2.1 “复古机器人”本体的现代化改造所谓的“复古机器人”平台我们假设其主体是一个基于ATmega328P类似Arduino Uno或更早的8051核心的移动平台。它的优势是结构简单、皮实并且通常留有丰富的扩展接口如GPIO、PWM。我们的改造重点不是替换其主控为了保留复古感而是为其增加一个“现代协处理器”。主控选择保留原有机器人主控负责最底层的电机驱动、电源管理和基本传感器如碰撞开关的读取。它的程序固件通常用C或汇编写成我们将尽量维持其原有逻辑。协处理器引入新增一块ESP32开发板作为系统的“大脑”。选择ESP32的原因在于其强大的处理能力、双核结构、丰富的通信接口Wi-Fi/蓝牙以及极低的功耗。ESP32将通过UART或I2C与复古机器人的主控进行通信向其发送移动指令并接收其状态反馈。这样我们既保留了复古硬件的“躯干”又为其注入了智能的“灵魂”。2.2 “开源数字万用表”模块的集成这是项目的“感官”部分。我们不需要从头设计一个万用表而是集成一个成熟的开源模块。模块选型我强烈推荐使用ADS1115这款16位高精度ADC模数转换器模块或者功能更全面的OpenDMM项目的核心测量板。ADS1115价格低廉、精度高能分辨到0.125mV通过I2C与ESP32通信非常适合用来测量电压、电流需搭配分流器和电阻需搭配基准电压源。如果追求更多功能可以寻找集成了RMS计算、电容测量等功能的开源万用表模组。接口与扩展将ADS1115的输入通道通过精密的香蕉插座或测试钩引出来作为机器人的“探针”。同时可以在ESP32上编程实现自动量程切换、数据滤波如移动平均滤波等传统万用表的功能。测量结果通过ESP32处理后既可以显示在本地加装的小OLED屏幕上也可以通过Wi-Fi发送到电脑或手机端进行记录和分析。2.3 实现“免单扰”功能的软件策略“免单扰”是项目的精髓其本质是信号处理与噪声抑制。在电子测量中干扰可能来自电源纹波、空间电磁波甚至机器人自身电机产生的噪声。硬件基础抗干扰电源隔离为ADS1115测量模块和ESP32使用独立的线性稳压电源如LM7805与机器人电机的驱动电源通常是电池直接供电进行隔离避免电机启停时的大电流波动影响测量精度。信号走线测量用的信号线从探针到ADS1115尽量使用双绞线或屏蔽线并远离电机驱动线和电源线。滤波电路在ADS1115的输入前端可以增加简单的RC低通滤波电路滤除高频噪声。软件算法抗干扰过采样与均值滤波这是最直接有效的方法。让ADS1115以远高于信号频率的速率进行采样过采样然后对一组采样值取算术平均可以显著抑制随机白噪声。例如对于变化缓慢的直流电压可以每秒采样100次然后取这100个值的平均值作为一个输出点。中值滤波对于信号中偶尔出现的、幅度很大的脉冲干扰尖峰噪声算术平均会失效。这时可以采用中值滤波即对一组采样值排序后取中间值。这种方法能有效剔除异常值。数字陷波滤波器如果知道干扰噪声的特定频率例如50Hz的工频干扰可以在ESP32中实现一个数字陷波滤波器Notch Filter在算法层面将该频率成分的能量大幅衰减。这需要一定的数字信号处理知识但对于ESP32的双核来说实现一个固定频率的陷波器计算量并不大。我将“免单扰”的逻辑集成在ESP32的固件中。在每次测量指令下达后固件会自动启动一个包含过采样、均值和中值滤波的综合处理流程最后输出一个“干净”的测量值。这个过程对用户是透明的他们得到的就是已经过初步抗干扰处理的数据。2.4 系统整体架构与数据流最终的硬件架构如下图所示文字描述[环境信号] - (测试探针) - [RC硬件滤波] - [ADS1115 ADC模块] --I2C-- [ESP32 协处理器] | |-- UART/I2C -- [复古机器人主控] -- [电机/执行器] | |-- Wi-Fi -- [PC/手机终端] | |-- OLED 屏幕 (本地显示)ESP32是整个系统的枢纽。它负责从PC终端或手机APP接收高级指令如“移动到A点测量对地电阻”。将移动指令翻译成复古机器人主控能理解的简单协议并通过UART发送。控制ADS1115进行测量并执行软件抗干扰算法。将处理后的数据本地显示并上传。根据测量结果做出简单决策如“电压超标触发声光报警”。3. 复古机器人主控与ESP32的通信协议实现这是新旧系统融合的关键一步。我们需要在复古机器人的老旧主控假设为Arduino Uno和新的ESP32之间建立一套稳定、简单的通信协议。3.1 协议设计原则由于复古主控性能有限协议必须足够轻量。采用ASCII字符协议而非二进制协议便于在开发阶段通过串口调试助手直接观察和测试降低调试难度。命令-响应模式ESP32作为主机Master发送命令帧复古主控作为从机Slave回复响应帧。包含校验和确保数据传输的可靠性最简单的可以用字节和取低8位。3.2 具体协议定义示例我们定义几个基本命令移动控制命令格式MOVE,[方向],[速度],[时间]\n示例MOVE,F,150,2000\n表示以速度值150向前移动2000毫秒。方向F(前),B(后),L(左转),R(右转)速度0-255的整数值对应PWM占空比。时间毫秒数。状态查询命令格式STATUS?\n响应STATUS,[电池电压],[左电机电流],[右电机电流],[碰撞传感器状态]\n示例STATUS,780,120,115,000\n表示电池电压7.80V左右电机电流120mA和115mA三个碰撞传感器均未触发。通用配置命令格式CONFIG,[参数名],[值]\n示例CONFIG,MAX_SPEED,200\n设置最大速度限制为200。校验和计算在实际项目中为了更可靠可以在命令末尾增加一个校验和。例如对于命令MOVE,F,150,2000将每个字符的ASCII码相加77798669...484848某个总和然后取这个总和的低8位转换为两个十六进制字符追加到命令末尾。命令变为MOVE,F,150,2000,CHECKSUM\n。从机收到后重新计算校验和进行比对。3.3 Arduino端复古主控代码实现要点// Arduino 端伪代码 String inputString ; bool stringComplete false; void setup() { Serial.begin(9600); // 与ESP32通信的串口 inputString.reserve(64); // 初始化电机驱动引脚等 } void loop() { // 解析来自ESP32的命令 if (stringComplete) { parseCommand(inputString); inputString ; stringComplete false; } // 其他机器人控制逻辑... } void serialEvent() { while (Serial.available()) { char inChar (char)Serial.read(); if (inChar \n) { stringComplete true; } else { inputString inChar; } } } void parseCommand(String cmd) { // 简单的字符串分割解析 if (cmd.startsWith(MOVE,)) { // 解析方向、速度、时间 // 执行移动... Serial.println(OK,MOVE); // 响应ESP32 } else if (cmd.startsWith(STATUS?)) { int volt readBatteryVoltage(); // ...读取其他状态 Serial.print(STATUS,); Serial.print(volt); // ...输出其他状态 Serial.println(); } // 其他命令解析... }3.4 ESP32端代码实现要点// ESP32 端伪代码 (Arduino框架) HardwareSerial RobotSerial(1); // 使用UART1与机器人通信 void setup() { Serial.begin(115200); // 用于调试 RobotSerial.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN); // 连接复古机器人 // 初始化ADS1115、Wi-Fi等 } void sendRobotCommand(String cmd) { RobotSerial.print(cmd); RobotSerial.print(\n); // 可以添加重发和超时机制 } String readRobotResponse(int timeout 500) { unsigned long start millis(); String response ; while (millis() - start timeout) { if (RobotSerial.available()) { char c RobotSerial.read(); if (c \n) { return response; } response c; } } return TIMEOUT; // 超时 } void controlLoop() { // 例如收到上位机指令后 sendRobotCommand(MOVE,F,150,2000); delay(50); // 等待一下 String resp readRobotResponse(); if (resp.startsWith(OK)) { // 命令执行成功接下来可以发测量指令 float voltage measureVoltage(); // 调用ADS1115测量函数 // ... 处理并上传数据 } else { // 处理错误或重试 Serial.println(Robot command failed: resp); } }注意串口通信最怕数据帧不完整或粘包。务必确保每条命令以明确的结束符如\n结尾并且在解析端等待到结束符才开始处理。对于关键指令实现“发送-确认”机制是保证系统可靠性的基础。4. 基于ADS1115的高精度测量与软件抗干扰实现有了通信基础我们就可以聚焦于核心的测量功能。这里以ADS1115为例详细讲解如何驱动它并获得稳定的读数。4.1 ADS1115基础配置与驱动ADS1115是一个I2C器件有4个单端或2个差分输入通道。我们需要为其配置几个关键参数数据速率采样速度从8SPS到860SPS可选。对于缓慢变化的直流信号128SPS或250SPS是不错的选择在速度和噪声之间取得平衡。增益放大器测量范围从±6.144V到±0.256V。要根据待测信号电压合理选择以获得最佳分辨率。例如测5V信号选±6.144V档测一个0.5V的传感器信号选±0.512V档更合适。工作模式单次转换模式省电或连续转换模式。我们通常使用单次模式由ESP32主动触发一次测量。使用Adafruit的ADS1X15库可以简化操作#include Adafruit_ADS1X15.h Adafruit_ADS1115 ads; // 使用ADS1115 void setupADS1115() { if (!ads.begin(0x48)) { // I2C地址默认为0x48 Serial.println(Failed to initialize ADS1115!); while (1); } // 设置增益为±6.144V (1 bit 0.1875mV) ads.setGain(GAIN_TWOTHIRDS); // 设置数据速率为128SPS相对较低的噪声 // 注意Adafruit库默认速率可能已是128SPS如需更改需修改底层配置或换用其他库 } float readVoltageSingleEnded(int channel) { int16_t adc_raw ads.readADC_SingleEnded(channel); // 将原始值转换为电压 float voltage ads.computeVolts(adc_raw); return voltage; }4.2 软件抗干扰算法的具体实现仅仅读取一次ADC值是远远不够的噪声会使得该值跳动。我们需要在readVoltageSingleEnded函数中集成抗干扰算法。#define SAMPLE_NUM 100 // 过采样次数 #define MEDIAN_FILTER_SIZE 5 // 中值滤波器窗口大小 float readVoltageSingleEndedWithFilter(int channel) { float samples[SAMPLE_NUM]; float sorted[MEDIAN_FILTER_SIZE]; // 1. 过采样 for (int i 0; i SAMPLE_NUM; i) { samples[i] ads.readADC_SingleEnded(channel) * 0.1875F / 1000.0F; // 直接计算电压(mV转V) // 可以加入微小延时避免ADS1115内部转换未完成但单次模式本身会等待 // delayMicroseconds(10); } // 2. 先进行均值滤波得到一组“平滑”值 float mean_filtered[SAMPLE_NUM / MEDIAN_FILTER_SIZE]; // 假设每5个点均值后得到一个点 int mean_index 0; for (int i 0; i SAMPLE_NUM; i MEDIAN_FILTER_SIZE) { float sum 0; for (int j 0; j MEDIAN_FILTER_SIZE (ij) SAMPLE_NUM; j) { sum samples[i j]; } mean_filtered[mean_index] sum / MEDIAN_FILTER_SIZE; } // 3. 对均值滤波后的结果进行中值滤波剔除可能的脉冲干扰 // 这里我们简单地对 mean_filtered 数组的最后 MEDIAN_FILTER_SIZE 个值进行中值滤波 int valid_mean_samples mean_index; if (valid_mean_samples MEDIAN_FILTER_SIZE) { // 取最后几个均值点进行中值滤波 for (int i 0; i MEDIAN_FILTER_SIZE; i) { sorted[i] mean_filtered[valid_mean_samples - MEDIAN_FILTER_SIZE i]; } // 简单的冒泡排序找中值 for (int i 0; i MEDIAN_FILTER_SIZE - 1; i) { for (int j i 1; j MEDIAN_FILTER_SIZE; j) { if (sorted[i] sorted[j]) { float temp sorted[i]; sorted[i] sorted[j]; sorted[j] temp; } } } float final_voltage sorted[MEDIAN_FILTER_SIZE / 2]; // 取中值 return final_voltage; } else { // 如果数据不够返回最后一个均值 return mean_filtered[valid_mean_samples - 1]; } }这个函数实现了“过采样 - 均值滤波 - 中值滤波”的三级处理。SAMPLE_NUM越大抑制随机噪声的效果越好但测量耗时也越长。MEDIAN_FILTER_SIZE选择奇数通常3或5即可。对于50Hz工频干扰如果采样速率设置得当是其整数倍过采样加均值滤波本身就能起到很好的抑制作用。如果需要更彻底的抑制可以额外实现一个数字陷波滤波器但这需要更深入的信号处理知识。实操心得抗干扰算法的参数如采样次数、滤波窗口大小需要在实际环境中调试。将一个稳定的电压源如锂电池接入观察算法输出值的波动范围。调整参数在“响应速度”和“读数稳定性”之间找到一个平衡点。有时单纯增加采样次数带来的提升会边际递减而合理的硬件滤波前面提到的RC电路能从根本上减轻软件的压力。5. 系统集成与任务自动化编程当通信和测量模块都准备好后我们就可以编写ESP32的主控逻辑让整个系统协同工作完成一些自动化任务。5.1 任务示例自动巡线测量电路板节点电压假设我们的复古机器人是轮式的并且我们在地面上贴了一条简单的黑色电工胶带作为引导线。机器人底部装有红外巡线传感器。我们的任务是让机器人沿着线走并在特定的“测量点”我们用胶带贴了一个“X”标记停下来伸出机械臂或直接利用车体上的探针接触电路板上的两个测试点测量电压然后继续前进。ESP32主程序逻辑框架// 定义系统状态 enum SystemState { STATE_IDLE, STATE_FOLLOW_LINE, STATE_APPROACH_MARK, STATE_POSITION_FINE_TUNE, STATE_MEASURE, STATE_REPORT, STATE_ERROR }; SystemState currentState STATE_IDLE; int measurementPoints[][2] {{100, 200}, {300, 400}}; // 假设的测量点坐标基于巡线距离 int currentPointIndex 0; float measuredVoltage 0.0; void taskFollowLineAndMeasure() { switch (currentState) { case STATE_IDLE: if (startCommandReceived) { currentState STATE_FOLLOW_LINE; sendRobotCommand(MOVE,F,100,0); // 速度100时间0表示持续运行由传感器中断控制停止 } break; case STATE_FOLLOW_LINE: // 读取巡线传感器通过串口发送微调指令给机器人主控 // 例如if(lineSensorLeft) sendRobotCommand(MOVE,L,80,100); // if(lineSensorRight) sendRobotCommand(MOVE,R,80,100); // 检查是否到达标记点例如通过额外的地面传感器识别“X” if (markSensorDetected()) { sendRobotCommand(STOP); // 自定义的停止命令 currentState STATE_APPROACH_MARK; } break; case STATE_APPROACH_MARK: // 可能需要进行精确对准。这里发送指令让机器人缓慢前进一小段确保探针在测试点正上方 sendRobotCommand(MOVE,F,30,500); delay(600); // 等待移动完成 currentState STATE_POSITION_FINE_TUNE; break; case STATE_POSITION_FINE_TUNE: // 可以结合摄像头或更精密的传感器进行最终定位本项目简化 // 定位完成后触发测量 currentState STATE_MEASURE; break; case STATE_MEASURE: measuredVoltage readVoltageSingleEndedWithFilter(0); // 测量通道0 Serial.printf([Point %d] Voltage: %.3f V\n, currentPointIndex, measuredVoltage); // 通过Wi-Fi上传数据 uploadDataToServer(currentPointIndex, measuredVoltage); currentState STATE_REPORT; break; case STATE_REPORT: // 检查是否所有点测量完毕 currentPointIndex; if (currentPointIndex sizeof(measurementPoints)/sizeof(measurementPoints[0])) { currentState STATE_IDLE; Serial.println(All measurements completed.); } else { // 继续前往下一个点 currentState STATE_FOLLOW_LINE; sendRobotCommand(MOVE,F,100,0); } break; case STATE_ERROR: // 处理错误如机器人卡住、测量超限等 sendRobotCommand(STOP); blinkErrorLED(); break; } } void loop() { taskFollowLineAndMeasure(); // 处理Wi-Fi连接、接收上位机指令等其他任务 delay(50); // 主循环延迟避免过于频繁的状态切换 }5.2 无线数据传输与上位机界面为了让项目更有实用性我们可以添加Wi-Fi数据传输和简单的上位机界面。ESP32作为Web服务器使用ESP32的Arduino库可以轻松创建一个简单的Web服务器。当ESP32连接到本地Wi-Fi后我们可以在电脑浏览器输入其IP地址访问一个控制页面。#include WiFi.h #include WebServer.h WebServer server(80); void handleRoot() { String html htmlbody; html h1Retro Robot DMM Controller/h1; html pCurrent Voltage: span idvoltage--/span V/p; html button onclick\fetch(/measure)\Take Measurement/button; html button onclick\fetch(/start)\Start Auto Run/button; html scriptsetInterval(()fetch(/data).then(rr.text()).then(tdocument.getElementById(voltage).innerTextt), 1000)/script; html /body/html; server.send(200, text/html, html); } void handleData() { server.send(200, text/plain, String(measuredVoltage)); } void handleMeasure() { measuredVoltage readVoltageSingleEndedWithFilter(0); server.send(200, text/plain, Measured: String(measuredVoltage)); } void setupWiFi() { WiFi.begin(Your_SSID, Your_PASSWORD); while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.print(.); } Serial.println(\nConnected! IP: WiFi.localIP().toString()); server.on(/, handleRoot); server.on(/data, handleData); server.on(/measure, handleMeasure); server.begin(); } void loop() { server.handleClient(); // 处理Web客户端请求 // ... 原有的主循环逻辑 }这样我们就实现了一个可以通过网页远程触发测量、查看实时数据的基本控制系统。数据也可以定期上传到物联网平台如ThingsBoard、Blynk进行记录和可视化。6. 调试、优化与项目演进方向将这么多模块整合在一起调试是不可避免的“大坑”。分享几个我踩过的坑和优化思路。6.1 多任务与实时性处理ESP32虽然性能强大但在同时处理Wi-Fi、Web服务器、串口通信、ADC读取和复杂算法时如果使用delay()函数很容易导致系统响应迟钝甚至看门狗复位。解决方案使用非阻塞式编程。将所有的定时、状态切换都基于millis()函数来实现。unsigned long previousMeasureTime 0; const long measureInterval 1000; // 测量间隔1秒 void loop() { unsigned long currentMillis millis(); // 定时测量任务 if (currentMillis - previousMeasureTime measureInterval) { previousMeasureTime currentMillis; performMeasurement(); // 这是一个需要快速执行完的函数 } // 状态机任务本身已是非阻塞的 taskFollowLineAndMeasure(); // 处理网络请求WebServer.handleClient() 是非阻塞的 server.handleClient(); // 可以短暂延时释放CPU delay(1); }利用ESP32的双核可以将Web服务器和Wi-Fi处理放在一个核心Arduino框架默认运行在Core1而将实时性要求高的传感器读取、状态机控制放在另一个核心Core0。这需要使用xTaskCreatePinnedToCore创建FreeRTOS任务。6.2 电源管理与噪声抑制的再优化系统运行后可能会发现当机器人电机启动时ADS1115的读数会有毛刺。深入排查用另一个万用表或示波器观察给ADS1115供电的5V电源线。在电机启动瞬间是否看到电压跌落如果跌落超过ADS1115的容忍范围就会导致读数错误甚至复位。优化方案使用大电容在ADS1115的电源入口处并联一个100uF的电解电容和一个0.1uF的陶瓷电容用于缓冲低频和高频的电源波动。彻底隔离为测量系统ESP32、ADS1115使用独立的电池组与电机驱动电源完全物理隔离。这是最有效但增加复杂度和成本的方法。软件容错在测量函数中增加判断如果连续多次读数差异巨大则丢弃该次测量并重试或报告错误。6.3 项目可能的演进方向这个项目像一个乐高底座有巨大的扩展潜力增加测量功能通过继电器切换不同的前端电路可以让ADS1115测量电流、电阻甚至电容。配合一些模拟开关芯片可以实现自动量程切换。升级“机器人”平台将复古轮式机器人换成二自由度或三自由度的机械臂就可以实现更精确的“点测”功能直接对电路板上的特定元件进行测量。引入机器视觉给ESP32加上一个摄像头模块如OV2640结合OpenCV的嵌入式版本如ESP32-CAM让机器人能够识别电路板上的测试点位置实现全自动定位测量。数据记录与分析将测量数据不仅上传到Web界面还保存到SD卡或发送到云端数据库如InfluxDB结合Grafana制作长期的趋势图表用于电路老化监测或生产测试统计。这个从“复古机器人”和“开源万用表”想法开始的DIY项目最终呈现出的是一个融合了嵌入式系统、传感器技术、信号处理、自动控制和物联网的综合性实践平台。它最有价值的部分不在于某个模块有多先进而在于如何用有限的资源和新旧结合的思路去解决一个实际的问题。每一次调试排错每一次算法的微调都是对底层原理更深的理解。希望这个详细的拆解能为你启动自己的“跨界”创造项目提供一份扎实的路线图。

相关新闻