当您获得本文档时,可至 RVS 开发者社区下载 unstacking_runtime.zip 拆垛教程配套数据。解压后其内容应包括:

image-20240415152204406

  • data 包含了本案例中所使用的机器人模型,如果您需要其他模型请前往 RVS 软件发布模型网站下载。

  • group_xml 中提供了常用的一些 Group 组,包含保存加载和矩阵转换,根据需要加载即可。

  • MaskRCNN适用于AI。

    • train_data :包含了 AI 训练所录制的图像和已经训练好的 pth 和 annotations.json 文件,用户直接加载 AI 所需文件时注意路径。

    • ty_ai_savedata.xml:适用于AI图像录制。

    • ty_ai_train.xml:适用于AI训练。

  • unstacking_data 适用于拆垛程序离线模式数据的加载,使用时请注意路径正确。

  • rgb_tcp.txt 本案例中 Elite 机器人眼在手上手眼标定结果。

  • ty_color/depth_calib.txt 适用于 AI 拆垛程序的 ProjectMask 参数加载,使用不同相机时会更新这个文件内容。

  • unstacking.xml 适用于拆垛程序,可做参考,当加载离线数据时,请先更新 LoadLocalData Group 中离线数据的路径,点击 Restart 后再点击 LocalCapture 迭代离线数据。

拆垛的整个流程将由手眼标定和拆垛主流程两大模块进行。案例所有使用到的文件均保存在 unstacking_runtime 下,请放在 RVS 安装目录下的 projects 内运行。

说明:如不想调整 XML 中的文件路径。可以将 unstacking_runtime 重命名为 runtime 。

注意:本文档所使用 RVS 软件版本为 1.5.174 ,若在使用中出现问题或版本不兼容问题,请及时与我们反馈,发送邮件 rvs-support@percipio.xyz !

拆垛主流程

拆垛流程

自动拆垛的实际目标是:1.找到箱型几何中点 2.根据一定顺序进行抓取。具体拆垛流程可分为以下步骤:

  • 加载本地数据:本案例中使用离线数据完成拆垛项目。

  • 坐标系转换:将相机坐标系下的点云转到机器人坐标系下。

  • AI推理:箱子摆放紧密时使用AI推理。

  • 识别定位:识别到目标点云平面的中心点,并返回给机器人进行移动。

  • 机器人抓取:使用机器人模拟按照顺序进拆垛。

加载本地数据

在此案例中,unstacking_data 文件夹下保存了多组离线拆垛数据,数据内容包含点云、joints、2d图像、tcp。需要加载这些数据并遍历取完成拆垛项目。

说明:若需要根据现场的实际情况进行拆垛流程,可连接相机与相机资源完成在线拆拆垛。

操作流程:

  1. 新建工程项目 Unstacking.xml。

  2. 拖入 Trigger 、TestTCPCommand 算子。用于第一次运行 XML 时,自动触发后续算子。

    • 设置 Trigger 算子参数:

      • 算子名称 → InitTrigger

      • 类型 → InitTrigger

      说明:用于第一次运行 XML 时,自动触发后续算子。

    • 设置 TestTCPCommand 算子参数:服务模式 → Once

      说明:单次与模拟机器人通信,获取机器人当前的 TCP 。

      算子图连接如下图所示。

      Unstacking_group1

  3. 拖入 Trigger 至算子图中。获取模拟机器人 TCP 时也可通过手动触发 Trigger 来获取。

    • 设置 Trigger 算子参数:

      • 算子名称 → LocalCapture

      • Trigger → light 曝光 →与交互面板中的输入工具“按钮”进行绑定。将该按钮重命名 → LocalCapture 。

  4. 当终端按照一定报文发送 echo "test3 " | nc localhost 2013 触发此算子。

  5. 拖入 Or 算子至算子图中,并重命名为 Capture_Or ,使用 TCP 或者本地两种方式触发后续算子。

    算子图连接如下图所示。

    Unstacking_capture

  6. 当终端按照一定报文发送 echo "test3 " | nc localhost 2013 触发此算子。完成整个工程后,输入和结果返回如下:

    Unstacking_terminal1

  7. 在算子组中导入 unstacking_runtime/RVSCommonGroup/LoadLocalData.group.xml (用于加载本地数据),将 Or 算子的 output 端口连接到该算子组的iterate 端口。

    说明:当直接加载 group 后,请注意调整 DirectoryOperation 算子中 parent_directory 属性的文件路径。

  8. 拖入 Trigger 算子,用于重新加载本地数据。

    • 设置 Trigger 算子参数:

      • 算子名称 → Restart

      • Trigger → light 曝光 → 与交互面板中输入工具“按钮”进行绑定,将按钮重命名→ Restart 。

  9. 双击 LoadLocalData 算子组展开该组。找到名为 “LoadImage” 的算子,打开 image → icon_visOn可视化属性。打开 “LoadCloud” 算子的 cloud → icon_visOn可视化属性。可在 3D 视图中看到离线的点云。算子连接如下图所示:

