Skip to content
←返回博客
机器如何感知世界:一份可落地的 ESP32 传感器指南
ESP32Embedded Systems传感器

机器如何感知世界:一份可落地的 ESP32 传感器指南

从光敏电阻黄昏灯到多传感器避障小车,用同一条“读数—滤波—判断—动作”闭环讲清 ESP32-S3 的 GPIO、ADC、I2C、SPI、迟滞、持续确认与失效安全。

ard (@ardchain)2026年8月30日4 分钟阅读31次阅读
EN

机器不需要摄像头,也能理解物理世界里足够多的信息。它可以察觉天色变暗,发现前方有障碍,感知一次碰撞,或在房间出现变化时被唤醒。真正困难的不是买一颗传感器,而是把带噪声的电信号变成可靠决策,并保证读数出错时设备仍处于安全状态。

一台低成本无人机可以只用测距传感器避开墙壁,不必搭载摄像头,不过仅靠测距并不足以支撑安全的自主飞行。轮式小车可以意识到自己撞到了东西;桌面设备可以在有人经过时醒来;一块小板子还能感知热、振动、声音、光照、倾斜和距离。

这些系统都从同一条链路开始:物理变化变成电信号,程序清洗并解释信号,最后用输出改变现实世界。

物理世界 -> 传感器读数 -> 滤波 -> 决策 -> 动作

ESP32 让这条闭环便宜到可以在桌面上搭出来。它真正值得学习的能力,不是记住一排开发板型号,而是判断哪个物理量有信息价值,怎样安全接线,什么条件才算一次事件,以及读数不可信时如何让设备退回无害状态。

下面从一颗光敏电阻开始,一路走到一台多传感器小车。所有项目都沿用同一套架构。换传感器只是改变输入,不需要把整个工程方法重新学一遍。

先确定一块板和一个问题

本文的完整入门实验使用 ESP32-S3-DevKitC-1、面包板、跳线、一颗光敏电阻(LDR)、一颗 10 kΩ 电阻、一颗 LED,以及一颗用于限流的 220-330 Ω 电阻。DevKitC-1 的 GPIO4 可作为 ADC 输入,GPIO5 可作普通数字输出,两个引脚都在带丝印的排针上,便于逐项检查。接线时应以开发板官方文档为准:ESP32-S3-DevKitC-1 参考文档。

六类物理量与传感器及对应动作的示意图:距离用于停止,热量用于唤醒,运动用于稳定,触摸用于切换模式,声音用于告警,光照或图像用于选择动作。
每颗传感器只提供一条线索。先定义设备要做什么,再选择硬件,项目才会保持在可测试的范围内。

这些原则适用于整个 ESP32 家族,但不同芯片和开发板的存储容量、内置触摸能力、ADC 特性与排针布局并不相同。本文的具体引脚只适用于点名的 DevKitC-1。换成其他开发板时,保留整体架构,再根据该板的官方引脚图重新接线。

传感器只给机器一条线索,而不是完整答案

距离传感器可以告诉你“有东西靠近”,却不能判断那是椅子、手还是墙。加速度计可以给出加速度和角速度,却无法自己得出“机器人正在摔倒”;程序必须先定义什么样的变化序列算摔倒。

这种局限反而是好事。它让第一版原型足够小,也足够容易验证。应该从要做的判断反推传感器,而不是先买了功能最花哨的模块,再替它寻找用途。

设备需要察觉什么 可以优先考虑的传感器 第一版可以做什么
变暗或强光光敏电阻或数字光照传感器点亮低功率 LED 或状态指示
物体正在靠近ToF 或超声波测距传感器让电机减速或停止
附近有人运动或较热物体PIR 或热传感器唤醒屏幕或显示本地提示
倾斜、晃动或疑似跌倒惯性测量单元(IMU)切换模式或记录事件
触摸或按压按键、电容触摸板或压力传感器启动或停止一段流程
持续的大声事件麦克风记录或提示一次声音事件
视觉图案摄像头拍照或运行受支持的视觉任务

