来源:https://zhuanlan.zhihu.com/p/1897274764768285841
2、机械臂建模
在我们进入运动规划的核心组成部分之前,我们应该先介绍一些基础知识。这包括如何用数学方法表示机器人操纵器以促进运动规划,以及一些关键概念和软件表示,这将帮助你更好地理解后面的部分。
2.1、刚体机构
在处理操纵器时,我们通常从一个大规模的简化假设开始,即它们是由刚体组成的。这在大多数情况下都是有效的,但如果我不提到整个软机器人领域,我(毫无疑问还有许多其他人)会不高兴的。虽然这篇文章中的许多技术都可以应用于兼容系统,但你确实需要更复杂的模型和假设来实现这一点。顺便说一句,即使是“刚性”金属机器人,你也可能需要考虑在特别艰苦或高精度的条件下的振动和/或弯曲。所以,是的,所有的模型都是错误的,但有些是有用的。
根据刚体假设,我们可以说我们的机器人是由刚性连杆和关节组成的,这些连杆和关节定义了它们之间的运动。关节可以是旋转(旋转运动)或棱柱形(平移运动)。尽管软件工具可以具有其他类型的关节表示,无论是为了方便还是处理数值问题,但所有关节都是从这两种基本关节的组合中导出的。
有了这些信息,我们现在可以执行正向运动学(FK)。这使用链接之间的(静态)变换和关节位置(通常称为配置)来计算移动坐标系之间的(动态)变换。到目前为止,FK在机械手中最常见的应用是在给定一组关节位置的情况下,找到末端执行器(夹具、手等)相对于机器人底座或世界的姿态。

2.2、自由度、驱动和冗余度
粗略地说,机器人可以移动的独立维度的数量定义了它的自由度(DOF)。例如,具有3个关节的操纵器具有3个自由度。可能还有其他限制因素会减少这一数字;例如,由于机械耦合,四杆连杆机构或曲柄滑块机构有效地将四个关节变成一个自由度。要了解更多信息,请查看凯文·林奇和弗兰克·帕克的现代机器人教科书中的这段视频,该视频将在这里的许多其他地方引用。同样重要的是要注意,机器人不一定能直接控制其所有自由度。因此,根据执行器与自由度的比率,机器人分为3类。
- 完全驱动:每个自由度只有一个执行器。在实践中,大多数机器人操纵器都是完全启动的,因为它们需要保持直立而不移动,但也需要在每个维度上进行精确控制来操纵物体。
- 欠驱动:执行器的数量少于自由度。这些设计经常出现在腿式机器人等动态系统中;例如,被动脚踝有弹簧(有时是实际的弹簧,有时是柔性表面)而不是致动器。
- 冗余驱动:执行器数量超过自由度。一个例子是具有6个以上发动机和螺旋桨的多旋翼飞机。额外的致动器对于6-DOF控制不是必需的,但在一个或多个螺旋桨断裂的情况下可能很有用,或者可能是因为需要额外的推力来保持车辆质量在选定的致动器尺寸下保持在空中。在机械手或腿式机器人等铰接系统中,你也可以在单个关节上放置多个执行器,但这需要非常好的控制,以免执行器最终相互“打架”……所以这并不常见。
操纵者还有一个考虑因素。端部执行器可以定位在任何有效的可达姿势中,至少有6个精心设计的*自由度的唯一方法;3次平移和3次旋转。如果操纵器的自由度超过6,则称为冗余操纵器。稍后我们将回到这个问题,但当规划需要考虑碰撞避免、关节限制甚至用户对运动应该是什么样子的偏好等约束时,冗余可能非常有用。如果你想更深入地探讨这个话题,可以阅读文章“我的机器人需要多少个轴“。