LoadlocalData

坐标系转换

加载本地数据后,需进行坐标系转换,将点云所处的坐标系—-相机 rgb 镜头坐标系转换至机器人坐标系。这一转换涉及相机外参及手眼标定结果。

请参考手眼标定在线文档获得实际标定结果。

离线标定结果数据存在 rgb_tcp.txt 中。

操作流程:

  1. 在算子图中右键选择在此处导入 Group XML ,导入 RVSCommonGroup 中的 HandInEye_Depth2Robot.group.xml 。

    注意:除了该文件之外,还有 HandToEye_Depth2Robot.group.xml,在实际的项目中,请根据您实际的相机安装方式选择对应的算子组。

    HandInEye_Depth2Robot

  2. LoadLocalData 组的 tcp_pose 端口与 HandInEye_depth2Robot 组的 tcp2robot 端口连接。

  3. 拖入 LoadCalibFile 算子,用于加载标定文件,finished 端口连接至 HandInEye_depth2Robot 组的 initial_start 端口。 extrinsic_pose 端口与 rgb2depth 端口连接。start 端口与 InitTrigger 端口 finished 端口连接。具体连接如下:

    LoadCalibfile

  4. 双击 Group,找到 “rgb2tcp” 算子,在属性面板的 pose 属性处,粘贴手眼标定的结果。

    rgb2tcp

    通过前述步骤,已经获取了相机 rgb 镜头转机器人坐标系的矩阵 rgb2robot 和相机深度镜头转机器人坐标系的矩阵 depth2robot,将相机深度镜头坐标系下点云转换至机器人坐标系下。

    说明:HandToEye 模式,请选择 unstacking_runtime/RVSCommonGroup/HandToEye_Depth2Robot.group.xml,将这个 group 展开后,找到 rgb2robot 算子,将标定程序中的交互面板上的标定结果复制粘贴到此处。在标定程序中点击 TyCameraResource ,在属性面板中找到 extrinsic,将相机外参复制填入到 rgb2depth 算子。

  5. 添加 Transform 算子至 group 中。

    • 设置 Transform 算子参数:

      • 算子名称 → Cloud2Robot

      • type → PointCloud

      • cloud → icon_visOn 可视 → icon_color -2

  6. 将 depth2robot 端口连接至该算子的 pose 输入端口,将 LoadLocalData 算子组的 pointcloud 端口连接到本算子的同名输入端口。点击运行后便可在输出端口获取机器人坐标系下的点云。

    Transform

AI推理

实际工作中箱型摆放较为紧密,多个箱子的上表面点云会连在一起变成不易分割的一大块点云。针对这个问题,可训练 AI 模型,在此基础上进行 AI 推理,便可获取箱子的点云列表。由于AI训练需要一定配置(详情参考 RVS 安装教程),如果未能满足配置要求,可以根据 unstacking_runtime 文件夹内提供的 pth 文件直接进行推理。总的流程将分为录制、标注、训练、推理。 采集训练图像、标注训练图像、训练AI模型教程放在 AI训练章节中。

操作流程:

  1. 加载 unstacking_runtime/RVSCommonGroup/GetRobotTCP 组。

  2. 添加 Emit 并设置算子参数。该算子在后续的算子中使用。将该算子中 pose 端口与 GetRobotTCP 组 down_pose 端口连接。

    • 算子名称 → TowardsDownPose

    • type → pose

    • pose_roll → 3.141592654

  3. 将Transform(Cloud2Robot)中 cloud 端口与该组的同名端口连接,LoadLocalDate 组中 image 端口与该组同名端口连接, HandInEye_depth2Robot 组中 rgb2Robot 端口与该组同名端口连接,LoadCalibfile 算子中 calib_info 端口与该组同名端口连接。具体连接如下图所示。

    GetRobotTCP

  4. 双击展开 GetRobotTCP 组,设置 AIDetectGPU 算子的类型→ MaskRCNN ,并修改其余配置文件路径。

    说明:此案例使用的MaskRCNN。由于AI推理算子在正式运行前需要初始化运行一次,所以需要在算子前额外添加一个Trigger( type → InitTrigger )。

  5. 点击运行。

    说明:运行后,AIDetectGPU 算子获得目标在 2D 图像中的位置区域(即掩码图,对应的是 mask_list 端口),ProjectMask 算子根据输入的 2D 图对应的点云、2D 图采图时所用的 rgb 镜头坐标系同点云坐标系的转换矩阵(由于上述操作以及进行坐标系转换,将点云转换到了机器人坐标系,所以此处需要输入 rgb 镜头到机器人坐标系的转换矩阵)、相机 rgb 镜头的内参,将 AI 推理算子获得的 mask_list 转化成所有检测目标的点云列表。

    image-20231101143450760

