三台华为昇腾 310B4 下位机的分布式推理yolo26:上位机调度、内网穿透与 8K 目标检测
title: “三台昇腾 310B4 下位机的分布式推理实践:上位机调度、内网穿透与 8K 目标检测”
date: 2026-05-11
categories:
- AI 工程实践
tags: - 昇腾 310B4
- 分布式推理
- YOLOv26
- 8K 检测
- Tailscale
- SSH
- SFTP
三台昇腾 310B4 下位机的分布式推理实践:上位机调度、内网穿透与 8K 目标检测
这篇记录的是我最近整理的一套分布式目标检测推理方案。硬件主角是三台昇腾 310B4 下位机,上位机是一台 Windows 机器。上位机不跑模型,主要负责选图、切图、调度、传输、回收结果和统计;三台 310B4 下位机只负责加载 OM 模型并执行推理。
这套方案的重点不是“模型并行”,而是“任务级分布式推理”:把一批图片,或者一张 8K 大图切出来的多个 tile,拆成独立任务分发给多台 310B4。每台下位机各跑各的,最后由上位机统一合并结果。
本文只放配置、流程、伪代码和关键设计,不展开完整源码。
1. 系统定位
这个系统解决的是一个很实际的问题:单台下位机能跑,但是批量图片和 8K 大图跑起来比较慢;三台下位机都在手边,如果每台都单独手工上传、执行、下载,效率又太低。
所以我的处理方式是:
- Windows 上位机作为总控端;
- 三台昇腾 310B4 下位机作为推理节点;
- 上位机通过 SSH 执行远程命令;
- 上位机通过 SFTP 上传图片、下载标签;
- 普通图片按图片粒度分发;
- 8K 图片先切成大 tile,再按 tile 粒度分发;
- 每台下位机推理结束后,上位机统一回收 YOLO 标签并做汇总。
最终得到的是一个比较清晰的“上位机调度 + 多下位机推理”的结构,后续要增加第四台、第五台下位机时,只需要扩展设备配置和分发策略。
2. 四阶段流程图
这张图是上位机调用下位机检测算法的四阶段流程。