2.3、雅可比矩阵
这是一个思维实验。假设你想知道移动关节对机器人上特定帧(如末端执行器)位置的影响。就像正向运动学一样,这种计算取决于机器人的当前关节配置。请看下面的图片。
- 在左侧,你可以看到以正速度旋转第三个关节将导致手部沿着一条弧线移动,该弧线在特定位置的切线恰好沿着基架中的-X方向(或手部框架中的+Y方向)。
- 在右侧,你可以看到,移动第一个关节将导致基础帧中沿-X和+Y方向的局部运动。因为这个圆弧的半径更大,随着关节离手越远,这个关节运动也会产生更大的影响。
在这两种情况下,手部框架也将围绕+Z方向旋转。你可以想象,对于一个不仅仅存在于二维平面上的机器人来说,这会变得更加复杂。

表达这些关系的一般方法是通过一个名为几何雅可比矩阵的矩阵。雅可比矩阵在机器人运动规划和控制的几个应用中至关重要。这包括反向运动学、遥操作(或伺服)、低级位置和力控制,甚至在路径规划和轨迹生成中强制执行任务空间和碰撞约束。这就是为什么我写了这么多关于他们的文章,以及为什么这不会是他们最后一次出现在这篇文章中。
如果你有数学背景(或可以访问维基百科),你可能知道雅可比矩阵是定义某个函数相对于其变量的梯度,并将其打包成一个整洁的矩阵。在机器人操纵器的背景下,几何雅可比矩阵使用机器人运动学的一阶导数来告诉你关节速度与六个笛卡尔(或任务空间)速度之间的关系,即XYZ平移和旋转速度。要了解更多细节,你你应该观看此视频。

注1:几何雅可比矩阵也适用于速度的导数,如加速度、急动、突然、爆裂、爆裂等……但不适用于位置!这就是为什么从笛卡尔姿态计算关节位置的逆问题,或逆运动学,有自己的部分。
注2:在这种情况下使用限定词“几何”,因为还有其他类型的雅可比矩阵,它们同样会使用机器人的动力学而不是运动学来关联关节扭矩和任务空间广义力(关于XYZ的力和扭矩)。数学是相似的,但我们现在处理的不是长度和角度,而是质量、惯性、刚度、阻尼以及迄今为止我们忽略的所有好东西。
有趣的是,雅可比矩阵不一定是正方形的——只有当机器人恰好有6个自由度时,它们才是正方形的。即使在6自由度系统的情况下,特定配置的雅可比矩阵也不一定在所有方向上都具有完全的可操作性(在数学方面,矩阵不是全秩的);上一节中的“单平面上的6个关节”示例就是这样一种情况。这意味着,你通常不能“直接反转”雅可比矩阵,从而从任务空间速度中获得关节速度。我们将在后面的部分探讨其影响。
2.4、碰撞检查
到目前为止,我们的刚体模型无法捕捉特定关节配置或路径是否没有自碰撞或与环境的碰撞。通常,我们需要用可用于检查碰撞的额外几何信息来增强这些模型。
大多数现代碰撞检查库,如灵活碰撞库(FCL),都可以用于执行这些计算。碰撞几何体可以表示为简单的图元(球体、圆柱体、长方体等)或直接来自CAD模型的网格。这带来了一种权衡:虽然与网格相比,几何图元之间的碰撞计算成本相对较低,但它们是机器人真实几何形状的近似值。通常,机器人建模框架在视觉几何(用于可视化机器人的全分辨率网格)和碰撞几何(简单图元和粗略碰撞网格的组合)之间有区别。如果你想了解更多,我建议你看看2012年国际放射防护协会上发表的FCL官方论文。

我们还需要讨论与环境的碰撞检查。确实,我们还可以使用几何图元和网格来表示机器人周围的世界。如果我们先验地知道环境是什么样子的(例如,机器人在工业工作单元中),或者我们有一种机制来检测物体并适应它们周围的形状(如盒子),那么这是可行的。但是,您也可以直接使用传感器数据将整个环境视为障碍物。一种常见的方法是将深度相机和/或激光雷达的点云转换为易于碰撞检查的表示。许多软件库支持的一种格式是Octomap,它只是在软件中表示体素网格的一种更有效的方法。

