这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 树莓派5小智AI汽车仪表

共1条 1/1 1 跳转至

树莓派5小智AI汽车仪表

菜鸟
2025-06-23 12:48:22     打赏

一、项目概述

本系统基于树莓派5构建一个现代化的智能汽车仪表盘核心平台,利用其强大的计算能力和丰富的接口,专注于车辆内部数据的实时采集、姿态动态感知以及智能语音助手“小智”的自然交互。系统核心目标是采集并处理车辆本身的运行状态信息以及车辆在行驶中的动态姿态,并通过精心设计的用户界面和语音交互,为驾驶员提供全面、直观、安全的行车状态反馈和辅助操作体验。

系统特点

实时车辆状态监控(速度、转速、温度等)

车辆姿态感知(基于MPU6050传感器)

小智智能助手交互系统

模块化设计,易于扩展和维护

高性能的Qt图形界面

二、系统架构

2.1 整体架构图

deepseek_mermaid_20250623_ead239.png

2.2 硬件连接图

deepseek_mermaid_20250623_3b1f18.png

2.3 实物连接图

7e9ee71f3c66a360477017c5d4f4c6f.jpg

2.4详细数据流程图

image.png

三、核心模块设计3.1 数据采集层3.1.1 OBD-II数据采集模块

通过MCP2515 CAN控制器获取车辆数据

支持标准OBD-II PID协议

采集参数:

发动机转速 (RPM)

车速 (km/h)

冷却液温度 (°C)

燃油压力 (kPa)

燃油水平 (%)

发动机负载 (%)

3.1.2 MPU6050姿态采集模块

通过I²C接口读取传感器数据

实时计算:

三轴加速度 (m/s²)

三轴角速度 (°/s)

俯仰角、横滚角 (°)

使用Madgwick算法进行姿态解算

3.1.3 小智服务模块

语音识别与语义理解

状态管理:

待机状态

监听中

说话中

思考中

连接中

错误状态

文本生成与响应

3.2 数据传输层 (IPC/UDP管理器)

3.2.1 数据流图

deepseek_mermaid_20250623_217115.png

3.2.2 端口配置

端口数据类型描述
5001SIDE_UI小智状态/文本数据
5002MPU6050_DATA姿态传感器数据
5003OBD_DATA车辆OBD数据

3.3 用户界面层3.3.1 主仪表盘

速度表 (0-240 km/h)

转速表 (0-8000 RPM)

姿态指示器 (俯仰角/横滚角)

3.3.2 小智窗口部件

状态动画系统:

deepseek_mermaid_20250623_3effa9.png

文本气泡显示

交互控制:

窗口拖拽移动

关闭按钮

设置按钮

四、IPC核心代码

#include "ipcudp.h"
#include <QNetworkDatagram>
#include <QJsonDocument>
#include <QJsonParseError>
#include <QDebug>
#include <QDateTime>
#include <cmath>

ipc::ipc(QObject *parent)
    : QObject(parent), m_isRunning(false)
{
}

ipc::~ipc()
{
    ipc_stop();
}

bool ipc::addPort(quint16 port, DataType dataType)
{
    if (m_portMap.contains(port)) {
        emit errorOccurred(tr("端口 %1 已被占用").arg(port));
        return false;
    }

    if (!createSocket(port)) {
        emit errorOccurred(tr("无法绑定端口 %1").arg(port));
        return false;
    }

    m_portMap.insert(port, dataType);
    emit statusChanged(tr("添加端口 %1 用于接收 %2 数据")
                           .arg(port).arg(dataTypeToString(dataType)));
    return true;
}

void ipc::removePort(quint16 port)
{
    if (m_socketMap.contains(port)) {
        QUdpSocket *socket = m_socketMap.value(port);
        socket->close();
        socket->deleteLater();
        m_socketMap.remove(port);
        m_portMap.remove(port);
        emit statusChanged(tr("移除端口 %1").arg(port));
    }
}

void ipc::ipc_start()
{
    // 如果没有端口,添加默认端口
    if (m_portMap.isEmpty()) {
        addPort(DEFAULT_DASHBOARD_UI_PORT, DASHBOARD_UI);
        addPort(DEFAULT_SIDE_UI_PORT, SIDE_UI);
        addPort(DEFAULT_MPU6050_PORT, MPU6050_DATA);
        addPort(DEFAULT_GPS_PORT, GPS_DATA);
        addPort(DEFAULT_OBD_PORT, OBD_DATA);
        emit statusChanged("添加了所有默认端口");
    }

    m_isRunning = true;
    emit statusChanged("开始接收数据");
}

