一个完整的嵌入式 AIoT 边缘计算实战项目

百度云语音识别 + 云端大模型语义解析 + MQTT 远程控制三链路融合
本项目是嵌入式应用开发个人项目——基于 i.MX6ULL 架构的边缘计算智能语音交互终端【代码开源】这篇文章的基础上再次做了升级,因为本人在接触了agent开发后觉得可以和这个项目结合一下,原项目毕竟涉及到指令识别,本地的指令匹配不可能把所有可能的指令考虑到,更多的更复杂的指令也会使得本地规则复杂 代码冗杂,因此我就将语音识别后的文字上传到云端的agent,通过大模型进行解析输出严格统一的json格式,这样本地只需要解析json语句匹配对应的控制指令即可。

欢迎点赞收藏,但是这个博客不一定哪天心血来潮就……,大家可以点击下面的链接star一下,同时可以关注博主,后续会有更细节的项目内容更新。
github项目开源地址(欢迎 Star & Fork):
👉 VoiceControl-SmartDevice

系统逻辑流程

麦克风              OneNET平台
  ↓                  ↓
语音识别模块          MQTT
  ↓                  ↓   
指令分发器          命令执行器
 ├── 本地控制路径 ─────┘
 │      ├── LED
 │      ├── 蜂鸣器
 │      └── 音乐播放器
 │
 └── 云端AI路径
        ↓
    HTTP请求 → Dify
        ↓
    返回结构化JSON指令
        ↓
    命令执行器

同时:
传感器线程 → MQTT → OneNET平台

其他具体的实现以及实现的功能模块本文章我就不做过多的介绍了,想了解的可以去看上面的那个文章或者去git仓库看readme描述的都很清楚,本文章我就主要说一下云端大模型语义链路

云端大模型语义链路

嵌入式终端通过 HTTPS 调用云端 Agent,流程:

语音识别 → HTTP 请求 → Dify → 返回结构化 JSON → 执行器

通过 Dify 云端 Agent,实现:

  • 复杂语义理解
  • 多步骤指令解析
  • 自然语言转结构化控制
用户:“下一首”
agent响应:{  "type": "control",  "target": "music",  "command": "next",  "params": {}}
用户: 关灯
agent响应: {"type": "control", "target": "led", "command": "off", "params": {}}

通信模块实现

使用http调用实现,具体通信实现请参考工作室界面的API文档,他已经给出了接口地址和右上角的api密钥。
在这里插入图片描述
他在文档中已经详细说明介绍了发送请求的方式和消息结构:
在这里插入图片描述
需要使用Bearer Token

在qt中,构造请求:

QNetworkRequest request(QUrl("https://api.dify.ai/v1/chat-messages"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Authorization", "Bearer " + apiKey.toUtf8());

构造 JSON 请求体:

QJsonObject json;
json["inputs"] = QJsonObject();
json["query"] = recognizedText;
json["response_mode"] = "blocking";
json["conversation_id"] = QJsonValue();
json["user"] = "imx6ull_device_001";
QJsonDocument doc(json);
QByteArray data = doc.toJson();
字段 作用
query ASR 识别文本
response_mode blocking 同步等待
conversation_id 会话上下文
user 设备唯一标识

发送请求:

QNetworkReply* reply = manager->post(request, data);

响应数据解析:
Dify 在 blocking 模式下返回结构:

{
  "answer": "{ \"type\": \"control\", \"target\": \"music\", \"command\": \"play\" }"
}

answer是字符串类型的JSON,需要二次解析。

int startIndex = text.indexOf('{');
    int endIndex = text.lastIndexOf('}');
    
    if (startIndex != -1 && endIndex != -1 && endIndex > startIndex) {
        QString jsonString = text.mid(startIndex, endIndex - startIndex + 1);
        qDebug() << "提取到JSON子串:" << jsonString;
        
        QJsonDocument doc = QJsonDocument::fromJson(jsonString.toUtf8(), &parseError);
        
        if (parseError.error == QJsonParseError::NoError && doc.isObject()) {
            QJsonObject tempObj = doc.object();
            
            // 检查是否包含必需字段
            if (tempObj.contains("type") && tempObj.contains("target") && tempObj.contains("command")) {
                commandObj = tempObj;
                qDebug() << "成功提取控制指令(JSON子串解析):" << jsonString;
                return true;
            }
        }
    }

查找文本中第一个 { 和最后一个 } 的位置,提取这两个位置之间的内容作为JSON字符串,尝试将其解析为JSON对象

agent设计

agent基于dify平台开发其实很简单我就只设置了一个LLM节点进行处理后直接输出(chatflow):
在这里插入图片描述
LLM prompt设置

你是一个【嵌入式设备控制语义解析 Agent】。

你的职责:
- 接收用户的自然语言指令{{#context#}}
- 将指令解析为【结构化设备控制命令】
- 输出结果必须是【严格 JSON 格式】,用于嵌入式设备端自动解析与执行
-无法匹配任何已知指令,返回 error 类型 JSON

⚠️【重要规则 — 必须严格遵守】:
1. 只允许输出 JSON,不允许输出任何解释、注释、自然语言
2. JSON 结构必须完全符合下方“命令协议”,不要包含换行符或额外的格式化
3. 不允许缺失字段,不允许新增字段
4. 如果无法匹配任何已知指令,返回 error 类型 JSON
5. 所有字段值必须使用小写英文
6. params 必须是一个 JSON 对象,不可为 null(无参数时返回空对象 {})

--------------------------------
【统一命令协议(必须严格遵守)】

{
  "type": "control | query | error",
  "target": "led | beep| music | sensor | unknown",
  "command": "on | off | alarm | play | pause | stop | next | prev | getdata | unknown",
  "params": {}
}

--------------------------------
【已支持的语义能力】

1️⃣ 硬件控制:
- “开灯” → led + on
- “关灯” → led + off
- “报警” / “鸣笛” → beep+ alarm

2️⃣ 音乐控制:
- “播放音乐 / 播放歌曲” → music + play
- “暂停音乐 / 暂停歌曲” → music + pause
- “停止音乐 / 停止歌曲” → music + stop
- “下一首 / 下一曲” → music + next
- “上一首 / 上一曲” → music + prev

3️⃣ 环境查询:
- “环境数据 / 光照强度如何 / 传感器数据” → sensor + getdata
--------------------------------
【错误处理规则返回error 类型 JSON】
当用户输入无法匹配任何上述已支持命令时,必须返回:

{  "type": "error",  "target": "unknown",  "command": "unknown",  "params": {}}
--------------------------------
【示例(仅用于理解,不可原样输出)】
用户:“下一首”
正确输出:{  "type": "control",  "target": "music",  "command": "next",  "params": {}}
用户: 关灯
期望响应: {"type": "control", "target": "led", "command": "off", "params": {}}
--------------------------------
【再次强调】

- 不要推理过程
- 不要解释
- 不要输出多余文本
- 只输出 JSON,且不要包含换行符

我用的是deepseek-chat模型,不过对于复杂指令的识别他有时候也处理不好真心不如gpt、gemini但是鉴于我目前有他的token就用了这个。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到它可以理解复杂的语义信息转成设备可理解的json语句

指令执行器设计(CommandExecutor)

在之前的版本中,我的控制逻辑是典型的直接控制模式:
所有指令处理逻辑集中在一个主线程的一个大函数中,使用大量if-else判断,每个指令需要遍历所有if-else判断,时间复杂度O(n)

ASR识别文本
  ↓
if/else 关键词匹配
  ↓
直接调用硬件接口

这种方式在功能简单时尚可,但随着系统复杂度提升,指令复杂度提升,逻辑会越来越臃肿,下面还仅仅是部分功能。且运行在主线程中,加重主线程任务导致阻塞。

/*------------------------------原本地识别功能执行函数--------------------------------------*/
void MainWindow::onAsrReadyData(QString str)
{
    m_sensorThread->startCapture();//恢复传感器采集线程
    if (str.contains("开灯"))
    {
        myLed->setLedState(true);
        myMqttClient->publishDeviceState("led", true);
    }        

    else if (str.contains("关灯")){
        myLed->setLedState(false);
        myMqttClient->publishDeviceState("led", false);
    }

    else if (str.contains("报警")||str.contains("鸣笛")){
        myBeep->setbeepState(true);
        myMqttClient->publishDeviceState("beer", true);
    }


    // 新增:处理传感器数据查询
    else if (str.contains("光照强度") || str.contains("传感器数据") || str.contains("环境数据")) {
        qDebug() << "执行传感器数据查询";
        // 确保线程已启动
        if (!m_sensorThread) {
            qDebug() << "传感器线程未初始化";
            return;
        }

        if (!m_sensorThread->isRunning()) {
            m_sensorThread->startCapture();
            qDebug() << "启动传感器线程";
        }
         // 立即获取并打印数据
        QString alsData = m_sensorThread->getAlsData();
        qDebug() << "获取的光照强度:" << alsData;
        // qDebug() <<"传感器数据"<<m_sensorThread->getAlsData();

        //更新UI显示传感器数据
        m_showingSensorData = true;
        sensorDataLabel->show(); // 显示传感器数据标签
        textLabel->setText("正在获取环境数据...");

        // 更新传感器数据显示
        QString als = m_sensorThread->getAlsData();
        QString ps = m_sensorThread->getPsData();
        QString ir = m_sensorThread->getIrData();

        QString sensorText = QString("传感器数据:\n光照强度:%1 接近距离:%2 红外数据:%3")
                        .arg(als)
                        .arg(ps)
                        .arg(ir);

        sensorDataLabel->setText(sensorText);
        textLabel->setText("环境数据已更新");


        // 7秒后自动停止显示传感器数据
        QTimer::singleShot(7000, [this]() {
           m_showingSensorData = false;
           sensorDataLabel->hide();
           textLabel->setText("请点击,开始说话...");
       });
       }

引入 CommandExecutor 的设计思想

为了解决上述问题,我引入了统一执行层:CommandExecutor
核心思想:

所有控制来源,都只负责“生成结构化指令”
执行逻辑统一在一个模块完成

定义统一指令格式:

{
  "type": "control",
  "target": "light",
  "command": "on",
  "params": {}
}

CommandExecutor使用一个QHash映射表来存储指令字符串与对应的执行函数:

QHash<QString, std::function<void()>> m_controlCommands;

在构造函数中,系统会注册所有支持的控制指令:

CommandExecutor::CommandExecutor(QObject *parent) : QObject(parent)
{
    // LED控制指令
    m_controlCommands["led_on"] = [this]() { ledOn(); };
    m_controlCommands["led_off"] = [this]() { ledOff(); };
    
    // 蜂鸣器控制指令
    m_controlCommands["beep_on"] = [this]() { beepOn(); };
    m_controlCommands["beep_off"] = [this]() { beepOff(); };
    
    // 音乐控制指令
    m_controlCommands["music_play"] = [this]() { musicPlay(); };
    m_controlCommands["music_pause"] = [this]() { musicPause(); };
    m_controlCommands["music_stop"] = [this]() { musicStop(); };
    m_controlCommands["music_next"] = [this]() { musicNext(); };
    m_controlCommands["music_prev"] = [this]() { musicPrevious(); };
    
    // 传感器数据查询指令
    m_controlCommands["sensor_query"] = [this]() { querySensorData(); };
}

通过引入统一的 CommandExecutor 作为指令执行层,相当于建立了一个“中央调度中心(AppController)”实现业务逻辑与 UI 的彻底分离,系统从“界面驱动控制”升级为“业务驱动架构”。UI 仅负责数据展示与信号触发,所有控制路由(如“开灯”指令分发)、传感器阈值判断、云端指令解析等业务逻辑,全部由 Controller 在非 UI 线程中处理。这样不仅消除了控制逻辑散落在界面层导致的高耦合问题,还显著减轻了主线程负担,避免因网络请求、JSON 解析或复杂判断阻塞 GUI 事件循环,从而保证界面始终流畅响应。在嵌入式 Qt 场景下,这种设计本质上提升了系统的并发能力、可维护性与扩展性,使架构具备工程级稳定性,而非简单功能堆叠。
【这段话是ai评价的哈哈哈,大概这个意思】

欢迎大家多多交流我也是边干边学,很多东西可能考虑不全面表述不专业多多包涵仅供大家交流参考,有什么错误还请指出谢谢哦~~

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