· notes · 10 min 阅读
使用 ESP32 + LD2410D 实现人体存在检测
使用 ESP32 驱动 LD2410D 毫米波雷达传感器,通过 WebSocket 实现人体存在检测与远程联动
前言
传统 PIR(被动红外)传感器只能检测运动,人静止不动时就会误判为无人。而毫米波雷达传感器可以检测微小的生命体征(如呼吸),即使人静止坐着也能准确感知存在。
本文记录使用 ESP32 + LD2410D 毫米波雷达的完整开发过程,包括接线、串口协议解析、两种工作模式的处理,以及通过 WebSocket 与服务端联动的实现。
硬件介绍
LD2410D 参数
| 参数 | 值 |
|---|---|
| 工作频率 | 24GHz |
| 检测距离 | 0.2m ~ 6m |
| 检测角度 | ±60° |
| 通信方式 | UART |
| 工作电压 | 3.3V |
| 默认波特率 | 115200 |
接线方式
LD2410D ESP32
─────── ─────
VCC ──► 3.3V
GND ──► GND
TX ──► GPIO16 (Serial2 RX)
RX ──► GPIO17 (Serial2 TX) 注意:LD2410D 的逻辑电平为 3.3V,可直接连接 ESP32,无需电平转换。
两种工作模式
LD2410D 有两种数据输出模式:
| 模式 | 输出格式 | 适用场景 |
|---|---|---|
| 正常模式 | ASCII 文本(OFF 或 distance: XX) | 简单存在检测 |
| 工程模式 | 二进制帧(含各距离门能量数据) | 精细调试、多目标分析 |
正常模式
输出简单的 ASCII 行:
OFF— 无人distance: 42— 有人,距离 42cm
工程模式
输出二进制帧,包含运动/静止目标的距离、能量,以及各距离门的详细能量数据。
串口协议详解
帧格式
数据上报帧(正常/工程模式):
帧头: F4 F3 F2 F1
数据: 2字节长度(小端) + 2字节类型 + 数据体
帧尾: F8 F7 F6 F5 配置命令帧:
帧头: FD FC FB FA
数据: 2字节长度(小端) + 2字节命令字(小端) + 命令值
帧尾: 04 03 02 01 工程模式数据体结构
字节0: 头部状态 (0x00=无人, 0x01=有人运动, 0x02=有人静止)
字节1: 运动/静止标志 (bit0=运动, bit1=静止)
字节2-3: 运动目标距离(小端, cm)
字节4: 运动目标能量
字节5-6: 静止目标距离(小端, cm)
字节7: 静止目标能量
字节8~8+N-1: N个距离门的运动能量
字节8+N~8+2N-1: N个距离门的静止能量 常用配置命令
| 命令字 | 说明 |
|---|---|
0x00FF | 进入配置模式 |
0x00FE | 退出配置模式 |
0x00FD | 参数掉电保存 |
0x0012 | 设置数据输出模式 |
0x0007 | 写入传感器参数 |
0x0008 | 读取传感器参数 |
0x0000 | 读取固件版本 |
代码实现
初始化雷达串口
const int RADAR_RX_PIN = 16; // ESP32 RX <- LD2410D TX
const int RADAR_TX_PIN = 17; // ESP32 TX -> LD2410D RX
void setup() {
Serial.begin(115200);
// 初始化 LD2410D 雷达串口(波特率 115200)
Serial2.begin(115200, SERIAL_8N1, RADAR_RX_PIN, RADAR_TX_PIN);
delay(500);
// 配置雷达为正常模式(ASCII 输出)
if (enterRadarConfigMode()) {
setRadarOutputMode(0x00000064); // 正常模式
exitRadarConfigMode();
}
} 发送配置命令
const uint8_t LD2410_CFG_HEADER[4] = {0xFD, 0xFC, 0xFB, 0xFA};
const uint8_t LD2410_CFG_TAIL[4] = {0x04, 0x03, 0x02, 0x01};
bool sendRadarCommand(uint16_t cmd, const uint8_t* value, uint8_t valueLen) {
uint16_t dataLen = 2 + valueLen;
// 发送帧头
Serial2.write(LD2410_CFG_HEADER, 4);
// 发送长度(小端)
Serial2.write(dataLen & 0xFF);
Serial2.write((dataLen >> 8) & 0xFF);
// 发送命令字(小端)
Serial2.write(cmd & 0xFF);
Serial2.write((cmd >> 8) & 0xFF);
// 发送命令值
if (valueLen > 0 && value != NULL) {
Serial2.write(value, valueLen);
}
// 发送帧尾
Serial2.write(LD2410_CFG_TAIL, 4);
Serial2.flush();
// 等待 ACK(2秒超时)
unsigned long start = millis();
uint8_t ackBuf[64];
int ackBufLen = 0;
while (millis() - start < 2000) {
while (Serial2.available()) {
uint8_t b = Serial2.read();
if (ackBufLen < sizeof(ackBuf)) {
ackBuf[ackBufLen++] = b;
}
// 检测配置帧尾 04 03 02 01
if (ackBufLen >= 4 &&
ackBuf[ackBufLen-4] == 0x04 && ackBuf[ackBufLen-3] == 0x03 &&
ackBuf[ackBufLen-2] == 0x02 && ackBuf[ackBufLen-1] == 0x01) {
if (ackBufLen >= 10) {
uint16_t status = ackBuf[8] | (ackBuf[9] << 8);
return (status == 0); // 0=成功
}
}
}
delay(10);
}
return false;
}
bool enterRadarConfigMode() {
uint8_t val[] = {0x01, 0x00};
return sendRadarCommand(0x00FF, val, 2);
}
bool exitRadarConfigMode() {
return sendRadarCommand(0x00FE, NULL, 0);
}
bool setRadarOutputMode(uint32_t mode) {
uint8_t val[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
val[2] = (uint8_t)(mode & 0xFF);
val[3] = (uint8_t)((mode >> 8) & 0xFF);
val[4] = (uint8_t)((mode >> 16) & 0xFF);
val[5] = (uint8_t)((mode >> 24) & 0xFF);
return sendRadarCommand(0x0012, val, 6);
} 正常模式解析(ASCII)
void parseNormalModeLine(const char* line) {
if (strcmp(line, "OFF") == 0) {
// 无人
radar.presence = false;
Serial.println("[RADAR] 检测到无人");
} else if (strncmp(line, "distance:", 9) == 0) {
// 有人,解析距离
int dist = atoi(line + 9);
radar.presence = true;
radar.motionDistance = dist;
Serial.printf("[RADAR] 检测到有人 距离:%dcm\n", dist);
}
} 工程模式解析(二进制帧)
void processRadarFrame() {
if (radarFrameLen < 20) return;
// 帧类型: 偏移 头4 + 长度2
uint8_t frameType = radarBuf[6];
if (frameType != 0x01) return; // 只处理实时上报帧
int dataOffset = 8; // 头4 + 长度2 + 类型2
uint8_t headState = radarBuf[dataOffset]; // 0x00=无人, 0x01=运动, 0x02=静止
uint8_t detectType = radarBuf[dataOffset + 1]; // bit0=运动, bit1=静止
// 运动目标
int motionDist = radarBuf[dataOffset + 2] | (radarBuf[dataOffset + 3] << 8);
int motionEng = radarBuf[dataOffset + 4];
// 静止目标
int staticDist = radarBuf[dataOffset + 5] | (radarBuf[dataOffset + 6] << 8);
int staticEng = radarBuf[dataOffset + 7];
// 0x01=有人运动, 0x02=有人静止, 都视为有人
bool newPresence = (headState == 0x01 || headState == 0x02);
radar.presence = newPresence;
radar.motionDistance = motionDist;
radar.staticDistance = staticDist;
} 进阶:WebSocket 联动
在实际项目中,雷达数据需要上报到服务端进行处理。以下是通过 WebSocket 实现的方案:
架构
ESP32 (WebSocket Client)
│
├── LD2410D 雷达 → 人体存在检测
├── 433MHz 模块 → 射频遥控
└── WebSocket → 连接 Koa 服务端
│
├── MySQL 存储
├── JWT 设备鉴权
└── 业务逻辑处理 设备鉴权
通过 URL 参数传递设备令牌:
const char* WS_HOST = "esp.ll1025.cn";
const int WS_PORT = 443;
const bool WS_SSL = true;
const char* WS_PATH = "/ws";
const char* DEVICE_TOKEN = "device_esp32-s3_xxxxx";
void connectWebSocket() {
String wsPath = String(WS_PATH) + "?token=" + String(DEVICE_TOKEN);
if (WS_SSL) {
webSocket.beginSSL(WS_HOST, WS_PORT, wsPath);
} else {
webSocket.begin(WS_HOST, WS_PORT, wsPath);
}
webSocket.onEvent(webSocketEvent);
webSocket.setReconnectInterval(5000);
webSocket.enableHeartbeat(15000, 3000, 2);
} 状态上报
// 周期上报(每2秒)
void reportRadarPeriodic() {
String msg = "{\"type\":\"radar_status\",\"data\":{"
"\"presence\":" + String(radar.presence ? "true" : "false") + ","
"\"motionDistance\":" + String(radar.motionDistance) + ","
"\"staticDistance\":" + String(radar.staticDistance) + "}}";
webSocket.sendTXT(msg);
}
// 状态变化时立即上报
void sendRadarEvent(String eventType) {
String msg = "{\"type\":\"" + eventType + "\",\"data\":{"
"\"motionDistance\":" + String(radar.motionDistance) + ","
"\"staticDistance\":" + String(radar.staticDistance) + "}}";
webSocket.sendTXT(msg);
} RGB LED 状态指示
使用板载 WS2812B LED 显示设备状态:
// 绿色=在线无人, 橙色=有人, 蓝色闪烁=连接中, 红色闪烁=WiFi失败
void updateLed() {
if (!wifiConnected) {
// 红色闪烁
rgbLed.setPixelColor(0, blinkOn ? rgbLed.Color(80, 0, 0) : 0);
} else if (!wsConnected) {
// 蓝色闪烁
rgbLed.setPixelColor(0, blinkOn ? rgbLed.Color(0, 0, 80) : 0);
} else if (radar.presence) {
// 橙色常亮 = 检测到有人
rgbLed.setPixelColor(0, rgbLed.Color(80, 30, 0));
} else {
// 绿色常亮 = 在线,无人
rgbLed.setPixelColor(0, rgbLed.Color(0, 60, 0));
}
rgbLed.show();
} 常见问题
1. 雷达无响应
- 检查 TX/RX 是否接反
- 确认波特率为 115200(手册默认值)
- 确认供电电压为 3.3V
2. 检测不灵敏
- 调整雷达安装位置,避免金属遮挡
- 通过配置命令调整灵敏度参数
- 确保检测区域内无大型金属物体干扰
3. 误检测
- LD2410D 对窗帘晃动、风扇转动较敏感
- 可通过设置距离门阈值过滤干扰
- 建议在实际场景中调试参数
应用场景
- 智能灯光:人来灯亮,人走灯灭
- 空调控制:检测房间是否有人,自动开关空调
- 安防报警:检测异常存在,触发报警
- 办公工位:检测工位占用状态
总结
ESP32 + LD2410D 是一个性价比很高的人体存在检测方案。相比传统 PIR 传感器,毫米波雷达能检测静止人体,适用场景更广。
实际开发中需要注意:
- 默认波特率是 115200(部分文档写的 256000 是旧版本)
- 正常模式简单易用,工程模式数据更丰富
- 配置命令需要先进入配置模式(
0x00FF),操作完退出(0x00FE) - 建议加状态变化冷却时间,避免频繁上报
完整固件代码已开源,包含 433MHz 射频、WebSocket 通信、NVS 配置管理等功能。