Qt + 百度云语音识别+MQTT + agent大模型 的最新imx6ull平台项目【代码开源】
这篇文章的基础上再次做了升级,因为本人在接触了agent开发后觉得可以和这个项目结合一下,原项目毕竟涉及到指令识别,本地的指令匹配不可能把所有可能的指令考虑到,更多的更复杂的指令也会使得本地规则复杂 代码冗杂,因此我就将语音识别后的文字上传到云端的agent,通过大模型进行解析输出严格统一的json格式,这样本地只需要解析json语句匹配对应的控制指令即可。所有指令处理逻辑集中在一个主线程的一个大
一个完整的嵌入式 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评价的哈哈哈,大概这个意思】
欢迎大家多多交流我也是边干边学,很多东西可能考虑不全面表述不专业多多包涵仅供大家交流参考,有什么错误还请指出谢谢哦~~
更多推荐


所有评论(0)