机器人项目实战(5):从单次抓取,到接收指令连续做事
前面几篇,我分别整理了相机、识别、抓取参考、灵巧手求解和实机动作适配。
最后这一篇,把它们放回完整任务里。
任务目标仍然是橙子、苹果、香蕉、柠檬,以及锤子和螺丝刀。不过,程序除了完成一次抓取,还要处理目标挤在一起、连续搬运,以及小智发来的新指令。
我把这一阶段的流程组织成:接收任务、观察桌面、准备目标、抓取放置,再更新场景。
1. 先让指令变成明确的任务
接入小智后,机器人侧通过串口接收结构化指令。
我把串口解析和机器人任务执行分开。解析层负责从字节流中找出完整指令,检查帧头、帧尾和校验值;任务层再根据功能码和参数,决定调用哪个任务。
当前指令帧长度是 10 字节:
帧头|功能码|6 字节参数|校验值|帧尾
对应到抓取相关任务,主要有:
- 抓取指定类别并放置;
- 按给定类别顺序抓取;
- 按配置顺序循环搬运;
- 将指定物体递到交接位置。
这样,机器人侧拿到的是确定的任务名称和参数。上层输入方式变化时,底层抓取流程仍然可以复用。
2. 抓之前,先看目标周围有没有其他东西
如果目标单独放着,可以继续计算抓取。目标附近还有其他物体时,我在流程中加入了抓前打散。
这里先检测当前配置中的全部类别,再根据水平检测框是否相交进行分组。框的边或角接触,也归入相交关系。
分组还包含间接连接。例如:
markdown
苹果与香蕉的框相交
香蕉与柠檬的框相交
↓
三者归为同一组
判断依据是图像中的检测框关系。程序随后只处理当前目标所在的组,不去打散其他独立区域。
如果目标所在组只有它自己,就直接进入抓取准备。
3. 从哪边拨开?
确定目标所在组后,我先计算一个包住整组物体的水平矩形,并取它的中心 C。
接着,在矩形四个角里选择起点 P。
选择时,我计算每个角到各成员检测框的距离,取其中最小值,再选择这个最小值最大的角。直观上,就是优先从相对空一些的角进入。
拨动方向从 P 指向 C,并继续越过中心,终点设为:
css
终点 = 2C - P
也就是把中心作为中点,沿这条线拨到另一侧。
得到图像中的起点和中心后,我用组内有效深度的中位数,将它们还原到三维空间,再转换到机器人基坐标系,生成水平方向的拨动路径。
这样,拨动距离会随分组范围变化,而不是每组都使用同一个固定长度。
4. 拨完以后,重新拍照
拨动执行分成转手、移动到预位置、下探、水平拨动和抬升几个阶段,随后返回观察位置。
回到观察位之后,我重新拍照、重新识别,再为目标分组。
这一轮使用的是更新后的检测结果。物体已经被拨动,前一张图里的位置也就完成了它的作用。
整个过程形成一个循环:
markdown
观察与分组
↓
目标独立 → 进入抓取
↓ 否
规划并执行拨动
↓
返回观察位,重新识别
当前最多尝试 5 次拨动。达到次数后仍处于相交组,就结束这次准备流程并返回原因。
5. 把一次抓取接成完整动作
目标准备完成后,复用前面几篇的流程:
markdown
生成抓取参考
↓
灵巧手求解与 TCP 换算
↓
准备手形、到达预抓取位置
↓
接近、闭合、等待、抬升
↓
移动到放置位置、松手
↓
返回观察位
在当前搬运任务中,我使用镜像方式生成另一侧的放置位姿,并为放置轨迹增加基坐标系 Z 方向的偏移。
顺序抓取时,每个物体走完抓取和放置,再回到观察位处理下一个。
我没有在开始时一次算完所有物体的位置,而是让每轮抓取使用当时的场景数据。前一个物体被拿走,或者打散动作改变了布局,后面的目标位置就跟着更新。
6. 抓完之后,再看一次来源区域
顺序任务中,我还加入了抓取后的复拍检查。
完成抓取、放置并返回来源侧观察位后,程序重新检测当前类别。如果仍然检测到该类别,就按配置重新执行这轮流程;没有检测到,再推进到下一个类别。
这里采用的是类别级判断,适合当前按类别组织的任务。
目标没有识别到时,我也保留了跳过、重试和中止三种策略。当前默认使用跳过,让任务继续处理后面的类别;重试则有次数上限。
这样,任务列表与实际桌面之间就有了衔接:每一步既知道接下来要找什么,也会根据当前画面决定怎样继续。
7. 连续搬运,需要管理这一轮的目标
循环搬运建立在顺序抓取上,两侧分别配置观察位置。
每个搬运方向开始时,我先拍一张图,确定这一方向中实际出现的目标类别,并保留配置中的顺序。
这张图用于确定本方向的任务名单,后续每次抓取仍会更新目标位置。等这一方向完成后,再根据完成列表组织反向搬运。
我把"这一轮处理哪些类别"和"某个物体当前在哪里"分开管理:
- 类别列表决定任务顺序;
- 每轮识别提供当前的位置和抓取数据。
这样,连续任务既有明确进度,也能跟随桌面变化更新动作。
8. 正在搬运,又收到新指令怎么办?
小智接入以后,串口监听需要在任务运行期间继续接收数据。
因此,我让串口接收和任务执行分开运行。新指令到来时,先保存为待执行任务,不在当前动作中间直接启动另一套动作。
对于循环搬运这类持续任务,新指令会提出切换请求,等当前物体完成放置等阶段后,再交给下一个任务。
待执行位置只保留最新的一条指令。当前任务结束后,有新任务就继续执行;没有待执行任务,则返回初始观察位。
我希望这里的行为尽量清楚:收到指令、正在执行、等待切换和执行结束,是几个独立状态。
写在最后
从最初选相机,到接入深度、完成标定,再到识别目标、求解手形和组织任务,这个项目逐步从单个功能变成了相互配合的流程。
最后这部分,我主要做了三件事:目标挤在一起时先打散,动作改变场景后重新观察,再通过任务层连接连续搬运和外部指令。
回头看,各模块之间的数据衔接和执行顺序,占了很大一部分工作。相机提供当前场景,感知给出目标,求解生成姿态,设备执行动作,任务层决定下一步。
到这里,这个系列从设备选型写到了任务执行。后续继续调整时,我会优先记录那些有具体问题、实际改动和运行结果的部分,再围绕它们补充新的文章。