void ipc::ipc_stop()
{
    foreach (QUdpSocket *socket, m_socketMap.values()) {
        socket->close();
        socket->deleteLater();
    }
    m_socketMap.clear();
    m_portMap.clear();
    m_isRunning = false;
    emit statusChanged("停止接收数据");
}

bool ipc::createSocket(quint16 port)
{
    QUdpSocket *socket = new QUdpSocket(this);

    if (!socket->bind(QHostAddress::Any, port)) {
        delete socket;
        return false;
    }

    connect(socket, &QUdpSocket::readyRead, this, &ipc::processPendingDatagrams);
    m_socketMap.insert(port, socket);
    return true;
}

void ipc::processPendingDatagrams()
{
    if (!m_isRunning) return;

    // 获取发送信号的套接字
    QUdpSocket *socket = qobject_cast<QUdpSocket*>(sender());
    if (!socket) return;

    // 获取套接字对应的端口
    quint16 port = m_socketMap.key(socket, 0);
    if (port == 0) return;

    // 获取套接字对应的数据类型
    DataType dataType = m_portMap.value(port, UNKNOWN_DATA);

    while (socket->hasPendingDatagrams()) {
        QNetworkDatagram datagram = socket->receiveDatagram();
        QByteArray data = datagram.data();

        // 解析JSON数据
        QJsonObject jsonData = parseJsonData(data);

        if (!jsonData.isEmpty()) {
            // 添加元数据
            jsonData["_source_ip"] = datagram.senderAddress().toString();
            jsonData["_source_port"] = static_cast<int>(datagram.senderPort());
            jsonData["_receive_time"] = QDateTime::currentDateTime().toString(Qt::ISODate);

            // 处理数据
            processData(dataType, jsonData);

            // 发出通用数据接收信号
            emit dataReceived(dataType, jsonData);
        } else {
            emit errorOccurred(tr("端口 %1 接收到无效 JSON 数据").arg(port));
        }
    }
}

QJsonObject ipc::parseJsonData(const QByteArray &data)
{
    QJsonParseError parseError;
    QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);

    if (parseError.error != QJsonParseError::NoError) {
        emit errorOccurred(tr("JSON 解析错误: %1").arg(parseError.errorString()));
        return QJsonObject();
    }

    if (!doc.isObject()) {
        emit errorOccurred("接收的数据不是有效的 JSON 对象");
        return QJsonObject();
    }

    return doc.object();
}

void ipc::processData(DataType dataType, const QJsonObject &json)
{
    switch (dataType) {
    case DASHBOARD_UI:
        processDashboardUIData(json);
        break;
    case SIDE_UI:
        processSideUIData(json);
        break;
    case MPU6050_DATA:
        processMPU6050Data(json);
        break;
    case GPS_DATA:
        processGPSData(json);
        break;
    case OBD_DATA:
        processOBDData(json);
        break;
    default:
        // 未知数据类型,不做特殊处理
        break;
    }
}

void ipc::processDashboardUIData(const QJsonObject &json)
{
    // 处理速度 - 单位: km/h
    if (json.contains("speed")) {
        double speed = json["speed"].toDouble();
        qDebug() << "仪表速度更新:" << speed << "km/h";
        emit dashboardSpeedChanged(speed);
    }

    // 处理转速 - 单位: RPM
    if (json.contains("rpm")) {
        int rpm = json["rpm"].toInt();
        qDebug() << "仪表转速更新:" << rpm << "RPM";
        emit dashboardRpmChanged(rpm);
    }

    // 处理油量 - 百分比
    if (json.contains("fuel")) {
        double fuel = json["fuel"].toDouble();
        if (fuel >= 0 && fuel <= 100) {
            qDebug() << "仪表油量更新:" << fuel << "%";
            emit dashboardFuelLevelChanged(fuel);
        }
    }

    // 处理档位
    if (json.contains("gear")) {
        QString gear = json["gear"].toString();
        qDebug() << "仪表档位更新:" << gear;
        emit dashboardGearChanged(gear);
    }

    // 处理温度 - 单位: °C
    if (json.contains("temp")) {
        double temp = json["temp"].toDouble();
        qDebug() << "仪表温度更新:" << temp << "°C";
        emit dashboardTemperatureChanged(temp);
    }
}

