PaddleOCR-VL 文字识别
本例程把百度 PaddleOCR-VL 模型从 GPU 适配到 RKNN NPU 平台,通过 systemd ocr.service 托管,提供 curl /ocr 单图识别接口。
整体框图
客户端 curl → :7880/ocr
│
▼
ocr_server.py (systemd ocr.service)
├─→ vision encoder → RK1828 NPU (507ms)
├─→ vision-mlp_AR → RK1828 NPU
├─→ LLM decoder → RK1828 NPU (168ms)
└─→ response JSON {text, vision_ms, llm_ms, total_ms, tokens}两条处理域:
- RK3588:HTTP 服务进程、文件 IO、JSON 编解码
- RK1828(PCIe 协处理器):vision encoder + mlp_AR + LLM decoder 三段推理,通过
/dev/dri/renderD128(PCIe 地址0004:41:00.0)对接 Host
1. 装好依赖
# 1. NPU 节点
ls -l /dev/dri/renderD128
# 2. rknn3 systemd 服务
systemctl is-active rknn3
# 3. Python 依赖(rknn3lite / cv2 / gguf)
python3 -c "import rknn3lite, cv2; from gguf import GGUFReader; print('OK')"
# 4. 模型 + 服务单元
ls /userdata/models/Qwen3-VL-2B/PaddleOCR-VL/
ls -l /etc/systemd/system/ocr.service实测输出(节选):
crw-rw---- 1 root video 226, 128 8月27日 11:11 /dev/dri/renderD128
active
OK
PaddleOCR-llm PaddleOCR-vision
-rw-r--r-- 1 root root 477 8月27日 11:11 /etc/systemd/system/ocr.serviceNPU 节点 / rknn3.service / 模型 / 服务单元均就位。
ggufPython 包装在/home/linaro/.local/lib/python3.11/site-packages/(linaro 用户本地安装),root 默认 PYTHONPATH 找不到,见第 2 节。
2. 必要的 service 文件修改
SDK 自带的 ocr.service 用 root 跑,但 gguf 包装在 linaro 家目录,root 看不到 → 启动报 ModuleNotFoundError: No module named 'gguf'。
修复:在 [Service] 段加 User=linaro + PYTHONPATH:
[Service]
Type=simple
User=linaro
Group=linaro
Environment=PYTHONUSERBASE=/home/linaro/.local
Environment=PYTHONPATH=/home/linaro/.local/lib/python3.11/site-packages
ExecStart=/usr/bin/python3 /userdata/models/Qwen3-VL-2B/ocr_server.py
Environment=OCR_HOST=127.0.0.1
Environment=OCR_PORT=7880
Restart=on-failure
RestartSec=10自动修改命令(首次部署需手动跑一次):
sudo cp /etc/systemd/system/ocr.service /etc/systemd/system/ocr.service.bak.$(date +%Y%m%d)
sudo sed -i '/^\[Service\]$/a User=linaro\nGroup=linaro\nEnvironment=PYTHONUSERBASE=/home/linaro/.local\nEnvironment=PYTHONPATH=/home/linaro/.local/lib/python3.11/site-packages' /etc/systemd/system/ocr.service
sudo systemctl daemon-reload
sudo systemctl restart ocr3. 模型文件
/userdata/models/Qwen3-VL-2B/PaddleOCR-VL/
├── llm/
│ ├── PaddleOCR-llm.rknn 16M LLM 结构
│ ├── PaddleOCR-llm.weight 243M LLM 权重
│ ├── PaddleOCR-llm.embed.bin 202M LLM 词嵌入
│ └── PaddleOCR-llm.tokenizer.gguf 2.3M tokenizer
└── vision/
├── PaddleOCR-vision.rknn 28M vision encoder 结构
├── PaddleOCR-vision.weight 261M vision encoder 权重
├── PaddleOCR-vision-mlp_AR.rknn 213K mlp_AR 结构
└── PaddleOCR-vision-mlp_AR.weight 15M mlp_AR 权重合计 ~765 MB,运行时 RSS ~1.2 GB。
4. 一键部署
cd /home/linaro/paddleocr-vl
bash install.sh # 交互式(预检查 → y/n → 验证)
bash install.sh --yes # 跳过确认install.sh 预检查 3 项:8 个模型文件 / 3 个 Python 包 / ocr.service 单元。没有任何文件安装动作,纯复用 + 验证。
5. 日常使用
5.1 make status — systemd 状态
make status实测输出(节选):
● ocr.service - PaddleOCR-VL OCR HTTP Service
Loaded: loaded (/etc/systemd/system/ocr.service; disabled; preset: disabled)
Active: active (running) since ...
Main PID: 12345 (python3)
Tasks: 8 (limit: 7653)
Memory: 1.2G
CPU: 5.132s5.2 make health — 健康检查
make health实测输出:
{
"status": "ok",
"model": "PaddleOCR-VL"
}5.3 make ocr — 跑一次识别
make ocr # 用 SDK 自带 test.jpg输入图片(SDK 自带 test.jpg):

