面灵AI→

宇树机器人嵌入式AI岗面经(27届)

轮次
多轮合集
base
杭州
结果
挂
时间
2026-10
来源
牛客网

《面试题目》

一面(技术面,约 55 分钟)

  1. 自我介绍,然后挑一个最有挑战的项目讲一下。
  2. 为什么选这个模型?在目标板子上试过哪些方案,最后怎么取舍?
  3. 模型文件大小和运行时内存占用是一回事吗?中间激活、工作区、输入输出缓冲区算进去了吗?
  4. 预处理是在 CPU 上做,还是交给硬件加速?具体做了哪些操作?
  5. 报的耗时包含哪些阶段?第一次运行和预热后的耗时有没有区别?
  6. 摄像头来的速度比推理快,帧在队列里越堆越多怎么办?
  7. volatile 用过哪些场景?访问寄存器和中断共享标志时要注意什么?它能不能代替线程同步?
  8. static 修饰局部变量、全局变量、函数,分别有什么效果?
  9. 结构体内存对齐怎么算?怎样调整成员顺序减少填充?
  10. 指针和数组有什么区别?数组作为函数参数时,实际传进去的是什么?
  11. 堆和栈有什么区别?模型权重和中间结果的内存由谁分配、谁释放?
  12. malloc 在嵌入式项目里有哪些风险?内存碎片怎么产生,频繁分配释放有什么问题?
  13. 浮点和整数运算有什么区别?模型使用 INT8 量化,有什么收益和代价?
  14. 大小端怎么判断?解析二进制数据时,字节序不一致会有什么影响?
  15. 进程和线程的区别。推理任务放线程里,或者拆成独立进程,各有什么考虑?
  16. cache 有什么作用?为什么访问同一批数据,换一种访问顺序,耗时可能就不一样?
  17. 手撕:二维卷积的朴素版本(先把输入、卷积核、输出的关系写对,别下标越界)。

二面(技术面,约 75 分钟)

  1. INT8 量化具体怎么把浮点值映射成整数?scale 和 zero-point 分别干什么?
  2. 对称量化和非对称量化有什么区别?按张量量化和按通道量化又有什么区别?
  3. 量化后精度掉了,从哪几个方向排查?
  4. 训练后量化和量化感知训练有什么区别,各自有什么代价?
  5. 剪枝是什么?结构化和非结构化剪枝,对实际部署有什么不同?
  6. 知识蒸馏大概怎么做?为什么学生模型可能在体积小一些的情况下保留较好的效果?
  7. 算子融合有什么作用?为什么合并一些操作可能提速?
  8. ONNX 在部署链路里起什么作用?导出了 ONNX,是不是就能直接在任意板子上跑?
  9. 用过哪些推理框架?TensorRT、TFLite 或者芯片厂商的工具,选型要看什么?
  10. NPU、GPU、CPU 各有什么特点?为什么有的模型搬到加速器上,收益没有预想的大?
  11. 从训练好的模型到目标板子上运行,把部署过程完整讲一遍。
  12. 转换后电脑上结果正常,板子上结果不对,你先检查什么?
  13. 一个模型运行很慢,怎么区分瓶颈在计算、内存访问还是数据搬运?
  14. 怎样减少输入输出和中间数据的搬运?
  15. 摄像头到模型输入这一路,零拷贝可以做到什么程度?
  16. 假设推理和实时控制共享计算资源,推理负载突然增大,怎么保证控制任务按时运行?
  17. 多个模型同时跑,怎么安排执行顺序和资源?
  18. 推理结果来晚了,或者某一帧没有结果,上层应该怎么处理?

《参考解析》

INT8 量化的映射与两种分类。量化的本质是找一个仿射变换把浮点区间映射到整数区间:q = round(x / scale) + zero_point,反量化是 x ≈ (q − zero_point) × scale。scale 是步长,决定每个整数格代表多宽的浮点范围;zero_point 是浮点 0 对应的整数值,它的存在是为了让浮点 0 被精确表示(ReLU 后的激活大量为 0,若零不能精确表示,边界会产生系统性误差)。对称量化强制 zero_point = 0,scale 由权重绝对值最大值决定,实现简单、整数乘法更快;非对称量化用最小值与最大值一起定标,更贴合 ReLU 激活那种单边分布,但计算里多一个偏移项。按张量量化(per-tensor)全张量共用一组 scale,最省但误差大;按通道量化(per-channel)每个输出通道一组 scale,能显著降低权重侧的精度损失,代价是反量化要按通道取参数、对硬件算子有要求。