下单前先回答三个问题

1. 哪个物理变化真正重要?

“我想做一个智能房间”范围太大,无法直接搭建和验收。“桌面变暗时让台灯亮起来”是一个可执行的问题。“小车在碰到椅腿前停下”同样具体。

把事件写成一句话:

当[可测量的变化]持续[足够长时间]时,
设备应该执行[安全动作]。

补上条件后,缺失的信息会立刻显现:

当障碍物进入 250 mm 范围并持续 300 ms 时,小车停止。
当桌面持续变暗两分钟时,台灯打开。
当 IMU 检测到强烈冲击后转为静止时,记录一次疑似跌倒。

持续时间不能省略,因为真实信号一直在波动。单次读数不应直接控制电机、继电器、门锁或报警器。事件条件和持续时间必须一起定义。

2. 设备需要多快响应?

室温每隔几秒采样一次就够了,平衡机器人却需要快得多的控制环。响应速度会影响传感器选择、程序结构、功耗预算,甚至决定 ESP32 是否适合这个任务。

从“仍能让交互感觉自然”的最低采样率开始。高速连续采样是应用证明需要之后才增加的优化,不是工程水平的勋章。

3. 读数出错时会发生什么?

任何传感器都可能被遮挡、标定错误、安装位置不当,或被环境干扰。设计时必须决定:读数不可信时,设备应该处于什么状态。

第一版原型遇到不确定情况,应进入无害结果:显示指示灯、记录错误、暂停运动,或者再采一组样本。不要让原型去开锁、切换市电,或驱动可能伤人的执行器。

传感器和 ESP32 说话的四种常见方式

模块接口决定了开发板怎样接收信号。第一个项目不需要同时掌握所有协议。

接口 传输什么 适合的入门器件 需要留意什么
GPIO / 数字量 高电平或低电平状态 按键、基础人体感应模块 消抖、上拉配置、误触发
ADC / 模拟量 随物理量变化的电压,并转换为数字读数 光敏电阻分压、模拟压力传感器 标定、电噪声、有效输入范围
I2C 多个带地址设备共享两根线传输数据 距离、温度、IMU 模块 地址冲突、上拉电阻、线长
SPI 通过多根信号线进行更高速的数据传输 显示屏、部分 IMU、高速转换器 片选接线、开发板专用引脚
六种物理感知路径示意图:距离停止、热量唤醒、运动稳定、触摸切换、声音告警、光照或图像选择动作。
信号可以只是一个数字电平,也可以是持续变化的 ADC 值,或者由 I2C、SPI 传来的结构化传感器数据。

接线之前必须确认:ESP32 的 GPIO 和 ADC 引脚使用 3.3 V 逻辑。不要把 5 V 信号送进 GPIO 或 ADC。即使模块看起来相似,不同模块的供电和逻辑电平也可能不同,必须读取手中那块板的具体标注与文档。所有相连模块都要共地。初学者项目应把市电灯具、继电器模块、大电机和水泵排除在外。

模拟输入本质上是从电压转换出的数字。ESP32 的 ADC 会把电压量化,但原始值可能随芯片不同而变化,也会受到噪声影响。乐鑫在文档中介绍了 ADC 标定以及多次采样等降噪方法。原始数字只是起点,不能不加验证地当成精确物理量。可参阅 ESP-IDF ADC 概览与ADC 标定指南。

I2C 很适合让多块智能模块共享一根数据线和一根时钟线。每个设备通常有独立地址。许多模块自带上拉电阻,多块并联后等效上拉可能过强。应先接一个模块,使用短线,再运行 I2C 扫描器确认地址,然后才扩展总线。协议细节和控制器配置见 ESP-IDF I2C 指南。

做一个完整闭环:黄昏灯指示器

第一个项目让 ESP32-S3 测量环境光,在房间变暗时点亮一颗低功率 LED。LED 用来展示完整决策链,不接触市电。整个实验只使用 USB 供电和 3.3 V 逻辑,不控制家用灯、继电器、电机或水泵。

