ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Atlas 200DK开发实战:从制卡到人像分割模型部署全记录

Atlas 200DK开发实战:从制卡到人像分割模型部署全记录 上周末我把吃灰半年的华为Atlas 200DK拿出来从制卡开始一路折腾到人像语义分割模型在板子上跑通中间踩了不少坑也算把整套AI开发环境从零梳理了一遍。这篇文章就是我这次实操的完整记录。如果你手上正好有一块Atlas 200DK或者正打算用它做边缘AI Demo、参加昇腾相关的竞赛那这篇东西应该能帮你省下好几个周末的摸索时间。先交代一下背景我有一定的嵌入式Linux基础之前玩过树莓派和Jetson Nano但昇腾工具链确实是第一次认真接触。这次目标很明确——搭建一套可复用的AI开发环境然后把一个开源的人像语义分割模型部署到板子上让摄像头拍到的画面能实时输出人像Mask。整个过程中制卡、系统配置、CANN安装、模型转换、ACL推理接口这些环节全都要过一遍任何一个地方出问题都会卡住。1. 先把这个板子看懂Atlas 200DK的硬件定位与适用场景1.1 为什么不是树莓派也不是Jetson Nano很多刚接触Atlas 200DK的朋友第一个问题就是它到底是一块什么样的板子简单说这是一块搭载昇腾310处理器的AI推理开发板核心价值在“端侧AI推理”不是拿来当普通Linux服务器用的。昇腾310这颗芯片的AI算力大约8 TOPSINT8功耗却只有十几瓦比Jetson Nano的算力上限要高一个档次更不用说树莓派了。但注意这个算力是针对推理场景设计的它并不适合用来做模型训练。如果你想在板子上直接跑PyTorch训练循环那基本不用想老老实实用GPU服务器训练完再把模型转成OM离线模型部署下来。我做过一张对比表方便有Jetson或树莓派经验的朋友快速定位维度Atlas 200DKJetson Nano树莓派4B核心芯片昇腾310Tegra X1BCM2711AI算力约8 TOPSINT8约0.5 TFLOPS FP16无专用NPU典型功耗10-20W5-10W3-7W推理开发方式模型转OM ACL接口TensorRT/TFLite纯CPU/VPU适合场景端侧AI应用、视觉推理入门级AI边缘计算教育、轻量服务踩坑指数中等偏上中等低如果你是纯新手树莓派可以让你快速建立Linux和Python的感觉如果你想要比较成熟的AI部署生态Jetson的教程也多得多。但Atlas 200DK不一样它的生态相对封闭官方文档大量更新网上案例也少可一旦把工具链跑通你会发现它在推理性能上确实有两把刷子。1.2 到手后的硬件检查与接口对应关系Atlas 200DK的包装里通常就是一块开发板本体外加电源线。没有带SD卡没有带散热风扇更没有显示器。我第一次拿到时还有点不适应——这板子的设计思路压根没打算让你接显示器用而是通过SSH远程操作推理结果通过网络或GPIO输出。在动手之前建议先把接口认全电源接口DC圆口官方建议用12V电源不要用普通的5V手机充电器供电不足会导致板子随机重启。千兆网口用来和宿主机通信第一次调试必须靠它。USB 3.0口可以接摄像头、U盘等外设实际做视觉Demo时我一般直接接一个USB摄像头。20-Pin排针可以接UART串口、GPIO、I2C等适合做嵌入式联动。SD卡槽系统从这里启动所以制卡是第一步。还有一个容易忽略的点散热。昇腾310在持续推理时发热明显如果板子裸奔没有散热片跑个几分钟就可能降频。我后来买了一个带风扇的散热套件固定在芯片上稳定性提升很多。1.3 为什么必须有一台Ubuntu主机Atlas 200DK的开发模式和树莓派有个很大的区别树莓派可以自己编译、烧录系统但它不行。官方推荐的模式是“开发环境Ubuntu主机 运行环境Atlas 200DK”分离制卡需要在x86的Ubuntu主机上运行脚本把系统镜像写入SD卡。模型转换在Ubuntu主机上安装CANN开发套件用ATC工具把ONNX等模型转成OM格式。交叉开发代码可以用VS Code在主机上写好通过SSH同步到板子上运行。所以一台配置还行的Ubuntu主机是必需品。我用的是Ubuntu 20.04建议用官方长期支持版本不要用太新的22.04或24.04有些工具链兼容性问题会让你怀疑人生。板子端也建议装Ubuntu 20.04 aarch64版本和CANN的兼容性最好。2. 从一张空白SD卡开始制卡与基础系统安装2.1 制卡要准备的软件包清单制卡前需要准备的东西不多但版本一定要对。我整理一下我这次使用的清单一张16GB以上的高速SD卡建议Class 10或U1以上一个USB读卡器Ubuntu x86主机昇腾社区下载的Atlas 200DK制卡脚本make_sd_card.pyUbuntu 20.04 aarch64服务器镜像扩展名为.img后续需要安装的CANN安装包建议先下载好再制卡不然板子联网下载大文件会比较慢这里有个重点制卡脚本和SD卡镜像版本要匹配不要拿新脚本刷旧镜像。官方文档中每个版本都会标明配套的固件、镜像、CANN版本组合一定要严格按照那个表来。我第一次就是随手下了个最新镜像结果制卡脚本报错后来换成配套版本才顺利通过。另外SD卡的质量直接影响稳定性。我之前用一张杂牌卡板子启动后经常出现文件系统损坏后来换了一张闪迪的A2卡问题消失。这个钱不能省。2.2 制卡过程一步步走制卡其实就是一个脚本搞定的事但中间有几个细节很容易踩坑。把SD卡插到Ubuntu主机的读卡器上先执行lsblk找到SD卡的设备名比如/dev/sdb。这一步非常重要搞错了会把主机系统盘干掉。确认无误后执行sudo python3 make_sd_card.py local /home/user/atlas_ubuntu_20.04.img /dev/sdb注意如果报No module named tkinter需要先安装sudo apt install python3-tk制卡过程大约需要10到15分钟期间脚本会擦除SD卡、建立分区、写入镜像。完成后把SD卡从读卡器里拿出来插入Atlas 200DK的SD卡槽接上网线、电源板子就会开始启动。建议制卡期间不要动SD卡也不要在主机上休眠或待机。我中途因为主机休眠导致写卡中断最后重新制了一遍白白浪费了半小时。2.3 网络配置与SSH登录板子默认的系统用户和密码以官方文档为准我这次拿到的是HwHiAiUser用户密码是Mind123但不同版本可能不一样请务必查阅你对应版本的文档。网络配置是新手容易卡住的点。Atlas 200DK板子的默认IP通常是192.168.1.2主机需要设置成同一个网段的静态IP才能访问。我当时的操作是把主机的有线网卡IP手动设为192.168.1.1子网掩码255.255.255.0然后用网线直连开发板。配置完成后在主机上执行ping 192.168.1.2 ssh HwHiAiUser192.168.1.2能够SSH进去就说明系统已经正常起来了。这里有一个小建议如果以后要频繁传文件直接用scp或者VS Code的Remote-SSH插件比来回插拔SD卡方便太多。我后期的所有代码编辑和推理调试都是在VS Code里完成的。3. 安装CANN运行环境这块板子的“CUDA”到底怎么配3.1 CANN开发套件和运行套件要怎么选如果你用过NVIDIA平台CANN可以理解成昇腾的“CUDA CUDNN”。它负责管理算子的执行、内存的分配、模型的加载推理。CANN的安装包分成好几种我这次在板子上接触到的主要是两类Ascend-cann-toolkit开发套件包含编译工具、ATC模型转换工具、算子开发工具等。Ascend-cann-nnrt纯运行套件只包含模型推理所需的最小运行环境。对于板子端如果你只是跑模型推理官方建议装nnrt就够。但如果你需要在板子上现场做模型转换、或者调试算子那就得装toolkit。我的做法是直接在板子上装了toolkit因为调试时经常要碰ATC和算子相关的东西省去来回切换的麻烦。缺点是体积大对SD卡空间有一定要求建议至少准备32GB的卡。另外CANN还有x86版本和aarch64版本之分。板子是aarch64架构必须下载linux-aarch64的安装包。在主机上做模型转换时则要下载你主机架构对应的版本通常是linux-x86_64。3.2 命令行安装的具体过程与验证把CANN安装包传到板子上之后先赋可执行权限chmod x Ascend-cann-toolkit_6.2.RC1_linux-aarch64.run然后以普通用户执行安装不建议用root直接安到系统目录默认安装路径是/home/HwHiAiUser/Ascend/也有些人手动指定到/usr/local/Ascend。我按默认路径来省去后续权限管理的问题./Ascend-cann-toolkit_6.2.RC1_linux-aarch64.run --install安装过程会持续几分钟看到Successfully installed字样就表示成功。安装完成后需要source环境变量脚本否则命令行找不到atc和acl工具source ~/Ascend/ascend-toolkit/set_env.sh为了不用每次开终端都手动source我建议把这行写进~/.bashrc。然后执行atc --version验证环境是否生效。除了版本还能用官方给的样例快速验证环境。昇腾社区提供了很多sample可以直接跑一个人脸检测确认整条链路没问题。我当时跑通了官方的人脸检测样例后心里就有底了。3.3 版本匹配的坑固件、驱动、CANN要“锁死”一组这块板子最大的坑就是版本匹配。Atlas 200DK不只是CANN一个软件它还涉及固件和驱动。固件版本、驱动版本、CANN版本、模型转换时用的ATC版本、甚至Python接口版本整套东西必须是一套匹配的组合。官方文档中每个版本号旁边都会标注它配套的固件和驱动一定要对着看。我自己经历过一次惨痛的版本混搭板子CANN是6.2主机ATC是6.1在主机上转换出来的OM模型拿到板子上跑直接报E-ACL: Model was built by CANN 6.1, but current runtime is 6.2之类的错误。后来把主机CANN也升到6.2问题才解决。所以建议在动手之前就确定一个“黄金组合”比如我这次用的组件版本固件驱动配套6.2版本的固件包板子CANNAscend-cann-toolkit 6.2 RC1 (aarch64)主机CANNAscend-cann-toolkit 6.2 RC1 (x86_64)模型转换工具主机ATC 6.2如果不确定当前环境版本可以用命令检查npu-smi info这个命令会显示芯片信息、固件版本、驱动版本。和CANNN版本对照后能快速判断有没有冲突。4. 人像语义分割的模型选型什么样的模型适合端侧部署4.1 端侧分割任务要面对的现实约束人像语义分割这个任务本身不复杂就是给图像里的每个人像素打上“人”或“非人”的标签输出一张二值Mask。难点在于端侧部署时的三个约束分辨率训练时常用512x512或1024x1024分辨率越高边缘越精细但推理耗时成倍增加。速度如果要做实时摄像头推流至少需要10FPS以上也就是单帧推理时间不能超过100ms最好在30ms以内。内存昇腾310板载内存有限大模型加高分辨率输入很容易把内存吃满。所以模型选型的原则是参数量尽量小、算子尽量简单、精度够用就行。不要一上来就想上DeepLabV3虽然效果好但计算量感人在端侧跑起来会非常吃力。4.2 我最终选的方案PP-HumanSeg我对比了几个人像分割模型模型参数量输入尺寸端侧友好度是否容易拿到权重DeepLabV3较大512x512差算子重容易BiSeNetV2中等512x512中需转换容易U2-Net中等320x320中中等PP-HumanSeg小512x512高算子简单非常容易最后我选了百度PaddleSeg开源的PP-HumanSeg模型。原因有几个第一它专做人像分割提供了现成的人像类权重第二模型结构相对轻量计算量小适合端侧第三PaddleSeg有配套的模型导出工具导出ONNX比较顺畅减少很多转换期的痛苦。这里不是给Paddle打广告纯粹是实操中这条路最省事。如果团队主要用PyTorch选BiSeNetV2也一样但你要额外处理PyTorch转ONNX过程中一些算子的兼容性问题比如部分上采样和批归一化算子可能需要替换。4.3 数据预处理Mean、Std、通道顺序一个都不能错很多人在拿到模型后直接转OM完全忽略了预处理配置结果推理出来的Mask全是噪声或者根本是黑的。语义分割模型的预处理必须和训练时完全一致核心是三点尺寸、通道顺序、归一化参数。PP-HumanSeg在训练时一般使用BGR通道输入尺寸512x512归一化参数是[0.5, 0.5, 0.5]这类。如果你是在主机上用OpenCV读取图像默认就是BGR直接resize到512x512然后转成NCHW的float数组归一化到[-1, 1]就能直接给模型。如果你在模型转换时用AIPP配置做预处理那这一步可以被硬件接管不用在Python端手动做。但要注意AIPP里写的mean_chn_0、mean_chn_1、mean_chn_2的顺序是和模型的输入通道顺序严格对应的。我最初在AIPP里把RGB和BGR搞反了结果实验效果奇差排查了半天才发现是通道顺序的问题。5. 从Paddle/PyTorch到OM模型ATC转换才是最大的坎5.1 先把模型导出成ONNX昇腾的ATC工具不能直接吃Paddle模型要把Paddle模型先导出成ONNX。PaddleSeg的模型目录里有model.pdmodel和model.pdiparams两个文件用paddle2onnx做转换paddle2onnx --model_dir ./inference_model \ --model_filename model.pdmodel \ --params_filename model.pdiparams \ --save_file model.onnx \ --opset_version 11导出之后建议用onnx.checker验证一下模型结构python -c import onnx; monnx.load(model.onnx); onnx.checker.check_model(m); print(OK)如果这一步报错说明模型本身有问题先解决它再往下走。常见问题包括PaddleSeg版本和paddle2onnx版本不匹配导致某些算子丢失、模型的输入输出名称不确定等。5.2 ATC转换命令解析ATC工具是昇腾的模型转换核心它会把ONNX模型编译成OM离线模型。我的转换命令长这样atc --modelmodel.onnx \ --framework5 \ --outputpphumanseg_512 \ --soc_versionAscend310 \ --input_shapex:1,3,512,512 \ --output_typeFP32 \ --insert_op_confaipp.cfg参数含义--framework5表示输入是ONNX。--soc_versionAscend310指定目标芯片型号Atlas 200DK就是昇腾310。如果选错转换会直接失败。--input_shape必须和模型输入节点名称、维度一致。你可以用Netron打开ONNX查看输入节点的name和shape切忌想当然。--insert_op_conf是AIPP预处理配置文件可选项但强烈建议用。AIPP配置示例aipp_op { aipp_mode: static input_format: RGB888_U8 src_image_size_h: 512 src_image_size_w: 512 csc_switch: false rbuv_swap_switch: true mean_chn_0: 128 mean_chn_1: 128 mean_chn_2: 128 min_chn_0: 0 min_chn_1: 0 min_chn_2: 0 }这个配置文件的字段在不同CANN版本里会有差异我这里给的是参考正式使用前一定去查对应版本的官方样例。AIPP的好处是让硬件完成图像缩放、通道变换、归一化推理时CPU基本不参与预处理。代价是输入图像必须是U8格式的原始数据而且模型里就不能再有归一化相关的层了否则会双重归一化。5.3 转换报错怎么排查ATC转换是整条链路里最容易报错的地方而且报错信息通常很抽象。我遇到过的几类典型错误错误1算子不支持。报错日志里会有一行类似The OP [xxx] is not supported的信息。解决办法通常是在模型结构里把这个算子换掉或者用--disable_reuse_memory等参数调整优化策略实在不行就升级CANN版本。错误2输入shape不匹配。报错会明确指出模型的输入shape信息和--input_shape不一致。用Netron打开模型看一眼就知道了基本都是手误或者模型输入名写错。错误3内存或资源不足。转换过程中如果频繁报某个地址对齐错误多半是宿主机内存不够或者并发任务太多。我遇到过一次关闭VS Code、Docker等大内存程序后重新转换就过了。排查ATC报错重点是看日志。日志默认在~/ascend/log下里面有详细的算子编译过程。不要只看终端输出的最后几行要往下翻详细信息尤其是[ERROR]之前的那条日志往往才是根因。6. 在开发板上写推理代码ACL Python接口从初始化到拿到Mask6.1 推理代码的核心骨架OM模型转换成功后剩下的就是写推理代码了。昇腾的Python推理接口叫ACL语法和习惯跟TensorRT有点像但API完全不同。核心流程是初始化ACL → 设置设备 → 创建Context → 加载模型 → 准备输入输出内存 → 执行推理 → 释放资源。我贴一段简化版的代码骨架省略了错误处理方便看主流程import acl import numpy as np import cv2 # 初始化 acl.init() ret acl.rt.set_device(0) context, ret acl.rt.create_context(0) # 加载模型 model_id, ret acl.mdl.load_from_file(pphumanseg_512.om) model_desc acl.mdl.create_desc() acl.mdl.get_desc(model_desc, model_id) # 获取模型输入输出尺寸 input_size acl.mdl.get_input_size_by_index(model_desc, 0) output_size acl.mdl.get_output_size_by_index(model_desc, 0) # 准备输入输出数据用acl.rt.malloc分配device内存 input_data, input_ptr acl.rt.malloc(input_size, 2) output_data, output_ptr acl.rt.malloc(output_size, 2) # 读取图像并做预处理 img cv2.imread(test.jpg) img cv2.resize(img, (512, 512)) # 这里如果AIPP已经做了归一化只需做HWC-CHW和U8转float img img.astype(np.float32) img img[:, :, ::-1] # BGR转RGB img img.transpose(2, 0, 1) # HWC-CHW img np.ascontiguousarray(img) img_np img.reshape((1, 3, 512, 512)) # 拷贝数据到device acl.rt.memcpy(input_ptr, input_size, img_np.tobytes(), input_size, acl.rt.MEMCPY_DEVICE_TO_DEVICE) # 创建输入输出dataset input_dataset acl.mdl.create_dataset() acl.mdl.add_dataset_buffer(input_dataset, input_ptr, input_size) output_dataset acl.mdl.create_dataset() acl.mdl.add_dataset_buffer(output_dataset, output_ptr, output_size) # 执行推理 ret acl.mdl.execute(model_id, input_dataset, output_dataset) # 拷贝结果回host res_np np.zeros(output_size, dtypenp.uint8) acl.rt.memcpy(res_np, output_size, output_ptr, output_size, acl.rt.MEMCPY_DEVICE_TO_DEVICE) # 释放资源 acl.mdl.unload(model_id) acl.rt.free(input_ptr) acl.rt.free(output_ptr) acl.rt.destroy_context(context) acl.rt.reset_device(0) acl.finalize()这里面有几个容易被忽略的细节第一acl.rt.malloc的第二个参数是对齐粒度一般传1或2表示2字节对齐。如果用默认值传0有些版本会报参数错误。第二输入张量在拷贝到device之前必须是连续内存。我用np.ascontiguousarray确保这一点否则推理结果会莫名出错。第三模型执行是一个同步过程acl.mdl.execute返回后输出数据就能直接拷回来了。6.2 后处理从输出张量到人像Mask语义分割模型的输出通常是[1, num_classes, H, W]或[1, H, W]。PP-HumanSeg的输出是[1, 1, H, W]也就是一个单通道的Score图。我拿到输出后把它reshape回1, 512, 512然后做sigmoid再按阈值0.5二值化score res_np.reshape((1, 512, 512)).astype(np.float32) prob 1.0 / (1.0 np.exp(-score)) mask (prob 0.5).astype(np.uint8) * 255如果模型是多类别输出比如背景人两类的[1, 2, H, W]那就用np.argmax取最大概率的类别。拿到Mask之后可以叠加到原图上。用OpenCV很简单color_mask np.zeros_like(img_ori) color_mask[:, :, 2] mask # 红色高亮 result cv2.addWeighted(img_ori, 0.7, color_mask, 0.3, 0) cv2.imwrite(result.jpg, result)如果做实时视频流注意这里不要用cv2.imshow在板子端弹窗显示因为板子没有显示器。正确做法是把结果压缩成JPEG通过HTTP推流或者直接保存到文件。我是用Flask起了一个简单的HTTP服务把视频帧传上去浏览器实时查看效果调试起来方便很多。6.3 实测性能与优化心得我跑通之后用了一张1024x1536的真人照片输入resize到512x512测了一下速度配置单帧耗时备注FP32模型 AIPP约35msCPU预处理很少FP32模型 手动预处理约45ms预处理需要额外耗时FP16/INT8量化后约10-20ms效果需验证这个速度跑离线图片处理已经完全够用跑实时视频的话如果画面变化不剧烈勉强能到20FPS左右。如果你想进一步优化主要思路有几个批量推理把多帧拼成一个batch一起推理提高芯片利用率但内存占用会上升。模型量化用模型压缩工具把FP32转成INT8推理速度能提升一半以上但精度会有一定损失人像分割这种任务一般可以接受。减小输入尺寸从512x512降到384x384速度提升明显边缘质量稍有下降。多线程流水线采集线程、推理线程、后处理线程分离减少等待时间。我最后实际用的方案是摄像头采集线程 推理线程 结果显示线程。采集线程把帧放到队列里推理线程取帧推理后处理线程负责可视化。这样整体吞吐比单线程循环高一截也不容易掉帧。另外内存释放真的不能懒。ACL分配了device内存后如果忘记acl.rt.free跑长一点时间板子就会OOM程序直接崩。我试过跑了一个多小时然后突然报内存不足排查了半天才发现是某个分支没释放内存。建议每次都写try/finally或者with风格的上下文管理器。还有一个经验OM模型的文件名里最好带上输入规格和精度。比如pphumanseg_512fp32.om这样以后看到文件就知道参数不用反复查。我早期只存了model.om过了段时间拿别的代码验证时完全忘了输入尺寸是什么白白走了弯路。如果你准备参加华为杯、昇腾AI创新大赛或者ICT大赛这类比赛这套环境搭建好之后基本上可以直接作为你所有算法实验的底座。后面再换别的模型只需要过一遍转换流程就行。我甚至在板子上把YOLOv5也顺手转了一遍发现只要掌握了ATC的转换思路不同模型的部署套路都是通的。最后再分享一个小技巧保存OM模型的时候同时在旁边写一个txt文件记录输入节点名、shape、mean/std、通道顺序、后处理方式别小看这一两分钟的事它能避免你隔几天回来做二次开发时被自己当初的参数配置搞到怀疑人生。
返回列表