精度掉下来怎么排查。先定位是权重还是激活:只量化权重、激活保持浮点,看损失多少;再单独量化激活,通常激活的动态范围更难估计、影响更大。然后逐层比对量化前后的输出(余弦相似度或相对误差),异常层往往有离群值(outlier),常见处理是把极端离群值单独保留为浮点或用更细的粒度吸收。再确认校准集:PTQ 的 scale 依赖校准数据分布,样本必须能代表真实输入,否则 scale 会偏。最后看算子是否不支持导致回退到 CPU 或被拆成多段。如果 PTQ 怎么调都差,就上量化感知训练:训练时插入伪量化节点让模型适应量化误差,精度通常能拉回来,代价是需要训练资源和迭代周期。整数量化还有个容易忽略的收益:INT8 乘加可用 SIMD 一条指令算更多元素,访存带宽占用也降到四分之一,对嵌入式板子往往比算力更关键。

剪枝、蒸馏与算子融合。剪枝分结构化和非结构化:非结构化把单个权重置零,稀疏度可以做得很高,但通用硬件上稀疏矩阵并不会真的变快,通常只是压缩了存储;结构化剪枝以通道、卷积核、注意力头为单位整体删除,能直接减少计算量、在通用算子上就有加速,代价是要重新微调甚至重训,且必须保证删除后维度仍然对齐。蒸馏是让学生在拟合硬标签的同时拟合教师的软输出(带温度的 logits 分布),软标签里包含类别之间的相对关系这类「暗知识」,因此小模型也能保留较好的泛化。算子融合的收益来自减少中间张量的读写:Conv+BN+ReLU 这种链在推理时可以把 BN 参数折叠进卷积权重、激活就地做,省掉几轮内存往返和 kernel 启动开销,这也是同一套权重在不同框架下速度差很多的原因。

ONNX 与部署链路。ONNX 是中间表示层,解决「训练框架与推理引擎之间的格式统一」。但导出 ONNX 不等于能在任意板子上跑:目标平台的引擎可能只支持算子集的一个子集,也可能需要再转一次(转成芯片厂商的私有格式);opset 版本、动态 shape、控制流算子都是常见卡点。完整链路是:训练产出权重 → 导出 ONNX(确定输入输出名与 shape)→ 用目标工具链转换(算子替换、量化、融合)→ 板上加载并验证 → 性能调优(线程数、绑核、内存复用)。电脑上正常、板子上不对,排查顺序是:固定同一份输入与权重比对中间层输出,确认是「全链路偏差」还是「某一层开始偏」;再查预处理是否一致(RGB/BGR、归一化参数、resize 插值、NCHW/NHWC),这类不一致最常见;然后查数值精度(fp16/INT8 舍入、累加位宽)与后处理阈值。把中间张量 dump 出来逐层比对最有效。

性能瓶颈定位与减少搬运。区分计算密集、访存密集和搬运开销可以这样切:先看算子耗时占比与算力利用率(对标理论 FLOPS,利用率低说明不是纯计算问题);再把数据流画出来看中间张量有几份、每份多大,内存带宽是否被打满;最后看端到端里非模型部分占多少(预处理、后处理、拷贝与同步等待)。减少搬运的手段:算子融合减少中间张量、内存复用(一块 workspace 按生命周期复用而不是每次分配)、把预处理搬到加速器上或用 DMA 直写模型输入缓冲、避免「CPU 解码 → 拷到加速器 → 再拷回 CPU」的往返。零拷贝能做到什么程度取决于硬件通路:摄像头到 ISP、ISP 到内存、内存到 NPU 之间有 DMABUF/ION 这类共享缓冲时可以做到设备间零拷贝,前提是格式与对齐满足要求。实时控制与推理共享资源靠优先级与隔离:控制任务放最高优先级且可抢占(或独占一个核),推理放低优先级队列,用事件驱动而不是轮询等待,并给推理设超时与降级路径。多模型共存按截止期排优先级,能并行的小模型合并成批处理、大模型走独立队列。结果迟到或某帧无结果时,上层用「时间戳 + 有效窗口」判断:超出窗口的结果直接丢弃,并明确上报本帧无结果,由业务决定沿用上一帧(配 aging 上限)还是走安全兜底。

内存与 C 基础。模型的内存归属要能说清:权重由加载器在初始化阶段一次性分配(通常用静态大块或 mmap,不放堆),中间激活与工作区由推理引擎的 allocator 按图规划复用,输入输出缓冲由调用方提供或引擎预分配。嵌入式里 malloc 的风险在于碎片与不确定性:堆碎片会让「总量够但找不到连续块」的分配失败,失败时间点不可预测,长时间运行的小块频繁分配释放会持续恶化;常见做法是启动时一次性静态分配或用固定大小的内存池,并把分配失败当作可处理路径。结构体对齐按「成员对齐到自身大小的整数倍、整体对齐到最大成员的对齐值」计算,调整顺序的原则是大对齐成员放前面、小成员聚在后面。数组名在多数表达式中退化为首元素指针,而作为函数参数传进去后只是一个指针,sizeof 拿不到长度,必须额外传。volatile 只保证每次都从内存读写、不被优化掉,但它不保证原子性、也不提供内存序与互斥。