从零开始开发六脚架(第3部分)-运动学


大家好! 六足动物的发展日新月异,最后基本的数学部分已经过测试并准备好进行文档记录。 为了使该项目能够生存到最后,并且不会在架子上积聚灰尘,您需要看到它的积极变化,即使它们微不足道。 在本文中,我将讨论一种解决逆运动学问题的算法,并在实际中进行演示。 我希望这会很有趣。

发展阶段:
第1部分-设计
第2部分-组装
第3部分-运动学
第4部分-数学轨迹和序列
第5部分-电子产品
第6部分-过渡到3D打印
第7部分-新外壳,应用软件和通信协议

坐标系


首先,需要确定机器人的坐标系-其中最多有三个:

实际上,它们彼此没有区别,但是位于壳体的不同位置,并且可以相对于彼此旋转一定角度。 从上方看时,轴的位置如下(coxa_zero_rotate参数将在稍后描述):


您可能已经注意到,左侧和右侧的坐标系相对于身体中心对称放置。 在我看来,这会更容易。

相对于该点所针对的肢体设置每个点的坐标。 这种方法使您可以轻松地将肢体移动到任何地方,而不会干扰配置。

因此它们相对于肢体位于:


目标点的坐标是相对于主坐标系的。 最初,我想设置相对于案例中心的坐标,但是事实证明这并不是很方便,并且需要存储许多其他参数。

在下文中,符号X *,X **等。 将被X',X''代替。

解决逆运动学问题


让我们从对我来说最困难,最有趣的时刻开始-运动学。 我自己发明了该算法,有时浏览一下三角形和三角函数的属性。 对于机械师来说,这对我来说很困难,对于三角函数来说,情况甚至更糟,所以也许在某个地方我无法考虑到某些因素,但是这种算法有效(最后是视频)。

1.问题陈述


假设我们需要右前肢到达坐标为(150; -20; 100)的点A。 还已知肢体相对于身体旋转了45度(参数coxa_zero_rotate):


肢体本身具有以下参数:


我认为您无需描述这些参数的含义,名称本身就可以说明。 您仅可以添加所有这些参数,这些参数是由外壳的配置决定的,是永久的,并存储在FLASH存储器中,并且可以从外部进行编辑(出于灵活性)。

这就是为什么你需要它

图片显示了六足动物的船体和腿部位置的各种变化。 现在,它与ARACNID配置匹配。 假设我想到了将大小写更改为REPTILES,并且为此只需更改参数的值而无需在程序本身中进行测量就足够了,即使目标点也不必更改(除非正确地实现了这一点)。

以上所有参数都足以解决问题。

2.解决问题


2.1找到COXA的旋转角度

这个阶段是最简单的。 首先,您需要重新计算A点相对于LIMB坐标系的坐标。 显然,您需要旋转角度coxa_zero_rotate,可以使用以下公式进行此操作:

$$显示$$ x'= x⋅cos(α)+ z⋅sin(α)= 150⋅cos(45)+ 100⋅sin(45)= 176.78 $$显示$$


y=y=20


$$显示$$ z'= -x⋅sin(α)+ z⋅cos(α)= -150⋅sin(45)+ 100⋅cos(45)= -35.36 $$显示$$


因此,我们获得了相对于LIMB坐标系的目标点A(176.78; -20; -35.36)的坐标。

现在,您可以使用atan2函数找到COXA角:

COXA=atan2zx=atan235.36176.78=11.30°


因此,我们得到了旋转COXA伺服器所需的角度,以使点A处于X'Y'平面中。 现在让我们检查KOMPAS 3D中的计算:


好吧

2.2找出FEMUR和TIBIA的旋转角度
为了找到这些角度,必须去X'Y'平面。 要转到飞机,您需要将点旋转角度COXA,我们已经在前面计算了角度。

$$显示$$ x'= x'⋅cos(α)+ y'⋅sin(α)= 176.78⋅cos(-11)+ -20⋅sin(-11)= 180.28 $$显示$$


y=y=20


y坐标不变,因为我们沿Y轴旋转。

接下来,您需要从计算中删除COXA长度,即 我们进入平面X''Y'',为此,我们点的坐标x'进行了长度COXA的平移:

x=xcoxaLength=180.2840=140.28


y=y


经过所有这些操作后,问题的进一步解决方法变为找到三角形的角度ab


在找到角度之前,您需要找到该三角形的第三边C。 该距离不过是向量的长度,由以下公式计算得出:

$$ display $$ C = \ sqrt {x''^ 2 + y''^ ^ 2} = \ sqrt {140.28 ^ 2 +(-20)^ 2} = 141.70 $$ display $$


现在,您需要检查肢体是否可以达到这一点。 如果C大于FEMUR和TIBIA的长度之和,则该点不可达到。 在我们的情况下,141.70 <141 + 85-该点是可以达到的。

现在我们知道了三角形的所有边,并且可以使用余弦定理找到所需的角度ab

a=acosA2+C2B2 over2AC=72.05°


b=acosB2+A2C2 over2BA=72.95°


