【項目簡介】
在AI的時代,AI對人類的日常生活、工作的輔助日益普及,本項目通過采集ADXL354的三軸加速度模擬信號,通過轉(zhuǎn)換、卡曼濾波生成實時速度,通過藍牙發(fā)送給主控,通過AI計算判斷車輛的行駛狀態(tài),如果主控監(jiān)測到處理行駛狀態(tài),啟用攝像頭采集駕駛員的人臉信息。主控通過實時監(jiān)控,實時捕獲人臉的特征點,通過AI分析,如果有疲勞的驅(qū)勢,及時通過語音、智能馬達振動來提醒駕駛員,實現(xiàn)疲勞預(yù)警的功能。
【項目框圖】
【硬件簡介】
1、ADXL354CZ,它是一款由 Analog Devices(亞德諾半導體)推出的評估板,用于對 ADXL354 這款低噪聲、低功耗的三軸 MEMS 加速度計進行性能評估。
主要特點 支持多種量程:可支持 ±2g 或 ±8g 的加速度測量范圍,能夠滿足不同應(yīng)用場景對測量范圍的需求。
?模擬輸出?:生成模擬輸出信號,方便與具有模擬輸入接口的系統(tǒng)進行連接和數(shù)據(jù)采集。 小尺寸與高剛度:評估板尺寸小巧,且具有較高的剛度,在對現(xiàn)有系統(tǒng)進行評估時,能將評估板自身對系統(tǒng)和加速度測量的影響降至最低。
?便于連接?:設(shè)有 2 組間隔的過孔,用于安裝 6 針引腳頭,可輕松連接到原型開發(fā)板或印刷電路板(PCB)上,方便用戶進行電路連接和系統(tǒng)集成。 工作原理:ADXL354CZ 評估板上的 ADXL354 加速度計基于微機電系統(tǒng)(MEMS)技術(shù),通過檢測質(zhì)量塊在加速度作用下產(chǎn)生的位移,將其轉(zhuǎn)換為電信號,再經(jīng)過信號調(diào)理和處理電路,最終以模擬信號的形式輸出與加速度成正比的電壓值。用戶可以通過測量這些模擬輸出信號,獲取加速度計在不同方向上的加速度數(shù)據(jù),從而評估 ADXL354 加速度計在特定應(yīng)用中的性能表現(xiàn)。
應(yīng)用領(lǐng)域 消費電子:如智能手機、平板電腦、可穿戴設(shè)備等,用于實現(xiàn)屏幕自動旋轉(zhuǎn)、運動檢測、計步等功能。 汽車電子:可用于汽車的安全系統(tǒng),如碰撞檢測、翻滾檢測等,也可用于車輛的導航和姿態(tài)控制。 工業(yè)監(jiān)測:對工業(yè)設(shè)備的振動、傾斜等狀態(tài)進行監(jiān)測,以實現(xiàn)設(shè)備的故障診斷和預(yù)防性維護。 航空航天與國防:在飛行器的姿態(tài)控制、導航系統(tǒng)以及導彈的制導等方面發(fā)揮重要作用
2、樹莓派5:
采用博通 BCM2712 芯片,搭載 64 位四核 Arm Cortex - A76 處理器,時鐘頻率為 2.4GHz。
可以運行輕量級的 AI 模型進行訓練和推理,通過雙相機接口實現(xiàn)計算機視覺中的圖像處理和物體識別。
3、ESP32 是一款低功耗、高集成度的 MCU 系統(tǒng)級芯片(SoC)。提供 UART、SPI、I2C 等豐富的外設(shè)接口,便于與各類外部設(shè)備通信和連接,例如傳感器、執(zhí)行器、顯示屏等,輕松實現(xiàn)各種功能擴展。內(nèi)置藍牙 4.2BLE(低功耗藍牙)模塊,支持藍牙設(shè)備間的無線通信與連接,方便構(gòu)建藍牙物聯(lián)網(wǎng)系統(tǒng)。
4、智能馬達,智能馬達可以通過pwm實現(xiàn)智能振動,用于穿戴產(chǎn)品的智能提示功能。
5、USB攝像頭,用于圖像采集。
【設(shè)計原理】
1、ESP32實時采集ADXL354三軸的模擬電壓信號,通過電壓轉(zhuǎn)換,卡曼濾波等分析后,計算出實時車速,藍牙高速傳輸給樹莓派5。
2、樹莓派5通過對ADXL354的信號分析,計算到如果實時速超過預(yù)設(shè)閾值時,開始采集圖像信號。
使用dlib庫檢測人臉和人臉的 68 個特征點,通過檢測計算眼睛特征點之間的距離來計算眼睛的縱橫比,以及計算嘴巴縱橫比。通過計算疲勞檢測的累計,當累計達到一定數(shù)值時,產(chǎn)生預(yù)警信號。
3、當預(yù)警信號產(chǎn)生時,通過車載音箱提示駕駛員,同時通過藍牙發(fā)送預(yù)警信號給ESP32,驅(qū)動馬達實現(xiàn)振動提醒。
4、當有警示事件產(chǎn)生時,通過聲卡播放警示音,直到預(yù)警消失。
【項目實物圖】
1、項目總體實物圖:
2、EVAL_ADXL354CZ評估板:
3、ADXL354與ESP32的連接圖:
4、主控-樹莓派連接實物圖:
【程序代碼】
【esp32]
#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "Esp32PicoMini.h" // Esp32PicoMini.h is the hardware abstraction layer (HAL) to connect with ESP32 Pico Mini board.
#include "VectorHaptics.h" // VectorHaptics.h is the main header file that connects sublibraries included in the Vector Haptics Library.
#include <VHBasePrimitives.h> // VHBasePrimitives.h is fundamental building blocks for creating haptic effects (using primitives like Vibration, Pulse, Pause)
#include <VHChannels.h> // VHChannels.h allows developers to create multiple channels to play haptic effects.
// 服務(wù)UUID和特征UUID,用于標識BLE服務(wù)和特征
#define SERVICE_UUID "12345678-1234-1234-1234-123456789012"
#define CHARACTERISTIC_UUID "87654321-4321-4321-4321-210987654321"
// 定義連接 ADXL354 的 ADC 引腳
const int adcPinX = 0; // ADC1 通道 4
// const int adcPinY = 2; // ADC1 通道 5
// const int adcPinZ = 4; // ADC1 通道 6
/*BLE服務(wù)器、服務(wù)和特征的指針(服務(wù)器包含服務(wù),服務(wù)包含特征)
聲明為全局變量,否則消失后無法使用藍牙
*/
BLEServer *pServer = nullptr;
BLEService *pService = nullptr;
BLECharacteristic *pCharacteristic = nullptr;
bool deviceConnected = false; // 跟蹤設(shè)備連接狀態(tài)
bool tick_run = false; //馬達啟動狀態(tài)
// 采樣時間間隔(單位:秒)
const float samplingInterval = 1.0;
// 定義低通濾波器系數(shù)
const float alpha = 0.1; // 濾波系數(shù),取值范圍 0 - 1
float filteredAccelX = 0;
int adcValueX = 0;
int adcValueY = 0;
int adcValueZ = 0;
// 假設(shè)使用 ±2g 量程,靈敏度為 97.6 μV/g
const float sensitivity = 40000.6e-6;
// 假設(shè)電源電壓為 3.3V,零點輸出電壓為 1.65V
const float offsetVoltage = 2.8;
// 初始化速度為 0
float velocityX = 0;
float velocityY = 0;
float velocityZ = 0;
// Declaring the VectorHaptics and VHBasePrimitives objects to access the haptic channel and base primitives.
VectorHaptics<Esp32PicoMini> vh;
VHBasePrimitives bp;
// Creating a VH channel with channel number, GPIO pin, and channel tags. Channel tags are String ussed to identify the channel.
VHChannel chnl1(1, 25,{"Left channel", "Channel 1", "Left", "Finger"},15);
VHChannels chnlList({&chnl1}); // Adding all channels to a channel list
// 卡爾曼濾波器類
class KalmanFilter {
private:
float q; // 過程噪聲協(xié)方差
float r; // 測量噪聲協(xié)方差
float x; // 狀態(tài)估計值
float p; // 估計誤差協(xié)方差
float k; // 卡爾曼增益
public:
KalmanFilter(float processNoise, float measurementNoise) {
q = processNoise;
r = measurementNoise;
x = 0;
p = 1.0;
}
float update(float measurement) {
// 預(yù)測
p = p + q;
// 計算卡爾曼增益
k = p / (p + r);
// 更新狀態(tài)估計值
x = x + k * (measurement - x);
// 更新估計誤差協(xié)方差
p = (1 - k) * p;
return x;
}
};
// 服務(wù)器回調(diào)
class MyServerCallbacks : public BLEServerCallbacks
{
void onConnect(BLEServer *pServer, esp_ble_gatts_cb_param_t *param)
{
deviceConnected = true;
Serial.print("Device connected: ");
// Echo back
pCharacteristic->setValue("Received"); // 要發(fā)送的消息
pCharacteristic->notify(); // 發(fā)送消息
}
void onDisconnect(BLEServer *pServer, esp_ble_gatts_cb_param_t *param)
{
deviceConnected = false;
tick_run = false;
Serial.print("Device disconnected: ");
Serial.println(BLEAddress(param->disconnect.remote_bda).toString().c_str());
pServer->getAdvertising()->start(); // 被客戶端斷開后重新啟動廣播
}
};
// 特征回調(diào)
class MyCallbacks : public BLECharacteristicCallbacks
{
void onWrite(BLECharacteristic *pCharacteristic)
{
std::string value = pCharacteristic->getValue(); // 獲取接收到的消息
Serial.print("Received Value: ");
Serial.println(value.c_str());
if(value == "1"){
tick_run = true;
}else if(value == "0")
{
tick_run = false;
}
// Echo back
pCharacteristic->setValue("Received"); // 要發(fā)送的消息
pCharacteristic->notify(); // 發(fā)送消息
}
};
// FreeRTOS 任務(wù)函數(shù),用于讀取 ADXL354 的模擬輸入值
void readADXL354Task(void *pvParameters)
{
// 創(chuàng)建卡爾曼濾波器實例
KalmanFilter accelFilter(0.01, 0.1);
analogReadResolution(12);
pinMode(adcPinX, INPUT); // declare the sensorPin as an INPUT
// pinMode(adcPinY, INPUT); // declare the sensorPin as an INPUT
// pinMode(adcPinZ, INPUT); // declare the sensorPin as an INPUT
while (1)
{
// 讀取 ADXL354 的模擬輸入值
adcValueX = analogRead(adcPinX);
// adcValueY = analogRead(adcPinY);
// adcValueZ = analogRead(adcPinZ);
float voltageX = adcValueX * (3.3 / 4095.0);
// 計算加速度
float accelX = (voltageX - offsetVoltage) / sensitivity;
// 計算速度
// 應(yīng)用一階低通濾波器
// 應(yīng)用卡爾曼濾波
float filteredAccelX = accelFilter.update(accelX);
// 計算速度
if(filteredAccelX <0.4 && filteredAccelX>-0.4)
{
velocityX = 0;
}
else{
velocityX += filteredAccelX * samplingInterval;
}
Serial.print("X 電壓: ");
Serial.print(voltageX);
// 打印加速度和速度值
Serial.print(" X 軸加速度: ");
Serial.print(accelX);
Serial.print(" g, ");
Serial.print("X 軸速度: ");
Serial.print(velocityX *3.6);
Serial.println(" km/h, ");
if (deviceConnected) // 連接狀態(tài)下notify()才有用,否則不會執(zhí)行任何操作
{
// 格式化數(shù)據(jù)為字符串
char message[100];
if(velocityX > 20 || velocityX < -20 )
{
snprintf(message, sizeof(message), "X: 1", velocityX);
pCharacteristic->setValue(message);
pCharacteristic->notify();
}
else if(velocityX == 0)
{
snprintf(message, sizeof(message), "X: 0", velocityX);
pCharacteristic->setValue(message);
pCharacteristic->notify();
}
}
vTaskDelay(samplingInterval * 1000); // 延時,與采樣時間間隔對應(yīng)
}
}
void setup()
{
Serial.begin(115200);
BLEDevice::init("ESP32-C3-BLE-Server"); // 初始化BLE設(shè)備并設(shè)置設(shè)備名稱
pServer = BLEDevice::createServer(); // 創(chuàng)建BLE服務(wù)器
pServer->setCallbacks(new MyServerCallbacks()); // 設(shè)置回調(diào)
pService = pServer->createService(SERVICE_UUID); // 創(chuàng)建一個BLE服務(wù)并使用之前定義的服務(wù)UUID
// 創(chuàng)建一個BLE特征,并設(shè)置其屬性為可讀和可寫和通知
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ | // 允許客戶端讀取特征的值,當客戶端讀取特征時,服務(wù)器返回特征的當前值
BLECharacteristic::PROPERTY_WRITE | // 允許客戶端寫入特征的值
BLECharacteristic::PROPERTY_NOTIFY); // 允許服務(wù)器主動向客戶端發(fā)送特征的更新通知
pCharacteristic->setCallbacks(new MyCallbacks()); // 設(shè)置回調(diào)
pCharacteristic->setValue("Hello World"); // 設(shè)置特征的初始值,當客戶端第一次讀取這個特征時,它會接收到這個初始值
pService->start(); // 啟動廣播
// BLEAdvertising 廣告是BLE設(shè)備向外廣播其存在和提供的服務(wù)的方式。通過廣告,BLE設(shè)備可以被其他設(shè)備(如手機或其他BLE客戶端)發(fā)現(xiàn)和連接
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); // 配置廣告參數(shù)
pAdvertising->addServiceUUID(SERVICE_UUID); // 添加服務(wù)UUID到廣告包
pAdvertising->setScanResponse(true); // 設(shè)置掃描響應(yīng)
// 調(diào)整最小首選連接間隔
// pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue,約7.5毫秒,默認0x18約30ms
BLEDevice::startAdvertising(); // 啟動廣告
Serial.println("BLE server is now advertising");
vh.Init({&chnlList,&bp});
xTaskCreate(LoopDriver,"LoopDriver",8000,NULL,configMAX_PRIORITIES,NULL);
// 創(chuàng)建 FreeRTOS 任務(wù)
xTaskCreate(readADXL354Task, "ReadADXL354", 2048, NULL, 1, NULL);
}
void loop()
{
// Serial.println("......test bluetooth......");
vh.EffectDriver();
vTaskDelay(10);
}
void LoopDriver(void *param)
{
while (true)
{
if(tick_run)
{
vh.play({PULSE(1, 100, 0.8), PAUSE(2000)}, 1);
}
else{
vTaskDelay(10);
}
}
}
到此ADXL354代碼實現(xiàn)了,主要功能是采集ADXL的模擬信號,并通過藍牙發(fā)送給上位機,同時接收上位機的信號,如果接收到1則開啟智能馬達啟動,如果收到0,則關(guān)閉馬達。
二、樹莓派
【主要的軟件環(huán)境】
1、 OpenCV(Open Source Computer Vision Library)是一個廣泛使用的開源計算機視覺庫,它提供了豐富的工具和算法,可用于處理圖像和視頻數(shù)據(jù)。
2、 Dlib是一個現(xiàn)代化的 C++ 工具包,包含機器學習算法和工具,用于創(chuàng)建復(fù)雜的軟件以解決實際問題。 提供了預(yù)訓練的人臉檢測器和特征點檢測器,能夠準確地檢測人臉并定位人臉的關(guān)鍵點,可用于人臉識別系統(tǒng)、表情分析等
3、 Bleak 是一個用于在 Python 中進行藍牙低功耗(Bluetooth Low Energy, BLE)通信的庫,它提供了簡潔且跨平臺的 API,讓開發(fā)者能夠方便地與 BLE 設(shè)備進行交互。
4、 其他的必要的庫 numpy、asyncio
【程序源碼】
import threading
import cv2
import dlib
import numpy as np
import asyncio
from bleak import BleakScanner, BleakClient
import os
# 定義音頻文件路徑
audio_file = "warnnin.wav"
# 檢查音頻文件是否存在
if not os.path.exists(audio_file):
print(f"音頻文件 {audio_file} 不存在,請檢查文件路徑和文件名。")
else:
print(f"音頻文件 {audio_file} 存在,可以播放。")
# 定義要連接的設(shè)備的UUID
DEVICE_UUID = "E8:6B:EA:37:D4:BA"
CHARACTERISTIC_UUID = "87654321-4321-4321-4321-210987654321"
# 定義疲勞閾值
FATIGUE_THRESHOLD = 5
# 定義解除疲勞的閾值(例如,連續(xù)正常狀態(tài)的幀數(shù))
RECOVERY_THRESHOLD = 300 # 假設(shè)連續(xù)正常狀態(tài)300幀為解除疲勞
# 定義計算眼睛縱橫比(EAR)的函數(shù)
def eye_aspect_ratio(eye):
A = np.linalg.norm(np.array(eye[1]) - np.array(eye[5]))
B = np.linalg.norm(np.array(eye[2]) - np.array(eye[4]))
C = np.linalg.norm(np.array(eye[0]) - np.array(eye[3]))
ear = (A + B) / (2.0 * C)
return ear
# 定義計算嘴巴縱橫比(MAR)的函數(shù)
def mouth_aspect_ratio(mouth):
A = np.linalg.norm(np.array(mouth[2]) - np.array(mouth[10]))
B = np.linalg.norm(np.array(mouth[4]) - np.array(mouth[8]))
C = np.linalg.norm(np.array(mouth[0]) - np.array(mouth[6]))
mar = (A + B) / (2.0 * C)
return mar
# 定義閾值和連續(xù)幀數(shù)
EYE_AR_THRESH = 0.3
EYE_AR_CONSEC_FRAMES = 30
MOUTH_AR_THRESH = 0.5
MOUTH_AR_CONSEC_FRAMES = 15
COUNTER_EYES = 0
TOTAL_BLINKS = 0
COUNTER_MOUTH = 0
TOTAL_YAWNS = 0
# 狀態(tài)標志和計數(shù)器
is_fatigue = False
recovery_counter = 0
# 打開攝像頭
cap = cv2.VideoCapture(0)
# 設(shè)置攝像頭分辨率和亮度
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
cap.set(cv2.CAP_PROP_BRIGHTNESS, 1.0)
# 線程安全鎖
lock = threading.Lock()
# 全局事件循環(huán)
loop = asyncio.new_event_loop()
# 全局 BleakClient 實例
client = None
# 全局音頻播放線程
audio_thread = None
audio_playing = False
def play_audio_loop(file_path):
global audio_playing
while audio_playing:
try:
# 使用 aplay 播放音頻文件
os.system(f"aplay {file_path}")
print(f"播放音頻文件: {file_path}")
except Exception as e:
print(f"播放音頻文件時發(fā)生異常: {e}")
audio_playing = False
def face_detection():
global is_fatigue, recovery_counter, TOTAL_BLINKS, TOTAL_YAWNS, COUNTER_EYES, COUNTER_MOUTH, client, audio_thread, audio_playing
while True:
ret, frame = cap.read()
if not ret:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
rects = detector(gray, 0)
if len(rects) == 0:
with lock:
recovery_counter += 1
else:
for rect in rects:
shape = predictor(gray, rect)
shape = [(shape.part(i).x, shape.part(i).y) for i in range(68)]
leftEye = shape[36:42]
rightEye = shape[42:48]
mouth = shape[48:68]
leftEAR = eye_aspect_ratio(leftEye)
rightEAR = eye_aspect_ratio(rightEye)
ear = (leftEAR + rightEAR) / 2.0
mar = mouth_aspect_ratio(mouth)
leftEyeHull = cv2.convexHull(np.array(leftEye))
rightEyeHull = cv2.convexHull(np.array(rightEye))
mouthHull = cv2.convexHull(np.array(mouth))
cv2.drawContours(frame, [leftEyeHull], -1, (0, 255, 0), 1)
cv2.drawContours(frame, [rightEyeHull], -1, (0, 255, 0), 1)
cv2.drawContours(frame, [mouthHull], -1, (0, 255, 0), 1)
# 可視化關(guān)鍵點
for (x, y) in shape:
cv2.circle(frame, (x, y), 1, (0, 0, 255), -1)
if ear < EYE_AR_THRESH:
with lock:
COUNTER_EYES += 1
else:
with lock:
if COUNTER_EYES >= EYE_AR_CONSEC_FRAMES:
TOTAL_BLINKS += 1
COUNTER_EYES = 0
if mar > MOUTH_AR_THRESH:
with lock:
COUNTER_MOUTH += 1
else:
with lock:
if COUNTER_MOUTH >= MOUTH_AR_CONSEC_FRAMES:
TOTAL_YAWNS += 1
COUNTER_MOUTH = 0
# 檢測疲勞
with lock:
if TOTAL_BLINKS + TOTAL_YAWNS > FATIGUE_THRESHOLD:
if not is_fatigue:
print("疲勞檢測:連續(xù)眨眼次數(shù)加上打哈欠次數(shù)超過閾值,可能疲勞!")
is_fatigue = True
recovery_counter = 0 # 重置恢復(fù)計數(shù)器
# 發(fā)送字符 '1' 表示疲勞
if client and client.is_connected:
print("嘗試發(fā)送數(shù)據(jù) '1'")
future = asyncio.run_coroutine_threadsafe(ble_send(b'x31'), loop)
try:
future.result() # 等待協(xié)程完成并捕獲異常
except Exception as e:
print(f"發(fā)送數(shù)據(jù) '1' 時發(fā)生異常: {e}")
else:
print("BleakClient 未連接,無法發(fā)送數(shù)據(jù) '1'")
# 啟動音頻播放線程
if not audio_playing:
audio_playing = True
audio_thread = threading.Thread(target=play_audio_loop, args=(audio_file,))
audio_thread.start()
TOTAL_BLINKS = 0 # 重置眨眼計數(shù)器以避免持續(xù)報警
TOTAL_YAWNS = 0 # 重置打哈欠計數(shù)器以避免持續(xù)報警
else:
recovery_counter += 1
# 檢測解除疲勞
with lock:
if is_fatigue and recovery_counter >= RECOVERY_THRESHOLD:
print("疲勞解除:連續(xù)正常狀態(tài)超過閾值,疲勞狀態(tài)解除!")
is_fatigue = False
# 發(fā)送字符 '0' 表示解除疲勞
if client and client.is_connected:
print("嘗試發(fā)送數(shù)據(jù) '0'")
future = asyncio.run_coroutine_threadsafe(ble_send(b'x30'), loop)
try:
future.result() # 等待協(xié)程完成并捕獲異常
except Exception as e:
print(f"發(fā)送數(shù)據(jù) '0' 時發(fā)生異常: {e}")
else:
print("BleakClient 未連接,無法發(fā)送數(shù)據(jù) '0'")
# 停止音頻播放線程
if audio_playing:
audio_playing = False
if audio_thread and audio_thread.is_alive():
audio_thread.join()
cv2.putText(frame, "Blinks: {}".format(TOTAL_BLINKS), (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
cv2.putText(frame, "EAR: {:.2f}".format(ear), (300, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
cv2.putText(frame, "Yawns: {}".format(TOTAL_YAWNS), (10, 60),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
cv2.putText(frame, "MAR: {:.2f}".format(mar), (300, 60),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
cv2.imshow("Frame", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
async def ble_send(data):
global client
print("嘗試發(fā)送數(shù)據(jù)")
if client and client.is_connected:
try:
await client.write_gatt_char(CHARACTERISTIC_UUID, data)
print(f"發(fā)送數(shù)據(jù)成功: {data}")
except Exception as e:
print(f"發(fā)送數(shù)據(jù)失敗: {e}")
else:
print("BleakClient 未連接,無法發(fā)送數(shù)據(jù)")
async def ble_receive():
global client
client = BleakClient(DEVICE_UUID)
try:
await client.connect()
print(f"已連接到設(shè)備: {DEVICE_UUID}")
# 獲取所有服務(wù)
services = await client.get_services()
for service in services:
print(f"服務(wù) UUID: {service.uuid}")
for char in service.characteristics:
print(f" 特征 UUID: {char.uuid}, 特征屬性: {char.properties}")
# 讀取特征值
value = await client.read_gatt_char(CHARACTERISTIC_UUID)
print(f"讀取到的特征值: {value}")
# 訂閱特征值通知
def notification_handler(sender, data):
# 如果 data 是 bytearray 類型,先將其轉(zhuǎn)換為 bytes 類型,再解碼為字符串
if isinstance(data, bytearray):
data = bytes(data).decode('utf-8')
elif isinstance(data, bytes):
data = data.decode('utf-8')
# 按冒號分割字符串
parts = data.split(':')
if len(parts) == 2:
number_part = parts[1].strip() # 去除可能的空格
try:
extracted_number = int(number_part)
voltage = (extracted_number / 4095) * 3.3
print(f"接收到的電壓值: {voltage}")
except ValueError:
print("提取數(shù)字失敗,數(shù)字部分不是有效的整數(shù)")
else:
print("數(shù)據(jù)格式不正確,無法提取數(shù)字")
await client.start_notify(CHARACTERISTIC_UUID, notification_handler)
# 保持連接,直到手動停止
while True:
await asyncio.sleep(1) # 防止無限循環(huán)占用CPU
except Exception as e:
print(f"BLE連接失敗: {e}")
finally:
await client.disconnect()
print("BLE連接已斷開")
client = None
def run_ble_receive():
asyncio.set_event_loop(loop)
loop.create_task(ble_receive())
loop.run_forever()
if __name__ == "__main__":
# 使用dlib的get_frontal_face_detector()獲取人臉檢測器
detector = dlib.get_frontal_face_detector()
# 使用dlib的shape_predictor_68_face_landmarks.dat模型獲取面部標志預(yù)測器
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
# 創(chuàng)建線程
face_thread = threading.Thread(target=face_detection)
ble_thread = threading.Thread(target=run_ble_receive)
# 啟動線程
face_thread.start()
ble_thread.start()
# 等待線程結(jié)束(理論上不會結(jié)束)
face_thread.join()
ble_thread.join()
【項目總結(jié)】
ADXL354可以實時輸出高靈敏度xyz三軸加速度模擬信號。通過ADC實時采集后可以生成實時速度。
主控樹莓派5可以實時采集圖像,通過dlib開源的AI分析,可以實時生成駕駛員的疲勞預(yù)警信號,同時也可以通過藍牙與周圍的藍牙設(shè)備進行高速實時通信。
警個系統(tǒng)實現(xiàn)了低成本的智能預(yù)警與多種方式的提醒駕駛員,為提高駕駛體驗提供了高質(zhì)量的輔助功能,在汽車應(yīng)用方面有廣泛的應(yīng)用前景。
最后要感謝得捷電子、與非網(wǎng)、Analog Devices提供這么好的硬件平臺以及參賽機會,讓我體驗到現(xiàn)在AI技術(shù)在汽車電子應(yīng)用的前沿科技。
【附件】
程序源碼:
參考附件
作品文檔:
參考附件