识别定位

通过 AI 推理获得机器人坐标系下所有检测目标的点云列表后,需获得点云中心坐标。

操作流程:

步骤一:筛选箱子

  1. 双击展开 GetRobotTCP 组中 GetObj 组。

    GetObje

  2. 需要先筛选箱子,并按照箱子列表的 Z 轴坐标值进行筛选,筛选出最上层的箱子,并对上层箱子进行排序。如下图所示。

    sortBoxList

  3. 因此这里使用 FilterBoxList 算子,重命名→ SortBoxList,该算子的属性值调整如下:

    sortBoxListPannel

步骤二:提取上层箱子点云列表中的第一个箱子的点云平面中心点

  1. 提取上层箱子点云列表中的第一个箱子的点云,使用 AtLitst 算子。

    • 设置 AtList 算子参数:

      • 算子名称 → GetFirstInList

      • type → PointCloud

      • index → 0

  2. 获取平面,使用 FindElement,type → Plane,获得点云中适合抓取的平面。调整算子属性 distance_threshol 来调整所选取的平面。打开 cloud 可视化属性来查看选取的平面。

  3. 获取平面中心点,使用 MinimumBoundingBox 算子,重命名 → GetBoxCube,type → ApproxMVBB 获得一个方便机器人抓取的坐标中心点。这里需要给该算子一个 ref_pose,这里连接 Emit(TowardsDownPose),表示绕着 X 轴旋转 180° ,使 Z 轴朝下,便于机器人抓取。打开 MinimumBoundingBox(GetBoxCube)属性面板 box 和 box_pose 可视化属性即可显示计算出的平面中心点。

    MInimumBoundingBox

机器人抓取

在完成上述操作后,我们已经获得了目标点坐标,我们需要通过加载机器人模型,模拟抓取。

  1. 首先我们需要测量机器人的吸盘工具的长度 length ,由于吸盘在安装时同机器人的法兰盘是中心对齐的,则此时我们只需要沿着目标上表面中心姿态的 Z 轴向上调整 length,即可作为最终抓取时的机器人法兰盘位置。这里使用 Emit 算子,type →Pose,属性 z 输入 -0.192 。之后将调整好的姿态发送给机器人模型进行模拟移动,使用 Transform 算子,type → Pose ,算子重命名 → RobotToolTCP。算子连接如下:

    RobotToolTCP

  2. 移动机器人。在 Resource 算子组中拖入 SimulatedRobotResource ,设置属性 robot_filename 加载您的机器人文件,如 data 下的 EC66/EC66.rob 。设置属性 tool_filename 加载机器人的吸盘工具,如 data 下的 Tool/EliteRobotSucker1.tool.xml。

  3. 在算子图中拖入 RobotMovement ,type → MoveJoint ,robot_resource_name → SimulateRobotResource。设置velocity、acceleration来调整机器人的移动速度、加速度。左侧 Joint 端口与 LoadLocalData 组中的右侧 Jointarray 端口进行连接。start 端口与其 finished 端口连接。

  4. 拖入 WaitAllTrigger 算子,将 GetRobotTCP 组的 finished 端口连接至其 input_1 端口。RobotMovement算子的finished端口连接至其input_2端口。

  5. 在算子图中拖入RobotMovement,type → MoveTCP ,重命名 →MoveToGrasp ,robot_resource_name → SimulateRobotResource。设置velocity、acceleration 来调整机器人的移动速度、加速度。start 端口与 WaitAllTrigger 算子的 finished 端口进行连接。goal_pose 与 GetRobotTCP 组的 robot_tcp 端口连接,ref_pose 端口与 RobotMovement 的 joint 端口连接。

  6. 最后,将 GetRobotTCP 的 finished 和 robot_tcp 端口连接到最外层算子组的 MirrorOutput 端口,并连接回 TestTCPCommand。至此,项目文件的编辑已经完成。

    unstacking

完整的xml已提供在unstacking_runtime/unstacking.xml路径下。

AI训练

本章节主要目标为讲解采集训练图像、标注训练图像、训练AI模型。总的流程将分为采集、标注、训练。

说明:AI训练需要一定配置,详情参考下载与安装RVS

采集训练图像