由于此处未考虑直线C相对于X轴的初始位置和倾斜角度,因此所获得的角度不适用于将其馈入伺服机构,如果我们知道初始角度FEMUR和TIBIA(135°和45°),则不考虑C相对于X轴的倾斜角度众所周知。 您可以使用函数atan2(y'',x'')找到它:

$$显示$$φ= atan2(y'',x'')= atan2(-20,140.28)= -8.11°$$ display $$


最后,您可以计算FEMUR和TIBIA伺服器的旋转角度:

FEMUR=femurZeroRotateaφ=13572.05+8.11=71.06°


FEMUR=btibiaZeroRotate=4572.95=27.95°



让我们检查一下计算:


似乎是事实。

总结


计算出的角度COXA,FEMUR和TIBIA适用于馈送其伺服器。 您可能会注意到,COXA角为负,因此出现了一个问题:“如何将驱动器旋转-11.3度?”。 诀窍是,我将COXA伺服器的中性位置用作逻辑零,这使驱动器可以同时旋转正角和负角。 这当然是显而易见的,但我认为提到这一点并非不妥。 当我谈论以上所有内容的实现时,我将在以下文章中更详细地讨论这一点。

源代码


够用语,让我看一下代码
#define RAD_TO_DEG(rad) ((rad) * 180.0 / M_PI) #define DEG_TO_RAD(deg) ((deg) * M_PI / 180.0) typedef enum { LINK_COXA, LINK_FEMUR, LINK_TIBIA } link_id_t; typedef struct { // Current link state float angle; // Link configuration uint32_t length; int32_t zero_rotate; int32_t min_angle; int32_t max_angle; } link_info_t; typedef struct { point_3d_t position; path_3d_t movement_path; link_info_t links[3]; } limb_info_t; // *************************************************************************** /// @brief Calculate angles /// @param info: limb info @ref limb_info_t /// @return true - calculation success, false - no // *************************************************************************** static bool kinematic_calculate_angles(limb_info_t* info) { int32_t coxa_zero_rotate_deg = info->links[LINK_COXA].zero_rotate; int32_t femur_zero_rotate_deg = info->links[LINK_FEMUR].zero_rotate; int32_t tibia_zero_rotate_deg = info->links[LINK_TIBIA].zero_rotate; uint32_t coxa_length = info->links[LINK_COXA].length; uint32_t femur_length = info->links[LINK_FEMUR].length; uint32_t tibia_length = info->links[LINK_TIBIA].length; float x = info->position.x; float y = info->position.y; float z = info->position.z; // Move to (X*, Y*, Z*) coordinate system - rotate float coxa_zero_rotate_rad = DEG_TO_RAD(coxa_zero_rotate_deg); float x1 = x * cos(coxa_zero_rotate_rad) + z * sin(coxa_zero_rotate_rad); float y1 = y; float z1 = -x * sin(coxa_zero_rotate_rad) + z * cos(coxa_zero_rotate_rad); // // Calculate COXA angle // float coxa_angle_rad = atan2(z1, x1); info->links[LINK_COXA].angle = RAD_TO_DEG(coxa_angle_rad); // // Prepare for calculation FEMUR and TIBIA angles // // Move to (X*, Y*) coordinate system (rotate on axis Y) x1 = x1 * cos(coxa_angle_rad) + z1 * sin(coxa_angle_rad); // Move to (X**, Y**) coordinate system (remove coxa from calculations) x1 = x1 - coxa_length; // Calculate angle between axis X and destination point float fi = atan2(y1, x1); // Calculate distance to destination point float d = sqrt(x1 * x1 + y1 * y1); if (d > femur_length + tibia_length) { return false; // Point not attainable } // // Calculate triangle angles // float a = tibia_length; float b = femur_length; float c = d; float alpha = acos( (b * b + c * c - a * a) / (2 * b * c) ); float gamma = acos( (a * a + b * b - c * c) / (2 * a * b) ); // // Calculate FEMUR and TIBIA angle // info->links[LINK_FEMUR].angle = femur_zero_rotate_deg - RAD_TO_DEG(alpha) - RAD_TO_DEG(fi); info->links[LINK_TIBIA].angle = RAD_TO_DEG(gamma) - tibia_zero_rotate_deg; // // Check angles // if (info->links[LINK_COXA].angle < info->links[LINK_COXA].min_angle || info->links[LINK_COXA].angle > info->links[LINK_COXA].max_angle) { return false; } if (info->links[LINK_FEMUR].angle < info->links[LINK_FEMUR].min_angle || info->links[LINK_FEMUR].angle > info->links[LINK_FEMUR].max_angle) { return false; } if (info->links[LINK_TIBIA].angle < info->links[LINK_TIBIA].min_angle || info->links[LINK_TIBIA].angle > info->links[LINK_TIBIA].max_angle) { return false; } return true; } 


运行中的算法



然后,偶然地出现了一段让人想起舞蹈的片段


聚苯乙烯


如果有人可以简化此算法,我将非常高兴。 我这样做是为了让我在半年后能理解它。

Source: https://habr.com/ru/post/zh-CN436748/


All Articles