Отчёт: полный пайплайн от установки до запуска LLM на RK3562
Часть 1. Настройка виртуальной машины (Lubuntu)
Работа велась в существующей VM с Lubuntu, где уже был развёрнут Buildroot для RK3562 — это позволило использовать её же как среду конвертации.
1.1. Установка Miniforge (менеджер окружений)
Скачали и установили Miniforge3 — облегчённую сборку Conda с каналом conda-forge. Это дало изолированное управление Python-окружениями, чтобы не сломать систему сборки образа:
wget
https://github.com/conda-forge/miniforge/releases/download/25.11.0-0/Miniforge3-25.11.0-0-Linux-x86_64.sh
bash Miniforge3-25.11.0-0-Linux-x86_64.sh
При установке возникла ошибка сертификата (certificate verify failed), вызванная отставанием системных часов VM от реального времени. Исправили синхронизацией времени (timedatectl set-ntp true). После этого установка прошла успешно.
1.2. Клонирование репозитория RKNN-LLM
git clone
https://github.com/airockchip/rknn-llm.git
cd rknn-llm
git checkout release-v1.3.0
Выбрали релиз v1.3.0 — последний стабильный, поддерживающий RK3562.
1.3. Создание изолированного окружения и установка RKLLM-Toolkit
conda create -n rkllm python=3.10 -y
conda activate rkllm
pip install rkllm-toolkit/packages/rkllm_toolkit-1.3.0-cp310-cp310-linux_x86_64.whl
Версия Python 3.10 выбрана потому, что именно под неё собран .whl-пакет.
1.4. Установка зависимостей и чистка от CUDA
Установили зависимости из requirements.txt, включая torch==2.6.0, transformers==5.8.0, auto_gptq==0.7.1.
Поскольку GPU NVIDIA в VM нет, изначально pip подтянул CUDA-пакеты (nvidia-*, triton) общим весом ~2.5 ГБ. Заменили torch на CPU-сборку и удалили CUDA-зависимости:
pip uninstall -y torch torchvision triton
pip install torch==2.6.0 torchvision==0.21.0 --index-url
https://download.pytorch.org/whl/cpu
pip uninstall -y nvidia-cublas-cu12 nvidia-cuda-cupti-cu12
nvidia-cuda-nvrtc-cu12 nvidia-cuda-runtime-cu12 nvidia-cudnn-cu12
nvidia-cufft-cu12 nvidia-curand-cu12 nvidia-cusolver-cu12
nvidia-cusparse-cu12 nvidia-cusparselt-cu12 nvidia-nccl-cu12
nvidia-nvjitlink-cu12 nvidia-nvtx-cu12
pip cache purge
В итоге размер site-packages уменьшился с 6.1 ГБ до 1.7 ГБ.
Проверка работоспособности:
python -c "from rkllm.api import RKLLM; print('RKLLM Toolkit OK')"
RKLLM Toolkit OK
Часть 2. Подготовка модели и конвертация
2.1. Скачивание модели Qwen2-0.5B-Instruct
pip install "huggingface_hub[cli]"
export HF_ENDPOINT=https://hf-mirror.com
hf download Qwen/Qwen2-0.5B-Instruct --local-dir ~/models/Qwen2-0.5B-Instruct
Из-за нестабильной сети к hf-mirror.com большие файлы скачались сразу, а мелкие пришлось докачивать по отдельности. В итоге получили полный набор: config.json, model.safetensors (988 МБ), tokenizer.json, vocab.json, merges.txt, generation_config.json, tokenizer_config.json.
Проверка целостности:
python -c "
from transformers import AutoModelForCausalLM, AutoTokenizer
m = AutoModelForCausalLM.from_pretrained('/home/user/models/Qwen2-0.5B-Instruct')
t = AutoTokenizer.from_pretrained('/home/user/models/Qwen2-0.5B-Instruct')
print('params:', sum(p.numel() for p in m.parameters()))
"
params: 494032768
2.2. Правка скрипта экспорта
Скрипт examples/rkllm_api_demo/export/export_rkllm.py (в v1.3.0) адаптировали под RK3562. Изменили 5 параметров.
Параметр CUDA_VISIBLE_DEVICES:
было: '0'
стало: ''
Параметр modelpath:
было: /path/to/DeepSeek-R1-Distill-Qwen-1.5B
стало: /home/user/models/Qwen2-0.5B-Instruct
Параметр device (в load_huggingface):
было: 'cuda'
стало: 'cpu'
Параметр target_platform:
было: "RK3588"
стало: "RK3562"
Параметр num_npu_core:
было: 3
стало: 1
Остальные параметры оставили: quantized_dtype="W8A8", quantized_algorithm="normal", optimization_level=1, max_context=4096, калибровочный датасет — готовый data_quant.json из репозитория (20 КБ).
2.3. Запуск конвертации
cd examples/rkllm_api_demo/export
python export_rkllm.py 2>&1 | tee export_log.txt
Процесс занял ~5 минут (на CPU, с готовым калибровочным датасетом):
загрузка модели — 1–2 мин,
квантизация — 5 мин,
экспорт — секунды.
Результат:
INFO: Model saved to ./Qwen2-0.5B-Instruct_W8A8_RK3562.rkllm!
Файл — 787 МБ, формат W8A8 (8 бит веса, 8 бит активации), целевая платформа RK3562.
Часть 3. Кросс-компиляция демо-приложения
Использовали компилятор из существующего Buildroot, а не официальный тулчейн ARM.
Компилятор:
/home/user/src/buildroot-2024.02-rk3562-sk/output/host/bin/aarch64-linux-gcc (GCC 12.3.0, обёртка toolchain-wrapper)
Sysroot:
output/host/aarch64-buildroot-linux-gnu/sysroot
Библиотека runtime:
rkllm-runtime/Linux/librkllm_api/aarch64/librkllmrt.so
Создали собственный скрипт сборки build-linux-buildroot.sh, заменив путь к компилятору. Также пришлось добавить в PATH cmake из Buildroot (output/host/bin/cmake).
export PATH="/home/user/src/buildroot-2024.02-rk3562-sk/output/host/bin:$PATH"
cd ~/npu/rknn-llm/examples/rkllm_api_demo/deploy
./build-linux-buildroot.sh
Результат сборки:
install/demo_Linux_aarch64/llm_demo — ELF 64-bit ARM aarch64, 26 КБ
install/demo_Linux_aarch64/lib/librkllmrt.so — 7.6 МБ
Часть 4. Упаковка и перенос на плату
4.1. Сборка payload
Создали папку ~/rk3562_payload/demo_Linux_aarch64/ с четырьмя файлами:
llm_demo (26 КБ)
lib/librkllmrt.so (7.6 МБ)
Qwen2-0.5B-Instruct_W8A8_RK3562.rkllm (787 МБ)
fix_freq_rk3562.sh (1 КБ)
4.2. Упаковка в bz2-архив
cd ~/rk3562_payload
tar -cjf rk3562_payload.tar.bz2 demo_Linux_aarch64/
sha256sum rk3562_payload.tar.bz2 | tee rk3562_payload.tar.bz2.sha256
Размер архива: 602 МБ.
SHA256: 62e6e7e0260cc0805ba1481185b1ea0ad404677322b451a21c5c3931c47f1fcd.
4.3. Перенос
Сетевого доступа между VM и платой не было, поэтому архив перенесли вручную через USB-флешку. Путь к архиву в VM:
/home/user/rk3562_payload/rk3562_payload.tar.bz2
4.4. Распаковка на плате
На плате (в /root) стандартный tar BusyBox не поддерживал -j (bzip2), поэтому распаковка шла в два шага:
cd /root
bunzip2 -k rk3562_payload.tar.bz2
tar -xf rk3562_payload.tar
Получили папку /root/demo_Linux_aarch64/ с теми же 4 файлами.
Часть 5. Проверка окружения на плате
Ключевые данные перед запуском.
SoC: RK3562 (4× Cortex-A53)
RAM: 3.8 ГБ
Драйвер NPU: RKNPU2, SDK 2.3.0, версия 0.9.8
Проверка NPU: rknn_test.sh (YOLOv5) — 40 мс/кадр, 25 FPS
Файловая система: /dev/root 6.9 ГБ, свободно 5 ГБ
Отдельно выяснили проблему: /dev/rknpu отсутствует, поскольку RKNPU2 работает через DRM-узел. Это норма, и dmesg-предупреждения при старте ядра не блокирующие.
Также выяснилось, что shell на UART-консоли не имеет полноценного tty: read line возвращал EOF сразу, stty ругался Inappropriate ioctl. Из-за этого std::cin в llm_demo сразу получал пустую строку, и программа в цикле генерировала шаблонный ответ "Hello! How can I assist you today?".
Решение: запуск через хостовый терминал (SSH/minicom), где stdin — реальный tty.
Часть 6. Запуск LLM на плате
6.1. Финальная команда запуска
cd /root/demo_Linux_aarch64
export LD_LIBRARY_PATH=./lib
export RKLLM_LOG_LEVEL=1
./llm_demo Qwen2-0.5B-Instruct_W8A8_RK3562.rkllm 2048 4096
Параметры запуска:
2048 — максимум новых токенов в ответе,
4096 — длина контекста (совпадает с max_context=4096 при конвертации).
6.2. Результат запуска
Лог инициализации:
rkllm-runtime version: 1.3.0, rknpu driver version: 0.9.8, platform: RK3562
max_context_limit: 4096, npu_core_num: 1, target_platform: RK3562, model_dtype: W8A8
Enabled cpus: [0, 1, 2, 3]
rkllm init success
Метрики производительности:
Время инициализации модели: ~1.1–3.0 с
Prefill (TTFT для 19 токенов): 182 мс (104 ток/с)
Скорость генерации: 11.5–12.0 токенов/с
Пиковая память: 690 МБ
6.3. Примеры диалога
user: столица франции
robot: Столицей Франции является Париж.
user: сколько пальцев на трех руках?
robot: На трех руках у вас есть 6 пальцев.
Модель отвечает осмысленно на простые вопросы. Ошибки в логических/арифметических задачах и потеря контекста — ожидаемое ограничение модели на 0.5B параметров (а также следствие того, что llm_demo не хранит историю диалога).
Итог
Весь пайплайн «Hugging Face → RKLLM → NPU RK3562» полностью отработан.
Этап VM: Miniforge + RKLLM-Toolkit 1.3.0 в изолированном окружении → готовая среда конвертации.
Этап «Модель»: скачали Qwen2-0.5B-Instruct (494M параметров) → локальная копия HF-модели.
Этап «Конвертация»: W8A8, target=RK3562, num_npu_core=1 → файл *.rkllm 787 МБ.
Этап «Кросс-сборка»: GCC 12.3.0 из Buildroot + cmake → llm_demo (26 КБ) + librkllmrt.so.
Этап «Перенос»: bz2-архив 602 МБ через USB-флешку → распакован в /root/demo_Linux_aarch64/.
Этап «Запуск»: llm_demo через tty-совместимый терминал → 11.5–12 токенов/с, 690 МБ.
Получен работающий локальный LLM-инференс на встраиваемой плате: без интернета, полностью на NPU, со скоростью генерации, достаточной для интерактивного общения.