成人免费无码不卡毛片,亚洲AⅤ无码精品一区二区三区,国产尤物精品视频,久久精品日本亚洲,欧美成人一区三区无码乱码A片,中文字日产幕码一区二区色哟哟,亞洲日韓中文字幕網AV

  • 方案介紹
    • 【項(xiàng)目簡(jiǎn)介】
    • 【項(xiàng)目框圖】
    • 【硬件簡(jiǎn)介】
    • 【設(shè)計(jì)原理】
    • 【項(xiàng)目實(shí)物圖】
    • 【程序代碼】
    • 二、樹莓派
    • 【項(xiàng)目總結(jié)】
    • 【附件】
  • 附件下載
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

車載防疲勞駕駛預(yù)警系統(tǒng)

05/28 09:40
273
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

dlib_face_test.zip

共2個(gè)文件

【項(xiàng)目簡(jiǎn)介】

AI的時(shí)代,AI對(duì)人類的日常生活、工作的輔助日益普及,本項(xiàng)目通過采集ADXL354的三軸加速度模擬信號(hào),通過轉(zhuǎn)換、卡曼濾波生成實(shí)時(shí)速度,通過藍(lán)牙發(fā)送給主控,通過AI計(jì)算判斷車輛的行駛狀態(tài),如果主控監(jiān)測(cè)到處理行駛狀態(tài),啟用攝像頭采集駕駛員的人臉信息。主控通過實(shí)時(shí)監(jiān)控,實(shí)時(shí)捕獲人臉的特征點(diǎn),通過AI分析,如果有疲勞的驅(qū)勢(shì),及時(shí)通過語(yǔ)音、智能馬達(dá)振動(dòng)來提醒駕駛員,實(shí)現(xiàn)疲勞預(yù)警的功能。

【項(xiàng)目框圖】

【硬件簡(jiǎn)介】

1、ADXL354CZ,它是一款由 Analog Devices(亞德諾半導(dǎo)體)推出的評(píng)估板,用于對(duì) ADXL354 這款低噪聲、低功耗的三軸 MEMS 加速度計(jì)進(jìn)行性能評(píng)估。

主要特點(diǎn) 支持多種量程:可支持 ±2g 或 ±8g 的加速度測(cè)量范圍,能夠滿足不同應(yīng)用場(chǎng)景對(duì)測(cè)量范圍的需求。

?模擬輸出?:生成模擬輸出信號(hào),方便與具有模擬輸入接口的系統(tǒng)進(jìn)行連接和數(shù)據(jù)采集。 小尺寸與高剛度:評(píng)估板尺寸小巧,且具有較高的剛度,在對(duì)現(xiàn)有系統(tǒng)進(jìn)行評(píng)估時(shí),能將評(píng)估板自身對(duì)系統(tǒng)和加速度測(cè)量的影響降至最低。

?便于連接?:設(shè)有 2 組間隔的過孔,用于安裝 6 針引腳頭,可輕松連接到原型開發(fā)板印刷電路板PCB)上,方便用戶進(jìn)行電路連接和系統(tǒng)集成。 工作原理:ADXL354CZ 評(píng)估板上的 ADXL354 加速度計(jì)基于微機(jī)電系統(tǒng)(MEMS)技術(shù),通過檢測(cè)質(zhì)量塊在加速度作用下產(chǎn)生的位移,將其轉(zhuǎn)換為電信號(hào),再經(jīng)過信號(hào)調(diào)理和處理電路,最終以模擬信號(hào)的形式輸出與加速度成正比的電壓值。用戶可以通過測(cè)量這些模擬輸出信號(hào),獲取加速度計(jì)在不同方向上的加速度數(shù)據(jù),從而評(píng)估 ADXL354 加速度計(jì)在特定應(yīng)用中的性能表現(xiàn)。

應(yīng)用領(lǐng)域 消費(fèi)電子:如智能手機(jī)、平板電腦、可穿戴設(shè)備等,用于實(shí)現(xiàn)屏幕自動(dòng)旋轉(zhuǎn)、運(yùn)動(dòng)檢測(cè)、計(jì)步等功能。 汽車電子:可用于汽車的安全系統(tǒng),如碰撞檢測(cè)、翻滾檢測(cè)等,也可用于車輛的導(dǎo)航和姿態(tài)控制。 工業(yè)監(jiān)測(cè):對(duì)工業(yè)設(shè)備的振動(dòng)、傾斜等狀態(tài)進(jìn)行監(jiān)測(cè),以實(shí)現(xiàn)設(shè)備的故障診斷和預(yù)防性維護(hù)。 航空航天與國(guó)防:在飛行器的姿態(tài)控制、導(dǎo)航系統(tǒng)以及導(dǎo)彈的制導(dǎo)等方面發(fā)揮重要作用

