上篇聊了FPGA在机器人中的应用,很多读者私信问我:现在AI模型越来越火,但机器人算力有限,怎么把深度学习模型塞进嵌入式设备里跑?

这个问题太实际了。你在服务器上训练好的模型,动辄几百MB,放到Jetson Nano或者树莓派上根本跑不动。就算能跑,帧率可能只有个位数,根本没法做实时推理。

今天就来聊聊端侧AI部署的核心技术和工具链,面试的时候这块考得越来越多。

TensorRT:NVIDIA的推理加速利器

TensorRT是NVIDIA专门做推理优化的库。它的核心思路是把训练好的模型"压缩"成在NVIDIA GPU上跑得最快的形式。

工作流程很直接:先用TensorFlow或PyTorch训练好模型,导出ONNX格式,然后用TensorRT的解析器加载,经过一系列优化后生成针对具体GPU型号优化的推理引擎。

import tensorrt as trt
logger = trt.Logger(trt.Logger.WARNING)
builder = trt.Builder(logger)
network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
parser = trt.OnnxParser(network, logger)
parser.parse_from_file("model.onnx")

构建完网络后,还需要配置builder并序列化引擎。builder的max_workspace_size参数决定了优化过程中可使用的临时显存上限,设太小了找不到好的优化方案,设太大了嵌入式设备没那么多显存。序列化后的engine文件是和GPU型号绑定的——在Jetson Xavier上生成的engine不能直接拿到Orin上用,部署到不同硬件必须重新生成。

TensorRT的优化手段有好几层。算子融合是最基础的一层——把Conv+BN+ReLU三个算子合成一个,减少显存读写。精度校准也很关键——INT8量化能把推理速度提升2-3倍,但需要校准数据集来保证精度不崩。动态batch支持让同一个引擎可以处理不同batch size的输入。

面试经常问的一个点是:TensorRT的INT8量化和训练后量化有什么区别?训练后量化是直接把FP32权重截断成INT8,简单粗暴但精度损失大。TensorRT的校准方式是用一小批真实数据跑一遍,统计每层激活值的分布,找到最优的量化范围。这个校准过程叫PTQ(Post-Training Quantization)。

还有个进阶话题是QAT(Quantization-Aware Training)。在训练阶段就模拟INT8的量化误差,让模型学会适应低精度。精度损失比PTQ小很多,但需要重新训练。如果你的模型对精度很敏感,QAT是更好的选择。

实际项目中用TensorRT,踩坑最多的地方是算子不支持。你模型里用了个冷门算子,TensorRT不认,解析直接报错。解决办法要么换掉这个算子,要么自己写Plugin。写Plugin需要懂CUDA编程,门槛不低。

NCNN:腾讯开源的纯CPU推理框架

不是所有机器人都用得起Jetson AGX。很多场景下你的计算平台就是个ARM CPU,没有GPU。这时候NCNN就派上用场了。

NCNN是腾讯开源的轻量级推理框架,专门为移动端和嵌入式优化。它不依赖任何第三方库,纯C++实现,可以直接交叉编译到ARM平台。

ncnn::Net net;
net.load_param("model.param");
net.load_model("model.bin");
ncnn::Extractor ex = net.create_extractor();
ex.input("input", in);
ex.extract("output", out);

NCNN的优化策略和TensorRT思路类似但实现不同。ARM NEON指令集加速是核心——用SIMD指令一次处理多个数据。内存复用也做得很极致,同一块内存在不同层之间复用,减少分配开销。

NCNN支持从PyTorch、ONNX、Caffe等多种格式转换模型。转换工具链是ncnn-tools,里面有convertmodel可以一键转换。但实际用起来没这么顺滑——经常遇到算子映射不上的情况,需要手动调整或者用ncnn自带的算子替换。

面试有个经典问题:NCNN和TensorFlow Lite怎么选?我的看法是,如果你的目标平台是ARM且没有GPU,NCNN通常更快,因为它对ARM的优化更深入。如果你需要跨平台(Android/iOS/嵌入式),TFLite的生态更成熟。另外NCNN的社区主要在中文圈,遇到问题查资料反而比TFLite方便。

端侧部署的工程挑战

模型转换只是第一步。真正做产品级部署,有一堆工程问题要解决。

内存管理是第一个坑。嵌入式设备内存有限,你要精确控制每一层的内存占用。NCNN有个选项可以设置blob内存对齐方式,对齐到4字节还是16字节,直接影响内存用量和访问速度。在内存紧张的设备上,每一点优化都很关键。

多线程调度也很讲究。推理本身是多线程的,但你的系统还有其他任务在跑——传感器数据采集、控制循环、通信。怎么分配CPU核心?一般做法是把推理绑到特定的核心上,避免和其他任务抢资源。Linux下用taskset或者sched_setaffinity就能实现。

模型更新是另一个容易忽略的问题。你的模型不可能一成不变,OTA更新是必须的。但模型文件动辄几十MB,下载和加载都要时间。工程上一般用双buffer方案——一个buffer跑当前模型,后台加载新模型,加载完切换。切换过程对用户无感知。

class ModelManager:
    def __init__(self):
        self.active_model = None
        self.standby_model = None
    def load_new_model(self, path):
        self.standby_model = load_engine(path)
    def switch_model(self):
        self.active_model, self.standby_model = \
            self.standby_model, self.active_model

功耗控制在移动机器人上特别重要。推理跑满CPU/GPU的时候功耗可能是空闲时的5-10倍。如果你的机器人靠电池供电,续航直接打折。解决办法包括:动态调整推理频率(不需要每帧都推理的时候降频)、用更小的模型、或者只在感兴趣区域做推理。

面试追问环节

面试官最爱追问的是精度和速度的trade-off。"你把模型量化到INT8,精度掉了多少?怎么评估的?"这个问题你得有数据支撑。一般做法是准备一个测试集,跑FP32和INT8分别统计精度指标(mAP、IoU等),画出精度-速度曲线。

"你做过模型剪枝吗?"结构化剪枝是端侧部署的常用手段——把卷积核中不重要的通道直接砍掉,模型变小变快,精度损失可控。PyTorch里可以用torch.nn.utils.prune做非结构化剪枝,但结构化剪枝需要自己实现或者用NVIDIA的sparse-maintool。

"端侧推理的延迟怎么测?"别只说"用time.time()"。工程上要测的是端到端延迟(从输入到输出)、每层耗时分布、GPU利用率、内存峰值。TensorRT有自带的profiler,NCNN可以编译时开启NCNN_BENCHMARK_LAYER。

上篇讲了FPGA可以做AI加速,其实FPGA+CPU的异构方案在高端机器人里越来越常见。FPGA做低延迟的预处理(图像缩放、归一化),GPU/NPU做推理,CPU做后处理和业务逻辑。这种架构设计能力,是高级岗位的加分项。


端侧AI部署是机器人面试的热门话题,因为几乎所有做产品的公司都会遇到这个问题。你不需要把每个框架都摸透,但至少要亲手部署过一个模型——从训练到转换到优化到跑通,完整走一遍。

下一篇聊单元测试。模型部署完了,怎么保证推理代码的正确性?Google Test来帮忙。


如果这篇文章对你有帮助,欢迎点赞、在看、转发三连。 你的支持是我持续更新的最大动力。

「机器人软件开发面试·从入门到精通」连载系列 

上一篇:第325篇 FPGA基础——机器人中的硬件加速

下一篇预告:第327篇 单元测试实战——Google Test在机器人项目中的应用

有任何问题欢迎评论区留言,我会尽量回复。

 

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