物联网 $5 打造 ESP32 人体存在传感器:HLK-LD2410 毫米波雷达 DIY 完全指南
为什么你需要一个毫米波人体存在传感器
传统 PIR 红外传感器只能检测运动——当你坐在沙发上看书、蹲在马桶上刷手机时,PIR 会认为”没人”然后关灯。这是智能家居中最令人抓狂的体验之一。
毫米波雷达(mmWave Radar) 彻底解决了这个问题。它通过发射 24GHz 电磁波并分析反射信号,能够检测到人体的微小运动——甚至包括呼吸引起的胸腔起伏。这意味着只要有人在房间里,哪怕一动不动,传感器也能准确报告”存在”。
毫米波雷达的工作原理基于多普勒效应和FMCW(调频连续波)技术。模块发射的电磁波遇到物体后反射回来,通过分析反射波的频率变化和相位差,可以精确计算出目标的距离、速度和存在状态。这种技术最初用于军事和航空领域,现在已经民用化并大幅降低成本。
好消息是:一颗 ESP32(约 $3)加上一块 HLK-LD2410 毫米波雷达模块(约 $2),总成本不到 5 美元,就能做出一个性能媲美商业产品的存在传感器。
传感器选型:PIR vs 毫米波雷达 vs 超声波
在动手之前,先搞清楚三种主流人体检测方案的优劣:
| 特性 | PIR 红外 | 毫米波雷达 | 超声波 |
|---|---|---|---|
| 检测静止人体 | ❌ 不能 | ✅ 能 | ✅ 能 |
| 检测距离 | 5-12m | 0.2-6m(可调) | 0.2-3m |
| 穿透能力 | 需直视 | 可穿透薄墙/塑料外壳 | 需直视 |
| 功耗 | 极低(μA 级) | 中等(~100mA) | 中等 |
| 抗光干扰 | 差(受温度影响) | 强 | 强 |
| 成本 | $1-2 | $2-5 | $2-3 |
| 隐私安全 | 高 | 高(不采集图像) | 高 |
PIR 红外传感器通过检测人体发出的红外辐射变化来工作。当人体移动时,红外辐射的变化被热释电传感器捕捉。但如果你静止不动,红外辐射分布稳定,传感器就无法检测。PIR 的优势是功耗极低,适合电池供电的走廊灯、楼梯灯等”必须有人走动”的场景。
毫米波雷达的优势在于能够检测微小运动,甚至可以区分运动目标和静止目标。HLK-LD2410 模块会分别报告”运动目标”和”静止目标”的距离和能量,让你可以判断房间里是有人走动还是有人静坐。这种能力对于智能照明、空调控制、安防监控等场景至关重要。
超声波传感器通过发射超声波并测量回波时间来计算距离。它可以检测静止物体,但波束角较窄,覆盖范围有限。超声波更适合精确测距(如倒车雷达),不太适合做房间级的存在检测。
结论:如果你要做智能照明自动化,毫米波雷达是最佳选择——能检测静止人体、可隐藏安装在墙壁/天花板内、成本极低。PIR 适合走廊、楼梯等”必须有人走动”的场景。超声波在短距离测距场景有优势,但不适合做存在检测。
硬件清单
| 物料 | 型号 | 参考价格 |
|---|---|---|
| 主控板 | ESP32-DevKitC / ESP32-WROOM-32 | $3 |
| 毫米波雷达模块 | HLK-LD2410(24GHz) | $2 |
| 杜邦线 | 母对母 4 根 | $0.5 |
| Micro-USB 数据线 | $1 | |
| 面包板(可选) | 830 孔 | $2 |
HLK-LD2410 模块说明:该模块基于 24GHz FMCW(调频连续波)雷达技术,内置信号处理算法,可直接输出目标状态(有人/无人)、目标距离和运动能量。模块内部集成了发射天线和接收天线,无需外接天线。支持 UART 串口通信,波特率默认 256000(可通过指令修改为 115200 以兼容 ESP32 软串口)。
模块引脚定义:
- VCC:供电引脚,支持 3.3V 或 5V
- GND:地线
- OUT(TX):数据输出引脚,发送雷达数据帧
- IN(RX):数据输入引脚,接收配置指令
购买建议:HLK-LD2410 有多个版本,包括 LD2410(标准版)、LD2410P(增强版,检测距离更远)、LD2420(新版本,支持更多功能)。对于初学者,标准版 LD2410 已经足够,价格最便宜且资料最多。
接线图
HLK-LD2410 与 ESP32 的接线非常简单,仅需 4 根线:
HLK-LD2410 ESP32-DevKitC
┌──────────┐ ┌──────────┐
│ VCC │───────▶│ 3.3V │
│ GND │───────▶│ GND │
│ OUT │───────▶│ GPIO16 │ (UART2 RX)
│ IN │◀───────│ GPIO17 │ (UART2 TX)
└──────────┘ └──────────┘
接线要点:
- VCC → 3.3V:LD2410 支持 3.3V 供电,无需电平转换。如果你使用 5V 供电(如 USB 5V),模块也能正常工作,但要注意 ESP32 的 GPIO 是 3.3V 逻辑,可能需要电平转换(实际上 LD2410 的 3.3V 输出兼容 ESP32,无需转换)
- OUT → GPIO16:模块的 TX 引脚接 ESP32 的 UART RX。GPIO16 是 ESP32 的 UART2 RX 引脚
- IN → GPIO17:模块的 RX 引脚接 ESP32 的 UART TX。GPIO17 是 ESP32 的 UART2 TX 引脚
- 如果你只需要读取传感器数据(不需要配置参数),可以只接 VCC、GND、OUT 三根线
常见接线错误:
- TX/RX 接反:模块的 OUT(TX)应该接 ESP32 的 RX,模块的 IN(RX)应该接 ESP32 的 TX。如果接反,串口通信会失败
- 供电不足:使用 USB 供电时,确保数据线支持供电(不是纯充电线)。某些劣质 USB 线电阻过大,导致电压跌落
- 地线未连接:VCC 和 GND 必须同时连接,否则模块无法工作
Arduino 完整代码
以下代码通过 UART 读取 HLK-LD2410 的数据帧,解析目标状态和距离,并通过 MQTT 发布到 Home Assistant:
#include <WiFi.h>
#include <PubSubClient.h>
#include <HardwareSerial.h>
// ===== WiFi & MQTT 配置 =====
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
const char* mqtt_server = "192.168.1.100"; // Home Assistant IP
const char* mqtt_user = "your_mqtt_user";
const char* mqtt_pass = "your_mqtt_password";
// ===== 引脚定义 =====
#define RADAR_RX 16 // 接 LD2410 的 OUT/TX
#define RADAR_TX 17 // 接 LD2410 的 IN/RX
// ===== MQTT 主题 =====
const char* topic_presence = "home/sensor/living_room/presence";
const char* topic_distance = "home/sensor/living_room/distance";
const char* topic_energy = "home/sensor/living_room/energy";
HardwareSerial RadarSerial(2);
WiFiClient espClient;
PubSubClient mqtt(espClient);
// ===== 数据解析变量 =====
bool targetState = false; // true=有人, false=无人
int moveDistance = 0; // 目标距离 (cm)
int moveEnergy = 0; // 运动能量 (0-100)
void setup() {
Serial.begin(115200);
RadarSerial.begin(256000, SERIAL_8N1, RADAR_RX, RADAR_TX);
// 连接 WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected");
// 配置 MQTT
mqtt.setServer(mqtt_server, 1883);
mqtt.setBufferSize(512);
}
void reconnectMQTT() {
while (!mqtt.connected()) {
Serial.print("Connecting MQTT...");
if (mqtt.connect("ESP32-Radar-Sensor", mqtt_user, mqtt_pass)) {
Serial.println("OK");
} else {
Serial.printf("Failed, rc=%d\n", mqtt.state());
delay(5000);
}
}
}
// ===== 解析 LD2410 数据帧 =====
void parseRadarData() {
static uint8_t buffer[256];
static int bufIndex = 0;
while (RadarSerial.available()) {
uint8_t c = RadarSerial.read();
// 帧头: 0xF4 0xF3 0xF2 0xF1
if (bufIndex == 0 && c != 0xF4) continue;
if (bufIndex == 1 && c != 0xF3) { bufIndex = 0; continue; }
if (bufIndex == 2 && c != 0xF2) { bufIndex = 0; continue; }
if (bufIndex == 3 && c != 0xF1) { bufIndex = 0; continue; }
buffer[bufIndex++] = c;
// 帧尾: 0xF8 0xF7 0xF6 0xF5
if (bufIndex >= 8 &&
buffer[bufIndex-4] == 0xF8 &&
buffer[bufIndex-3] == 0xF7 &&
buffer[bufIndex-2] == 0xF6 &&
buffer[bufIndex-1] == 0xF5) {
// 解析目标状态 (byte 8-9, 0x0000=无人, 0x0001=有人)
if (bufIndex >= 12) {
targetState = (buffer[9] == 0x01);
moveDistance = buffer[10] | (buffer[11] << 8);
}
// 解析运动能量
if (bufIndex >= 16) {
moveEnergy = buffer[14];
}
bufIndex = 0; // 重置缓冲区
}
if (bufIndex >= sizeof(buffer)) bufIndex = 0;
}
}
void loop() {
if (!mqtt.connected()) reconnectMQTT();
mqtt.loop();
parseRadarData();
// 每 2 秒发布一次数据
static unsigned long lastPublish = 0;
if (millis() - lastPublish > 2000) {
lastPublish = millis();
mqtt.publish(topic_presence, targetState ? "ON" : "OFF");
mqtt.publish(topic_distance, String(moveDistance).c_str());
mqtt.publish(topic_energy, String(moveEnergy).c_str());
Serial.printf("Presence: %s | Distance: %d cm | Energy: %d\n",
targetState ? "YES" : "NO", moveDistance, moveEnergy);
}
}
代码说明:
- 使用 ESP32 的
HardwareSerial(2)与 LD2410 通信,波特率 256000。注意不能使用默认的 Serial(UART0),因为那会被用于调试输出 - 数据帧以
F4 F3 F2 F1开头,F8 F7 F6 F5结尾。代码通过状态机方式逐字节解析,确保即使在数据流中间开始读取也能正确同步 - 解析目标状态(有人/无人)、目标距离(cm)和运动能量(0-100)。距离值由两个字节组成(低字节在前),能量值是一个字节
- 通过 MQTT 发布三个主题:presence(ON/OFF)、distance(cm)、energy(0-100)。每 2 秒发布一次,避免 MQTT 服务器过载
依赖库:
PubSubClient:MQTT 客户端库,通过 Arduino IDE 的库管理器安装WiFi:ESP32 内置 WiFi 库,无需额外安装
Home Assistant 集成
方式一:MQTT 传感器(推荐)
在 Home Assistant 的 configuration.yaml 中添加:
mqtt:
sensor:
- name: "客厅存在传感器"
state_topic: "home/sensor/living_room/presence"
payload_on: "ON"
payload_off: "OFF"
device_class: occupancy
unique_id: "lr_occupancy"
- name: "客厅目标距离"
state_topic: "home/sensor/living_room/distance"
unit_of_measurement: "cm"
icon: "mdi:signal-distance-variant"
unique_id: "lr_distance"
- name: "客厅运动能量"
state_topic: "home/sensor/living_room/energy"
unit_of_measurement: "%"
icon: "mdi:pulse"
unique_id: "lr_energy"
配置说明:
device_class: occupancy让 Home Assistant 自动识别这是一个占用传感器,会在界面上显示相应的图标unique_id确保传感器在重启后保持历史记录,不会重复创建- 如果你使用 MQTT 集成(通过 Home Assistant 的”配置” → “设备与服务” → “MQTT”),可以直接在界面上添加传感器,无需编辑 YAML
方式二:ESPHome(零代码方案)
如果你不想写 Arduino 代码,可以使用 ESPHome 的 ld2410 组件。ESPHome 是 Home Assistant 官方推荐的固件框架,支持通过 YAML 配置生成固件,无需编程。
esphome:
name: living-room-presence
friendly_name: "客厅存在传感器"
esp32:
board: esp32dev
framework:
type: arduino
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
api:
encryption:
key: !secret api_key
logger:
baud_rate: 0 # 禁用串口日志,避免与雷达冲突
uart:
tx_pin: GPIO17
rx_pin: GPIO16
baud_rate: 256000
ld2410:
timeout:
no_target: 5s
has_target: 2s
max_distances:
motion: 6m
static: 4.5m
binary_sensor:
- platform: ld2410
has_target:
name: "有人"
device_class: occupancy
sensor:
- platform: ld2410
moving_distance:
name: "运动目标距离"
static_distance:
name: "静止目标距离"
moving_energy:
name: "运动能量"
static_energy:
name: "静止能量"
将以上 YAML 保存为 living-room-presence.yaml,通过 ESPHome Dashboard 上传即可。ESPHome 原生支持 LD2410,无需额外组件。
ESPHome 的优势:
- 零代码:通过 YAML 配置,无需编程
- 自动发现:Home Assistant 自动识别并添加设备,无需手动配置 MQTT
- OTA 更新:支持无线更新固件,无需重新接线
- 内置组件:LD2410 组件已经过充分测试,稳定性好
功耗优化:电池供电方案
HLK-LD2410 的工作电流约 100mA,持续运行。如果用 18650 电池(2500mAh)供电,理论续航约 25 小时——对于存在传感器来说不够理想。
优化方案:
-
深度睡眠 + 定时唤醒:ESP32 进入深度睡眠(~10μA),每 30 秒唤醒 2 秒读取雷达数据。综合功耗降至约 10mA,18650 续航可达 10 天以上。这种方案适合对实时性要求不高的场景,如房间占用统计。
-
LD2410 OUT 引脚中断:LD2410 的 OUT 引脚在检测到目标时输出高电平。将该引脚接到 ESP32 的 GPIO,配置为唤醒源。无人时 ESP32 深度睡眠,有人时唤醒上报数据。这种方案兼顾了低功耗和实时性,是最佳选择。
-
降低发射功率:通过串口指令降低 LD2410 的发射功率,可将模块功耗降至 50mA 以下,代价是检测距离缩短。适合小房间(如卫生间)使用。
深度睡眠示例代码:
#define WAKE_PIN GPIO4 // 接 LD2410 OUT 引脚
void setup() {
// 配置 GPIO4 为唤醒源(高电平唤醒)
esp_sleep_enable_ext0_wakeup(WAKE_PIN, HIGH);
// ... 初始化代码 ...
}
void loop() {
// 读取数据并发布
readAndPublish();
// 进入深度睡眠,等待 OUT 引脚高电平唤醒
esp_deep_sleep_start();
// 唤醒后从 setup() 重新开始
}
电池选择建议:
- 18650 锂电池:容量大(2000-3500mAh),适合长期运行。需要配合充电模块(如 TP4056)使用
- 18650 电池盒:购买带开关的电池盒,方便更换电池
- USB 供电:如果使用 USB 充电头供电,无需考虑功耗问题,但失去了电池供电的灵活性
实际部署建议
- 安装位置:天花板中央或墙壁高处,避免正对门窗(室外移动物体会误触发)。最佳高度是 2.5-3 米,可以覆盖整个房间
- 灵敏度调节:通过串口指令调整门限阈值,适应不同房间大小。大房间(客厅)可以提高灵敏度,小房间(卫生间)可以降低灵敏度以避免误触发
- 抗干扰:远离微波炉(2.4GHz)和金属反射面。微波炉工作时会产生强烈的 2.4GHz 干扰,可能导致传感器误报
- 外壳设计:3D 打印外壳时注意雷达波可穿透塑料(ABS/PLASTIC),但不能穿透金属。外壳厚度建议不超过 3mm,过厚会衰减信号
- 多传感器部署:如果房间有多个入口,可以部署多个传感器,通过 Home Assistant 的逻辑判断综合确定房间占用状态
- 调试技巧:使用串口监视器查看传感器输出的原始数据,观察距离和能量值的变化,帮助理解传感器的工作状态
常见问题排查
问题 1:串口没有数据输出
- 检查接线:TX/RX 是否接反
- 检查波特率:LD2410 默认波特率是 256000,代码中必须匹配
- 检查供电:用万用表测量 VCC 引脚电压,确保在 3.3V 左右
问题 2:传感器一直报告”有人”
- 检查安装位置:是否正对门窗、空调出风口、风扇等移动物体
- 降低灵敏度:通过串口指令调整门限阈值
- 检查干扰源:附近是否有微波炉、无绳电话等 2.4GHz 设备
问题 3:传感器一直报告”无人”
- 检查检测范围:是否超出传感器的最大检测距离(6m)
- 提高灵敏度:通过串口指令调整门限阈值
- 检查目标:传感器是否能检测到微小运动(如呼吸引起的胸腔起伏)
问题 4:MQTT 连接失败
- 检查 WiFi 信号:确保 ESP32 能稳定连接 WiFi
- 检查 MQTT 服务器:确认 Home Assistant 的 MQTT 集成已启用
- 检查用户名密码:确保 MQTT 用户名和密码正确
总结
5 美元就能做出一个商业级的人体存在传感器——这正是开源硬件的魅力所在。HLK-LD2410 毫米波雷达模块解决了 PIR 传感器”检测不到静止人体”的痛点,配合 ESP32 的 WiFi 和 MQTT 能力,可以无缝接入 Home Assistant 智能家居系统。
这个项目的核心优势:
- 成本极低:总成本不到 5 美元,远低于商业产品(通常 $20-50)
- 性能优秀:能检测静止人体,检测距离可达 6 米
- 易于集成:通过 MQTT 或 ESPHome 无缝接入 Home Assistant
- 高度可定制:可以根据需求调整灵敏度、检测范围、功耗等参数
如果你已经完成了 ESP32-C6 Matter 指南,这个传感器项目可以作为 Matter 网络中的第一个自定义设备——下一步就是把它封装成 Matter 传感器配件,实现真正的跨平台智能家居自动化。