如果博客平台不方便显示 SVG,可以用下面的 Mermaid 版本:
四个阶段里,最关键的是第二和第三阶段:上位机要把任务真正分散到多台 310B4 上,同时保证每台下位机的输入输出目录相互独立、状态可控、失败可定位。
3. 文件结构
主要文件放在 distributed/ 目录里:
distributed/
├── gui_client_parallel.py # 上位机 GUI 和分布式调度入口
├── preprocess_8k.py # 8K 填充、切块、tile 标签合并
├── config.py # 配置读取
├── config.yaml # 设备、远程目录、推理命令、8K 参数
├── requirements.txt # 上位机依赖
├── SSH与SFTP调度推理流程说明.md
├── 8K裁剪流程详解.md
└── 上位机调用下位机检测算法-四阶段流程草图.svg
项目根目录/
├── inference_yolov26_full.py # 下位机侧 OM 推理脚本
└── best.om # 昇腾 OM 模型
这里有一个原则:上位机代码不直接处理 NPU 推理,下位机代码也不掺杂 GUI 调度逻辑。两边通过“输入目录、输出目录、远程命令、标签文件”这几个约定连接起来。
4. 网络结构与内网穿透
三台下位机在实验室内网里,IP 固定为:
下位机 1:192.168.137.100
下位机 2:192.168.137.101
下位机 3:192.168.137.102
Windows 上位机有两张网络意义上的接口:
Windows 上位机
├── 内网网关地址:192.168.137.1
└── Tailscale 地址:100.x.y.z
内网里,上位机可以直接访问三台下位机:
ping 192.168.137.100
ping 192.168.137.101
ping 192.168.137.102
如果同门或另一台电脑不在实验室内网,就通过 Tailscale 做子网路由。这里不是把三台下位机都安装 Tailscale,而是让 Windows 上位机作为子网网关,把三台下位机的 /32 路由广播出去。
网络关系可以理解成:
远程电脑
|
| Tailscale 私有网络
|
Windows 上位机 / 子网网关
|
| 192.168.137.0/24 实验室内网
|
三台昇腾 310B4 下位机
上位机侧需要做三件事。
第一,开启 Windows IPv4 转发:
Set-NetIPInterface -InterfaceAlias "Tailscale" -AddressFamily IPv4 -Forwarding Enabled
Set-NetIPInterface -InterfaceAlias "<内网网卡名称>" -AddressFamily IPv4 -Forwarding Enabled
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" `
-Name IPEnableRouter -Value 1 -PropertyType DWord -Force
第二,广播三台下位机的子网路由:
tailscale set --advertise-routes=192.168.137.100/32,192.168.137.101/32,192.168.137.102/32
第三,在 Tailscale 管理后台批准这些路由。批准后,其他加入同一个 tailnet 的电脑就能像在局域网一样访问三台下位机。
远程电脑侧测试:
tailscale status
ping 192.168.137.100
ping 192.168.137.101
ping 192.168.137.102
Test-NetConnection 192.168.137.100 -Port 22
Linux 客户端如果没有自动接受路由,需要执行:
sudo tailscale set --accept-routes
跑通以后,远程电脑也可以直接 SSH 到 310B4:
ssh root@192.168.137.100
ssh root@192.168.137.101
ssh root@192.168.137.102
这个方案的好处是:分布式推理程序本身不用改。无论人在内网还是外网,只要能通过 Tailscale 访问这三个 192.168.137.x 地址,上位机仍然按原来的 SSH/SFTP 方式调度下位机。
5. 上下位机部署
上位机环境比较轻:
cd E:\project\shijiazhuang\distributed
python -m pip install -r requirements.txt
python gui_client_parallel.py
依赖主要是:
paramiko
Pillow
pyyaml
每台 310B4 下位机需要准备:
/shijiazhuang/
├── best.om
├── inference_yolov26_full.py
├── incoming/
└── outgoing/
└── labels/
下位机侧主要检查 CANN、Python、ais_bench 和 SSH:
source /usr/local/Ascend/ascend-toolkit/set_env.sh
/usr/local/miniconda3/bin/python --version
/usr/local/miniconda3/bin/python -c "from ais_bench.infer.interface import InferSession; print('ais_bench OK')"
npu-smi info
6. 配置文件
核心配置放在 config.yaml。下面是公开版配置,密码和私钥路径都用占位符,实际发布博客时不要写真实凭据。
HOST: "192.168.137.100"
USER: "root"
PASSWORD: "<SSH_PASSWORD>"
KEY_FILE: "<SSH_PRIVATE_KEY_PATH>"
REMOTE_IN: "/shijiazhuang/incoming"
REMOTE_OUT: "/shijiazhuang/outgoing"
ENABLED_HOSTS:
- "192.168.137.100"
- "192.168.137.101"
- "192.168.137.102"
EIGHTK_OUTPUT_CLASS_ID: 2
EIGHTK_NMS_CLASS_AGNOSTIC: true
EIGHTK_NMS_CONTAINMENT: 0.80
EIGHTK_BOX_SCALE: 1.5
EIGHTK_TILE_SIZE: 7168
EIGHTK_OVERLAP: 0.10
EIGHTK_CONF: 0.05
EIGHTK_POST_CONF: 0.05
INFER_CMD: >-
source /usr/local/Ascend/ascend-toolkit/set_env.sh &&
/usr/local/miniconda3/bin/python /shijiazhuang/inference_yolov26_full.py
--source {remote_in} --save-dir {remote_out}
--model /shijiazhuang/best.om
--device 0
--labels ""
--no-save-vis
INFER_TIMEOUT: 600
SSH_TIMEOUT: 120
SCP_TIMEOUT: 60
STATS_INTERVAL: 2
UPLOAD_WORKERS: 4
RETRY_COUNT: 3
RETRY_DELAY: 3
几个字段比较重要:
ENABLED_HOSTS:真正参与推理的 310B4 下位机列表。REMOTE_IN:上位机上传图片或 tile 的远程输入目录。REMOTE_OUT:下位机写出预测标签和摘要文件的目录。INFER_CMD:下位机实际执行的检测命令模板。UPLOAD_WORKERS:每台下位机上传文件时的并发路数。EIGHTK_TILE_SIZE和EIGHTK_OVERLAP:8K 切块大小和重叠比例。
如果某台下位机临时关机,只需要从 ENABLED_HOSTS 里移除对应 IP,GUI 就不会再给它分配任务。
7. 分布式执行是怎么跑起来的
这一节是整套系统的核心。
我这里做的是任务级并行。每张普通图片是一个任务;在 8K 模式下,每个 tile 是一个任务。任务准备好以后,上位机根据启用设备列表做分组,然后为每台下位机启动一个独立执行单元。
公开版伪代码如下:
读取启用的 310B4 下位机列表
读取待推理图片列表
如果是 8K 模式:
把每张大图填充到 8192 x 8192
按大窗口切成多个 tile
把 tile 列表作为待分发任务
否则:
把原始图片列表作为待分发任务
按照设备数量对任务做轮询分组
对每台下位机并行启动一个执行单元:
检查 SSH 连接
创建远程输入/输出目录
清理上一次残留文件
通过 SFTP 并发上传本组任务
通过 SSH 执行远程推理命令
下载 labels 和 summary
清理远程输入目录
等待所有下位机完成
如果是 8K 模式:
把 tile 标签映射回完整大图坐标
执行全图去重和合并
如果是验证模式:
计算 AP50、TP、FP、FN
写出日志、标签和摘要文件
这里有两个设计点比较关键。
第一,每台下位机是独立执行单元。某台设备上传慢、推理慢或出现错误,不会直接阻塞其他设备继续完成自己的任务。上位机最终根据各 worker 的返回状态统一汇总。
第二,任务分发粒度可以变化。小图模式按图片分发,8K 模式按 tile 分发。这样三台 310B4 不需要知道输入是原图还是大图切片,下位机只看到一批普通图片,处理方式保持一致。
实际分发时,10 张 8K 大图会先生成 40 个 tile,然后分给三台设备,例如:
下位机 1:14 个 tile
下位机 2:13 个 tile
下位机 3:13 个 tile
这种分发方式简单,但很稳。后面如果要继续优化,可以把静态轮询换成动态队列,让空闲下位机主动领取下一批任务。
8. SSH/SFTP 调度链路
上位机和下位机之间只依赖 SSH/SFTP,不需要额外部署服务端程序。这样做的好处是部署简单,排错也直接。
单台下位机的调度顺序可以概括成:
SSH: echo connected
SSH: mkdir -p incoming/outgoing/labels
SSH: rm -f old inputs and old labels
SFTP: upload images or tiles
SSH: run remote inference command
SSH: read predict_summary.json
SFTP: download labels
SSH: cleanup remote inputs
推理命令由配置文件统一管理。运行前,上位机会把 {remote_in} 和 {remote_out} 替换成对应目录。如果启用 8K 或 AP50 验证,还会追加必要的置信度输出参数。
远程命令形态大致如下:
source /usr/local/Ascend/ascend-toolkit/set_env.sh &&
/usr/local/miniconda3/bin/python /shijiazhuang/inference_yolov26_full.py \
--source /shijiazhuang/incoming \
--save-dir /shijiazhuang/outgoing \
--model /shijiazhuang/best.om \
--device 0 \
--labels "" \
--no-save-vis
这条命令是公开文章里可以说明的,因为它只是部署和调用方式。真正涉及调度、异常处理、性能采集、标签合并的细节,源码里保留即可。
9. 8K 大图推理
8K 模式的处理思路是:大图在上位机切,下位机仍然只推理普通图片。
参数如下:
目标画布:8192 x 8192
切块大小:7168 x 7168
重叠比例:0.10
步长:6451
处理流程:
原始大图
-> 等比例缩放
-> 居中填充到 8192 x 8192
-> 生成 2 x 2 大窗口 tile
-> 记录每个 tile 在全图里的 left/top/right/bottom
-> 分发 tile 到三台 310B4 推理
-> 下载每个 tile 的 YOLO 标签
-> 将 tile 坐标还原到 8192 全图坐标
-> 全图 NMS 去重
-> 输出每张大图的最终标签
一张 8K 图切成 4 个 tile 后,三台下位机可以同时跑不同 tile。下位机完全不需要知道这些 tile 来自哪张大图;上位机在本地保存了 tile 与原图之间的映射关系,合并时再用。
最终标签仍然是 YOLO 格式:
class_id center_x center_y width height confidence
8K 模式的 FPS 按完整大图统计,不按 tile 统计。也就是说,系统显示的 fps_8k 表示每秒处理多少张完整 8K 图,而不是每秒处理多少个裁剪块。
10. 下位机侧推理
下位机脚本负责加载 OM 模型、读取输入目录图片、执行预处理、调用 NPU 推理、后处理并写出标签。
这里不贴完整源码,只保留执行逻辑:
加载 best.om
遍历输入目录图片
对图片做 letterbox 和归一化
调用 ais_bench InferSession 推理
把输出还原成原图坐标
执行置信度过滤和 NMS
写出 labels/*.txt
写出 predict_summary.json
predict_summary.json 里会记录图片数量、检测数量、纯推理耗时和平均 FPS。上位机读取这个文件后,可以区分“模型实际推理耗时”和“上传下载等端到端耗时”。
11. AP50 验证
验证模式要求输入目录包含:
dataset/
├── images/
└── labels/
普通图片验证时,上位机可以把同名标签同步到远程临时目录;8K 验证时,GT 标签需要和大图填充规则同步变换到 8192 坐标系,再与合并后的预测框计算 AP50。
匹配规则如下:
按类别分组
预测框按置信度从高到低排序
同类别预测框与未匹配 GT 计算 IoU
IoU >= 0.50 记为 TP
没有匹配成功的预测记为 FP
没有被匹配的 GT 记为 FN
根据 precision-recall 曲线计算 AP50
最终输出:
validation_metrics.json
里面包含整体 AP50、每类 AP50、TP/FP/FN,以及每张图片的 best IoU。
12. 一次 8K + AP50 测试结果
这是一组 10 张 8K 大图的测试摘要:
{
"mode": "8k",
"source_count": 10,
"tile_count": 40,
"raw_tile_detections": 43,
"total_detections": 12,
"pure_inference_sec": 1.5406,
"fps_8k": 6.490978,
"wall_time_sec": 70.2809,
"ap50": 0.36
}
这里要分清两种时间:
pure_inference_sec:来自下位机侧推理摘要,三台并行时取最慢的那台作为瓶颈。wall_time_sec:上位机从开始分发到下载、合并完成的墙钟时间。
调试日志里能看到分布式执行过程:
8K 前置处理完成:10 张大图生成 40 张 tile
开始 8K 并行推理:3 台下位机,10 张 8K 大图,40 张 tile
下位机 1 分配 14 张
下位机 2 分配 13 张
下位机 3 分配 13 张
远程推理结束:ok=True,纯推理耗时=...
8K tile 结果合并完成:raw tile 目标 43,全局合并后 12
验证完成:AP50=0.3600
从这个日志也能看出来,分布式执行不是简单地“同时点三次运行”,而是上位机统一准备任务、统一分发、统一监控、统一回收。
13. 输出目录
普通推理输出:
output/
├── labels/
│ ├── image_001.txt
│ └── image_002.txt
├── predict_summary.json
├── predict_summary.csv
├── inference_fps.txt
└── parallel_debug.log
8K 推理输出:
output/
├── labels/
│ ├── big_image_001.txt
│ └── big_image_002.txt
├── predict_summary_8k.json
├── validation_metrics.json
└── parallel_debug.log
中间生成的 tile 和 tile 标签会放在临时目录,推理完成后自动清理。最终保留下来的主要是大图标签、摘要文件和调试日志。
14. 排查方法
检查上位机代码是否能正常导入:
cd E:\project\shijiazhuang\distributed
python -m py_compile config.py preprocess_8k.py gui_client_parallel.py
检查三台 310B4 是否在线:
ping 192.168.137.100
ping 192.168.137.101
ping 192.168.137.102
检查 SSH 端口:
Test-NetConnection 192.168.137.100 -Port 22
Test-NetConnection 192.168.137.101 -Port 22
Test-NetConnection 192.168.137.102 -Port 22
检查模型和推理脚本:
ssh root@192.168.137.100 "test -f /shijiazhuang/best.om && test -f /shijiazhuang/inference_yolov26_full.py && echo ok"
如果 GUI 提示失败,优先看:
output/parallel_debug.log
这份日志会记录连接、上传、远程命令、stderr、下载和结果缺失情况,比单纯看界面提示有用得多。
15. 小结
这套方案的核心卖点很明确:用 Windows 上位机把三台昇腾 310B4 下位机组织成一个分布式推理系统。下位机负责 NPU 推理,上位机负责调度、传输、8K 切块、结果合并、AP50 验证和日志。
我比较满意的一点是,下位机侧保持了“只做推理”的简单形态。只要 SSH、SFTP、CANN、ais_bench 和 OM 模型正常,上位机就可以把普通图片或 8K tile 分发过去跑。后续无论是增加下位机数量,还是把任务分配改成动态调度,都有继续扩展的空间。
更多推荐


所有评论(0)