场景
本文将介绍如何利用BW21-CBV人脸识别套件,实现智能家居的两个有趣场景:
- 室内人脸识别欢迎回家: 在入户门内安装BW21-CBV套件,通过人脸识别判断回家的人是谁,并将识别结果推送至Home Assistant (HA) 平台。HA可以根据识别到的人员,播放不同的欢迎语,实现个性化的智能家居体验。
- 室外人脸检测安防提醒: 在入户门外安装BW21-CBV套件,当检测到有人靠近时,将抓拍的图片推送给HA平台。结合HA的图片处理能力,甚至可以接入GPT等图像识别服务,分析来访者信息并推送相关提醒,提升家居安全性。
实现思路
由于笔者不太熟悉C++,因此在方案选择上,以能够最大程度复用现有资源和代码为原则。对于人脸识别部分,优先考虑使用BW21-CBV自带的人脸识别示例,目标是能够获取识别到的人脸姓名。
在将识别结果推送到HA平台方面,常见的方案有MQTT和Web API。虽然MQTT具有实时性高的优点,但考虑到HA的MQTT功能需要安装额外的第三方组件,并且运行HA的树莓派3资源相对有限,为了尽量减少资源占用和简化配置,最终选择了使用HA的Web API进行数据推送。Web API的优势在于无需额外安装组件,只需要在HA账户中生成一个长期有效的访问令牌(Long-Lived Access Token)即可。
代码实现
代码实现主要采用RTSPFaceRecognition示例,需要我们编写的两个关键部分是:向HA发送POST请求和处理BW21-CBV的人脸识别结果。
1. 使用WiFiClient发送POST请求
BW21-CBV官方示例中,虽然使用了POST方法,但其数据通常是通过GET方式传递的。因此,我们需要自定义一个函数,使用 WiFiClient
库来构建并发送带有JSON数据的POST请求。
#include <WiFiClient.h>
#include <ArduinoJson.h>
const char* kHostname = "your_home_assistant_ip_or_domain"; // 替换为你的Home Assistant IP地址或域名
const int kPort = 8123; // Home Assistant 默认端口
const char* kPath = "/api/states/sensor.face_recognition"; // 你希望更新的HA实体ID
const char* kToken = "your_long_lived_access_token"; // 替换为你的HA长效访问令牌
WiFiClient wifiClient;
StaticJsonDocument doc; // 根据实际数据大小调整
void hapost() {
if (wifiClient.connect(kHostname, kPort)) {
doc["state"] = facename; // 将识别到的人名设置为HA实体的状态
String jsonString;
serializeJson(doc, jsonString);
wifiClient.println("POST " + String(kPath) + " HTTP/1.1");
wifiClient.println("Host: " + String(kHostname));
wifiClient.println("Content-Type: application/json");
wifiClient.println("Authorization: Bearer " + String(kToken));
wifiClient.println("Content-Length: " + String(jsonString.length()));
wifiClient.println("Connection: keep-alive");
wifiClient.println(); // 空行表示HTTP头部结束
wifiClient.print(jsonString); // 发送JSON数据
Serial.println("Data sent to Home Assistant");
} else {
Serial.println("Failed to connect to Home Assistant");
}
wifiClient.stop(); // 关闭连接
2. 处理人脸识别结果
BW21-CBV在检测到人脸后可能会产生较多的识别结果回调。如果不加以处理,频繁的HTTP推送可能会导致设备失去响应。为了解决这个问题,我们在人脸识别结果处理函数(假设为 FRPostProcess
)中进行了如下优化:
String _facename = ""; // 保存上一次识别到的人名
String facename = ""; // 保存当前识别到的人名
int facesta = 0; // 标记是否检测到新人脸
void FRPostProcess(FaceRecognition::FRResult& result) {
if (result.isDetected()) {
for (size_t i = 0; i < result.face_count(); ++i) {
FaceRecognition::FRItem item = result.get(i);
if (i == 0) { // 只取第一个识别结果
_facename = facename;
facename = item.name();
facesta = 1;
break; // 找到第一个后跳出循环
}
}
} else {
facename = "unknown"; // 未检测到人脸
facesta = 1;
}
}
3. 在Loop函数中上报数据
在主循环函数 loop()
中,我们根据 facename
的变化来判断是否需要向HA发送数据:
void loop() {
// ... 其他代码 ...
if (facesta == 1) { // 只有当检测到新人脸时才进行上报
if (facename != _facename) {
if (facename != String("unknown")) {
printf("Face name:\t %s \n\r", facename.c_str());
hapost();
} else if (_facename != String("unknown")) {
// 如果之前识别到人,现在变成unknown,也上报一次
printf("Face name:\t %s \n\r", facename.c_str());
hapost();
}
_facename = facename; // 更新上一次的人名
}
facesta = 0; // 重置标志位
}
// ... 其他代码 ...
}
4.自动加载人脸数据及开启畸变修正
void setup() {
// ... 其他初始化代码 ...
CameraSetting configCam;
facerecog.setResultCallback(FRPostProcess);
// 注册人脸识别回调函数
facerecog.restoreRegisteredFace();
// 开启畸变修正
configCam.setLDC(1);
}
效果测试
按照上述步骤配置完成后,当BW21-CBV识别到人脸后,你可以在Home Assistant中看到名为 sensor.face_recognition
的实体,其状态会显示识别到的人名,你可以基于这个实体创建自动化。让我们简单测试下看看效果:
看起来还不错,响应速度不比mqtt慢,完全达到预期目标。
结束
既然三位巨头已经同框,而且他们之间还有些分歧,那么让他们每人给我们讲几句吧,也希望现实中三巨头早日同框!
BW21-CBV->ha->gemini->edge-tts->homepod,目前在实现场景一“欢迎回家”的效果。