所需器件

  • ESP32-S3-DevKitC-1 和一根可传数据的 USB 线;
  • 面包板与跳线;
  • 一颗光敏电阻(LDR);
  • 一颗 10 kΩ 电阻;
  • 一颗 LED;
  • 一颗 220-330 Ω 的 LED 限流电阻。

接线

连接点 接到哪里
3V3光敏电阻的一只引脚
光敏电阻的另一只引脚GPIO4 与 10 kΩ 电阻的一端
10 kΩ 电阻的另一端GND
GPIO5220-330 Ω 电阻,再连接 LED 长脚(阳极)
LED 短脚(阴极)GND

光敏电阻和固定电阻组成分压器。在这套接法下,光照越强,ADC 原始读数通常越大。实际范围取决于光敏电阻、固定电阻、开发板和房间,因此程序先打印读数,再让 LED 做判断。

在面包板上,把光敏电阻空着的一端、10 kΩ 电阻的一端和前往 GPIO4 的跳线插进同一行相连的孔位。这一行就是测量节点。

ESP32-S3 黄昏灯接线图:光敏电阻分压接 GPIO4,LED 经限流电阻接 GPIO5。
3.3 V 光敏分压器接 GPIO4;GPIO5 只通过串联电阻驱动低功率 LED。实际连接以接线表为准。

示意图解释的是信号路径,不是可直接照搬的面包板物理布局,也不能代替核对开发板丝印。修改接线前先断开 USB,再接回电源前检查光敏电阻与电阻的公共节点,以及两端接地。

上传程序

在 Arduino IDE 的 Boards Manager 中安装当前 esp32 平台,选择 ESP32S3 Dev Module,再选择开发板的 USB 端口。除非板卡文档另有要求,其余设置先保持默认。如果上传没有开始,按住 BOOT、启动上传,并在连接建立后松开。安装步骤见乐鑫维护的 Arduino-ESP32 安装指南。

const int LIGHT_PIN = 4;  // ESP32-S3-DevKitC-1 的 GPIO4 / ADC1_CH3
const int LED_PIN = 5;

const int DARK_ON = 1200;   // 测完真实环境后替换
const int LIGHT_OFF = 1600; // 更高阈值用于防止闪烁
bool indicatorOn = false;

int averageLightReading() {
  long total = 0;
  for (int i = 0; i < 10; i++) {
    total += analogRead(LIGHT_PIN);
    delay(10);
  }
  return total / 10;
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  int light = averageLightReading();
  Serial.println(light);

  if (!indicatorOn && light < DARK_ON) {
    indicatorOn = true;
  } else if (indicatorOn && light > LIGHT_OFF) {
    indicatorOn = false;
  }

  digitalWrite(LED_PIN, indicatorOn ? HIGH : LOW);
  delay(150);
}

以 115200 波特率打开串口监视器。遮住光敏电阻,记录一组读数;再移开遮挡,记录另一组。把 DARK_ON 放在两组范围之间,并让 LIGHT_OFF 略高于它。

例如遮住时读数约为 400-650,移开时约为 2400-3000,那么 DARK_ON = 1200、LIGHT_OFF = 1500 可以作为起点。必须在实际希望触发指示的光照条件下验证。两组读数最好只有很少重叠;如果重叠严重,应先调整光敏电阻位置或分压电阻,不能直接相信阈值。若遮住传感器后读数反而上升,应反转两个比较方向,而不是为原有接线另编一套解释。

串口没有输出时,检查所选端口与 USB 线是否支持数据传输。读数始终为零或固定不变时,重新检查光敏电阻、固定电阻和 GPIO4 的公共节点以及接地。LED 始终不亮时,检查极性并确认电阻确实串联在回路里。

把原始读数变成稳定事件

传感器项目最有价值的工作,发生在“读到数字”和“执行动作”之间。假设一台慢速室内小车把距离传感器装在前方。下面这条规则很容易失效:

