让应用自己‘看’又会‘听’,还能懂你?——鸿蒙多模态 AI 我就问你香不香!
开篇语
哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛
今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。
我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!
前言
直说了吧:用户不想点一堆按钮,他们更想说一句话、对准一下,应用就懂了、还办得漂亮。要做到这件事,咱得把 鸿蒙(HarmonyOS / OpenHarmony)AI 子系统框架摸清,搞定语音识别(ASR)与图像识别两大入口,再用多模态感知把“看”和“听”合成“懂”。放心,下面既有架构图纸,也有能落地的示例代码(带权限与生命周期节奏),还给你一整套性能优化与工程化清单。咱一口气把“套路”讲透,少踩坑,快上线。
前言:AI 不是魔法,是“数据—模型—算力—工程”的组合拳
AI 最怕两件事:花里胡哨跑不动,和能跑但不稳。多模态又更贪心——既要实时语音、又要视觉推理、还要省电。所以我们需要模块化思维:采集 → 预处理 → 推理 → 融合 → 决策/交互,每一步都可替换、可扩展。只要接口分得清、状态管得住,AI 能力就能像乐高一样——拼得快、拆得稳、调得动。
一、AI 子系统框架:端侧为主,云端兜底,数据只走“最短路”
1) 分层视角(开发者友好版)
┌───────────────────────────────────────────────────────────┐
│ App Layer(业务编排 / UI / 多模态融合) │
├───────────────────────────────────────────────────────────┤
│ AI Service Layer(ASR / TTS / CV / OCR / NLP…接口层) │
│ • 系统能力(如端侧语音、相机管线) │
│ • NAPI 扩展(接入 MindSpore Lite / TFLite / MNN 等) │
├───────────────────────────────────────────────────────────┤
│ Runtime & Model Layer(推理运行时 / 模型管理) │
│ • 模型加载/热切换/版本与签名校验 │
│ • 推理执行(CPU/GPU/NPU)、量化/并行/流水线 │
├───────────────────────────────────────────────────────────┤
│ IO & Device Layer(麦克风 / 相机 / 编解码 / Buffer) │
│ • AudioCapturer / Camera / ImageSource │
│ • 零拷贝 Buffer / DMA(取决设备) │
└───────────────────────────────────────────────────────────┘
要点
- 端侧优先:隐私与时延第一,模型尽量本地跑,云端只做长尾兜底(如超大模型或异常纠错)。
- 统一抽象:把 ASR/CV 都抽象成“流(audio/video)+ 算子图(operators)”。接口统一,替换引擎不伤筋骨。
- 模型管理:版本、签名、兼容级别、预热与回滚要有机制,不要把 .model 文件随手一丢就开跑。
二、接入路径地图:官方能力、HMS 能力、第三方推理引擎三条路
- 系统/生态内置能力:音频采集、相机、图像解码、并发与权限。
- HMS / ML Kit 能力(可选):若目标设备生态完备,可复用语音/视觉现成服务(前提是合规与发行要求匹配)。
- 第三方端侧推理:把 MindSpore Lite / TFLite / MNN 封装成 NAPI 扩展供 ArkTS 调用,灵活可控、可离线、可量化。
实战建议:语音识别优先考虑流式 ASR(低延时),图像识别分“大模型离线分类/检测”与“云端特征检索”两段式,先本地粗筛、再云端精排(可选)。
三、语音识别(ASR)接口:从麦克风到文字的最短链路
1) 必要权限(module.json5)
{
"module": {
"name": "entry",
"requestPermissions": [
{
"name": "ohos.permission.MICROPHONE",
"reason": "用于语音指令识别与语音输入",
"usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
},
{
"name": "ohos.permission.INTERNET",
"reason": "用于云端兜底识别(仅在用户同意时)",
"usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
}
]
}
}
2) 采集与流式送入 ASR(ArkTS,示意)
思路:前台点击后创建
AudioCapturer→ 16k/16bit/Mono → 使用 Worker 线程做VAD+分帧,送入 ASR 引擎(本地 NAPI 或云端)。
// asr-capture.ts —— 语音采集 + 流式送入
import audio from '@ohos.multimedia.audio';
export async function createCapturer(): Promise<audio.AudioCapturer> {
const options: audio.AudioCapturerOptions = {
streamInfo: {
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_16000,
channels: audio.AudioChannel.CHANNEL_1,
sampleFormat: audio.AudioSampleFormat.SAMPLE_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_PCM
},
capturerInfo: { source: audio.SourceType.SOURCE_TYPE_MIC }
};
const cap = await audio.createAudioCapturer(options);
await cap.start();
return cap;
}
// 读取 PCM 并送入 ASR(伪接口 asrFeed)
export async function pumpToASR(cap: audio.AudioCapturer, asrFeed: (pcm: ArrayBuffer) => void) {
const frameBytes = 16000 /*hz*/ * 2 /*bytes*/ * 0.02 /*20ms*/; // 20ms 帧
const buf = new ArrayBuffer(frameBytes);
while (true) {
const n = await cap.read(buf, frameBytes, true); // 阻塞读
if (n <= 0) break;
asrFeed(buf); // 送入 ASR 引擎(本地/云端)
}
}
3) NAPI 封装本地 ASR 引擎(C++/伪代码)
把第三方 ASR(如端侧 WeNet/Vosk/MindSpore-Lite 模型)封成
asr.init(modelPath) / asr.feed(pcm) / asr.result()。
// napi_asr.cc(示意)—— 编译成 .so 供 ArkTS 调用
// init(modelPath), feed(pcm), result();内部做 VAD & streaming decode。
napi_value Init(napi_env env, napi_callback_info info) { /* 加载模型/创建解码器 */ }
napi_value Feed(napi_env env, napi_callback_info info) { /* 接收 PCM 帧,推进解码 */ }
napi_value Result(napi_env env, napi_callback_info info) { /* 返回增量转写/最终文本 */ }
ArkTS 调用(示意)
import asr from 'libasr.so'; // 假设已导出 NAPI
await asr.init('/data/storage/el2/base/files/models/asr_stream');
await pumpToASR(capturer, (pcm) => asr.feed(pcm));
const text = await asr.result(); // 可周期性获取增量
经验:桌面级准确率不代表移动端实时性。流式 ASR 要边听边出字,VAD(端点检测)与噪声抑制很关键。
四、图像识别接口:相机 → 预处理 → 推理 → 后处理
1) 必要权限
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.CAMERA",
"reason": "实时画面识别"
},
{
"name": "ohos.permission.READ_MEDIA",
"reason": "从相册选择图片进行识别"
}
]
}
}
2) 相机取流与帧转换(ArkTS,示意)
拍一张或取预览帧,转成 RGB/NCHW Tensor,送入本地推理引擎。
// camera-shot.ts —— 拍照取帧(示意)
import camera from '@ohos.multimedia.camera';
import media from '@ohos.multimedia.media';
export async function takePhotoAsRGBA8(): Promise<Uint8Array> {
// 省略相机会话创建/权限检查/Surface 配置
// 最终拿到一张 JPEG/YCbCr,转 RGBA8(实际可用更高效的 YUV->RGB)
const image: media.Image = await captureOneShot();
const rgba = yuvToRgba(image); // 自行实现或用图像库
return rgba;
}
3) NAPI 推理引擎(C++/伪代码)
封装 loadModel / run(inputTensor) / getOutput,底层接 TFLite/MNN/MindSpore-Lite。
// napi_cv.cc —— 加载图像模型并推理(示意)
napi_value Load(napi_env env, napi_callback_info info) { /* 读取 .mslite/.tflite */ }
napi_value Run(napi_env env, napi_callback_info info) { /* 调用推理 runtime 执行 */ }
napi_value Output(napi_env env, napi_callback_info info) { /* 返回分类/检测结果 */ }
ArkTS 调用(示意)
import cv from 'libcv.so';
await cv.load('/data/storage/el2/base/files/models/mobilenetv3.tflite');
const rgba = await takePhotoAsRGBA8();
const logits = await cv.run({ data: rgba, width: 224, height: 224, stride: 4 }); // 示例
const result = await cv.output(); // TopK 分类或检测框
小技巧:输入尺寸固定最省事;不固定尺寸就做CenterCrop/Letterbox再归一化,避免拉伸带来识别偏差。
五、智慧交互应用案例:把“会看会听”串成“会办事儿”
案例 A:“看图说话”语音助手(离线优先)
- 场景:用户用口令“这是什么?”对着物体拍一下。
- 流程:唤醒词 → 流式 ASR 捕捉意图 → 相机拍照 → 本地分类模型给出标签 → 多模态融合器把“意图+视觉标签”转成动作 → 回答/执行。
- 加分:如果本地 TopK 置信度低,匿名特征走云端精排(用户同意时)。
案例 B:“语音 + 视觉”上架审核助手(企业内)
- 语音描述商品关键信息 → 拍照 → CV 模型抽取品类/质量问题 → 多模态规则引擎决定是否放行或给出整改建议。
- 重点是结构化抽取与可视化质检报告。
案例 C:无障碍读物 & 导览
- 摄像头 OCR + 场景概括(Caption)+ 语音播报;
- 再加手势/头部朝向做交互(有硬件时),使弱视用户可“抬头—指—听到描述”。
六、性能优化策略:延迟、内存、功耗“三角平衡”
-
模型侧
- 量化(int8 线性/对称/感知):延迟可降 30–60%,谨慎校准精度;
- 裁剪 & 蒸馏:用小 backbone(MobileNetV3/EfficientNet-Lite),对目标域蒸馏;
- Batch=1 优化:移动端多为在线实时,关注单样本延迟而非吞吐。
-
算力侧
- NPU/GPU 优先(设备支持则启用),无就用 CPU Neon/AVX;
- 预热(warmup):应用启动或切前台时,低优先级跑 1–2 次假样本,避免首轮抖动;
- 零拷贝/少拷贝:尽量共享 buffer,图像解码直接贴合推理输入。
-
管线侧
- 并行流水:采集 → 预处理 → 推理 → 后处理分线程,使用有界队列;
- 早停/降级:ASR 长静音时暂停送帧;CV 仅在画面变化明显时推理(基于帧差/VMD);
- 调度策略:前台优先,后台限频;切后台即停传感器与推理。
-
I/O 与内存
- 模型缓存:按需加载,LRU 卸载;
- 贴图与纹理:避免重复转换;
- 监控:p50/p95 延迟、内存峰值、NPU 利用率、温度/降频事件。
七、隐私与合规模块:默认“端侧处理”,云端“显式同意”
- 最小化采集:语音只在唤醒后采,图像只在点击/授权后采;
- 可解释:权限
reason写人话,设置**“清除本地数据”**入口; - 端云界线:默认端侧推理,仅“匿名特征向量”可出网,且明示同意;
- 模型与日志安全:模型签名校验;日志脱敏与本地环回;
- 离线可用:弱网仍能完成主要交互(本地 ASR/关键 CV)。
八、质量与评估:别靠感觉,靠指标
- ASR:WER(词错率)、RTF(实时因子)< 1、长语音稳定性、噪声鲁棒性;
- CV:Top-1/Top-5,检测 mAP、召回/精确率、混淆矩阵;
- 交互:首响应时间(VAD->首词)、整句完成时间、误触发率、用户完成任务成功率;
- 端到端:功耗曲线、温度、降频点、异常重试率、崩溃率。
九、落地脚手架(示意目录)
ai-multimodal-app/
├─ entry/src/main/ets/
│ ├─ App.ets # 轻量全局初始化/模型预热调度
│ ├─ EntryAbility.ets # 生命周期与前后台调度
│ ├─ pages/
│ │ ├─ Home.ets # 语音/图像入口
│ │ └─ Settings.ets # 隐私与性能选项
│ ├─ ai/
│ │ ├─ asr-capture.ts # 音频采集与分帧
│ │ ├─ asr-bridge.ts # NAPI ASR 封装
│ │ ├─ camera-shot.ts # 相机取帧与预处理
│ │ ├─ cv-bridge.ts # NAPI CV 封装
│ │ └─ fusion.ts # 多模态融合与策略
├─ native/
│ ├─ asr/
│ │ ├─ napi_asr.cc # 本地 ASR NAPI
│ │ └─ runtime/… # 第三方引擎适配
│ └─ cv/
│ ├─ napi_cv.cc # 本地 CV NAPI
│ └─ runtime/… # TFLite/MNN/MindSporeLite 适配
└─ models/
├─ asr_stream/… # 端侧流式 ASR 模型
└─ mobilenetv3.tflite # 轻量分类模型
十、可直接搬用的 UI 触发 + 管线粘合(ArkTS,简化)
@Entry
@Component
struct Home {
private asrRunning: boolean = false
private text: string = ''
private label: string = ''
build() {
Column({ space: 12 }) {
Text('🧠 多模态助手').fontSize(22).fontWeight(FontWeight.Bold)
// 语音按钮
Button(this.asrRunning ? '停止识别' : '开始语音识别')
.onClick(() => this.toggleASR())
Text(`🗣️ 识别结果:${this.text}`).fontSize(16)
// 图像识别
Button('拍照识别').onClick(async () => {
const rgba = await takePhotoAsRGBA8()
const logits = await cv.run({ data: rgba, width: 224, height: 224, stride: 4 })
const r = await cv.output()
this.label = top1(r)
})
Text(`👁️ 图像标签:${this.label}`).fontSize(16)
}.padding(20)
}
async toggleASR() {
if (!this.asrRunning) {
const cap = await createCapturer()
await asr.init('/data/.../models/asr_stream')
this.asrRunning = true
pumpToASR(cap, (pcm) => asr.feed(pcm))
// 周期拉取增量
const timer = setInterval(async () => {
if (!this.asrRunning) { clearInterval(timer); return }
const t = await asr.result()
if (t) this.text = t
}, 200)
} else {
this.asrRunning = false
// 停止采集/释放资源(略)
}
}
}
小贴士:拉取增量频率别太高(200–300ms 较稳),避免 UI 抖动;识别结束记得合并标点再展示。
十一、贴墙 Checklist(上线前最后一眼 ✅)
- [ ] 权限声明人话理由,只在前台 inuse请求
- [ ] ASR 流式 + VAD + 断网降级(本地/云端切换有提示)
- [ ] CV 预处理固定尺寸 + 量化模型 + 预热
- [ ] NAPI 零拷贝/少拷贝,Buffer 复用
- [ ] 前后台切换:传感器/推理开关统一收口
- [ ] 指标:ASR WER/RTF、CV mAP、端到端 p95、功耗/温度
- [ ] 模型签名校验与可回滚、日志脱敏
- [ ] 隐私面板可一键清除本地数据与模型缓存
结语:会看会听不难,难的是“稳、快、合规、可维护”
把多模态拆成清晰的模块、用统一的接口串起来,再用端侧优先的理念“护住隐私、压住时延”,你会发现:AI 不是玄学,是工程。当你的应用做到“一句话+一张图就能帮用户把事办了”,用户不会夸你“AI 多厉害”,他们只会说:**这应用,真好用。**😉
… …
文末
好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。
… …
学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!
wished for you successed !!!
⭐️若喜欢我,就请关注我叭。
⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。
版权声明:本文由作者原创,转载请注明出处,谢谢支持!
- 点赞
- 收藏
- 关注作者
评论(0)