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/NPUQwen3-VL LLM根据文本和视觉特征生成回答RKLLM/NPUSherpa-Onnx流式语音识别独立 Python 进程和 RKNN/NPUQwen3-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 平台。Linuxaarch64系统。可用的 RKNN NPU 驱动和设备节点。如果启用语音识别需要一个 ALSA 可识别的麦克风。如果启用音乐播放需要开发板连接音频输出设备。5.2 基础软件gcc g cmake python3 mpv # 可选音乐播放需要 alsa-utils # 可选检查麦克风需要检查环境uname-mpython3--versioncmake--versioncommand-vgcccommand-vg本文示例统一使用以下变量避免把个人目录写死到命令中exportPROJECT_DIR$HOME/rknn-llm-mainexportWEB_DIR$PROJECT_DIR/examples/multimodal_model_demo/webcd$PROJECT_DIR六、获取项目和准备模型6.1 获取源码如果使用本人的 GitHub 仓库请点star跪谢gitclone https://github.com/shaddockpeel2/RKNN-LLM.gitcdRKNN-LLM如果使用上游仓库gitclone https://github.com/airockchip/rknn-llm.gitcdrknn-llm6.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_DIRtest-fexamples/multimodal_model_demo/data/rknn/qwen3-vl_vision_rk3588.rknntest-fexamples/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.2RKLLM Toolkit 的 Python 依赖位于rkllm-toolkit/packages/requirements.txt建议在独立虚拟环境中安装转换依赖不要把 PC 端转换环境和开发板运行环境混在一起。7.2 Vision 模型导出为 ONNX项目的export_vision.py支持多种视觉模型。Qwen3-VL 示例cd$PROJECT_DIR/examples/multimodal_model_demo/exportpython export_vision.py\--path/path/to/Qwen3-VL-2B-Instruct\--model_nameqwen3-vl\--batch_size1\--height448\--width448\--devicecpu脚本会在当前export目录下生成类似文件onnx/qwen3-vl_vision.onnx注意以下参数必须和后续推理链路保持一致model_name。输入图片的高和宽。batch size。Qwen3-VL 的 patch size 和grid_thw计算方式。图像特殊 token 和模型配置。7.3 ONNX 转换为 RKNNcd$PROJECT_DIR/examples/multimodal_model_demo/exportpython export_vision_rknn.py\--path./onnx/qwen3-vl_vision.onnx\--model_nameqwen3-vl\--target-platform rk3588\--batch_size1\--height448\--width448输出目录通常为rknn/qwen3-vl_vision_rk3588.rknnexport_vision_rknn.py会根据模型类型设置输入、均值、标准差和grid_thw。不要仅仅修改输出文件名而忽略输入尺寸和预处理配置。7.4 生成 RKLLM 量化校准数据多模态语言模型量化需要校准数据。项目提供了数据集和输入 embedding 生成脚本cd$PROJECT_DIR/examples/multimodal_model_demopython data/make_input_embeds_for_quantize.py\--path/path/to/Qwen3-VL-2B-Instruct\--model_typeqwen3vl生成的校准数据位于data/inputs.json7.5 导出 RKLLM 模型cd$PROJECT_DIR/examples/multimodal_model_demopython export/export_rkllm.py\--path/path/to/Qwen3-VL-2B-Instruct\--target-platform rk3588\--num_npu_core3\--quantized_dtypew8a8\--devicecpu\--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 Worker8.1 进入 Web 目录cd$PROJECT_DIR/examples/multimodal_model_demo/web8.2 在 RK3588 板端直接编译GCC_COMPILERgcc ./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.so8.3 使用交叉编译器在开发机上安装并配置aarch64-linux-gnu工具链后GCC_COMPILERaarch64-linux-gnu ./build-web.sh如果使用其他编译器也可以传入对应的 gcc 或 clang 前缀。构建完成后需要把install/、模型和运行时环境部署到 RK3588 板端。8.4 为什么需要 C 常驻 WorkerPython Web 层不直接加载 RKNN/RKLLM 原生库而是通过 Unix Socket 调用 C Worker主要有三个原因原生模型只初始化一次避免每个 HTTP 请求重复加载模型。Python HTTP 生命周期和 NPU 推理生命周期解耦。Worker 可以严格串行复用同一个 RKLLM handle 和 KV Cache。九、启动局域网 Web Demo9.1 仅启用文字、图片和音乐cd$PROJECT_DIR/examples/multimodal_model_demo/web./run-web.sh--port80809.2 启用 Type-C 耳机 ASRcd$PROJECT_DIR/examples/multimodal_model_demo/web# 先确认真实的 ALSA 设备编号arecord-l./run-web.sh\--port8080\--enable-asr\--asr-device plughw:3,0plughw: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 CtrlC 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\--port8080十、Web 接口验证10.1 健康检查curl-sShttp://127.0.0.1:8080/healthz|python3-mjson.tool正常情况下可以看到{ok:true,model_loaded:true,busy:false,platform:rk3588,asr:{enabled:true,ready:true,listening:false}}10.2 纯文字对话curl-N-sS\-Fmessage请只回复“服务正常”。\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\-Fmessage请描述这张图片。\-Fimage/absolute/path/test.jpg;typeimage/jpeg\http://127.0.0.1:8080/api/chat当前服务支持image/jpeg image/png image/webp图片请求完成后服务会删除本轮生成的临时图片文件。10.4 连续追问和重置上下文curl-N-sS\-Fmessage请用一句话总结刚才的内容。\http://127.0.0.1:8080/api/chatcurl-sS-XPOST http://127.0.0.1:8080/api/reset当前版本按照单用户、单上下文和请求串行方式设计。上一轮回答没有完成时下一轮请求会收到忙碌提示。10.5 ASR 接口查看 ASR 状态curl-sShttp://127.0.0.1:8080/api/asr/status|python3-mjson.tool开始和停止监听curl-sS-XPOST http://127.0.0.1:8080/api/asr/startcurl-sS-XPOST 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-Onnxcd$PROJECT_DIR/ASRpython3-mvenv .venvsource.venv/bin/activate python-mpipinstall-f\https://k2-fsa.github.io/sherpa/onnx/rk-npu-cn.html\sherpa-onnx11.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-lASR/.venv/bin/python-c\import sherpa_onnx; print(sherpa_onnx.__file__)ldd$(command-vsherpa-onnx)|grep-Elibrknnrt|libonnxruntime如果耳机重新插拔先重新执行arecord -l再修改--asr-device参数。十二、本地音乐 Function Calling当前音乐能力不是让模型执行任意 Shell而是限定为两个工具理论上可以下一个音乐软件让大模型调用可自行尝试play_music stop_music完整数据流用户输入“播放起风了” → RKLLM 输出受限工具调用 → Python 解析工具名称和 JSON 参数 → MusicController 扫描 music/ → 校验歌曲名和扩展名 → 使用固定参数启动 mpv → 播放结果回传 RKLLM → 模型生成最终文本安全约束包括不执行模型生成的任意命令。歌曲名称不能包含/、\\或路径穿越内容。只读取音乐目录的直接子文件。拒绝软链接和不支持的音频扩展名。播放新歌曲前停止上一首。工具结果作为结构化 JSON 回传模型。准备音乐目录mkdir-p$PROJECT_DIR/musiccp/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.sh13.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/该示例提供两类服务FlaskOpenAI 兼容的/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 × 448Image Encoder约 2.08 sPrefill约 649 msDecode约 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 启动后马上退出检查模型、动态库和架构fileweb/install/bin/qwen3_vl_workerls-lhweb/install/bin/qwen3_vl_workerls-lhweb/install/lib ldd web/install/bin/qwen3_vl_worker重点确认Worker 是aarch64架构。librknnrt.so和librkllmrt.so存在。模型路径可以读取。系统 NPU 驱动正常。15.2 页面能打开但提示模型未连接ls-lweb/run/worker.sockcurl-sShttp://127.0.0.1:8080/healthz pgrep-afqwen3_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-lASR/.venv/bin/pythonls-lASR/rknn-runtime/librknnrt.sols-lASR/sherpa-onnx-rk3588-streaming-zipformer-small-bilingual-zh-en-2023-02-16 ldd$(command-vsherpa-onnx)|greplibrknnrtASR 启动时必须优先加载ASR/rknn-runtime/下的 Runtime。不要用系统 Runtime 覆盖已经验证的 ASR Runtime。15.5 麦克风没有声音arecord-l确认设备后再启动./run-web.sh --enable-asr --asr-device plughw:实际编号,015.6 音乐工具找不到歌曲command-vmpvfindmusic-maxdepth1-typef-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。后续有时间会继续加一些小功能玩玩。十七、参考资料RKNN-LLM 上游项目RKNN-Toolkit2Sherpa-OnnxSherpa-Onnx RKNN 文档Qwen 模型主页项目 RK3588 Web 复现手册项目 RK3588 ASR 配置说明