RK3588 部署 Qwen3-VL 多模态大模型:RKNN-LLM 项目部分实践日志
RK3588 部署 Qwen3-VL 多模态大模型:RKNN-LLM 项目部分实践日志
本文整理自
RKNN-LLM项目的实际目录、构建脚本和 RK3588 运行链路,作为项目的公开使用说明。本文重点介绍当前项目中已经验证的 Qwen3-VL-2B 局域网 Web Demo,同时说明仓库内的 RKLLM API 示例、服务端示例、模型转换脚本、运行时库和性能测试模块。
一、文章摘要
本文介绍如何在 Rockchip RK3588 开发板上使用 RKNN、RKLLM Runtime 和 RKLLM Toolkit 部署 Qwen3-VL 多模态大模型,实现以下功能:
- 在 RK3588 NPU 上运行 Qwen3-VL-2B。
- 通过局域网网页完成图片问答和连续文字对话。
- 使用 Python Web 服务、SSE 和 Unix Socket 连接 C++ 推理进程。
- 通过 Sherpa-Onnx + RKNN 接入 Type-C 耳机麦克风,实现中文实时语音识别。
- 通过受限 Function Calling 控制本地音乐播放。
- 保留 RKLLM 原生 C++ API 示例、OpenAI 兼容服务示例和模型转换流程。
关键词: RK3588、RKNN、RKLLM、Qwen3-VL、边缘计算、NPU、RKNN-Toolkit2、Sherpa-Onnx、语音识别、局域网 Web
二、项目最终效果
项目的核心运行形态如下:
电脑浏览器
│ HTTP / SSE
▼
Python Web 网关
│ Unix Socket 长度帧协议
▼
C++ Qwen3-VL Worker
├── RKNN 视觉编码器
└── RKLLM Qwen3-VL-2B
Type-C 耳机麦克风
│ ALSA
▼
独立 ASR 进程
│ Sherpa-Onnx + RKNN Zipformer
▼
ASR 最终文本自动提交到 Web 网关
打开浏览器访问开发板地址后,可以完成以下操作:
- 输入文字,让本地大模型回答。
- 上传一张 JPG、PNG 或 WebP 图片并提问。
- 在上一轮对话的基础上继续追问。
- 点击麦克风按钮,通过 Type-C 耳机输入中文语音。
- 发送“播放某首歌”等指令,让模型调用白名单音乐工具。
- 点击“新建对话”或调用接口清理当前上下文。
三、RKNN、RKLLM 和 Toolkit 的关系
很多初次接触 Rockchip NPU 的开发者容易把几个组件混在一起。它们的职责并不相同。
| 组件 | 主要职责 | 运行位置 |
|---|---|---|
| RKNN-Toolkit2 | 将 ONNX 等视觉模型转换为 RKNN 模型 | PC 或服务器 |
| RKNN Runtime | 在 Rockchip 芯片上执行 RKNN 模型 | 开发板 |
| RKLLM-Toolkit | 将 HuggingFace 语言模型量化并转换为 RKLLM 模型 | PC 或服务器 |
| RKLLM Runtime | 在开发板上加载并推理 RKLLM 模型 | 开发板 |
| Qwen3-VL Vision | 图片编码和视觉特征提取 | RKNN/NPU |
| Qwen3-VL LLM | 根据文本和视觉特征生成回答 | RKLLM/NPU |
| Sherpa-Onnx | 流式语音识别 | 独立 Python 进程和 RKNN/NPU |
Qwen3-VL 的部署不是把一个完整模型直接复制到开发板,而是拆成两条推理链路:
图片
→ Vision Encoder
→ RKNN 模型
→ image embedding
→ RKLLM 语言模型
→ 文本回答
四、项目目录说明
仓库是上游 RKLLM SDK、示例代码和当前 RK3588 Web Demo 的组合。重点目录如下:
rknn-llm-main/
├── README.md # 项目总说明
├── LICENSE # Rockchip 和第三方许可证说明
├── benchmark.md # 各平台模型性能数据
├── CHANGELOG.md # RKLLM 版本变更记录
├── .gitignore # 模型、运行库和构建产物忽略规则
│
├── doc/
│ ├── rk3588-qwen3-vl-web-reproduction.md
│ ├── sherpa-onnx-rk3588-asr.md
│ └── rk3588-qwen3-vl-rknn-llm-csdn.md # 本文
│
├── examples/
│ ├── multimodal_model_demo/
│ │ ├── data/ # 数据集和本地模型目录
│ │ ├── export/ # Vision/RKLLM 模型转换脚本
│ │ ├── deploy/ # 原生 C++ 多模态示例
│ │ └── web/ # 当前局域网 Web Demo
│ ├── rkllm_api_demo/ # 原生 RKLLM API 示例
│ └── rkllm_server_demo/ # Flask/OpenAI API 和 Gradio 示例
│
├── rkllm-runtime/ # Linux/Android RKLLM Runtime 头文件和库
├── rkllm-toolkit/ # RKLLM Toolkit 示例与 Python 依赖
└── scripts/ # 频率设置和性能测试脚本
当前 Web Demo 的核心目录:
examples/multimodal_model_demo/web/
├── server.py # Python HTTP、SSE 和会话网关
├── asr_service.py # 独立 Sherpa-Onnx ASR 进程
├── tools.py # 音乐工具解析和安全执行
├── run-web.sh # 启动、清理和参数入口
├── build-web.sh # CMake 构建入口
├── CMakeLists.txt # C++ Worker 构建配置
├── static/
│ ├── index.html # Web 页面
│ ├── app.js # 前端交互和 SSE 处理
│ └── style.css # 页面样式
└── worker/
└── qwen3_vl_worker.cpp # RKNN + RKLLM 常驻推理进程
五、硬件和软件环境
5.1 硬件要求
- Rockchip RK3588 或兼容的 Rockchip NPU 平台。
- Linux
aarch64系统。 - 可用的 RKNN NPU 驱动和设备节点。
- 如果启用语音识别,需要一个 ALSA 可识别的麦克风。
- 如果启用音乐播放,需要开发板连接音频输出设备。
5.2 基础软件
gcc
g++
cmake
python3
mpv # 可选,音乐播放需要
alsa-utils # 可选,检查麦克风需要
检查环境:
uname -m
python3 --version
cmake --version
command -v gcc
command -v g++
本文示例统一使用以下变量,避免把个人目录写死到命令中:
export PROJECT_DIR="$HOME/rknn-llm-main"
export WEB_DIR="$PROJECT_DIR/examples/multimodal_model_demo/web"
cd "$PROJECT_DIR"
六、获取项目和准备模型
6.1 获取源码
如果使用本人的 GitHub 仓库(请点star,跪谢):
git clone https://github.com/shaddockpeel2/RKNN-LLM.git
cd RKNN-LLM
如果使用上游仓库:
git clone https://github.com/airockchip/rknn-llm.git
cd rknn-llm
6.2 准备 Qwen3-VL 模型(参考另一篇文章,可跳过下面部分转换内容-直接到八)
当前 Web Demo 默认查找以下两个文件:
examples/multimodal_model_demo/data/rknn/qwen3-vl_vision_rk3588.rknn
examples/multimodal_model_demo/data/rkllm/qwen3-vl-2b-instruct_w8a8_rk3588.rkllm
检查模型:
cd "$PROJECT_DIR"
test -f examples/multimodal_model_demo/data/rknn/qwen3-vl_vision_rk3588.rknn
test -f examples/multimodal_model_demo/data/rkllm/qwen3-vl-2b-instruct_w8a8_rk3588.rkllm
七、模型转换流程
如果已经拿到了可用的 .rknn 和 .rkllm 模型,可以直接跳过本节。下面介绍项目中保留的转换脚本。
7.1 安装 PC 端工具链
多模态模型转换至少涉及:
rkllm-toolkit >= 1.3.0
rknn-toolkit2 >= 2.3.2
RKLLM Toolkit 的 Python 依赖位于:
rkllm-toolkit/packages/requirements.txt
建议在独立虚拟环境中安装转换依赖,不要把 PC 端转换环境和开发板运行环境混在一起。
7.2 Vision 模型导出为 ONNX
项目的 export_vision.py 支持多种视觉模型。Qwen3-VL 示例:
cd "$PROJECT_DIR/examples/multimodal_model_demo/export"
python export_vision.py \
--path /path/to/Qwen3-VL-2B-Instruct \
--model_name qwen3-vl \
--batch_size 1 \
--height 448 \
--width 448 \
--device cpu
脚本会在当前 export 目录下生成类似文件:
onnx/qwen3-vl_vision.onnx
注意以下参数必须和后续推理链路保持一致:
model_name。- 输入图片的高和宽。
- batch size。
- Qwen3-VL 的 patch size 和
grid_thw计算方式。 - 图像特殊 token 和模型配置。
7.3 ONNX 转换为 RKNN
cd "$PROJECT_DIR/examples/multimodal_model_demo/export"
python export_vision_rknn.py \
--path ./onnx/qwen3-vl_vision.onnx \
--model_name qwen3-vl \
--target-platform rk3588 \
--batch_size 1 \
--height 448 \
--width 448
输出目录通常为:
rknn/qwen3-vl_vision_rk3588.rknn
export_vision_rknn.py 会根据模型类型设置输入、均值、标准差和 grid_thw。不要仅仅修改输出文件名,而忽略输入尺寸和预处理配置。
7.4 生成 RKLLM 量化校准数据
多模态语言模型量化需要校准数据。项目提供了数据集和输入 embedding 生成脚本:
cd "$PROJECT_DIR/examples/multimodal_model_demo"
python data/make_input_embeds_for_quantize.py \
--path /path/to/Qwen3-VL-2B-Instruct \
--model_type qwen3vl
生成的校准数据位于:
data/inputs.json
7.5 导出 RKLLM 模型
cd "$PROJECT_DIR/examples/multimodal_model_demo"
python export/export_rkllm.py \
--path /path/to/Qwen3-VL-2B-Instruct \
--target-platform rk3588 \
--num_npu_core 3 \
--quantized_dtype w8a8 \
--device cpu \
--savepath /tmp/qwen3-vl-2b.rkllm
当前脚本会根据模型名称、量化类型和目标平台组织最终输出路径,默认输出到 export 目录下的 rkllm/。--savepath 参数在当前脚本中会被解析,但实际命名仍由脚本内部规则决定;如果需要完全自定义名称,应先检查或调整脚本。
7.6 模型转换常见问题
- PC 端
transformers版本必须和模型代码兼容。 - Vision 和 LLM 必须来自同一模型版本。
- 量化校准数据不能随意缺失,否则容易导致转换失败或精度下降。
max_context_len必须能够容纳文本 token、图片 token 和新生成 token。- 转换成功不等于板端一定能运行,还需要匹配 Runtime、平台和 NPU 核心参数。
八、编译 Qwen3-VL Web Worker
8.1 进入 Web 目录
cd "$PROJECT_DIR/examples/multimodal_model_demo/web"
8.2 在 RK3588 板端直接编译
GCC_COMPILER=gcc ./build-web.sh
构建脚本会:
- 清理并创建
web/build/。 - 使用 CMake 配置 C/C++ 编译器。
- 编译
qwen3_vl_worker。 - 将 Worker 和 RKNN/RKLLM 动态库安装到
web/install/。
构建结果:
web/install/bin/qwen3_vl_worker
web/install/lib/librknnrt.so
web/install/lib/librkllmrt.so
8.3 使用交叉编译器
在开发机上安装并配置 aarch64-linux-gnu 工具链后:
GCC_COMPILER=aarch64-linux-gnu ./build-web.sh
如果使用其他编译器,也可以传入对应的 gcc 或 clang 前缀。构建完成后,需要把 install/、模型和运行时环境部署到 RK3588 板端。
8.4 为什么需要 C++ 常驻 Worker
Python Web 层不直接加载 RKNN/RKLLM 原生库,而是通过 Unix Socket 调用 C++ Worker,主要有三个原因:
- 原生模型只初始化一次,避免每个 HTTP 请求重复加载模型。
- Python HTTP 生命周期和 NPU 推理生命周期解耦。
- Worker 可以严格串行复用同一个 RKLLM handle 和 KV Cache。
九、启动局域网 Web Demo
9.1 仅启用文字、图片和音乐
cd "$PROJECT_DIR/examples/multimodal_model_demo/web"
./run-web.sh --port 8080
9.2 启用 Type-C 耳机 ASR
cd "$PROJECT_DIR/examples/multimodal_model_demo/web"
# 先确认真实的 ALSA 设备编号
arecord -l
./run-web.sh \
--port 8080 \
--enable-asr \
--asr-device plughw:3,0
plughw:3,0 只是一次实际测试中的设备名。USB 耳机重新插拔后,声卡编号可能变化,必须以 arecord -l 的结果为准。
9.3 启动脚本执行的步骤
run-web.sh 会依次完成:
- 检查 Worker 和 Qwen 模型是否存在。
- 设置 Qwen Worker 使用的
LD_LIBRARY_PATH。 - 启动
qwen3_vl_worker。 - 等待
web/run/worker.sock创建。 - 根据参数启动独立 ASR 进程。
- 启动 Python Web 服务。
- 打印局域网访问地址。
成功后会看到类似输出:
Vision Desk is ready
Open from the same Wi-Fi: http://开发板IP:8080
Press Ctrl+C to stop both services.