2、樹莓派5:

采用博通 BCM2712 芯片,搭載 64 位四核 Arm Cortex - A76 處理器,時(shí)鐘頻率為 2.4GHz。

可以運(yùn)行輕量級(jí)的 AI 模型進(jìn)行訓(xùn)練和推理,通過雙相機(jī)接口實(shí)現(xiàn)計(jì)算機(jī)視覺中的圖像處理和物體識(shí)別。

3、ESP32 是一款低功耗、高集成度的 MCU 系統(tǒng)級(jí)芯片(SoC)。提供 UART、SPI、I2C 等豐富的外設(shè)接口,便于與各類外部設(shè)備通信和連接,例如傳感器、執(zhí)行器、顯示屏等,輕松實(shí)現(xiàn)各種功能擴(kuò)展。內(nèi)置藍(lán)牙 4.2BLE(低功耗藍(lán)牙)模塊,支持藍(lán)牙設(shè)備間的無線通信與連接,方便構(gòu)建藍(lán)牙物聯(lián)網(wǎng)系統(tǒng)。

4、智能馬達(dá),智能馬達(dá)可以通過pwm實(shí)現(xiàn)智能振動(dòng),用于穿戴產(chǎn)品的智能提示功能。

5、USB攝像頭,用于圖像采集。

【設(shè)計(jì)原理】

1、ESP32實(shí)時(shí)采集ADXL354三軸的模擬電壓信號(hào),通過電壓轉(zhuǎn)換,卡曼濾波等分析后,計(jì)算出實(shí)時(shí)車速,藍(lán)牙高速傳輸給樹莓派5。

2、樹莓派5通過對(duì)ADXL354的信號(hào)分析,計(jì)算到如果實(shí)時(shí)速超過預(yù)設(shè)閾值時(shí),開始采集圖像信號(hào)。

使用dlib庫(kù)檢測(cè)人臉和人臉的 68 個(gè)特征點(diǎn),通過檢測(cè)計(jì)算眼睛特征點(diǎn)之間的距離來計(jì)算眼睛的縱橫比,以及計(jì)算嘴巴縱橫比。通過計(jì)算疲勞檢測(cè)的累計(jì),當(dāng)累計(jì)達(dá)到一定數(shù)值時(shí),產(chǎn)生預(yù)警信號(hào)。

3、當(dāng)預(yù)警信號(hào)產(chǎn)生時(shí),通過車載音箱提示駕駛員,同時(shí)通過藍(lán)牙發(fā)送預(yù)警信號(hào)給ESP32,驅(qū)動(dòng)馬達(dá)實(shí)現(xiàn)振動(dòng)提醒。
4、當(dāng)有警示事件產(chǎn)生時(shí),通過聲卡播放警示音,直到預(yù)警消失。

【項(xiàng)目實(shí)物圖】

1、項(xiàng)目總體實(shí)物圖:

2、EVAL_ADXL354CZ評(píng)估板:

3、ADXL354與ESP32的連接圖:

4、主控-樹莓派連接實(shí)物圖:

【程序代碼】

【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,用于標(biāo)識(shí)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ù)包含特征)
  聲明為全局變量,否則消失后無法使用藍(lán)牙
  */
BLEServer *pServer = nullptr;
BLEService *pService = nullptr;
BLECharacteristic *pCharacteristic = nullptr;

bool deviceConnected = false; // 跟蹤設(shè)備連接狀態(tài)
bool tick_run = false;        //馬達(dá)啟動(dòng)狀態(tài)
// 采樣時(shí)間間隔(單位:秒)
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,零點(diǎn)輸出電壓為 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;  // 測(cè)量噪聲協(xié)方差
    float x;  // 狀態(tài)估計(jì)值
    float p;  // 估計(jì)誤差協(xié)方差
    float k;  // 卡爾曼增益

  public:
    KalmanFilter(float processNoise, float measurementNoise) {
      q = processNoise;
      r = measurementNoise;
      x = 0;
      p = 1.0;
    }

    float update(float measurement) {
      // 預(yù)測(cè)
      p = p + q;

      // 計(jì)算卡爾曼增益
      k = p / (p + r);

      // 更新狀態(tài)估計(jì)值
      x = x + k * (measurement - x);

      // 更新估計(jì)誤差協(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(); // 被客戶端斷開后重新啟動(dòng)廣播
    }
};

