RK3588 上部署 Qwen3-VL-4B:ROCK5T 本地多模态实践
RK3588 上部署 Qwen3-VL-4B:ROCK5T 本地多模态实践
这篇记录的是一次比较完整的 RK3588 多模态部署:在 ROCK5T 12GB / Radxa OS 上运行 Qwen3-VL-4B-Instruct,完成本地图文问答。
整个过程的核心不是“把模型拷到板子上”,而是把 Qwen3-VL 拆成两部分处理:视觉编码器走 RKNN,语言模型走 RKLLM。最终板端实际加载的是两个文件:
qwen3-vl_vision_rk3588.rknn
qwen3-vl-4b-instruct_w8a8_rk3588.rkllm
其中:
.rknn是视觉编码器,负责把图片转成 image embedding;.rkllm是语言模型,负责接收图文输入并生成回答。
推理链路大致是:
图片 -> RKNN 视觉编码器 -> image embedding
│
问题文本 + <image> token ----------┘
↓
RKLLM 语言模型
过程中遇到的几个问题也比较典型:ONNX 导出不兼容、RK3588 不支持 w4a16、WSL2 内存不够导致转换中断。下面按实际流程整理。
1. 环境
开发机
WSL2 / x86_64
RTX 4060 Laptop GPU 8GB
rknn-toolkit2 2.3.2
rkllm-toolkit 1.2.3
transformers 4.57.0
我用了两个 conda 环境:
rknn # 转视觉模型,生成 .rknn
rkllm # 转语言模型,生成 .rkllm
板端
ROCK5T 12GB
RK3588
Radxa OS
用户:radxa
部署目录:/home/radxa/rock
最终板端目录:
/home/radxa/rock/
├── demo_Linux_aarch64/
│ ├── demo
│ ├── imgenc
│ ├── demo.jpg
│ └── lib/
│ ├── librknnrt.so
│ └── librkllmrt.so
└── models/
├── qwen3-vl_vision_rk3588.rknn
└── qwen3-vl-4b-instruct_w8a8_rk3588.rkllm
2. 下载模型
原始模型放在开发机:
/home/lhx/RKSDK/models/Qwen3-VL-4B-Instruct
下载完成后大约 8.3GB,主要是两个 safetensors 分片:
model-00001-of-00002.safetensors
model-00002-of-00002.safetensors
这只是原始模型,还不能直接放到 RK3588 上跑。接下来要拆成两条线:
Vision Encoder -> RKNN
LLM -> RKLLM
3. 视觉编码器:ONNX 到 RKNN
进入 RKNN-LLM 的多模态示例目录:
cd /home/lhx/RKSDK/rknn-llm/examples/multimodal_model_demo
conda activate rknn
先导出 ONNX:
python export/export_vision.py \
--path /home/lhx/RKSDK/models/Qwen3-VL-4B-Instruct \
--model_name qwen3-vl \
--height 448 \
--width 448 \
--device cpu
这里踩了第一个坑:新版 PyTorch 默认走 dynamo ONNX exporter,Qwen3-VL 的动态 shape 会导出失败。解决方式很直接,在 torch.onnx.export() 里加:
dynamo=False
导出成功后得到:
onnx/qwen3-vl_vision.onnx # 约 1.6GB
然后转 RKNN:
python export/export_vision_rknn.py \
--path ./onnx/qwen3-vl_vision.onnx \
--model_name qwen3-vl \
--target-platform rk3588 \
--height 448 \
--width 448
输出:
rknn/qwen3-vl_vision_rk3588.rknn # 约 829MB
4. 语言模型:转成 RKLLM
切到 RKLLM 环境:
cd /home/lhx/RKSDK/rknn-llm/examples/multimodal_model_demo
conda activate rkllm
RKLLM 量化需要校准数据。Qwen3-VL 和 Qwen2-VL 的结构细节不完全一样,所以我用 Qwen3-VL 适配脚本生成 input embeddings:
python data/make_input_embeds_qwen3_vl.py \
--path /home/lhx/RKSDK/models/Qwen3-VL-4B-Instruct \
--max_samples 3
cp data/inputs_qwen3_vl.json data/inputs.json
一开始我想用 w4a16,但 RKLLM 直接提示:
target_platform: rk3588 not support quantized_dtype: w4a16
所以这里改成 w8a8。
开发机有 RTX4060,于是转换时用 CUDA:
python export/export_rkllm.py \
--path /home/lhx/RKSDK/models/Qwen3-VL-4B-Instruct \
--target-platform rk3588 \
--num_npu_core 3 \
--quantized_dtype w8a8 \
--device cuda \
--savepath /home/lhx/RKSDK/models/converted/qwen3-vl-4b-instruct_w8a8_rk3588.rkllm
最终得到:
/home/lhx/RKSDK/models/converted/qwen3-vl-4b-instruct_w8a8_rk3588.rkllm
大小约 4.6GB。
5. WSL2 内存:真正的拦路虎
模型转换最开始不是慢,而是直接把 WSL2 干没了。
日志停在 Optimizing model,进程没报错,WSL2 直接退出。后来一看,转换时内存吃到了 15GB 左右,而当时 WSL2 总共也就分了 15GB。
解决方法是在 Windows 用户目录写 .wslconfig:
[wsl2]
memory=28GB
processors=8
swap=32GB
localhostForwarding=true
然后:
wsl --shutdown
重新打开 WSL 后再转,顺利完成。成功那次峰值内存大约 24GB。
如果你也要转 4B 级模型,建议先把 WSL2 内存和 swap 配好,不然很容易在最后阶段白跑。
6. 交叉编译板端 demo
板端程序用仓库自带的 C++ demo。交叉编译工具链是:
gcc-arm-11.2-2022.02-x86_64-aarch64-none-linux-gnu
解压到:
/home/lhx/opts/gcc-arm-11.2-2022.02-x86_64-aarch64-none-linux-gnu
修改 deploy/build-linux.sh,让它指向这个工具链:
GCC_COMPILER=/home/lhx/opts/gcc-arm-11.2-2022.02-x86_64-aarch64-none-linux-gnu
然后编译:
cd /home/lhx/RKSDK/rknn-llm/examples/multimodal_model_demo/deploy
./build-linux.sh
输出目录:
install/demo_Linux_aarch64/
里面有:
demo
imgenc
demo.jpg
lib/librknnrt.so
lib/librkllmrt.so
检查一下架构:
file install/demo_Linux_aarch64/demo
应该是:
ELF 64-bit LSB executable, ARM aarch64
7. 拷到 ROCK5T
板端部署根目录是:
/home/radxa/rock
创建模型目录:
ssh radxa@ROCK5T_IP "mkdir -p /home/radxa/rock/models"
拷贝 demo:
scp -r /home/lhx/RKSDK/rknn-llm/examples/multimodal_model_demo/deploy/install/demo_Linux_aarch64 \
radxa@ROCK5T_IP:/home/radxa/rock/
拷贝两个模型:
scp /home/lhx/RKSDK/rknn-llm/examples/multimodal_model_demo/rknn/qwen3-vl_vision_rk3588.rknn \
radxa@ROCK5T_IP:/home/radxa/rock/models/
scp /home/lhx/RKSDK/models/converted/qwen3-vl-4b-instruct_w8a8_rk3588.rkllm \
radxa@ROCK5T_IP:/home/radxa/rock/models/
8. 在板端跑起来
登录 ROCK5T:
ssh radxa@ROCK5T_IP
cd /home/radxa/rock/demo_Linux_aarch64
先测视觉编码器:
sudo env LD_LIBRARY_PATH=$PWD/lib ./imgenc \
/home/radxa/rock/models/qwen3-vl_vision_rk3588.rknn \
demo.jpg \
3
正常的话会生成:
img_vec.bin
然后跑完整图文问答:
sudo env LD_LIBRARY_PATH=$PWD/lib ./demo demo.jpg \
/home/radxa/rock/models/qwen3-vl_vision_rk3588.rknn \
/home/radxa/rock/models/qwen3-vl-4b-instruct_w8a8_rk3588.rkllm \
256 2048 3 \
"<|vision_start|>" \
"<|vision_end|>" \
"<|image_pad|>"
这里的参数含义:
256 max_new_tokens
2048 max_context_len
3 RK3588 三个 NPU core
12GB 内存下建议先用 256 / 2048,确认稳定后再试 512 / 4096。
9. 最容易忽略的一点:输入必须带 <image>
这个 demo 判断是否走多模态,不是靠你有没有传图片路径,而是看输入文本里有没有:
<image>
如果你直接问:
这张图片中有什么?
它会按纯文本处理,模型可能回答“我看不到图片”。
正确问法是:
<image>这张图片中有什么?
demo 里也预置了:
[0] <image>What is in the image?
[1] <image>这张图片中有什么?
运行后直接输入:
1
就会触发图文问答。
10. 总结
完整流程可以浓缩成一句话:
把 Qwen3-VL 拆成视觉编码器和语言模型,分别转换成 RKNN 和 RKLLM,再用板端 demo 把两者串起来。
最终产物:
qwen3-vl_vision_rk3588.rknn
qwen3-vl-4b-instruct_w8a8_rk3588.rkllm
demo
imgenc
librknnrt.so
librkllmrt.so
这套流程不是“点一下就能部署”的体验,但跑通之后会很有意思:一块 RK3588 板子,不依赖云端,就能完成本地图文理解。
对于 ROCK5T 12GB 来说,4B 模型属于能跑但不轻松。如果追求稳定和速度,2B 级别会更舒服;如果想验证 RK3588 上限,Qwen3-VL-4B 是一个很好的测试对象。
参考文档
-
Radxa ROCK5T RKLLM 安装文档
https://docs.radxa.com/rock5/rock5t/app-development/ai/rkllm-install -
Radxa ROCK5T RKNN 安装文档
https://docs.radxa.com/rock5/rock5t/app-development/ai/rknn-install -
RKNN-LLM GitHub 仓库
https://github.com/airockchip/rknn-llm -
RKNN-Toolkit2 GitHub 仓库
https://github.com/airockchip/rknn-toolkit2 -
Qwen 模型主页
https://huggingface.co/Qwen -
PyTorch ONNX Export 文档
https://pytorch.org/docs/stable/onnx.html -
Arm GNU Toolchain 下载页面
https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)