
直接开干把LeRobot和ACT策略搬上SO-ARM101的真实过程这两年物理AI这个词被炒得火热但大多数教程停在了仿真环境里跑个demo真正把模型部署到真实机械臂上做任务的实操内容少得可怜。我从LeRobot入手用SO-ARM101机械臂配合ACT策略做了一套物体自动分拣的完整流程从数据采集、模型训练到真实部署全部跑通。这篇文章把整个过程拆开来讲包括硬件准备、环境搭建、示教采集、训练评估、部署推理以及我踩过的各种坑。无论你是想入门机器人模仿学习还是已经在跑LeRobot但卡在真实部署环节这篇都值得花时间看完。1. 为什么是LeRobot SO-ARM101 ACT这套组合的真实定位1.1 物理AI项目的第一课先分清玩具和工具物理AI这个概念听起来很玄乎拆开来看就是让AI模型输出能够驱动真实物理设备动作的指令。2018年前后我玩过一阵子机械臂那时候想实现一个分拣任务得自己写运动规划、逆解算、抓取姿态估计一套代码下来好几千行而且换个物体就要重新调参数。2023年开始模仿学习路子起来了尤其是斯坦福的ALOHA和Mobile ALOHA开源之后大家发现原来可以让机械臂看人怎么做然后自己学会做这个思路的门槛比传统方案低了一个数量级。但仿真里的模仿学习和真实机械臂上的模仿学习是两回事。仿真里你不需要考虑相机畸变、关节回差、串口通信延迟、电机过热保护这些真实世界才有的问题。我见过不少人在MuJoCo里跑ACT跑得很漂亮一到真实机械臂上就彻底翻车。这次项目选型的核心诉求是在一个真实可用的机械臂上走通示教采集—训练—部署的完整闭环同时成本控制在入门级让个人开发者和实验室都能承受。1.2 LeRobot的价值把训练管线变成标准流程LeRobot是Hugging Face开源的机器人学习框架它的定位是降低机器人学习的技术门槛。它内置了数据采集、数据集管理、模型训练和评估的完整管线支持ACT、Diffusion Policy等主流模仿学习算法。这套框架最大的价值不是某个算法有多新而是把整个流程标准化了。你在命令行里敲一条指令就能启动数据采集再敲一条指令就能开始训练训练完还能自动做模型评估。对于刚接触这个领域的人来说这意味着不用从零开始写数据加载器、不用自己处理HDF5格式、不用手写训练循环把精力集中在真正重要的任务设计和数据质量上。LeRobot还支持多种真实机器人的接口包括ALOHA、SO-ARM系列、UR系列等。我选择SO-ARM101而不是UR5e这类工业臂主要是成本原因。SO-ARM101的价格在入门级机械臂里算合理而且官方提供了LeRobot的适配接口这就省掉了很多自己写底层通信的麻烦。1.3 选SO-ARM101与ACT的现实理由SO-ARM101是SO-ARM系列里偏入门的一款六轴机械臂末端支持电动夹爪最大负载在1.5公斤左右重复定位精度宣称能达到±0.05毫米。这个参数在工业级里谈不上惊艳但对于分拣小型物体比如螺丝、螺母、小积木块完全够用。更重要的是它的通信协议对开发者友好提供了Python SDK能直接读取关节状态、控制关节运动这在做模仿学习时非常关键因为你需要采集关节角度作为动作标签。ACT策略Action Chunking with Transformers是ALOHA团队提出的一种模仿学习方法。它的核心思路是让模型一次性预测未来一段时间的动作序列而不是像传统方法那样逐帧预测。这就解决了模仿学习中常见的复合误差问题——每个时刻的小误差不会无限累积因为模型是直接规划一段轨迹而不是一步步叠加。ACT在精细操作任务上表现非常稳定这也是我选它做分拣任务的原因。2. 从拆箱到机械臂能听话硬件接线与控制验证2.1 接线和通信先把手脚打通SO-ARM101的控制箱拆开之后主要接口有一个电源接口、一个串行通信接口RS485和一个末端IO口。供电是24V DC需要用原厂适配的电源适配器别随便拿别的电源代替电流不够会导致机械臂在运动过程中掉压重启。通信方式我推荐直接用串口。SO-ARM101的Python SDK底层通过pyserial走RS485协议默认波特率是115200数据位8、停止位1、无校验。接线很简单USB转RS485的调试线一端插控制箱的RS485口一端插电脑的USB口。这里有个容易踩坑的地方部分USB转RS485模块需要驱动建议先装好驱动然后在设备管理器里确认串口号比如Linux下一般是/dev/ttyUSB0。# 验证机械臂串口连接是否正常 python -c from soarm.server import RobotController robot RobotController(port/dev/ttyUSB0, baudrate115200) robot.enable() robot.move_to_home() print(机械臂连接成功当前关节角度, robot.get_joint_angles()) 如果这一步能顺畅执行说明机械臂基本控制链路没问题。如果你看到超时或者连接失败先检查串口权限linux下需要sudo chmod 666 /dev/ttyUSB0或者把用户加进dialout组再看是不是有别的进程占用了串口。2.2 先跑通官方Demo再谈训练很多人的错误是跳过这一步直接开始采集数据。我建议把机械臂接好之后先用SDK把官方Demo完整跑一遍。SO-ARM官方提供了几个基础demo包括点到点运动、轨迹插补运动、夹爪开合控制。这些demo的意义不只是验证硬件而是让你理解这套SDK的坐标系约定、正逆解接口的调用方式以及最关键的——机械臂的运动平稳性。我测试中发现SO-ARM101在低速运动时关节会有轻微抖动尤其在关节J1和J2接近极限位置的时候。这个抖动对传统运动规划影响不大但对模仿学习的数据采集来说是个隐患因为抖动会污染关节角度数据。解决办法是在SDK里把轨迹插补的平滑参数调高一点或者采集数据时尽量让机械臂在非极限位置工作。2.3 相机选位与标定分拣任务的第一道分水岭分拣任务需要机械臂看到物体。我在这个项目里用了两个摄像头一个固定在机械臂上方做全局视角high view一个装在末端执行器旁边做腕部视角wrist view。全局视角负责定位物体位置腕部视角负责精细抓取阶段的姿态微调。相机选型上我用的是一块普通的USB彩色摄像头分辨率1280x72030帧。对于分拣这种不需要超精细纹理识别的任务普通USB摄像头足够。关键是固定方式要稳固我用3D打印的支架把全局摄像头固定在一个距工作台大约60厘米高的位置确保视野覆盖整个分拣区域。标定这里我多说两句。LeRobot虽然不强制要求你做手眼标定但它依赖相机标定参数来对齐相机坐标系和机械臂基座坐标系。如果跳过标定模型训练时虽然能跑通但部署到真实机械臂上时抓取位置会有几厘米的偏差这在分拣任务里基本等于抓不到东西。我用的是手眼标定里的眼在手外eye-to-hand方式用棋盘格标定板转了几十个位姿然后用OpenCV的calibrateCamera算出了相机的内参和外参。具体过程我后面在踩坑部分会再提这里先记住一个原则标定结果不好后面全白干。3. 环境搭建Ubuntu、Python与LeRobot的安装细节3.1 系统与Python版本的取舍LeRobot对操作系统的要求在官方文档里写的是Ubuntu 22.04以上Python版本最好是3.10以上。如果你还在用Python 3.8建议先升上去因为LeRobot依赖的很多库比如PyTorch、huggingface_hub的新版本已经放弃对老版本Python的支持了。我自己的环境是这样的组件版本操作系统Ubuntu 22.04.3 LTSPython版本3.10.12conda环境PyTorch2.1.2CUDA 12.1LeRobotlerobot1.0.0CUDA12.1如果你有NVIDIA显卡建议直接用CUDA版的PyTorch训练速度快很多。没有显卡也不是不能跑但训练一个ACT策略可能要多花好几倍时间建议至少准备一张显存8GB以上的卡。3.2 安装LeRobot:分步走LeRobot的安装流程我分了三步走每一步都有验证手段方便排查问题。第一步是创建conda环境并激活conda create -n lerobot python3.10 conda activate lerobotPython版本一定要写清楚我之前试过直接用默认的Python版本结果装完依赖后版本冲突浪费了半天时间。第二步是安装PyTorch。根据你的CUDA版本在PyTorch官网选对应的安装指令我的是pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121第三步是安装LeRobot本体git clone https://github.com/huggingface/lerobot.git cd lerobot pip install -e .注意我用了pip install -e .而不是pip install lerobot。前者是开发者模式安装这样如果你后续想修改LeRobot源码比如自定义奖励函数或者数据处理逻辑改动会实时生效不用反复重装。安装完成后运行一下验证脚本python -c import lerobot; print(lerobot.__version__)如果没有任何报错说明环境基本就绪了。如果报缺库看缺的是哪个再单独装不建议一口气把报错全复制到百度搜索一条条解决更快。3.3 LeRobot的配置文件逻辑别被YAML绕晕LeRobot用YAML配置文件来管理机器人类型、算法参数、数据集路径等信息。这个设计本身很好但新手容易在配置上翻车。最核心的一个配置是robot.type如果你的机器人在LeRobot官方支持的列表里直接指定类型就行。SO-ARM101在较新的LeRobot版本里已经有适配了配置时填so101即可。还有一个重要的配置是数据集的存储路径。LeRobot默认使用Hugging Face Hub的缓存目录来存数据集但国内访问Hugging Face不太稳定。解决办法是在环境变量里指定本地缓存路径export HF_HOME/path/to/your/cache export HF_HUB_OFFLINE1 # 纯离线模式训练时不需要联网下载模型权重如果你用的是离线模式需要提前把预训练的基础模型权重下载好放到缓存目录里。ACT策略其实是需要预训练的backbone的LeRobot会从Hugging Face下载ResNet等预训练权重第一次训练时如果网络不行就会卡住。3.4 连接SO-ARM101到LeRobot硬件和软件环境都准备好之后最后一步是把SO-ARM101接入LeRobot的抽象接口。LeRobot支持通过Robot类来操作真实机器人你需要写一个简单的适配器脚本把SO-ARM101的SDK调用封装成LeRobot的接口格式。核心接口就几个connect()、disconnect()、get_observation()、send_action()。这个封装逻辑不算复杂但它决定了你的数据采集能否跑通。from lerobot.robot import Robot class SOARM101Robot(Robot): def __init__(self, port/dev/ttyUSB0, baudrate115200): from soarm.server import RobotController self.controller RobotController(portport, baudratebaudrate) self.controller.enable() self.controller.move_to_home() def get_observation(self): joints self.controller.get_joint_angles() # 返回末端位姿和关节角LeRobot里面需要统一成dict格式 return {joint_angles: joints} def send_action(self, action): # action是desired joint angles或者delta按你采集时的格式来 self.controller.move_joints(action.tolist())写完这个封装之后建议先用脚本测试一下python -c from soarm_lerobot import SOARM101Robot robot SOARM101Robot() obs robot.get_observation() print(观测值, obs) robot.disconnect() 如果观测值能正常打印出来说明SO-ARM101已经和LeRobot成功对接了可以开始数据采集。4. 示教数据采集模仿学习的质量上限由这一步决定4.1 采集流程设计需要几张示范课模仿学习的逻辑是你给什么样的示范模型就学到什么样的行为。所以采集数据是整个项目里最耗时也最影响最终效果的环节。我在这个分拣项目里设计了4种物料的分类分拣流程不锈钢螺母、紫铜垫片、黑色塑料螺丝、黄色小积木块。每种物料人工示范20次一共采集了80条轨迹数据。每条轨迹时间在10到15秒之间包含从初始位置移动到目标物体上方、夹爪闭合抓取、平移到对应分类槽、夹爪释放、回到初始位置这个完整循环。采集频率我用的是20Hz也就是每秒采20帧。每帧数据包含两个摄像头的图像1280x720的RGB图、6个关节的角度值、夹爪的开合状态。这个配置下一条15秒的轨迹大约是300帧80条轨迹一共2万多帧数据存储空间大概10GB左右完全在可控范围内。4.2 用遥控器还是手动拖拽采集方式的取舍SO-ARM101是六轴协作臂支持拖拽示教。你直接用手抓住末端把机械臂拖到目标位置机械臂会自动记录关节角度变化。这对新手来说最直观但我实际用下来发现一个问题拖拽示教时机械臂的重力补偿做得一般拖动手感比较沉做大幅度动作时容易有顿挫感导致采出来的数据有抖动。我后来换成了遥控器示教。LeRobot支持PS4手柄遥控你把PS4手柄通过蓝牙连接电脑然后写一个遥控脚本把左摇杆映射为机械臂末端在X-Y平面的运动右摇杆映射为Z轴运动和夹爪开合。这个方法虽然上手曲线比拖拽示教陡一点但采出来的数据质量高得多——运动平滑轨迹可控性强同样的任务能采出一致的示范。4.3 数据质量自检别等训练完才发现数据不行采集完成后LeRobot会生成一个HDF5格式的数据文件里面按时间序列存储了每一帧的观测和动作。但文件能生成不代表数据质量合格。我每次采完一批数据都会跑一段可视化脚本把采样的轨迹在3D坐标上画出来看看有没有明显的跳变点或者重复路径。还有一步很重要检查图像帧是否清晰、有没有遮挡。我踩过一个坑腕部摄像头在机械臂运动过程中会被夹爪挡住视野导致一部分帧的有效信息严重损失。如果你发现某个时间段的图像一直是黑的或者花屏大概率是摄像头排线接触不良或者镜头被机械臂本体遮住。这种脏数据混进训练集里会让模型的抓取精度大打折扣。提示数据采集宁缺毋滥。一次示范里如果出现夹爪提前闭合、错抓物体、轨迹严重抖动这些情况直接删掉这条数据重新采不要抱有后面训练时模型自己会修正的幻想。5. ACT策略为什么动作分块适合精细分拣5.1 从逐帧预测到一次性规划传统的行为克隆方法输入的是一帧观测输出的是一帧动作。这样做的弊端很明显机器人是逐帧决策的上一帧预测有一点误差下一帧就会在这个误差基础上继续预测误差一步步累积最后动作越来越偏这在模仿学习里叫复合误差。ACT把这个问题绕过去了。它要求模型一次性输出未来一段时间的整段动作序列这个时间段长度称为一个动作块action chunk。我在这个项目里设置动作块大小为50也就是模型每次预测未来2.5秒按照20Hz控制频率算的动作轨迹。你把这段预测当作一个整体去执行而不是每帧重新预测。这样可以显著减少累积误差同时让动作更平滑。5.2 ACT里的CVAE给分拣任务加上多解能力ACT的另一个核心设计是使用了条件变分自编码器CVAE来处理多模态行为。什么叫多模态就是同样一个任务可以有不同做法。在分拣任务里同一个螺母从左边拿和从右边拿都行只要能放到分拣槽里就算成功。如果用确定性模型去拟合这种多模态数据它会学到一个平均方案结果可能是一个既不左也不右的奇怪轨迹。CVAE通过引入隐变量来捕获这种多样性模型在推理时从隐空间采样可以生成多种不同的有效轨迹而不是简单取平均。ACT的模型结构大致分三条线一是视觉编码器用ResNet提取图像特征二是CVAE的编码器和解码器用来生成动作块三是Transformer主干网络用来建模视觉特征到动作块的映射。LeRobot已经把这些组件封装好了你不需要自己搭模型但理解这个结构有助于你调参。5.3 训练关键参数从batch size到学习率在LeRobot里训练ACT最关键的几个超参数是参数我的配置说明policy.typeact选择ACT策略policy.optimizer.lr1e-4学习率太大容易振荡太小收敛慢policy.chunk_size50动作块大小需要和你的控制频率匹配policy.dim_model512Transformer的隐藏维度policy.n_heads8Transformer注意力头数training.batch_size32批次大小取决于显存大小training.num_epochs200训练轮数配合作早停机制chunk_size是我觉得最需要解释的一个参数。它直接决定模型规划轨迹的时间跨度。如果设太小比如只有10那模型实际上退化为逐帧预测ACT的优势就没了如果设太大比如200模型预测一条10秒的轨迹这期间如果出现意外比如物体被碰歪模型无法快速调整也会导致抓取失败。50是一个比较折中的值2500ms的控制跨度给了模型足够的反应时间又不会太长导致灵活性丧失。6. 训练与评估Loss下降不等于能分拣6.1 启动训练一条命令背后的数据流训练命令在LeRobot里很简洁python lerobot/scripts/train.py \ --policy.typeact \ --dataset.repo_idyourname/so101_sorting \ --output_diroutputs/train/act_so101 \ --robot.typeso101 \ --training.num_epochs200dataset.repo_id指向你存放数据集的目录output_dir指定训练输出路径。训练时会自动加载数据集里的所有轨迹按8:1:1划分训练集、验证集和测试集。我在3090显卡上训练200个epoch大概花了7个小时每个epoch大约2分钟。训练过程中终端会打印loss的下降曲线LeRobot也支持用wandb做训练可视化方便远程监控。6.2 早停与模型选择别死磕最后一个checkpoint训练完成后LeRobot会保存多个checkpoint包括每个epoch的中间模型和最终模型。我强烈建议不要直接用最后一个checkpoint而是根据验证集上的loss来选模型。我训练时发现loss在第100个epoch左右就已经趋于收敛后面100个epoch验证集loss有小幅波动说明模型可能开始过拟合了。所以我把第95到第105个epoch之间的checkpoint都拿出来做评估选效果最好的一个用于后续部署。LeRobot训练脚本里默认保存的是loss最低的那个checkpoint但低训练loss不一定意味着高成功率所以我会额外跑评估程序验证。6.3 评估模式自动跑100次分拣测试LeRobot提供了评估接口可以把训练好的模型部署到真实机械臂上自动执行分拣任务并统计成功率python lerobot/scripts/eval.py \ --policy.pathoutputs/train/act_so101/checkpoints/epoch_100 \ --robot.typeso101 \ --eval.num_repetitions100我设置的评估方式是每一种物料随机摆放在分拣区域内机械臂自动执行分拣流程判断标准是物料最终是否被正确放入对应的分拣槽整个过程不需要人工干预。100次测试的成功率在85%左右这个数据看着还行但距离好用还有差距。我仔细分析了失败案例发现大多数失败集中在紫铜垫片这类薄片状物体上——垫片太薄太光滑夹爪闭合后容易滑脱。这个问题的根源不完全在算法跟夹爪的摩擦力和抓取姿态也有关系。6.4 失败案例分析定位不到物体 vs 抓不稳失败情况可以分两类。第一类是定位失败模型输出轨迹的目标点偏离物体位置导致夹爪在空中闭合。这类问题通常出在图像特征上可能是Camera视角里物体的纹理和背景太接近比如银色垫片放在黑色工作台上其实还好但要是放在灰色背景上就容易被忽略。第二类是抓持失败模型准确定位到了物体但夹爪合上后物体滑落。这类问题要么是夹爪接触力不够要么是动作块里夹爪闭合的时序不对——闭合太晚物体已经被夹爪触碰移动位置闭合太早可能还没碰到物体就合上了。理解这些失败类型对后续优化很有帮助。如果是定位失败优先考虑增加训练数据多样性、调整相机角度如果是抓持失败优先看看夹爪的行程和力度设置而不是马上重训模型。7. 部署推理与实测把模型跑在真实物理世界的最后一步7.1 部署模式从训练机到分拣现场训练完的模型不能直接把训练机搬到分拣现场就跑。我在部署环境上踩了不小的坑这里总结一下经验。我的训练机是带3090显卡的台式机但在真实的部署场景里我不可能把这么大一台机器一直放在机械臂旁边。所以我用了两种部署方式做对比。第一种直接把含训练环境的机器搬到机械臂旁边用训练时的conda环境跑推理脚本。优点是环境完全一致不容易出兼容性问题缺点是机器占用空间大而且显卡风扇噪音不小。第二种把模型导出为TorchScript格式然后部署到一台不带独立显卡的迷你主机上用CPU推理。ACT模型的inference计算量其实不大CPU也能跑到每秒20次推理以上完全满足分拣任务20Hz的控制频率。这个方法省空间、省能耗是真正工程化的部署方案。导出TorchScript的脚本很简单import torch from lerobot.policies.act import ACTPolicy policy ACTPolicy.from_pretrained(outputs/train/act_so101/checkpoints/epoch_100) policy.eval() traced_policy torch.jit.script(policy) traced_policy.save(deploy/act_so101_scripted.pt)然后用TorchScript加载推理。注意部署时至少需要一个Reader线程来接收摄像头的图像一个控制线程来发送动作指令不然实时性会跟不上。7.2 摄像头实时输入与推理循环在真实部署时摄像头的数据输入和模型推理必须同步好。我在LeRobot的数据采集里用的是一帧一帧的图像和关节角度到了部署阶段也是一样——每一轮控制循环里先读取当前两个摄像头的图像加上当前关节角度组成观测向量输入模型得到未来2.5秒的动作序列然后步进执行。这里要特别小心观测和动作的频率匹配。训练时数据是20Hz采样的推理时如果摄像头帧率掉到10Hz模型的输入模式和训练时就不一致了效果会下降。所以我在部署时强制把摄像头帧率锁到20Hz如果某一帧读取超时就复用上一帧图像保证控制频率稳定。7.3 真实分拣实测与训练环境差异带来的问题在训练环境里我在固定摄像头位置、固定物料摆放的区域采了80条示范。等到部署到另一个桌面环境时我发现机械臂的分拣成功率明显下降。一开始我以为是模型过拟合了后来排查原因发现是光照条件变了。训练环境用的是日光灯部署环境有一侧窗户的自然光进来导致摄像头图像的整体亮度和对比度都不同。这个问题的解决办法有几个方向。一是做数据增强在训练时对图像随机调整亮度、对比度、饱和度增强模型的泛化能力二是控制部署环境的光照条件用遮光帘挡住自然光让工作台的光照稳定。两个方向我都试了最有效的还是第一个——在LeRobot的数据加载部分加一个轻微的随机亮度扰动模型的成功率从85%提到92%左右对光照变化的鲁棒性好了很多。7.4 成功率统计与误差圈分析测评成功率只是一个数字要真正定位问题还是得看误差分布。我在分拣区域的每个物料位都画了一个20毫米直径的圆机械臂每次抓取结束后我用上位机记录实际抓取点和目标点之间的偏移量最后画成散点图。分析发现规律性的偏移主要来自相机标定的外参误差方向固定且量级在5-8毫米左右跟标定板摆放位置有关。随机性偏移的幅度就比较离散往往和物体姿态的随机性相关。如果预料到有偏可以在模型推理出的目标位置上加一个固定方向的补偿量这个补偿量就是用上面的散点图算出来的平均值。我加了2毫米的补偿后抓取命中率又提升了几个点。8. 踩坑记录那些文档里不会写的细节8.1 采集与推理速度不匹配画面卡顿让数据直接作废采集数据时我一开始用LeRobot默认配置摄像头把每一帧图像都存到内存里然后用HDF5格式落盘。但SO-ARM101的SDK在采集过程中偶尔会阻塞导致图像采集线程和控制线程时间不同步录出来的数据里会出现图像重复或跳帧。后来我用了一个独立线程专门负责摄像头读取并用队列做缓冲区才保证采集帧率稳定在20Hz。8.2 夹爪力控设置过紧过松都有问题SO-ARM101的电动夹爪支持设置夹持力矩。一开始我按机械爪默认的最大力矩来抓取结果垫片和积木块被夹太紧直接弹飞。后来我把力矩系数调到0.6左右根据物料不同稍微调整问题就解决了。这个细节很容易被忽略但直接影响抓取成功率。8.3 训练时偶尔爆显存想办法减batch size我一开始训练batch size设64结果在3090上跑了一百多轮显存溢出爆掉了。后来降到32就稳定了。显存不足时也可以缩短图像分块、调低图像分辨率来解决。如果你用的显卡是8GB显存的建议从batch size8开始试。8.4 LeRobot版本更新带来的坑LeRobot迭代速度很快我前几个月写的数据采集接口在版本更新后API变了导致之前封装的SOARM101Robot类不兼容。建议你在项目里记录当前使用的LeRobot版本号如果长时间不更新可以锁定版本依赖别追新版本。如果有升级需求升级后先跑一遍基本的采集和训练流程再继续干。9. 基于我个人实操经验的一些重要体会这个项目从零到跑通前后花了大概三周时间。中间大部分时间不是花在训练上而是花在数据采集、清洗和调试各种接口问题上。这可能是模仿学习路线最容易被低估的部分——算法本身已经有成熟的开源实现但数据质量和工程细节才是成败关键。如果你准备复现这个项目我的建议排序是先花时间把摄像头标定做好标定结果直接决定后续所有环节的地基然后多花时间采高质量数据每条示范都像对待考试答题一样认真如果再有余力再考虑调参和优化模型结构。这个顺序能帮你少走很多弯路。最后一个小技巧训练的时候多存几个checkpoint不要偷懒只存最后一个。我在实际评估中发现epoch 100的模型在实际分拣上比epoch 200的最终模型表现更好。模型的训练loss不是越低越好尤其是对真实物理环境的适应性来说中间状态有时候反而更稳。