• 方案介绍
  • 附件下载
  • 相关推荐
申请入驻 产业图谱

基于树莓派5和ESP32的车载防疲劳驾驶预警系统

05/28 09:40
2456
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

dlib_face_test.zip

共2个文件

【项目简介】

AI的时代,AI对人类的日常生活、工作的辅助日益普及,本项目通过采集ADXL354的三轴加速度模拟信号,通过转换、卡曼滤波生成实时速度,通过蓝牙发送给主控,通过AI计算判断车辆的行驶状态,如果主控监测到处理行驶状态,启用摄像头采集驾驶员的人脸信息。主控通过实时监控,实时捕获人脸的特征点,通过AI分析,如果有疲劳的驱势,及时通过语音、智能马达振动来提醒驾驶员,实现疲劳预警的功能。

【项目框图】

【硬件简介】

1、ADXL354CZ,它是一款由 Analog Devices(亚德诺半导体)推出的评估板,用于对 ADXL354 这款低噪声、低功耗的三轴 MEMS 加速度计进行性能评估。

主要特点 支持多种量程:可支持 ±2g 或 ±8g 的加速度测量范围,能够满足不同应用场景对测量范围的需求。

?模拟输出?:生成模拟输出信号,方便与具有模拟输入接口的系统进行连接和数据采集。 小尺寸与高刚度:评估板尺寸小巧,且具有较高的刚度,在对现有系统进行评估时,能将评估板自身对系统和加速度测量的影响降至最低。

?便于连接?:设有 2 组间隔的过孔,用于安装 6 针引脚头,可轻松连接到原型开发板印刷电路板PCB)上,方便用户进行电路连接和系统集成。 工作原理:ADXL354CZ 评估板上的 ADXL354 加速度计基于微机电系统(MEMS)技术,通过检测质量块在加速度作用下产生的位移,将其转换为电信号,再经过信号调理和处理电路,最终以模拟信号的形式输出与加速度成正比的电压值。用户可以通过测量这些模拟输出信号,获取加速度计在不同方向上的加速度数据,从而评估 ADXL354 加速度计在特定应用中的性能表现。

应用领域 消费电子:如智能手机平板电脑可穿戴设备等,用于实现屏幕自动旋转、运动检测、计步等功能。 汽车电子:可用于汽车的安全系统,如碰撞检测、翻滚检测等,也可用于车辆的导航和姿态控制。 工业监测:对工业设备的振动、倾斜等状态进行监测,以实现设备的故障诊断和预防性维护。 航空航天与国防:在飞行器的姿态控制、导航系统以及导弹的制导等方面发挥重要作用

2、树莓派5:

采用博通 BCM2712 芯片,搭载 64 位四核 Arm Cortex - A76 处理器,时钟频率为 2.4GHz。

可以运行轻量级的 AI 模型进行训练和推理,通过双相机接口实现计算机视觉中的图像处理和物体识别。

3、ESP32 是一款低功耗、高集成度的 MCU 系统级芯片(SoC)。提供 UARTSPII2C 等丰富的外设接口,便于与各类外部设备通信和连接,例如传感器执行器显示屏等,轻松实现各种功能扩展。内置蓝牙 4.2BLE(低功耗蓝牙)模块,支持蓝牙设备间的无线通信与连接,方便构建蓝牙物联网系统。

4、智能马达,智能马达可以通过pwm实现智能振动,用于穿戴产品的智能提示功能。

5、USB摄像头,用于图像采集。

【设计原理】

1、ESP32实时采集ADXL354三轴的模拟电压信号,通过电压转换,卡曼滤波等分析后,计算出实时车速,蓝牙高速传输给树莓派5。

2、树莓派5通过对ADXL354的信号分析,计算到如果实时速超过预设阈值时,开始采集图像信号。

使用dlib库检测人脸和人脸的 68 个特征点,通过检测计算眼睛特征点之间的距离来计算眼睛的纵横比,以及计算嘴巴纵横比。通过计算疲劳检测的累计,当累计达到一定数值时,产生预警信号。

3、当预警信号产生时,通过车载音箱提示驾驶员,同时通过蓝牙发送预警信号给ESP32,驱动马达实现振动提醒。
4、当有警示事件产生时,通过声卡播放警示音,直到预警消失。

【项目实物图】

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.

// 服务UUID和特征UUID,用于标识BLE服务和特征
#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服务器、服务和特征的指针(服务器包含服务,服务包含特征)
  声明为全局变量,否则消失后无法使用蓝牙
  */
BLEServer *pServer = nullptr;
BLEService *pService = nullptr;
BLECharacteristic *pCharacteristic = nullptr;

bool deviceConnected = false; // 跟踪设备连接状态
bool tick_run = false;        //马达启动状态
// 采样时间间隔(单位:秒)
const float samplingInterval = 1.0;
// 定义低通滤波器系数
const float alpha = 0.1;  // 滤波系数,取值范围 0 - 1
float filteredAccelX = 0;

int adcValueX = 0;
int adcValueY = 0;
int adcValueZ = 0;