打开 unstacking_runtime/MaskRCNN/ty_ai_savedata.xml,内容基本与录制 RGB 图像一致,在这里我们只需要调整 Emit 中的 string 参数,设置为我们想要的路径即可。点击 Capture 按钮录制图像。录制图像数量保证在 10 张以上,越多越好。

ai

在图像采集时,我们应注意以下几点:

  1. 背景光照

    • 单一稳定光源,亮暗适宜且没有过多反光。

    • 户外光照情况变化过大,不建议。

  2. 复杂工况考虑:尽量使得采样样本对实际运行的全局样本有足够的代表性而不仅仅是全局样本的一种特例

    • 目标物件多样化,尽量不选用固定单一目标物体。如果当前可供使用的样本数目比较少,可以考虑将样本正反两个面(两个面一样)都使用来倍增样本。

    • 工况多样化,对于目标物体,尽可能多的考虑其在工况中出现的情形,多情况拍摄:

      • 如果项目中垛车大小、垛车姿态、目标在垛车中的姿态、目标外形外观等有多种情形,则我们采集的训练数据应该把这些多样化的情形都囊括到。

      • 要充分考虑训练样本同实际运行样本的偏差余量。比如垛车只有水平摆放一种姿态,我们在训练数据的时候也要考虑到给垛车增加小角度的倾斜,因为实际中不可能完美水平。再比如实际运行的都是机器摆放,目标两两比较贴近,在我们在录制训练集数据的时候,如果用的是人为摆放,则一定要保证目标两两比较贴近,而不是随意摆放导致目标两两之间间隙松散。

      • 对于少部分的可能出现的目标破损褶皱等情况,虽然很少出现,但是如果项目实际有识别的需要,则要专门寻找较多的破损目标进行数据采集,虽然最后在训练样本中破损目标占的比例大于破损目标在实际样本中占有的比例,但是这也很有意义。

  3. 图像质量

    • 人眼需清晰可见目标边缘,尤其是距离相机最远的那层目标,否则考虑更换相机具体图像录制可以参考 unstacking_runtime/MaskRCNN/train_data 中的rgb 图像

为训练图像进行标注

使用 labelme 这款软件为已录制好的 RGB 标注,本文档提供一种 labelme 的安装方法。

1.5.639 版本及以上已集成标注软件,无需安装 labelme。点击菜单栏image-20231020182747977图像标注工具即可打开。

步骤1:安装 labelme

  1. 安装pip

    sudo apt install python3-pip
    
  2. 安装pyqt5

    sudo apt install python3-pyqt5
    
  3. 在labelme-4.5.7.tar.gz路径下执行下述命令(网络自行查询)

    python3 -m pip install labelme -i https://pypi.tuna.tsinghua.edu.cn/simple
    
  4. 安装完成,运行

    python3 -m labelme
    

步骤2:标注前准备

  1. 首先确定任务目标,明确在检测过程中什么物体需要被检测,什么物体不需要被检测,从而有针对性的进行标注。

  2. 给定的标注条件无需过分苛刻,不要按照人的思维去考虑,而是按照自己主观设定的标注思路是否便于落实代码。

步骤3:标注图像

  1. 打开labelme软件,点击 OpenDir按钮,选择我们标注的路径。

    labme

  2. 点击 Create Polygons按钮,为箱子绘制红色的边框,绘制完成后如下图所示。

    说明:支持 Create Polygons 、Create Circle、Create Rectangle 三种标注方式。

    openlabelme

  3. 完成后会弹出命名框,第一次命名为:box,后续同类直接选择即可。

    box

  4. 当图像内所有箱子标注完成后,点击 Save 进行保存,默认当前文件夹,默认名称即可,随后选择 Next Image 切换到下一个图像。

    next_image

在标注过程中我们应当注意以下几点:

  1. 标记物体可见区域的边界框(不是估计的物体总范围)。

  2. 标注画框结束,注意做边界检查,比如确保框坐标不在图像边界上。

  3. 质量较差的图像(例如过度运动模糊),部分遮挡的目标,结合项目目标需求选择标注或舍弃。

我们应尽量避免以下问题:

  • 边界标注不准

    label1

  • 遗漏

label2

  • 被遮挡部分不应该标注

label3

训练AI模型

打开 unstacking_runtime/MaskRCNN/ty_ai_train.xml ,这里我们只需要调整数据目录类名文件路径路径。触发 generate_jsonfile 开始训练。 最终会生成一个 train_output 文件夹,在这个文件夹中有命名为 model_final.pth(默认可更改),是我们所需要的权重文件。

注意:AITrain 算子数据目录和类名文件路径中不支持中文与空格。

aiTrain