void ipc::processSideUIData(const QJsonObject &json)
{
    // 处理状态字段
    if (json.contains("state")) {
        int stateValue = json["state"].toInt(-1);
        if (stateValue >= kDeviceStateUnknown && stateValue <= kDeviceStateFatalError) {
            DeviceState state = static_cast<DeviceState>(stateValue);
            QString stateStr = convertStateToString(state);
            qDebug() << "小窗口状态更新:" << stateStr;
            emit sideUIStateChanged(stateStr);

            // 根据状态设置表情
            if (state == kDeviceStateSpeaking) {
                emit sideUIEmotionChanged("img_joke.png");
            } else if (state == kDeviceStateListening) {
                emit sideUIEmotionChanged("img_naughty.png");
            }
        }
    }

    // 处理文本字段
    if (json.contains("text")) {
        QString text = json["text"].toString();
        qDebug() << "小窗口文本更新:" << text;
        emit sideUITextUpdated(text);
    }

    // 处理WIFI强度字段
    if (json.contains("wifi")) {
        int wifi = json["wifi"].toInt(-1);
        if (wifi >= 0 && wifi <= 100) {
            emit sideUIWifiStrengthChanged(wifi);
        }
    }

    // 处理电量字段
    if (json.contains("battery")) {
        int battery = json["battery"].toInt(-1);
        if (battery >= 0 && battery <= 100) {
            emit sideUIBatteryLevelChanged(battery);
        }
    }
}

void ipc::processMPU6050Data(const QJsonObject &json)
{
    // 检查必要字段
    if (json.contains("accelX") && json.contains("accelY") && json.contains("accelZ") &&
        json.contains("gyroX") && json.contains("gyroY") && json.contains("gyroZ")) {

        // 发出MPU6050数据信号
        emit mpu6050DataReceived(json);
    } else {
        emit errorOccurred("MPU6050数据缺少必要字段");
    }
}

void ipc::processGPSData(const QJsonObject &json)
{
    // 检查必要字段
    if (json.contains("latitude") && json.contains("longitude")) {
        // 发出GPS数据信号
        emit gpsDataReceived(json);
    } else {
        emit errorOccurred("GPS数据缺少必要字段");
    }
}

void ipc::processOBDData(const QJsonObject &json)
{
    // 检查必要字段
    if (json.contains("rpm") && json.contains("speed") &&
        json.contains("coolant_temp")) {

        // 更新仪表数据
        emit dashboardRpmChanged(json["rpm"].toInt());
        emit dashboardSpeedChanged(json["speed"].toDouble());
        emit dashboardTemperatureChanged(json["coolant_temp"].toDouble());

        // 发出OBD数据信号
        emit obdDataReceived(json);
    } else {
        emit errorOccurred("OBD数据缺少必要字段");
    }
}

QString ipc::convertStateToString(DeviceState state)
{
    switch (state) {
    case kDeviceStateUnknown:         return tr("未知状态");
    case kDeviceStateStarting:        return tr("初始化中");
    case kDeviceStateWifiConfiguring: return tr("网络配置中");
    case kDeviceStateIdle:            return tr("待机状态");
    case kDeviceStateConnecting:      return tr("连接中");
    case kDeviceStateListening:       return tr("监听中");
    case kDeviceStateSpeaking:        return tr("说话中");
    case kDeviceStateUpgrading:       return tr("升级中");
    case kDeviceStateActivating:      return tr("激活中");
    case kDeviceStateFatalError:      return tr("错误状态");
    }
    return tr("未知状态");
}

QString ipc::dataTypeToString(DataType type) const
{
    switch (type) {
    case DASHBOARD_UI: return "仪表主界面数据";
    case SIDE_UI:      return "侧边小窗口UI数据";
    case MPU6050_DATA: return "MPU6050传感器数据";
    case GPS_DATA:     return "GPS数据";
    case OBD_DATA:     return "OBD数据";
    default:           return "未知数据类型";
    }
}

QMap<quint16, ipc::DataType> ipc::getActivePorts() const
{
    return m_portMap;
}

五、成果展示

视频网址:树莓派5仪表盘哔哩哔哩bilibili

image-20250623114454284.png

image-20250623114520197.png

image-20250623114555334.png


共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]