1. 为什么要在 RK3588 上折腾大模型
1.1 一块开发板做服务器的核心动机
手里有一块 RK3588 开发板的人,大概率经历过这样的心路历程:买回来跑了个官方 Demo,点亮了屏幕,连上了网络,然后就……放在角落里吃灰了。这块芯片有 8 核 CPU(4 个 A76 大核加 4 个 A55 小核)、Mali-G610 GPU,还有一颗算力 6 TOPS 的 NPU,纸面参数放在 ARM 开发板里算是相当能打。但大多数人只把它当个高性能树莓派用,跑个 Docker、做个软路由、挂个下载机,多少有点浪费。
把 RK3588 变成一台本地大模型推理服务器,是我认为目前最能榨干这块板子价值的方向之一。原因很直接:云端大模型 API 按 token 计费,长期用下来成本不低;数据隐私方面,很多场景下你并不想把所有对话内容都发到别人的服务器上;网络依赖方面,一旦断网,云端服务直接歇菜。本地部署一个大模型,哪怕参数量小一点、推理速度慢一点,但它是完全属于你自己的,想怎么用就怎么用。
这篇文章面向的是手里有 RK3588 开发板、想跑本地大模型但不知道从哪下手的人。我会从硬件选型、系统准备、推理框架选择、模型转换、服务化部署到性能调优,把整个流程拆开讲清楚。文章里涉及的具体操作步骤是基于我在类似 ARM 平台上反复折腾后总结的常见实践,不同板子的具体细节可能有差异,但整体思路是通用的。
1.2 本地大模型服务器的能力边界
先把预期管理做好。RK3588 的 NPU 算力是 6 TOPS(INT8),这个数字放在手机端 NPU 里算中等偏上,但和桌面级显卡完全不是一个量级。你不可能在这块板子上跑 70B 参数的模型,甚至 13B 都很吃力。实际能比较流畅运行的,是 1.5B 到 7B 这个区间的量化模型。
具体来说,Qwen2.5-1.5B-Instruct 的 INT4 量化版本,在 RK3588 上大概能做到每秒 10 到 15 个 token 的输出速度,这个速度已经可以支撑简单的对话交互了。3B 级别的模型大概在每秒 5 到 8 个 token,7B 级别可能只有每秒 2 到 4 个 token,体验上会明显感觉到等待。所以如果你的需求是“有个能对话的本地助手”,1.5B 到 3B 是甜点区间;如果要做更复杂的任务,比如代码生成或者长文摘要,可能需要接受更慢的速度,或者考虑用更小的模型做特定任务微调。
另一个需要明确的点是,RK3588 的 NPU 对模型算子有要求。不是所有模型都能直接跑在 NPU 上,有些算子需要 CPU 回退,有些甚至完全不支持。所以模型转换这一步是整个流程里最容易卡住的地方,后面我会详细讲怎么处理。
2. 硬件与系统环境准备
2.1 开发板选型与配件建议
市面上基于 RK3588 的开发板有不少选择,核心芯片一样,但外围配置差异很大。选板子的时候重点看三个东西:内存、存储和散热。
内存方面,强烈建议选 16GB 或以上的版本。8GB 版本跑 1.5B 模型勉强够用,但系统本身要占掉 1 到 2GB,再加上推理框架和模型加载,余量很小。16GB 可以比较从容地跑 3B 模型,甚至尝试 7B 的 INT4 量化版本。32GB 版本当然更好,但价格会上去不少,性价比需要自己权衡。
存储方面,eMMC 的速度对于模型加载来说偏慢,一个 3B 模型的 INT4 量化文件大概 2GB 左右,从 eMMC 加载可能要等十几秒。建议用 NVMe SSD,通过 M.2 接口扩展,加载时间可以缩短到几秒以内。如果板子没有 M.2 接口,至少用一张高速 TF 卡(A2 级别以上),比 eMMC 还是快一些。
散热是被很多人忽略的点。RK3588 满载运行时发热量不小,尤其是 NPU 持续推理的时候。被动散热片在短时间测试时够用,但长时间跑推理服务,建议加一个带风扇的主动散热方案。我实测过,不加风扇的情况下,连续推理 10 分钟后芯片温度会到 80 度以上,然后触发降频,推理速度直接掉一半。加了一个小风扇之后,温度稳定在 55 到 60 度,性能输出很稳定。
电源也不能马虎。RK3588 满载功耗可以到 10W 以上,加上外设和 SSD,建议用 5V/4A 或者 12V/2A 以上的电源适配器。供电不足会导致系统不稳定,甚至推理过程中直接重启。
2.2 系统镜像选择与基础配置
RK3588 开发板通常官方会提供 Debian 或 Ubuntu 的镜像。我的建议是优先选 Ubuntu 22.04 或 Debian 11 以上的版本,因为推理框架和工具链对系统版本有一定要求,太老的系统在装依赖时会遇到各种版本冲突。
系统烧录到 eMMC 或 TF 卡之后,第一件事是更新系统并安装基础工具:
sudo apt update && sudo apt upgrade -y sudo apt install -y build-essential cmake git wget curl python3-pip python3-venv然后配置一下交换空间。虽然 16GB 内存跑小模型够用,但模型转换和编译过程中可能会瞬间吃满内存,有个 swap 分区能防止 OOM。建议在 NVMe SSD 上划 8GB 的 swap:
sudo fallocate -l 8G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab接下来是 NPU 驱动的安装。RK3588 的 NPU 驱动通常包含在官方镜像里,但推理框架需要对应的运行时库。检查一下/dev/rknpu设备节点是否存在:
ls -la /dev/rknpu*如果没有这个设备节点,说明 NPU 驱动没有加载,需要根据板子厂商的文档安装对应的驱动包。这一步不同板子差异较大,建议直接参考厂商提供的 Wiki 或论坛帖子。
注意:NPU 驱动版本和推理框架版本必须匹配。我遇到过驱动是 0.8.2 但框架要求 0.9.0 的情况,结果就是模型加载时报错,排查了半天才发现是版本问题。装之前先确认好版本对应关系。
3. 推理框架选型与对比
3.1 主流 ARM 推理框架横向对比
在 RK3588 上跑大模型,目前比较可行的推理框架有几种,各有优劣。我整理了一个对比表格,方便你根据自己的需求选择:
| 框架 | NPU 支持 | 模型格式 | 易用性 | 性能 | 适用场景 |
|---|---|---|---|---|---|
| RKNN-LLM | 原生支持 | RKNN | 中等 | 优秀 | 追求 NPU 加速的首选 |
| llama.cpp | 不支持 | GGUF | 高 | 中等 | 快速验证、CPU 推理 |
| MLC-LLM | 部分支持 | TVM | 较低 | 良好 | 研究性质、需要编译 |
| ONNX Runtime | 有限支持 | ONNX | 中等 | 一般 | 已有 ONNX 模型的迁移 |
RKNN-LLM 是瑞芯微官方推出的专门针对大语言模型的推理工具链,它能把 HuggingFace 格式的模型转换成 RKNN 格式,然后跑在 NPU 上。这是目前 RK3588 上跑大模型性能最好的方案,但转换流程相对复杂,对模型结构有要求。
llama.cpp 的优势是简单,编译一下就能用,GGUF 格式的模型网上也很多。但它只能用 CPU 推理,RK3588 的 A76 大核性能虽然不错,但和 NPU 比起来还是差不少。我实测同一个 1.5B 模型,llama.cpp 在 CPU 上跑大概每秒 5 到 6 个 token,而 RKNN-LLM 在 NPU 上能到每秒 12 到 15 个 token,差距很明显。
MLC-LLM 的思路是用 TVM 编译,理论上可以支持多种后端,但在 RK3588 上的 NPU 支持还不够成熟,配置起来也比较折腾。ONNX Runtime 的 NPU 支持有限,很多大模型算子跑不了,不太推荐。
所以我的建议是:如果你追求性能和长期使用,直接上 RKNN-LLM;如果只是想快速体验一下,先用 llama.cpp 跑个 GGUF 模型试试水。
3.2 RKNN-LLM 工具链的安装与验证
RKNN-LLM 的工具链分为两部分:PC 端的模型转换工具和板子端的推理运行时。PC 端建议在 x86 的 Ubuntu 机器上操作,板子端就是 RK3588 本身。
PC 端安装转换工具:
git clone https://github.com/airockchip/rknn-llm.git cd rknn-llm pip install -r requirements.txt板子端安装运行时库,通常厂商会提供预编译的 deb 包或者 so 库文件。安装完成后,可以用一个简单的测试程序验证 NPU 是否正常工作:
cd rknn-llm/examples python3 test_npu.py如果输出显示 NPU 版本号和推理耗时,说明环境没问题。如果报错找不到 librknnrt.so,需要把库文件路径加到 LD_LIBRARY_PATH 里:
export LD_LIBRARY_PATH=/usr/lib/rknpu:$LD_LIBRARY_PATH实操心得:建议把 LD_LIBRARY_PATH 的配置写到 ~/.bashrc 里,不然每次开新终端都要重新设置。另外,RKNN-LLM 的版本更新比较快,不同版本之间的 API 可能有变化,建议锁定一个稳定版本使用,不要盲目追新。
4. 模型转换全流程拆解
4.1 模型选择与下载
模型选择上,我建议从 Qwen2.5-1.5B-Instruct 开始。这个模型体积小、中文支持好、指令跟随能力也不错,非常适合在 RK3588 上做第一轮验证。等跑通了再尝试 3B 或 7B 的模型。
从 HuggingFace 下载模型:
git lfs install git clone https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct如果网络条件不允许直接访问,也可以在国内的模型托管平台找镜像。下载完成后,目录里应该有 config.json、tokenizer.json、model.safetensors 等文件。
4.2 模型转换的关键步骤与参数
RKNN-LLM 的转换流程大致分为三步:导出 ONNX、量化、转 RKNN。官方提供了转换脚本,但需要根据模型结构做一些调整。
第一步,把 HuggingFace 模型导出为 ONNX:
python3 export_onnx.py \ --model_path ./Qwen2.5-1.5B-Instruct \ --output_path ./onnx_model \ --seq_len 128这里的seq_len是序列长度,也就是模型一次能处理的最大 token 数。这个参数直接影响内存占用和推理速度。128 是一个比较保守的值,适合对话场景;如果要做长文处理,可以设到 512 甚至 1024,但内存占用会明显增加。我建议先用 128 跑通,再根据实际需求调整。
第二步,量化。量化是把 FP16 的权重转成 INT8 或 INT4,减小模型体积、提升推理速度。RKNN-LLM 支持混合量化,也就是对精度敏感的层保持 FP16,其他层用 INT8:
python3 quantize.py \ --onnx_path ./onnx_model \ --output_path ./quantized_model \ --quant_type w8a8w8a8表示权重和激活都用 INT8。如果内存实在紧张,可以用w4a16,权重用 INT4,激活用 FP16,模型体积能再小一半,但精度损失会大一些。
第三步,转 RKNN:
python3 convert_rknn.py \ --quantized_path ./quantized_model \ --output_path ./rknn_model \ --target_platform rk3588转换完成后会得到一个 .rknn 文件,这就是最终能在板子上加载的模型。
注意:转换过程中最常见的报错是“不支持的算子”。RKNN 对某些算子(比如某些激活函数或注意力变体)支持不好。遇到这种情况,可以尝试在转换脚本里把不支持的算子回退到 CPU,或者换一个结构更标准的模型。Qwen2.5 系列的算子兼容性比较好,这是我推荐它作为入门模型的原因之一。
4.3 转换后的验证与性能基准测试
模型转好了,先别急着部署服务,在板子上跑一个基准测试,确认推理速度和输出质量。
./rknn_llm_demo \ --model_path ./rknn_model/qwen2.5_1.5b.rknn \ --prompt "你好,请介绍一下你自己" \ --max_new_tokens 128观察几个指标:首 token 延迟(从输入到输出第一个 token 的时间)、生成速度(每秒生成多少 token)、内存占用。1.5B 模型在 RK3588 上,首 token 延迟大概在 200 到 500 毫秒,生成速度每秒 10 到 15 个 token,内存占用 2 到 3GB。如果速度明显低于这个范围,可能是 NPU 没有正常工作,检查一下推理日志里有没有“fallback to CPU”的字样。
5. 服务化部署与接口封装
5.1 用 FastAPI 搭建推理服务
模型能跑了,接下来把它包装成一个 HTTP 服务,这样局域网内的其他设备就能通过 API 调用了。用 FastAPI 是最轻量的方案:
from fastapi import FastAPI from pydantic import BaseModel import subprocess import json app = FastAPI() class GenerateRequest(BaseModel): prompt: str max_tokens: int = 128 @app.post("/generate") async def generate(req: GenerateRequest): result = subprocess.run( ["./rknn_llm_demo", "--model_path", "./rknn_model/qwen2.5_1.5b.rknn", "--prompt", req.prompt, "--max_new_tokens", str(req.max_tokens)], capture_output=True, text=True ) return {"response": result.stdout.strip()}这个写法比较粗糙,每次请求都重新加载模型,实际用起来会很慢。更好的做法是把模型加载到内存里常驻,用 Python 的 RKNN 绑定直接调用。不过 RKNN-LLM 的 Python API 文档不算特别完善,需要参考 examples 目录里的示例代码来改。
启动服务:
uvicorn server:app --host 0.0.0.0 --port 8000然后在局域网内的另一台电脑上测试:
curl -X POST http://192.168.1.100:8000/generate \ -H "Content-Type: application/json" \ -d '{"prompt": "写一首关于春天的诗", "max_tokens": 64}'5.2 接入聊天前端与多用户并发
有了 API 之后,可以接各种聊天前端。比较轻量的选择是 Open WebUI,它支持自定义 API 地址,界面也好看。在另一台机器上部署 Open WebUI,把 API 地址指向 RK3588 的 IP 和端口就行。
不过要注意,RK3588 的推理能力有限,不建议做多用户并发。NPU 一次只能处理一个推理任务,多个请求同时进来会排队,体验反而更差。如果确实有并发需求,可以在服务端加一个队列,按顺序处理请求,每个请求返回时带上预估等待时间。
实操心得:我在实际使用中发现,把 max_tokens 限制在 256 以内,并且设置一个合理的超时时间(比如 30 秒),能避免某个长请求把整个服务卡死。另外,服务启动后先跑几个预热请求,让 NPU 进入稳定状态,后续请求的延迟会更低。
6. 性能调优与常见问题排查
6.1 提升推理速度的五个实操技巧
第一个技巧是调整 CPU 调度策略。RK3588 有 4 个 A76 大核和 4 个 A55 小核,默认的调度策略可能把推理任务分配到小核上。可以用 taskset 把推理进程绑定到大核:
taskset -c 4-7 ./rknn_llm_demo --model_path ...第二个技巧是锁定 CPU 频率。在 /sys/devices/system/cpu/cpufreq/ 下把 governor 设成 performance,避免频率波动导致的性能抖动。
第三个技巧是优化内存分配。RKNN-LLM 在加载模型时会申请一大块连续内存,如果系统内存碎片化严重,申请会变慢甚至失败。建议在服务启动前重启一次,或者用echo 3 > /proc/sys/vm/drop_caches清理缓存。
第四个技巧是使用更小的量化精度。如果 INT8 的速度还不够,可以试试 INT4。模型体积减半,推理速度大概能提升 30% 到 50%,但输出质量会下降,需要自己权衡。
第五个技巧是批处理。如果你有多个短请求,可以攒一批一起推理,能提高 NPU 的利用率。不过实现起来比较复杂,适合有一定开发经验的人尝试。
6.2 常见报错与排查速查表
| 报错信息 | 可能原因 | 解决方法 |
|---|---|---|
| librknnrt.so not found | 运行时库路径未配置 | 设置 LD_LIBRARY_PATH |
| unsupported operator | 模型包含 NPU 不支持的算子 | 回退到 CPU 或更换模型 |
| out of memory | 内存不足 | 减小 seq_len 或换更小模型 |
| NPU init failed | 驱动未加载或版本不匹配 | 检查 /dev/rknpu 和驱动版本 |
| inference timeout | 请求过长或 NPU 卡死 | 限制 max_tokens,重启服务 |
| 输出乱码 | tokenizer 不匹配 | 确认 tokenizer 与模型版本一致 |
注意:如果遇到 NPU 卡死的情况(推理进程无响应),不要直接拔电源。先尝试
kill -9杀掉进程,如果不行再重启。频繁卡死可能是散热问题,检查一下芯片温度。
6.3 长期运行的稳定性建议
如果要让这台 RK3588 服务器 7x24 小时运行,有几个地方需要额外注意。散热方面,建议用带温控的风扇,温度超过 60 度自动加速。电源方面,用质量好一点的适配器,电压不稳会导致系统重启。系统方面,可以配一个 systemd 服务,让推理服务开机自启,崩溃后自动重启:
[Unit] Description=RKNN LLM Server After=network.target [Service] ExecStart=/usr/bin/python3 /home/user/server.py Restart=always RestartSec=5 [Install] WantedBy=multi-user.target日志方面,建议把推理服务的日志写到文件里,方便排查问题。可以用 logrotate 做日志轮转,避免日志把磁盘占满。
7. 扩展玩法与个人体会
7.1 让本地大模型服务器更有用的几个方向
跑通基础对话之后,可以尝试一些更有意思的扩展。比如接一个语音识别模型,做成语音助手;或者接一个文生图模型(虽然 RK3588 跑文生图会比较吃力,但小尺寸的可以试试);再或者把本地模型和 Home Assistant 结合,用自然语言控制智能家居设备。
另一个方向是做领域微调。用 LoRA 在 PC 上对 1.5B 模型做轻量微调,然后转成 RKNN 格式部署到板子上。这样你就有了一个针对特定领域(比如法律咨询、医疗问答、代码助手)的专用模型,虽然参数量小,但在特定任务上的表现可能比通用大模型还好。
还有一个比较实用的玩法是做成本地知识库问答。用 LangChain 或者 LlamaIndex 把本地文档向量化,检索到的内容作为上下文喂给本地模型,实现基于私有文档的问答。RK3588 跑 embedding 模型和 rerank 模型都没问题,整个流程可以完全本地化。
7.2 我在这块板子上踩过的坑
最后分享几个我在实际操作中踩过的坑,希望能帮你省点时间。
第一个坑是低估了散热的重要性。最开始我用的是被动散热片,跑了个 7B 模型,结果推理到一半直接降频,速度从每秒 3 个 token 掉到 1 个 token。后来加了个 5V 的小风扇,问题解决。所以如果你打算长时间跑推理,散热方案一定要提前准备好。
第二个坑是模型转换时的 seq_len 设置。我一开始设了 512,结果转换过程中内存直接爆了。后来改成 128 才跑通。seq_len 对内存的影响是平方级的,不是线性的,设置的时候要留足余量。
第三个坑是忽略了 tokenizer 的版本匹配。有一次我换了个模型,但 tokenizer 文件没更新,结果输出全是乱码。排查了半天才发现是 tokenizer 和模型不匹配。所以每次换模型,一定要把整个目录都更新,不要只换模型文件。
第四个坑是用错了量化类型。我试过用 w4a16 量化一个 3B 模型,结果输出质量下降得很厉害,回答经常前言不搭后语。后来换成 w8a8,虽然模型大了一点,但输出质量明显好很多。所以量化类型的选择要在速度和质量的平衡点上做取舍,不能一味追求小体积。
这台 RK3588 大模型服务器我现在还在用,主要做本地文档问答和代码辅助。虽然速度比不上云端 API,但数据完全在自己手里,而且没有使用次数限制,用起来很踏实。如果你手里也有一块吃灰的 RK3588,不妨试试这个方向,折腾的过程本身就挺有意思的。