if distance < 250 mm: stop

目标材质、入射角、量程、环境条件或传感器自身极限,都可能让单次读数出错。更可靠的第一版规则包含四个阶段:

连续读取多个样本
        |
        v
剔除不可能的读数
        |
        v
计算小窗口平均值或中位数
        |
        v
条件持续时间足够长后再动作

代码本身仍然可以很简单:

distanceMm = readDistanceMm(); // 由所用传感器库提供

if (distanceMm > 0 && distanceMm < 250) {
  closeReadings++;
} else {
  closeReadings = 0;
}

if (closeReadings >= 3) {       // 每次采样之间要有时间间隔
  stopMotors();
}

这里给出的是模式,不是可直接烧录的完整固件。正确的库、引脚、采样间隔、有效量程、电机速度和停止距离都由具体硬件决定。应在真实环境中测量小车制动距离,再给阈值留出余量。真正可迁移的经验是:用一小段读数历史做决定,而不是依赖某一个瞬间。

用迟滞避免输出反复抖动

如果自动灯在低于 300 时开启、高于 300 时关闭,那么围绕边界的一次微小波动就会让它反复亮灭。把两个状态切换点分开:

光照低于 280 -> 开灯
光照高于 340 -> 关灯
处于两者之间 -> 保持当前状态

这就是迟滞。它适用于距离、温度、触摸和声级等需要从噪声量中得到二元状态的场景。另一些任务更适合时间规则、状态机或自适应基线。不要先问“哪种滤波最先进”,而要先问“这个判断需要多少历史与环境信息”。

信号处理示意图:原始读数经过异常值剔除和平滑,再连续三次确认,最终生成 STOP 决策。
原始测量按顺序变成事件:剔除不可能值、平滑剩余样本、确认持续时间,然后动作。

双阈值和持续时间解决的是两个问题。迟滞防止稳定信号停在某个边界附近时输出来回切换;持续确认防止短暂尖峰直接触发动作。一个稳健的初版原型往往两个都需要。

一颗传感器负责反应,多颗传感器提供上下文

摄像头很强,但它不是让机器适应环境的唯一办法。小型机器人常把简单信号组合起来,因为每种信号可以覆盖另一路信号的不同失效模式。

考虑一台没有摄像头的慢速室内小车:

前方距离传感器 -> 判断障碍接近程度
左/右距离传感器 -> 选择更开阔的方向
IMU -> 检测碰撞或异常倾斜
轮编码器 -> 估算轮子转动
定时电机指令 -> 粗略假设移动距离

它仍有边界。反光、柔软、倾斜或太远的物体可能让某一类测距传感器判断错误;发出电机指令也不代表轮子真的转了。程序开始比较多路信号、不再完全相信单一读数后,行为才会更可靠。

这个阶段可以称为多传感器规则组合:做重要决定前,参考不止一条不完美的线索。正式的传感器融合是更深入的主题,要用带已知误差模型的传感器共同估计同一状态。入门项目不需要先上卡尔曼滤波。一个实用起点是:前方距离过近时先停车,再比较左右间隙,选择更安全的一侧转向。

带前方与两侧测距范围及 IMU 的俯视小车,下方依次展示前方受阻、比较左右、朝开阔处转向、重新读取四个步骤。
增加传感器是为了补上下文,不是为了堆复杂度。前方传感器发现障碍,两侧传感器选择方向,新一轮测量闭合控制环。

顺序很重要:先停止,再比较两侧间隙,然后转向,最后重新测量。机器人不能拿过期信息一路完成转弯。每个阶段还会产生可记录、可单独测试的状态。

七个从简单到惊喜的项目