// 假设使用 ±2g 量程,灵敏度为 97.6 μV/g
const float sensitivity = 40000.6e-6; 
// 假设电源电压为 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;  // 过程噪声协方差
    float r;  // 测量噪声协方差
    float x;  // 状态估计值
    float p;  // 估计误差协方差
    float k;  // 卡尔曼增益

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

    float update(float measurement) {
      // 预测
      p = p + q;

      // 计算卡尔曼增益
      k = p / (p + r);

      // 更新状态估计值
      x = x + k * (measurement - x);

      // 更新估计误差协方差
      p = (1 - k) * p;

      return x;
    }
};


// 服务器回调
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"); // 要发送的消息
        pCharacteristic->notify();             // 发送消息
    }
     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(); // 被客户端断开后重新启动广播
    }
};

// 特征回调
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"); // 要发送的消息
        pCharacteristic->notify();             // 发送消息
    }
};


// FreeRTOS 任务函数,用于读取 ADXL354 的模拟输入值
void readADXL354Task(void *pvParameters)
{
  // 创建卡尔曼滤波器实例
  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;
          // 计算速度

      // 应用一阶低通滤波器
      // 应用卡尔曼滤波
      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) // 连接状态下notify()才有用,否则不会执行任何操作
      {
          // 格式化数据为字符串

          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); // 延时,与采样时间间隔对应
    }
}

void setup()
{
    Serial.begin(115200);
    BLEDevice::init("ESP32-C3-BLE-Server"); // 初始化BLE设备并设置设备名称

    pServer = BLEDevice::createServer();             // 创建BLE服务器
    pServer->setCallbacks(new MyServerCallbacks());  // 设置回调
    pService = pServer->createService(SERVICE_UUID); // 创建一个BLE服务并使用之前定义的服务UUID

    // 创建一个BLE特征,并设置其属性为可读和可写和通知
    pCharacteristic = pService->createCharacteristic(
        CHARACTERISTIC_UUID,
        BLECharacteristic::PROPERTY_READ |       // 允许客户端读取特征的值,当客户端读取特征时,服务器返回特征的当前值
        BLECharacteristic::PROPERTY_WRITE |  // 允许客户端写入特征的值
        BLECharacteristic::PROPERTY_NOTIFY); // 允许服务器主动向客户端发送特征的更新通知

    pCharacteristic->setCallbacks(new MyCallbacks()); // 设置回调
    pCharacteristic->setValue("Hello World");         // 设置特征的初始值,当客户端第一次读取这个特征时,它会接收到这个初始值
    pService->start();                                // 启动广播

    // BLEAdvertising 广告是BLE设备向外广播其存在和提供的服务的方式。通过广告,BLE设备可以被其他设备(如手机或其他BLE客户端)发现和连接
    BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); // 配置广告参数
    pAdvertising->addServiceUUID(SERVICE_UUID);                 // 添加服务UUID到广告包
    pAdvertising->setScanResponse(true);                        // 设置扫描响应

    // 调整最小首选连接间隔
    // 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);
    // 创建 FreeRTOS 任务
    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代码实现了,主要功能是采集ADXL的模拟信号,并通过蓝牙发送给上位机,同时接收上位机的信号,如果接收到1则开启智能马达启动,如果收到0,则关闭马达。

二、树莓派

【主要的软件环境】

1、 OpenCV(Open Source Computer Vision Library)是一个广泛使用的开源计算机视觉库,它提供了丰富的工具和算法,可用于处理图像和视频数据。

2、 Dlib是一个现代化的 C++ 工具包,包含机器学习算法和工具,用于创建复杂的软件以解决实际问题。 提供了预训练的人脸检测器和特征点检测器,能够准确地检测人脸并定位人脸的关键点,可用于人脸识别系统、表情分析等

3、 Bleak 是一个用于在 Python 中进行蓝牙低功耗(Bluetooth Low Energy, BLE)通信的库,它提供了简洁且跨平台的 API,让开发者能够方便地与 BLE 设备进行交互。

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} 存在,可以播放。")

# 定义要连接的设备的UUID
DEVICE_UUID = "E8:6B:EA:37:D4:BA"
CHARACTERISTIC_UUID = "87654321-4321-4321-4321-210987654321"

# 定义疲劳阈值
FATIGUE_THRESHOLD = 5 

# 定义解除疲劳的阈值(例如,连续正常状态的帧数)
RECOVERY_THRESHOLD = 300  # 假设连续正常状态300帧为解除疲劳

# 定义计算眼睛纵横比(EAR)的函数
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)的函数
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

# 定义阈值和连续帧数
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

# 状态标志和计数器
is_fatigue = False
recovery_counter = 0

# 打开摄像头
cap = cv2.VideoCapture(0)

# 设置摄像头分辨率和亮度
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()