顺便说一句,请注意,我们模型中的一些几何体可能总是会相互碰撞。对于同一链路上的相邻几何体或直接跨越关节边界的几何体,有时会出现这种情况,但对于更远的几何体则不那么常见。大多数建模框架允许你定义要检查的碰撞对列表,而不是详尽地检查每一个可能的碰撞对。此外,这个配对列表经常会动态变化。例如,如果你的机器人正在围绕一个物体闭合它的抓取器手指,也许模型忽略手指和物体之间的碰撞是可以的……但如果你打算移动到其他地方,你不想最终用手指后部敲击物体。
在运动规划中使用的另一种相关表示,在计算机图形学和游戏开发中也很常见,是有符号距离函数或有符号距离场(无论哪种方式,它都缩写为SDF)。顾名思义,SDF返回一个距离值,如果几何体没有碰撞,则该值为正,如果它们发生碰撞,则为负,零就在边缘。这些可以是简单图元的分析函数,但通常的做法是从更复杂的形状(网格、Octomaps等)离线生成SDF,并将其存储在内存中,以便在运行时快速查找。对于SDF的基本介绍,我喜欢这个YouTube解释器。更接近我们的机器人应用程序,你会发现像MuJoCo和nvblox这样的最先进的软件工具支持SDF。

2.5、机器人描述文件格式
最终,我们的机器人模型表示需要与各种类型的软件配合使用,包括模拟器、运动规划框架、控制工具等。虽然没有“一种格式可以统治所有格式”,但已经有过几次标准化的尝试。在我看来,这些是在撰写本文时幸存的:
- 皮克斯开发的通用场景描述(USD)是计算机图形学和3D动画社区中事实上的场景表示格式。
- SDFormat(Simulation Description Format,缩写为SDF)是一种基于XML的格式,最初是为Gazebo模拟器开发的,现在仍在使用。
- 同样来自ROS生态系统和基于XML的是通用机器人描述格式(URDF)。我认为说这是刚体机器人描述的事实标准并没有争议。大多数机器人软件工具支持URDF导入,大多数CAD软件工具,如SolidWorks、Onshape等,都有URDF导出器,甚至还有美元到URDF的转换器。
要了解更多关于URDF的信息,请参阅官方教程;或者,如果你更喜欢视频,我推荐你访问铰接机器人YouTube频道。事实上,Articulated Robotics的内容非常好,我直接在这里嵌入了视频。
基于XML的表示法并不止于此。具体到运动规划,你有时还会遇到由MoveIt开创的补充语义机器人描述格式(SRDF),但也用于其他框架。这定义了URDF规范不包含的其他信息,包括关节组、保存的关节配置和停用的碰撞对。下面是一个SRDF文件的最小示例,但你可以在实际的机器人描述包中找到更真实的示例。
<?xml version="1.0" encoding="UTF-8"?>
<robot name="my_robot">
<!-- Joint groups -->
<group name="arm">
<joint name="arm_joint1"/>
<joint name="arm_joint2"/>
<joint name="arm_joint3"/>
</group>
<group name="hand">
<joint name="finger_joint1"/>
<joint name="finger_joint2"/>
</group>
<group name="arm_and_hand">
<group name="arm"/>
<group name="hand"/>
</group>
<!-- Saved states -->
<group_state name="home" group="arm_and_hand">
<joint name="finger_joint1" value="0.001"/>
<joint name="arm_joint1" value="0"/>
<joint name="arm_joint2" value="-0.785398"/>
<joint name="arm_joint3" value="0"/>
</group_state>
<!-- Collision filters -->
<disable_collisions link1="base_link" link2="shoulder_link" reason="Adjacent"/>
<disable_collisions link1="shoulder_link" link2="elbow_link" reason="Adjacent"/>
<disable_collisions link1="hand" link2="base_link" reason="Never"/>
</robot>

评论0