实测输出:

PaddleOCR-VL 已验证端到端跑通:vision encoder + LLM decoder 推理正确,识别到中文文字。
5.4 text 字段的格式
<fcel> / <lcel> / <nl> 是 PaddleOCR-VL 的特殊标记(类似 HTML 表格标签),表示识别框的位置。真实文字内容嵌在标签之间(本例 群号:1025468710)。
需要清洗时,客户端正则:
import re
text = re.sub(r'<[^>]+>', '', raw_text)输出:
群号:10254687105.5 make restart / make logs
make restart # systemctl restart ocr(模型加载 ~60s)
make logs # journalctl -u ocr -n 20
make stop # systemctl stop ocr(临时停)6. HTTP API
6.1 GET /health
curl -s http://127.0.0.1:7880/health响应:
{ "status": "ok", "model": "PaddleOCR-VL" }6.2 POST /ocr
参数:
{ "image": "<base64 of image>" }响应:
{
"text": "<fcel>群号:1025468710<lcel><lcel><nl>",
"vision_ms": 507,
"llm_ms": 168,
"total_ms": 676,
"tokens": 18
}base64 太长会超命令行,Makefile 走临时文件:
python3 -c "import json,base64; print(json.dumps({'image':base64.b64encode(open('test.jpg','rb').read()).decode()}))" > /tmp/ocr_req.json
curl -s -X POST http://127.0.0.1:7880/ocr -H "Content-Type: application/json" --data @/tmp/ocr_req.json7. 性能
| 阶段 | 实测耗时 |
|---|---|
| vision encoder | 507 ms |
| vision-mlp_AR | ~1 ms |
| LLM decoder (18 tok) | 168 ms |
| 总推理 | 676 ms |
| 端到端(含 HTTP) | ~700 ms |
8. 关键事实速查
| 项目 | 取值 |
|---|---|
| 模型路径 | /userdata/models/Qwen3-VL-2B/PaddleOCR-VL/ |
| HTTP 端口 | 7880 |
| systemd 单元 | /etc/systemd/system/ocr.service |
| 运行用户 | linaro(必须,否则 gguf 找不到) |
| API | GET /health / POST /ocr |
| 输入 | { "image": "<base64>" } |
| 输出 | { text, vision_ms, llm_ms, total_ms, tokens } |
| 文本含特殊标签 | <fcel> / <lcel> / <nl>(需客户端清洗) |
| 运行时 RSS | ~1.2 GB |
| 模型文件总大小 | ~765 MB |
| SDK bug | set_chat_template ValueError: -2(不影响 /ocr) |
9. 常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
ocr.service 启动报 ModuleNotFoundError: No module named 'gguf' | root 跑找不到 /home/linaro/.local/ 的 gguf | 按第 2 节加 User=linaro + Environment=PYTHONPATH=/home/linaro/.local/lib/python3.11/site-packages |
/ocr 返回 text 含 <fcel> 等标签 | SDK 输出原样,带位置标记 | 客户端用 re.sub(r'<[^>]+>', '', text) 清洗 |
set_chat_template ValueError: -2 | SDK 已知 bug | 服务仍 listening,/ocr 不受影响 |
| 端口被占用 | 7880 已被其他进程占用 | lsof -i :7880 查谁占,或改 service 的 OCR_PORT |
make ocr 报 Argument list too long | base64 过长超命令行 | Makefile 已用 --data @/tmp/ocr_req.json 临时文件,直接 make ocr |
| 内存不足 OOM | RSS 1.2 GB,板子内存仅 3.8 GB | 加 4 GB swap(同 qwen3-vl 步骤) |
restart 后 60s 还没 ready | 模型加载需要时间 | sleep 60 && make health 验证 |
10. 已知限制
set_chat_template ValueError: -2:SDK bug,服务仍可 listening,内部 prompt 处理可能用默认值,实测/ocr端点不受影响<fcel>等位置标签:需客户端二次清洗- 同 vision-mlp_AR SDK bug:与 qwen3-vl 共享 vision-mlp_AR,实测 vision 单独跑没问题(vision-mlp_AR 报错时)
相关文档
- RTSP 推流 + AI 分析 — 摄像头 + NPU + RTSP 端到端