# 全局事件循环
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"播放音频文件时发生异常: {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)

                # 可视化关键点
                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("疲劳检测:连续眨眼次数加上打哈欠次数超过阈值,可能疲劳!")
                            is_fatigue = True
                            recovery_counter = 0  # 重置恢复计数器
                            # 发送字符 '1' 表示疲劳
                            if client and client.is_connected:
                                print("尝试发送数据 '1'")
                                future = asyncio.run_coroutine_threadsafe(ble_send(b'x31'), loop)
                                try:
                                    future.result()  # 等待协程完成并捕获异常
                                except Exception as e:
                                    print(f"发送数据 '1' 时发生异常: {e}")
                            else:
                                print("BleakClient 未连接,无法发送数据 '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  # 重置眨眼计数器以避免持续报警
                        TOTAL_YAWNS = 0   # 重置打哈欠计数器以避免持续报警
                    else:
                        recovery_counter += 1

            # 检测解除疲劳
            with lock:
                if is_fatigue and recovery_counter >= RECOVERY_THRESHOLD:
                    print("疲劳解除:连续正常状态超过阈值,疲劳状态解除!")
                    is_fatigue = False
                    # 发送字符 '0' 表示解除疲劳
                    if client and client.is_connected:
                        print("尝试发送数据 '0'")
                        future = asyncio.run_coroutine_threadsafe(ble_send(b'x30'), loop)
                        try:
                            future.result()  # 等待协程完成并捕获异常
                        except Exception as e:
                            print(f"发送数据 '0' 时发生异常: {e}")
                    else:
                        print("BleakClient 未连接,无法发送数据 '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("尝试发送数据")
    if client and client.is_connected:
        try:
            await client.write_gatt_char(CHARACTERISTIC_UUID, data)
            print(f"发送数据成功: {data}")
        except Exception as e:
            print(f"发送数据失败: {e}")
    else:
        print("BleakClient 未连接,无法发送数据")

async def ble_receive():
    global client
    client = BleakClient(DEVICE_UUID)
    try:
        await client.connect()
        print(f"已连接到设备: {DEVICE_UUID}")
        
        # 获取所有服务
        services = await client.get_services()
        for service in services:
            print(f"服务 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 类型,先将其转换为 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("提取数字失败,数字部分不是有效的整数")
            else:
                print("数据格式不正确,无法提取数字")

        
        await client.start_notify(CHARACTERISTIC_UUID, notification_handler)
        
        # 保持连接,直到手动停止
        while True:
            await asyncio.sleep(1)  # 防止无限循环占用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模型获取面部标志预测器
    predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')

    # 创建线程
    face_thread = threading.Thread(target=face_detection)
    ble_thread = threading.Thread(target=run_ble_receive)

    # 启动线程
    face_thread.start()
    ble_thread.start()

    # 等待线程结束(理论上不会结束)
    face_thread.join()
    ble_thread.join()

【项目总结】

ADXL354可以实时输出高灵敏度xyz三轴加速度模拟信号。通过ADC实时采集后可以生成实时速度。

主控树莓派5可以实时采集图像,通过dlib开源的AI分析,可以实时生成驾驶员的疲劳预警信号,同时也可以通过蓝牙与周围的蓝牙设备进行高速实时通信。

警个系统实现了低成本的智能预警与多种方式的提醒驾驶员,为提高驾驶体验提供了高质量的辅助功能,在汽车应用方面有广泛的应用前景。

最后要感谢得捷电子、与非网、Analog Devices提供这么好的硬件平台以及参赛机会,让我体验到现在AI技术在汽车电子应用的前沿科技。

【附件】

程序源码:

参考附件

作品文档:

参考附件

  • dlib_face_test.zip
    下载
  • (刘建华)2024得捷大赛项目文档-汽车疲劳驾驶预警系统.zip
    下载
DigiKey得捷

DigiKey得捷

DigiKey 总部位于美国明尼苏达州锡夫里弗福尔斯市,是一家获得原厂授权的全球性、全类目电子元器件和自动化产品分销商。我们通过分销来自 2,300 多家优质品牌制造商的 1,020 多万种元器件获得了强大的技术优势。DigiKey 还为工程师、设计师、开发者和采购专业人员提供丰富的数字解决方案、无障碍互动和工具支持,以帮助他们提升工作效率。在中国,客户可以通过电子邮件、电话和客服获得全方位技术支持。如需了解更多信息和获取 DigiKey 广泛的产品,请访问 www.digikey.cn 并关注我们的微信、微博、腾讯视频和 BiliBili 账号。

DigiKey 总部位于美国明尼苏达州锡夫里弗福尔斯市,是一家获得原厂授权的全球性、全类目电子元器件和自动化产品分销商。我们通过分销来自 2,300 多家优质品牌制造商的 1,020 多万种元器件获得了强大的技术优势。DigiKey 还为工程师、设计师、开发者和采购专业人员提供丰富的数字解决方案、无障碍互动和工具支持,以帮助他们提升工作效率。在中国,客户可以通过电子邮件、电话和客服获得全方位技术支持。如需了解更多信息和获取 DigiKey 广泛的产品,请访问 www.digikey.cn 并关注我们的微信、微博、腾讯视频和 BiliBili 账号。收起

查看更多

相关推荐