// 特征回調(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)建卡爾曼濾波器實(shí)例
  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);

          // 計(jì)算加速度
      float accelX = (voltageX - offsetVoltage) / sensitivity;
          // 計(jì)算速度

      // 應(yīng)用一階低通濾波器
      // 應(yīng)用卡爾曼濾波
      float filteredAccelX = accelFilter.update(accelX);
      // 計(jì)算速度
      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()才有用,否則不會(huì)執(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); // 延時(shí),與采樣時(shí)間間隔對(duì)應(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)建一個(gè)BLE服務(wù)并使用之前定義的服務(wù)UUID

    // 創(chuàng)建一個(gè)BLE特征,并設(shè)置其屬性為可讀和可寫和通知
    pCharacteristic = pService->createCharacteristic(
        CHARACTERISTIC_UUID,
        BLECharacteristic::PROPERTY_READ |       // 允許客戶端讀取特征的值,當(dāng)客戶端讀取特征時(shí),服務(wù)器返回特征的當(dāng)前值
        BLECharacteristic::PROPERTY_WRITE |  // 允許客戶端寫入特征的值
        BLECharacteristic::PROPERTY_NOTIFY); // 允許服務(wù)器主動(dòng)向客戶端發(fā)送特征的更新通知

    pCharacteristic->setCallbacks(new MyCallbacks()); // 設(shè)置回調(diào)
    pCharacteristic->setValue("Hello World");         // 設(shè)置特征的初始值,當(dāng)客戶端第一次讀取這個(gè)特征時(shí),它會(huì)接收到這個(gè)初始值
    pService->start();                                // 啟動(dòng)廣播

    // BLEAdvertising 廣告是BLE設(shè)備向外廣播其存在和提供的服務(wù)的方式。通過廣告,BLE設(shè)備可以被其他設(shè)備(如手機(jī)或其他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毫秒,默認(rèn)0x18約30ms

    BLEDevice::startAdvertising(); // 啟動(dòng)廣告
    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代碼實(shí)現(xiàn)了,主要功能是采集ADXL的模擬信號(hào),并通過藍(lán)牙發(fā)送給上位機(jī),同時(shí)接收上位機(jī)的信號(hào),如果接收到1則開啟智能馬達(dá)啟動(dòng),如果收到0,則關(guān)閉馬達(dá)。

二、樹莓派

【主要的軟件環(huán)境】

1、 OpenCV(Open Source Computer Vision Library)是一個(gè)廣泛使用的開源計(jì)算機(jī)視覺庫(kù),它提供了豐富的工具和算法,可用于處理圖像和視頻數(shù)據(jù)。

2、 Dlib是一個(gè)現(xiàn)代化的 C++ 工具包,包含機(jī)器學(xué)習(xí)算法和工具,用于創(chuàng)建復(fù)雜的軟件以解決實(shí)際問題。 提供了預(yù)訓(xùn)練的人臉檢測(cè)器和特征點(diǎn)檢測(cè)器,能夠準(zhǔn)確地檢測(cè)人臉并定位人臉的關(guān)鍵點(diǎn),可用于人臉識(shí)別系統(tǒng)、表情分析等

3、 Bleak 是一個(gè)用于在 Python 中進(jìn)行藍(lán)牙低功耗(Bluetooth Low Energy, BLE)通信的庫(kù),它提供了簡(jiǎn)潔且跨平臺(tái)的 API,讓開發(fā)者能夠方便地與 BLE 設(shè)備進(jìn)行交互。

4、 其他的必要的庫(kù) 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} 不存在,請(qǐng)檢查文件路徑和文件名。")
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幀為解除疲勞

# 定義計(jì)算眼睛縱橫比(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

# 定義計(jì)算嘴巴縱橫比(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)標(biāo)志和計(jì)數(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 實(shí)例
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"播放音頻文件時(shí)發(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)鍵點(diǎ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

                # 檢測(cè)疲勞
                with lock:
                    if TOTAL_BLINKS + TOTAL_YAWNS > FATIGUE_THRESHOLD:
                        if not is_fatigue:
                            print("疲勞檢測(cè):連續(xù)眨眼次數(shù)加上打哈欠次數(shù)超過閾值,可能疲勞!")
                            is_fatigue = True
                            recovery_counter = 0  # 重置恢復(fù)計(jì)數(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' 時(shí)發(fā)生異常: {e}")
                            else:
                                print("BleakClient 未連接,無法發(fā)送數(shù)據(jù) '1'")
                            # 啟動(dòng)音頻播放線程
                            if not audio_playing:
                                audio_playing = True
                                audio_thread = threading.Thread(target=play_audio_loop, args=(audio_file,))
                                audio_thread.start()
                        TOTAL_BLINKS = 0  # 重置眨眼計(jì)數(shù)器以避免持續(xù)報(bào)警
                        TOTAL_YAWNS = 0   # 重置打哈欠計(jì)數(shù)器以避免持續(xù)報(bào)警
                    else:
                        recovery_counter += 1

            # 檢測(cè)解除疲勞
            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' 時(shí)發(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')

            # 按冒號(hào)分割字符串
            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)
        
        # 保持連接,直到手動(dòng)停止
        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()獲取人臉檢測(cè)器
    detector = dlib.get_frontal_face_detector()

    # 使用dlib的shape_predictor_68_face_landmarks.dat模型獲取面部標(biāo)志預(yù)測(cè)器
    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)

    # 啟動(dòng)線程
    face_thread.start()
    ble_thread.start()

    # 等待線程結(jié)束(理論上不會(huì)結(jié)束)
    face_thread.join()
    ble_thread.join()

