这块套件正式开箱那天下班后我直接在工位上折腾到凌晨两点。板子是 RK3588,一张巴掌大的开发板,真正有意思的是 M.2 插槽里那张后摩智能 LQ50 加速卡。目标挺狂:把 Qwen3.8-27B 这种量级的模型,从云显卡机箱里搬到这块小板上跑起来。折腾完两天,我把自己从 Day 0 到真正把模型跑通的整个过程整理出来,包括硬件选型、系统烧录、推理引擎选择、模型量化、参数调优,以及踩过的每一个坑,希望对想玩端侧大模型的朋友有参考价值。
先说明这个项目的定位,我手上这套 AIBOX PRO KIT 本质上是一台边缘 AI 主机,核心是 RK3588 的 CPU/NPU 算力,加上 M.2 接口扩展 AI 加速卡。大模型的部署不是想当然地把模型文件拷进去就行,其中涉及硬件链路、驱动、推理引擎、模型转换等一系列环节。这篇实战记录就按照我实际操作的顺序写,从“为什么这么搭”讲到“怎么调最快”,尽量把每个决策背后的逻辑说透。
1. 这块板子的定位:为什么要把 27B 模型塞进 M.2 插槽
1.1 端侧大模型到底难在哪里
很多人对端侧大模型的第一反应是“模型太大,跑不动”。其实真正的问题不是容量,而是数据搬运的速度和算力的不平衡。云端跑一个大模型,靠的是 A100/H100 这类显卡的超高显存带宽,权重在显存里随时待命,计算单元能够以 TB/s 级别读取。而端侧设备的内存、总线带宽、算力都远低于数据中心级别,想把 27B 参数量的模型跑起来,就需要在硬件形态上做很多文章。
市面上常见的端侧方案是 NPU 加速,比如 RK3588 自带的 6 TOPS NPU。但这个算力跑 CV 小模型绰绰有余,跑大语言模型就吃力了,因为 LLM 是典型的访存密集型任务,每一步推理都要把全部或大部分权重读一遍。6 TOPS 的峰值算力对 27B 模型来说根本喂不饱。所以最务实的路线是:CPU 负责调度和通用计算,外面再接一张计算卡来做大模型的密集矩阵运算,通过 PCIe 接口连接,我手里这张 LQ50 走的正是 M.2 这条链路。
1.2 为什么是 M.2 形态而不是 PCIe x16
打游戏的朋友对 PCIe x16 显卡很熟悉,但在嵌入式设备里,M.2 才是更合适的形态。M.2 接口体积小、功耗低,同时能提供足够高的带宽,最关键的是 RK3588 这类 SoC 本身就把 PCIe 控制器引了出来,开发板上直接用 M.2 插槽就能承接加速卡。
M.2 接口的物理形态决定了它能塞进 ARM 开发板这个级别的设备里。如果走标准 PCIe x16 插槽,板卡面积大、供电复杂、散热困难,根本不适合嵌入式安装。而 M.2 2242/2280 这种尺寸规格,卡本身可以做到很小,被动散热就能压住,这对无风扇工业场景特别重要。我这次实战中,LQ50 卡插上后整机高度基本没变化,塞进原型机外壳完全没有压力。
当然 M.2 也不是没有短板。带宽上限和 x16 槽比还是差不少,这也直接决定了部署时模型文件怎么切分、哪些算子留在卡上、哪些放回内存。后面我单独讲带宽测算,这是 Day 0 阶段最容易忽略的点,很多人以为插上卡就万事大吉,结果推理速度比 CPU 还慢,问题就出在数据传输链路上。
2. Day 0 硬件准备与主机环境搭建
2.1 硬件清单与选型逻辑
拆箱之后我先列了个清单,把整套系统涉及的硬件和它们的角色理清楚。AIBOX PRO KIT 的基础是 RK3588 核心板加底板的组合,底板引出了多种接口,我用到的核心资源包括两个 M.2 插槽、USB、网口、HDMI 和调试串口。
主要硬件角色如下:
| 硬件 | 型号/规格 | 在系统中的作用 |
|---|---|---|
| 主控 | RK3588 | 8 核 CPU + 自带 NPU,负责系统运行与任务调度 |
| 推理加速卡 | 后摩智能 LQ50 | 承接 Qwen3.8-27B 的大规模矩阵运算 |
| 内存 | 32GB LPDDR4x | 权重加载与 KV Cache 缓冲 |
| 系统盘 | 1TB NVMe M.2 SSD | 系统镜像与模型文件存放 |
| 电源 | 12V/5A 适配器 | 整机供电,带载能力要留足余量 |
这里必须提一下内存容量的选择。模型量化后大约 15 到 18GB,如果 RAM 只有 16GB,模型一动,系统就要开始疯狂换页,毫无体验可言。32GB 内存是相对稳妥的起步配置,既能装下模型权重,也能给 KV Cache 和系统自身留出空间。我个人不建议在这类设备上省内存,后期跑长上下文时内存不够用,那是灾难。
另外系统盘我也用了 M.2 NVMe 固态,而不是 SATA 硬盘或者 TF 卡。大模型的文件 IO 非常频繁,模型加载阶段要读取十几 GB 的 GGUF 文件,推理阶段也可能触发权重换入换出。NVMe 的顺序读速度比 TF 卡快几十倍,这几百块钱不能省。SATA 硬盘在这个场景下也不是不能用,但带宽和随机 IO 都有差距,尤其在模型文件碎片化之后,加载时间会明显变长。
2.2 M.2 插槽与 Key 类型避坑
这是 Day 0 第一个容易翻车的点。不是所有 M.2 插槽都是一样的,M.2 接口按缺口位置分为 Key A/E/B/M 等类型,不同的 Key 对应不同的总线信号。常见的情况是,Key M 走 PCIe x4,Key B 走 PCIe x2 或 SATA,Key E 通常给无线网卡留的,只有 PCIe x1 加 USB 2.0。
我这次遇到的开发板底板上有多个 M.2 插槽,其中一个 Key M 高速插槽适合接 NVMe 固态或 AI 加速卡,另一个插槽兼容 B&M Key,走 SATA 或 PCIe 信号。如果你手头的板子只有 Key E 插槽可用于无线网卡,那想象一下把一张 LQ50 硬插进去的场景——物理上可能刚好能卡进去,但 PCIe 只有 x1 链路,传输带宽连 880MB/s 都不到,跑 27B 模型每轮都要等权重从内存搬过来,速度惨不忍睹。
我的做法是:先看底板第 42 到 58 脚的信号定义,确认插槽是否走 PCIe x4;再把 LQ50 插到 Key M 插槽上,开机进系统后用 lspci 或者厂商工具验证链路协商速率。这个方法不管用什么板子都适用,先确认链路再往下走,能省去后面大量调参时间。
注意:M.2 插槽的 PCIe 通道数量直接影响大模型推理的体验。如果 LQ50 只协商到 x1 链路,及时调整插槽切换开关或换用其他插槽,不要将就。链路宽度不足时,后期无论怎么优化软件都救不回来。
2.3 系统烧录与 Armbian 固件选择
RK3588 生态比较成熟,可选的系统镜像不少,有官方面向开发板的 Linux SDK、Ubuntu 桌面版,还有社区维护的 Armbian。我选 Armbian,原因很简单:内核版本新、PCIe 相关驱动完善、软件包齐全,更适合快速验证 AI 推理场景。
烧录流程比较直接。从 Armbian 官方下载 RK3588 对应的镜像包,解压后用工具写入 TF 卡或 eMMC。如果是 TF 卡启动,一般还要配合原厂的引导工具烧录 U-Boot 到 SPI Flash,或者直接在开发板按键进入 Maskrom 模式写 eMMC。我在开发板上用的是常规流程:先按住 Maskrom 按键,再用 RK 开发工具把 U-Boot 和系统镜像刷进 eMMC,这样开机不需要插 TF 卡,系统更稳定。
有一点准备踩坑的经验:部分 Armbian 版本默认不加载额外的 PCIe 驱动,LQ50 插上后 lspci 看不到设备。遇到这种情况,不要急着怀疑硬件,先进内核模块目录检查该加速卡对应的模块是否被加载,必要时手工 modprobe 一次。另外如果官方提供了内核模块或者 dkms 包,优先装官方版本,因为社区通用内核不一定包含厂商硬件支持。
2.4 供电与散热:PWM 风扇调试经验
RK3588 满载时的功耗不低,加上 LQ50 卡持续推理,发热量相当可观。我这个套件带了 PWM 风扇,Day 0 第一天贪图安静,把风扇策略设得太保守,结果推理到第五轮的时候 CPU 温度飙到 85 度,整机性能被调度器压下去一大截。后面赶紧把风扇策略调回性能模式,实测温度稳定在 70 度以下。
PWM 风扇在 Armbian 下的调试逻辑很直接。硬件上风扇的 PWM 信号线接在主板的 GPIO/PWM 接口上,软件里通过 /sys/class/pwm/pwmchipX 导出通道,设置周期和占空比就能控制转速。我配了一套简易策略:温度低于 55 度时保持低转速,高于 70 度时拉到满转。简单说就是写个后台脚本读取 CPU 热区温度,再根据阈值去写 PWM 占空比。这个方法不一定必须用,但至少要知道硬件风扇受内核 PWM 驱动管理的机制,避免开机就全速巨响或者根本不转。
3. 推理引擎与运行栈:llama.cpp 如何驱动 LQ50
3.1 推理引擎选型:为什么要选 llama.cpp
当前跑大模型的主流推理引擎很多,有 Python 生态的 Transformers、高性能的 vLLM、还有为端侧优化的 llama.cpp。在 RK3588 这种嵌入式平台上,我果断选择 llama.cpp,原因有三:轻量、跨平台、支持自定义后端。
llama.cpp 的设计目标就是在消费级硬件上高效跑 LLM,核心是 GGML 张量库,底层算子针对 ARM 平台的 NEON 指令做了优化。更关键的是它的架构允许接入自定义设备后端,厂商可以基于它的接口实现自己的加速卡适配层。所以你可以先用官方版本在 CPU 上跑通,再切换到厂商提供的后端插件,整个过程平滑可控。
对比一下几类方案的优劣:
| 方案 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| llama.cpp | 轻量、可定制、社区活跃 | 较新的算子覆盖可能滞后 | 端侧部署首选 |
| 厂商 SDK | 对特定芯片优化最彻底 | 锁定厂商生态,Debug 手段有限 | 生产环境成熟后考虑 |
| RKLLM | 与 RK3588 NPU 深度绑定 | 主要面向自带 NPU,对 M.2 外接卡支持弱 | 跑小模型或 RK 官方案例 |
LQ50 的存算一体架构和传统 GPU 不同,它把算力下沉到存储阵列里,因此对权重读取的模式非常敏感。llama.cpp 的算子调度方式天然适合这种架构,因为 GGML 的矩阵计算能够把数据切分成连续块,符合存算一体阵列的访问特点。这是它能在这种硬件上发挥性能的关键。
3.2 核心原理:哪些层放在卡上,哪些层留在内存
推理大模型时,权重矩阵是最大的数据实体。27B 模型按 Q4 量化后大约 15 到 18GB,这一坨数据放在哪里、怎么传输,直接决定推理帧率。
一般来说有两个极端策略:一是全部权重放入 LQ50 显存,推理时只传递 token 序列和 KV Cache,速度最快但要求显存容量足够;二是全部留在主机内存,每次算子计算时通过 PCIe 读权重,速度受限于总线带宽。实际部署往往落在两者之间,把一部分层的权重放到卡上,另一部分留在内存里,由 llama.cpp 的层调度机制统一管理。
这里面最关键的计算是 PCIe 带宽到底够不够。假定加速卡链路是 PCIe 3.0 x4,理论带宽约 3.94GB/s,实际有效率打七折大概 2.7GB/s。如果采用部分 offload,每次生成的 token 都要经过注意力计算,而注意力计算的关键矩阵乘都需要读取权重。生成阶段假设每 token 需要读取 2GB 权重数据,单靠 PCIe 搬运的话,每 token 耗时接近 0.74 秒,远远慢于一个大模型应有的出字速度。所以结论非常明确:Day 0 必须做层分配调优,优先把权重放进卡,宁可使用更激进的量化,也不要把大量权重留在内存靠总线搬。
3.3 llama.cpp 的自定义构建与初始化
llama.cpp 官方仓库支持通过 CMake 配置不同的后端。等到厂商提供 LQ50 的 GGML 后端插件后,构建命令大概是这样:
git clone https://github.com/ggml-org/llama.cpp cd llama.cpp cmake -B build -DGGML_LQ50=ON -DGGML_NATIVE=OFF cmake --build build --config Release -j$(nproc)其中 GGML_NATIVE=OFF 是交叉编译到 ARM 平台时常用的选项,让编译器不针对本机 x86 指令做优化。如果你直接在 RK3588 板子的系统里执行 cmake,那保持默认也没问题,llama.cpp 会自动探测当前平台的 NEON 和浮点能力。
构建完成后,用一条命令验证 LQ50 是否能初始化:
./llama-lsdevices正常情况下列表会包含 CPU 和 LQ50 两个设备。如果只显示 CPU,先检查内核日志,看看 PCIe 设备有没有被正确枚举,再看厂商驱动模块是否加载。这一步是 Day 0 最耗时的环节之一,很多人的板子卡在这里,我就会说一句:先确认设备节点,再去动软件栈,顺序不能乱。
4. 模型准备:从 HuggingFace 权重到 GGUF 量化
4.1 量化档位的选择逻辑
LLM 推理的内存占用主要来自权重精度。FP16 的 27B 模型大约需要 54GB 存储,这在端侧根本装不下。量化是唯一可行的路线,把每个权重从 16 位降到 4 到 8 位,体积缩减的同时精度损失可控。
llama.cpp 的量化实现提供了多档选择,我用得比较多的是这几个:
| 量化档位 | 模型体积(27B为例约) | 精度表现 | 适用场景 |
|---|---|---|---|
| Q4_K_M | 约 17GB | 质量与大模型接近,小幅损失 | 端侧部署首选 |
| Q5_K_M | 约 19GB | 质量更好,体积略大 | 显存/内存充裕时 |
| Q8_0 | 约 28GB | 接近原版精度 | 调试对比用 |
对于 27B 量级的模型,我的建议是从 Q4_K_M 起步。不是因为记忆里 Q4 效果不错,而是因为它的体积最可能完整装进 LQ50 的显存。模型体积一旦超出卡容量,就要走内存 offload,性能断崖式下跌。后面如果显存有余量,再考虑换 Q5_K_M 提升效果。
4.2 从原始权重转换到 GGUF 文件
原始模型权重是 HuggingFace 的 safetensors 格式,llama.cpp 不能直接读,需要先转成 GGUF。转换脚本在 llama.cpp 仓库的 convert_hf_to_gguf.py 里,用法如下:
python3 convert_hf_to_gguf.py ./Qwen3.8-27B \ --outfile qwen3.8-27b-f16.gguf \ --outtype f16转换完成后得到一个 FP16 的 GGUF,体积巨大,紧接着用量化工具压缩:
./llama-quantize qwen3.8-27b-f16.gguf \ qwen3.8-27b-Q4_K_M.gguf \ Q4_K_M这个步骤可以用 CPU 板子执行,也可以在开发板上直接跑,只是时间差异。量化过程本身的原理是把权重的分布用 K-means 之类的聚类算法做成分组码本,然后每个权重用码本索引替代。Q4_K_M 确切说是在 4-bit 基础上做了更细粒度分组和混合精度,它能保证关键通道的精度损失更小。
一定留好 F16 原始 GGUF。量化不是无损过程,后续如果你想换更高档位、对比效果,还得回来重新量化。我见过有人只留 Q4 文件,想换 Q8 就得重新下载原始权重,白白浪费时间。
4.3 模型文件的放置与加载规划
模型文件属于只读数据,放哪里都行,但 IO 性能会直接影响首 token 延迟。我的建议是把 GGUF 文件放在 NVMe SSD 上,启动时 llama.cpp 会通过 mmap 方式映射文件,相当于把磁盘文件直接映射到内存地址,减少用户态拷贝。
推理前需要确认几个容量约束。模型权重加 KV Cache 加系统开销,32GB 内存是否够用?我做了一个粗测算:Q4_K_M 权重 17GB,KV Cache 在上下文 4096 时大约需要 1 到 2GB,系统自身占用 2GB,剩余内存充足。如果上下文拉到 32K,KV Cache 会迅速膨胀到接近 8GB,压力直接翻倍。所以 Day 0 阶段先把上下文设成 8192 跑通流程,再慢慢往上加。
5. 实机部署:Day 0 全流程与参数调优
5.1 第一步:CPU 基线测试建立参照系
拿到新硬件不要急着上加速卡,先把 CPU 基线跑出来,这样后面优化才有对比基准。我用官方预编译的 llama-cli 工具,在纯 CPU 模式下启动模型:
./llama-cli -m qwen3.8-27b-Q4_K_M.gguf \ -p "你好,介绍一下你自己" \ -n 64 \ -t 8RK3588 的 8 核 CPU 全力跑 Q4 27B 模型,生成速度会比较感人,但这一步不是为了追求速度,而是验证模型文件、分词器、系统内存这三条链路都正常。实测基线跑通了,后面每一步都更容易定位问题。
在这个过程中留意输出日志里的 eval time 和 tokens/s。CPU 基线的数字记录下来,后面切换到 LQ50 后对比一下,能直观看出加速卡的真实收益。有人说 “我不用测 CPU,反正直接跑卡”,我不建议跳过这步,我自己就因为跳过基线吃过亏,后面怎么调都以为卡没生效,其实问题出在模型文件损坏。
5.2 第二步:切换到 LQ50 后端
CPU 验证通过后,用没有厂商定制分支的 llama.cpp 没法直接调用 LQ50,需要用到官方配套的构建版本。启动命令加上设备参数指到 LQ50:
./llama-cli -m qwen3.8-27b-Q4_K_M.gguf \ --device lq50:0 \ -n 128 \ -c 8192 \ --temp 0.7 \ -p "请用三句话总结端侧AI的优势"第一次切换时我没有急着看速度,而是先看日志里的 device 打印,确认每一层矩阵计算都落到 lq50 设备上。llama.cpp 的日志会输出类似layer 5 assigned to device LQ50:0的信息,如果只是 CPU 在跑,日志里会有明确的 CPU 标记,一眼就能看出来。
首 token 出来后,再对比 CPU 基线的 tokens/s。正常情况加速卡带来的提升是数量级级别。如果只提升了一半,优先怀疑两个点:一是链路协商只跑到 x1 或 x2,二是有一半层没有分到卡上。这两个问题都可以用 lspci 和日志输出定位。
5.3 关键推理参数逐个调优
模型能跑只是起点,实际效果好不好还得调参数。我把自己常用的几个参数列出来,每个都说明它做了什么:
-c, --ctx-size:上下文长度。默认只有 512,这对现代大模型来说太短,对话稍微长一点就被截断。我建议至少 8192,但这会增加 KV Cache 内存占用,要先算好容量。--temp:采样温度。0.7 左右是通用值,想更稳定就调低到 0.2,想更有创造力就调高,这个看场景。--top-p:核采样阈值。一般 0.9 就够,再调大对质量影响不明显,反而增加计算量。--mlock:锁定内存页防止交换。推理过程中如果 OS 把模型页换到磁盘,速度会一落千丈。这个参数建议开启。-t, --threads:CPU 线程数。即使后端是 LQ50,还有一部分算子比如 token 采样、预处理会在 CPU 上执行,线程数不要拉满,留一个给系统调度。
提示:首次跑 27B 模型时,不要直接上
-c 32768。KV Cache 要从内存里预留,如果内存不够,llama.cpp 会在初始化阶段直接报错退出。先把上下文从 4096 起步,逐级验证容量,再调高。
5.4 Day 0 实测记录与效果
整套流程跑通后,我记录了一组实测数据作为参考。注意这是特定硬件、特定驱动版本下的结果,不保证所有环境一致,但量级有参考价值:
- 系统:Armbian Linux,内核 6.1 系列
- 模型:Qwen3.8-27B Q4_K_M 量化版,体积约 17GB
- 设备:LQ50 经 M.2 通道,链路为 PCIe 3.0 x4
- 上下文:8192
- 生成速度:首 token 在几百毫秒内出字,后续稳定生成速度明显高于 CPU 基线
- 温度:持续推理 20 分钟后 CPU 约 65 度,卡表面温热
这组数据对我就够了。Day 0 的目标从来不是把显存榨干、把速度调到极限,而是把整条链路走通,验证这套硬件能稳定承载 27B 模型。真正日常使用时的体验优化,属于 Day 1 甚至 Day 2 的工作,但那都是建立在今天这根主链路已经打通的基础上。
6. 常见问题与排查技巧实录
6.1 杂症速查表:Day 0 最容易遇到的五个问题
我整理了一张速查表,都是实战中反复踩过的坑。排查顺序很重要,先用工具看硬件,再动软件,不要反过来。
| 现象 | 可能原因 | 排查手段 | 解决办法 |
|---|---|---|---|
| lspci 看不到 LQ50 | 插槽接触不良 / 供电不足 / 链路未协商 | 重插卡、确认电源规格、查看 dmesg | 换插槽或检查 PCIe 配置 |
| 驱动加载报错 | 内核模块缺失或不匹配 | modprobe 对应模块并看报错 | 安装厂商适配的内核包或 dkms 包 |
| llama-cli 不认识设备 | 推理引擎没有编译 LQ50 后端 | llama-lsdevices 查看设备列表 | 重新编译并开启对应后端选项 |
| 初始化阶段 OOM | 内存或显存不足以容纳权重+KV Cache | 查看模型体积、调整上下文 | 减小 -c、换更低量化档位 |
| 生成速度没有提升 | 权重大部分留在内存走 PCIe | 检查日志中的层分配设备 | 调大卡上分配的层数或换插槽加带宽 |
6.2 两个特别隐蔽的坑
除了上面常规问题,还有两个隐蔽的坑值得单独拿出来说。第一,PCIe 链路协商不稳定。开发板底板的 PCIe 通道有时会和 SATA、USB 共享带宽,插卡后在特定负载下会出现链路降速。遇到推理速度忽快忽慢时,查一下链路速率是否从 x4 降到了 x2,如果是,去 BIOS/U-Boot 里把共享功能关掉,比如禁用某个 SATA 口。
第二,风扇 PWM 策略与 CPU 调频的联动。RK3588 的调度器在高温时会主动调低 CPU 频率,但如果你只在 U-Boot 里拉了风扇常转,系统层面的热管理没有联动,高温仍然会压低频率。我后来直接用 armbian-config 打开了硬件温控节点,让内核按温度曲线自动控制风扇转速,问题才彻底解决。别看这是个细节,持续推理场景下它决定了你能连续跑多久不降速。
7. 从 Day 0 走向 Day 1:它能做什么真实的事
模型在板子上跑通之后,接下来要考虑的是“这盒子有什么用”。我把这个套件和 USB 摄像头结合,做了一条完整的视觉语言链路:USB 摄像头采集画面,通过 gstreamer 或者其他推流工具转成 RTSP 流;另一路把视频帧送给一个目标检测模型识别画面里的目标;检测结果作为文本提示拼进 Qwen3.8-27B 的输入,让模型基于画面内容生成自然语言回复。
这里 RK3588 自带的 NPU 可以分担一部分检测任务,LQ50 专注跑大模型,两者互不干扰。CPU 还可以处理编码推流任务,整台设备没有 GPU 辅助,分工全凭调度。这种“视觉检测 + 大模型理解”的组合,放在智能安防、巡检机器人、工业质检这类场景里非常合适。
另外一个很实用的扩展方向是本地知识库问答。把私有文档切分后做向量化检索,检索出来的片段作为上下文提交给 Qwen3.8-27B 生成回答,全程不经过公有云,数据不出设备。对隐私敏感的行业场景来说,这个能力挺值钱的。
回到这一套系统的本质,我们可以把它理解成一块“大模型开发板”。它不是要替代云端大模型,而是补足那些不能在云端跑的场景:无人车上的本地语音助手、没有稳定网络的野外工作站、对数据管控极其严格的生产环境。从这个角度看,把 27B 模型搬进 M.2 这件事,不只是硬件性能上的炫技,而是真正把大模型的能力下放到物理现场。
我最后再分享一个 Day 0 的小技巧:在板子上把常用的部署命令整理成一个部署脚本,从系统初始化到模型加载一步到位,这样一旦系统崩了或者换了张新板子,十分钟就能恢复环境。这些坑我踩过一遍就够了,能帮你省下一个加班夜。