项目 传感器思路 第一版 升级路径
黄昏灯 环境光 变暗时点亮 LED 掌握电气安全和隔离后再加继电器
停车或接近指示器 距离 用多颗 LED 表示远近 增加蜂鸣器和小屏幕
桌面活动显示 PIR 人体感应 附近有人运动时唤醒屏幕 增加时段规则和本地日志
倾斜控制器 IMU 倾斜控制屏幕光标 用保守限位控制舵机
声级指示 麦克风电平 持续大声时点亮提示 采集样本后识别一类狭窄的声音模式
植物监测器 电容式土壤湿度与光照 显示读数并在土壤过干时告警 保留告警;水泵需要独立低压驱动与供电设计
避障小车 一颗距离传感器 遇到障碍前停车 增加侧向测距、IMU 上下文和受控转向

这些项目共用同一架构。换掉输入,但保持验证顺序:先证明读数正确,再加入可见指示,最后才接物理执行器。在能够独立核查功率、隔离、驱动和故障行为之前,不要碰水、市电、大电机和继电器。

触摸、声音和视觉也是传感器

“传感器项目”和“AI 项目”的边界,比表面看起来更模糊。

ESP32-S3 可以在受支持的触摸引脚上测量电容变化。导电触摸面接到合适的 GPIO 前,必须核对芯片与开发板文档。DevKitC-1 没有现成触摸板,载板、接线和标定会共同决定交互是否可靠。详见乐鑫的电容触摸文档。

麦克风最初只是一串幅度样本,摄像头最初只是一帧像素。第一个有用项目可以只估计音量或检测画面变化。更复杂的工作能识别唤醒词、分类振动、识别手势或检测物体,但基于摄像头的二维码解码与目标识别,需要兼容硬件、足够内存、可控光照和相应软件栈。

把层次分开:

传感器硬件 -> 干净信号 -> 简单规则
             -> 必要时加入训练模型 -> 动作

这个顺序让调试成为可能。设备行为异常时,可以判断问题出在接线、安装位置、数据噪声、规则还是模型本身。再复杂的模型也修不好已经饱和的输入或不可靠的地线。

先跑通第一条闭环,再扩展系统

从一颗传感器和一个无害输出开始。让输出可观察:打印读数、控制 LED,或在屏幕上画一根条。测试必须在设备真正工作的环境中进行。

然后在笔记里回答五个问题:

  1. 正常状态下读数大约是多少?
  2. 目标事件发生时读数是多少?
  3. 读数波动有多大?
  4. 最常见的误触发是什么?
  5. 传感器出错时,哪种动作仍然安全?

这五个答案,比从随机项目页面抄来的一张复杂接线图更有价值。它们定义了设备的工作范围,也暴露了成功演示容易掩盖的故障情况。

一个实用的上电验证顺序同样简单:

  1. 断开输出级,先确认供电、接地和逻辑电平。
  2. 读取传感器并打印原始值,暂时不做判断。
  3. 加入平滑、有效值检查和持续确认。
  4. 用合成读数测试判断逻辑,包括错误输入。
  5. 接上最终的低压输出,再在真实环境中重跑测试。

物理世界本来就很脏乱。好的嵌入式项目会给这种不确定性留出空间:多次采样、使用保守阈值、记录发生过什么,并且每次只改变一个变量。

闭环本身就是基础

在所在空间里挑一个真正重要的信号。测量它,让 LED 或屏幕对它作出反应,再给这个反应一个真实任务。

这条小闭环就是传感器设备的起点。它可以长成一圈尊重距离的小车、一间察觉光线的房间、一件发现振动的工具,或一个只在真人出现时才响应的本地界面。下一颗传感器并不需要新的工程哲学,它只需要另一个定义清楚的物理变化、兼容的接口、可测试的事件,以及读数出错时依然安全的响应。

资料来源与制作说明

文中示意图为本次编辑制作。实际连接应以接线表、开发板丝印、器件额定值和官方文档为准,不能只照视觉图搭建。所有数字阈值都是需要实测的起点,不是通用标定值。

原文来源:ard (@ardchain) on Xhttps://x.com/ardchain/status/2094142902842589217

相关文章

作为亚马逊联盟会员,我们可能从符合条件的购买中获得佣金。