在电脑浏览器中打开该地址即可。浏览器和开发板需要处于同一个局域网,端口 8080 不能被防火墙拦截。
9.4 常用启动参数
--vision-model PATH 指定 RKNN 视觉模型
--llm-model PATH 指定 RKLLM 语言模型
--host HOST Web 监听地址
--port PORT Web 端口
--core-num NUM NPU 核心数量
--platform NAME 目标平台
--max-new-tokens N 最大生成 token 数
--max-context-len N 上下文长度
--music-dir PATH 音乐目录
--music-player CMD 播放器命令
--enable-asr 启用 ASR
--asr-device NAME ALSA 麦克风设备
--worker PATH Worker 可执行文件
使用外部模型目录:
./run-web.sh \
--vision-model /data/models/qwen3-vl_vision_rk3588.rknn \
--llm-model /data/models/qwen3-vl-2b-instruct_w8a8_rk3588.rkllm \
--music-dir /data/music \
--port 8080
十、Web 接口验证
10.1 健康检查
curl -sS http://127.0.0.1:8080/healthz | python3 -m json.tool
正常情况下可以看到:
{
"ok": true,
"model_loaded": true,
"busy": false,
"platform": "rk3588",
"asr": {
"enabled": true,
"ready": true,
"listening": false
}
}
10.2 纯文字对话
curl -N -sS \
-F 'message=请只回复“服务正常”。' \
http://127.0.0.1:8080/api/chat
服务以 SSE 返回事件:
event: token
data: {"text":"服务"}
event: token
data: {"text":"正常"}
event: done
data: {}
10.3 图片问答
curl -N -sS \
-F 'message=请描述这张图片。' \
-F 'image=@/absolute/path/test.jpg;type=image/jpeg' \
http://127.0.0.1:8080/api/chat
当前服务支持:
image/jpeg
image/png
image/webp
图片请求完成后,服务会删除本轮生成的临时图片文件。
10.4 连续追问和重置上下文
curl -N -sS \
-F 'message=请用一句话总结刚才的内容。' \
http://127.0.0.1:8080/api/chat
curl -sS -X POST http://127.0.0.1:8080/api/reset
当前版本按照单用户、单上下文和请求串行方式设计。上一轮回答没有完成时,下一轮请求会收到忙碌提示。
10.5 ASR 接口
查看 ASR 状态:
curl -sS http://127.0.0.1:8080/api/asr/status | python3 -m json.tool
开始和停止监听:
curl -sS -X POST http://127.0.0.1:8080/api/asr/start
curl -sS -X POST http://127.0.0.1:8080/api/asr/stop
实时识别事件通过 SSE 推送:
asr_status
asr_partial
asr_final
asr_error
页面只把 asr_partial 用作预览,检测到一句话结束后,asr_final 会自动提交给 /api/chat。
十一、ASR 语音识别链路
11.1 ASR 资源结构
启用 ASR 前需要准备:
ASR/.venv/bin/python
ASR/rknn-runtime/librknnrt.so
ASR/sherpa-onnx-rk3588-streaming-zipformer-small-bilingual-zh-en-2023-02-16/
├── encoder.rknn
├── decoder.rknn
├── joiner.rknn
└── tokens.txt
安装带 RKNN 支持的 Sherpa-Onnx:
cd "$PROJECT_DIR/ASR"
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -f \
https://k2-fsa.github.io/sherpa/onnx/rk-npu-cn.html \
sherpa-onnx
11.2 为什么 ASR 使用独立进程
Qwen3-VL Worker 和 Sherpa-Onnx 可能依赖不同版本的 librknnrt.so。当前项目把 ASR 拆成独立进程,并为 ASR 设置单独的动态库目录:
Qwen Worker → web/install/lib/librknnrt.so
ASR → ASR/rknn-runtime/librknnrt.so
一次实际验证中,系统 Runtime 和 ASR 模型存在版本兼容问题,因此采用目录级隔离,而不是覆盖系统动态库。不要直接替换 /lib 或 /usr/lib 下的系统 Runtime。
11.3 ASR 调试命令
arecord -l
ASR/.venv/bin/python -c \
"import sherpa_onnx; print(sherpa_onnx.__file__)"
ldd "$(command -v sherpa-onnx)" | grep -E 'librknnrt|libonnxruntime'
如果耳机重新插拔,先重新执行 arecord -l,再修改 --asr-device 参数。
十二、本地音乐 Function Calling
当前音乐能力不是让模型执行任意 Shell,而是限定为两个工具(理论上可以下一个音乐软件让大模型调用可自行尝试):
play_music
stop_music
完整数据流:
用户输入“播放起风了”
→ RKLLM 输出受限工具调用
→ Python 解析工具名称和 JSON 参数
→ MusicController 扫描 music/
→ 校验歌曲名和扩展名
→ 使用固定参数启动 mpv
→ 播放结果回传 RKLLM
→ 模型生成最终文本
安全约束包括:
- 不执行模型生成的任意命令。
- 歌曲名称不能包含
/、\\或路径穿越内容。 - 只读取音乐目录的直接子文件。
- 拒绝软链接和不支持的音频扩展名。
- 播放新歌曲前停止上一首。
- 工具结果作为结构化 JSON 回传模型。
准备音乐目录:
mkdir -p "$PROJECT_DIR/music"
cp /path/to/your/music.mp3 "$PROJECT_DIR/music/"
当前支持的扩展名包括:
.aac .flac .m4a .mp3 .ogg .opus .wav
十三、项目中其他示例的用途
13.1 原生多模态 C++ Demo
目录:
examples/multimodal_model_demo/deploy/
该示例不经过 Web 网关,直接在开发板上执行图片编码和 RKLLM 推理。适合验证:
- RKNN Vision 模型是否能加载。
- RKLLM Runtime 是否能加载。
- 图片预处理和特殊 token 是否匹配。
- 原生 C++ 链路是否正常。
编译入口:
cd "$PROJECT_DIR/examples/multimodal_model_demo/deploy"
./build-linux.sh
13.2 RKLLM API Demo
目录:
examples/rkllm_api_demo/
该示例展示纯文本语言模型的原生 C++ API,包括:
rkllm_init初始化模型。rkllm_run执行生成。- Callback 接收流式 token。
rkllm_clear_kv_cache清理 KV Cache。- 可选的 LoRA 和 Prompt Cache 配置。
13.3 RKLLM Server Demo
目录:
examples/rkllm_server_demo/
该示例提供两类服务:
- Flask:OpenAI 兼容的
/v1/models和/v1/chat/completions接口。 - Gradio:适合快速搭建浏览器交互页面。
如果目标是快速把单纯文本 RKLLM 服务接入现有客户端,可以优先阅读该目录;如果需要图片问答、ASR 和音乐工具,应使用本文介绍的 multimodal_model_demo/web。
13.4 性能脚本
目录:
scripts/
常用脚本:
fix_freq_rk3588.sh # 设置 RK3588 频率
eval_perf_watch_cpu.sh # 观察 CPU 性能
eval_perf_watch_npu.sh # 观察 NPU 性能
执行性能测试前,建议先确认平台频率、模型量化方式、上下文长度、生成 token 数和 NPU 核心数,否则不同测试结果不能直接比较。
十四、参考性能数据
仓库 benchmark.md 中记录了参考测试结果。以 RK3588、Qwen3-VL-2B、W8A8 为例:
| 指标 | 参考值 |
|---|---|
| Vision Encoder 输入 | 448 × 448 |
| Image Encoder | 约 2.08 s |
| Prefill | 约 649 ms |
| Decode | 约 14.91 tokens/s |
| 语言模型单项测试 | 约 14.98 tokens/s |
| 内存 | 约 1868.98 MB |
这些数据来自仓库中的基准记录,不代表所有开发板、驱动、频率和 Runtime 版本的实际结果。复测时应记录:
- 芯片型号和系统版本。
- NPU、CPU 频率。
- RKNN/RKLLM Runtime 版本。
- 模型量化方式。
max_context_len和max_new_tokens。- NPU 核心数。
- 是否启用 Web、ASR 或其他后台进程。
十五、常见问题排查
15.1 Worker 启动后马上退出
检查模型、动态库和架构:
file web/install/bin/qwen3_vl_worker
ls -lh web/install/bin/qwen3_vl_worker
ls -lh web/install/lib
ldd web/install/bin/qwen3_vl_worker
重点确认:
- Worker 是
aarch64架构。 librknnrt.so和librkllmrt.so存在。- 模型路径可以读取。
- 系统 NPU 驱动正常。
15.2 页面能打开但提示模型未连接
ls -l web/run/worker.sock
curl -sS http://127.0.0.1:8080/healthz
pgrep -af 'qwen3_vl_worker|server.py'
如果 worker.sock 不存在,优先查看启动窗口中的 Worker 初始化日志。
15.3 图片问答失败
确认:
- 图片类型是 JPG、PNG 或 WebP。
- 图片没有超过服务限制。
- Vision 模型输入尺寸与导出参数一致。
- Qwen3-VL 特殊 token 没有配置错误:
<|vision_start|>
<|vision_end|>
<|image_pad|>
15.4 ASR 未就绪或出现段错误
检查 ASR 资源和动态库:
ls -l ASR/.venv/bin/python
ls -l ASR/rknn-runtime/librknnrt.so
ls -l ASR/sherpa-onnx-rk3588-streaming-zipformer-small-bilingual-zh-en-2023-02-16
ldd "$(command -v sherpa-onnx)" | grep librknnrt
ASR 启动时必须优先加载 ASR/rknn-runtime/ 下的 Runtime。不要用系统 Runtime 覆盖已经验证的 ASR Runtime。
15.5 麦克风没有声音
arecord -l
确认设备后再启动:
./run-web.sh --enable-asr --asr-device plughw:实际编号,0
15.6 音乐工具找不到歌曲
command -v mpv
find music -maxdepth 1 -type f -print
检查歌曲是否在 --music-dir 指定的目录中,以及扩展名是否受支持。服务会动态扫描目录,不需要把文件名写入代码。
15.7 多次请求被提示“上一轮仍在生成”
当前版本使用单用户串行模型。同一时刻只允许一轮推理,等待上一轮 SSE 返回 done 或 error 后再发送下一轮请求。
十六、总结
这个项目并不是一个单独的 Python Demo,而是一条完整的边缘端多模态推理链路:
模型转换
→ RKNN/RKLLM 模型
→ C++ NPU Worker
→ Python Web 网关
→ SSE 浏览器交互
→ ASR 和本地工具扩展
其中最关键的工程决策有三个:
- 把模型初始化放在常驻 C++ Worker 中,避免 HTTP 请求重复加载 NPU 模型。
- 把 ASR 放到独立进程中,隔离不同版本的 RKNN Runtime。
- 把音乐能力限制为白名单工具,不让模型生成内容直接变成 Shell 命令。
如果只想验证基础推理,可以先运行原生 deploy 示例;如果想提供纯文本接口,可以阅读 rkllm_server_demo;如果想实现图片问答、语音输入和局域网网页,则按照本文运行 multimodal_model_demo/web。
后续有时间会继续加一些小功能玩玩。
十七、参考资料
更多推荐


所有评论(0)