【項(xiàng)目總結(jié)】

ADXL354可以實(shí)時(shí)輸出高靈敏度xyz三軸加速度模擬信號(hào)。通過ADC實(shí)時(shí)采集后可以生成實(shí)時(shí)速度。

主控樹莓派5可以實(shí)時(shí)采集圖像,通過dlib開源的AI分析,可以實(shí)時(shí)生成駕駛員的疲勞預(yù)警信號(hào),同時(shí)也可以通過藍(lán)牙與周圍的藍(lán)牙設(shè)備進(jìn)行高速實(shí)時(shí)通信。

警個(gè)系統(tǒng)實(shí)現(xiàn)了低成本的智能預(yù)警與多種方式的提醒駕駛員,為提高駕駛體驗(yàn)提供了高質(zhì)量的輔助功能,在汽車應(yīng)用方面有廣泛的應(yīng)用前景。

最后要感謝得捷電子、與非網(wǎng)、Analog Devices提供這么好的硬件平臺(tái)以及參賽機(jī)會(huì),讓我體驗(yàn)到現(xiàn)在AI技術(shù)在汽車電子應(yīng)用的前沿科技。

【附件】

程序源碼:

參考附件

作品文檔:

參考附件

  • dlib_face_test.zip
    下載
  • (劉建華)2024得捷大賽項(xiàng)目文檔-汽車疲勞駕駛預(yù)警系統(tǒng).zip
    下載
DigiKey得捷

DigiKey得捷

DigiKey 總部位于美國(guó)明尼蘇達(dá)州錫夫里弗福爾斯市,是一家獲得原廠授權(quán)的全球性、全類目電子元器件和自動(dòng)化產(chǎn)品分銷商。我們通過分銷來自 2,300 多家優(yōu)質(zhì)品牌制造商的 1,020 多萬種元器件獲得了強(qiáng)大的技術(shù)優(yōu)勢(shì)。DigiKey 還為工程師、設(shè)計(jì)師、開發(fā)者和采購(gòu)專業(yè)人員提供豐富的數(shù)字解決方案、無障礙互動(dòng)和工具支持,以幫助他們提升工作效率。在中國(guó),客戶可以通過電子郵件、電話和客服獲得全方位技術(shù)支持。如需了解更多信息和獲取 DigiKey 廣泛的產(chǎn)品,請(qǐng)?jiān)L問 www.digikey.cn 并關(guān)注我們的微信、微博、騰訊視頻和 BiliBili 賬號(hào)。

DigiKey 總部位于美國(guó)明尼蘇達(dá)州錫夫里弗福爾斯市,是一家獲得原廠授權(quán)的全球性、全類目電子元器件和自動(dòng)化產(chǎn)品分銷商。我們通過分銷來自 2,300 多家優(yōu)質(zhì)品牌制造商的 1,020 多萬種元器件獲得了強(qiáng)大的技術(shù)優(yōu)勢(shì)。DigiKey 還為工程師、設(shè)計(jì)師、開發(fā)者和采購(gòu)專業(yè)人員提供豐富的數(shù)字解決方案、無障礙互動(dòng)和工具支持,以幫助他們提升工作效率。在中國(guó),客戶可以通過電子郵件、電話和客服獲得全方位技術(shù)支持。如需了解更多信息和獲取 DigiKey 廣泛的產(chǎn)品,請(qǐng)?jiān)L問 www.digikey.cn 并關(guān)注我們的微信、微博、騰訊視頻和 BiliBili 賬號(hào)。收起

查看更多

相關(guān)推薦

繁峙县| 盐池县| 城口县| 承德市| 青浦区| 大邑县| 富裕县| 西宁市| 常德市| 外汇| 洮南市| 南丰县| 临夏县| 六安市| 新津县| 珲春市| 积石山| 康平县| 曲沃县| 临沭县| 调兵山市| 贵南县| 贡山| 绥江县| 开封市| 梨树县| 卓尼县| 年辖:市辖区| 阿拉善左旗| 岫岩| 雷波县| 南投县| 平远县| 花莲县| 江阴市| 慈利县| 通化市| 普格县| 凤台县| 大荔县| 寻乌县|