首页 > 其他分享 >二维逆运动学 – 代码

二维逆运动学 – 代码

时间:2024-03-15 10:57:43浏览次数:28  
标签:length0 length2 代码 float 运动学 Mathf 二维 position length1

介绍

在本系列的前一部分中,我们讨论了具有两个自由度的机械臂的反向运动学问题;如下图所示。

在这种情况下,机械臂的长度c和 一个通常是已知的。如果我们必须到达的点是 C,那么配置就变成了一个三角形,其中所有边都是已知的。

然后,我们推导出了角度和一个B的方程,它控制着机械臂关节的旋转:

(1) \begin{equation*} A = \underset{\alpha}{\underbrace{\cos^{-1}{\left(\frac{b^2+c^2-a^2}{2bc}\right)}}} + \underset{A'}{\underbrace{\tan^{-1}{\left(\frac{C_Y-A_Y}{C_X-A_X}\right)}}} \end{equation*}

(2) \begin{equation*} B = \pi - \underset{\beta}{\underbrace{\cos^{-1}{\left(\frac{a^2 + c^2 -b^2}{2ac}\right)}}} \end{equation*}

乍一看,它们可能看起来相当吓人;另一方面,从上图来看,它们的几何解释应该非常直观。

创建机械臂

实施此解决方案的第一步是创建机械臂。“关节”的概念不是Unity附带的。但是,可以利用引擎提供的育儿系统来创建与机械臂完全相同的组件层次结构。

这个想法是使用

游戏对象

对于每个关节,因此旋转其变换将导致连接到它的臂也旋转。将第二个关节与第一个关节联系起来将导致它们旋转,如第一张图所示。

 

生成的层次结构将变为:

    • 关节 A
      • 骨 A
      • 关节 B
        • 骨B

然后,我们可以将脚本添加到名为SimpleIK,这将负责旋转关节以达到所需目标。

 1 using System.Collections;
 2 using UnityEngine;
 3 
 4 namespace AlanZucconi.IK
 5 {
 6     public class SimpleIK : MonoBehaviour
 7     {
 8         [Header("Joints")]
 9         public Transform Joint0;
10         public Transform Joint1;
11         public Transform Hand;
12 
13         [Header("Target")]
14         public Transform Target;
15 
16         ...
17     }
18 }

在本教程的前一部分中推导的方程需要知道前两块骨头(分别称为 c 和 一个)的长度。由于骨骼的长度不应该改变,因此可以在

开始功能。然而,这要求手臂在游戏开始时处于良好的配置。
1 private length0;
2 private length1;
3 
4 void Start ()
5 {
6     length0 = Vector2.Distance(Joint0.position, Joint1.position);
7     length1 = Vector2.Distance(Joint1.position, Hand.position  );
8 }

 

旋转关节

在显示代码的最终版本之前,让我们从一个简化的版本开始。如果我们将等式 (1) 和 (2) 直接转换为代码,我们最终会得到这样的结果:

 1 void Update ()
 2 {
 3     // Distance from Joint0 to Target
 4     float length2 = Vector2.Distance(Joint0.position, Target.position);
 5 
 6     // Inner angle alpha
 7     float cosAngle0 = ((length2 * length2) + (length0 * length0) - (length1 * length1)) / (2 * length2 * length0);
 8     float angle0 = Mathf.Acos(cosAngle0) * Mathf.Rad2Deg;
 9 
10     // Inner angle beta
11     float cosAngle1 = ((length1 * length1) + (length0 * length0) - (length2 * length2)) / (2 * length1 * length0);
12     float angle1 = Mathf.Acos(cosAngle1) * Mathf.Rad2Deg;
13 
14     // Angle from Joint0 and Target
15     Vector2 diff = Target.position - Joint0.position;
16     float atan = Mathf.Atan2(diff.y, diff.x) * Mathf.Rad2Deg;
17 
18     // So they work in Unity reference frame
19     float jointAngle0 = atan - angle0;    // Angle A
20     float jointAngle1 = 180f - angle1;    // Angle B
21 
22     ...
23 }

 

数学函数 cos^{-1} 和 tan^{-1} 在 Unity 中称为 Mathf.AcosMathf.Atan2,此外,最终角度也转换为度数Mathf.Rad2Deg,由于Transform组件接受度数,而不是弧度。

 

瞄准无法实现的目标

虽然上面的代码似乎有效,但存在失败的情况。如果无法访问目标,会发生什么情况?目前的实施没有考虑到这一点,导致了不良行为。

一种常见的解决方案是将手臂完全伸展到目标方向。这种行为与我们试图模拟的伸展运动是一致的。

下面的代码通过检查与根部的距离是否大于手臂的总长度来检测目标是否遥不可及。

 1 void Update ()
 2 {
 3     float jointAngle0;
 4     float jointAngle1;
 5 
 6     float length2 = Vector2.Distance(Joint0.position, Target.position);
 7 
 8     // Angle from Joint0 and Target
 9     Vector2 diff = Target.position - Joint0.position;
10     float atan = Mathf.Atan2(diff.y, diff.x) * Mathf.Rad2Deg;
11 
12     // Is the target reachable?
13     // If not, we stretch as far as possible
14     if (length0 + length1 < length2)
15     {
16         jointAngle0 = atan;
17         jointAngle1 = 0f;
18     }
19     else
20     {
21         float cosAngle0 = ((length2 * length2) + (length0 * length0) - (length1 * length1)) / (2 * length2 * length0);
22         float angle0 = Mathf.Acos(cosAngle0) * Mathf.Rad2Deg;
23 
24         float cosAngle1 = ((length1 * length1) + (length0 * length0) - (length2 * length2)) / (2 * length1 * length0);
25         float angle1 = Mathf.Acos(cosAngle1) * Mathf.Rad2Deg;
26 
27         // So they work in Unity reference frame
28         jointAngle0 = atan - angle0;
29         jointAngle1 = 180f - angle1;
30     }
31 
32     ...
33 }

 

旋转关节

现在剩下的就是旋转关节。这可以通过访问关节的Transform组件的localEulerAngles属性,不过很遗憾,无法直接更改角度,因此需要复制、编辑和替换矢量。

1 Vector3 Euler0 = Joint0.transform.localEulerAngles;
2 Euler0.z = jointAngle0;
3 Joint0.transform.localEulerAngles = Euler0;
4 
5 Vector3 Euler1 = Joint1.transform.localEulerAngles;
6 Euler1.z = jointAngle1;
7 Joint1.transform.localEulerAngles = Euler1;

 

结论

这篇文章结束了 2D 机械臂的反向运动学课程。

您可以在此处阅读此在线课程的其余部分:

此外,还提供以 3D 为重点的后续产品:

本教程中介绍的线条艺术动物的灵感来自WithOneLine的作品。

下载

您可以下载本教程中使用的所有资源,以便为 Unity 提供功能齐全的机械臂。

特征 标准 奖赏
逆向运动学 ✅ ✅
多种解决方案 ❌ ✅
平滑到达 ❌ ✅
测试场景 ❌ ✅
测试动画 ❌ ✅
下载 标准 奖赏

标签:length0,length2,代码,float,运动学,Mathf,二维,position,length1
From: https://www.cnblogs.com/soliang/p/18074934

相关文章

  • 稀疏数组与二维数组之间的转换
    稀疏数组介绍:稀疏数组:当一个数组中大部分元素为同一个值时,就可以考虑使用稀疏数组来保存数据节省空间。稀疏数组的原理:1)稀疏数组一共三列,第一行的第一列保存原二维数组的行数,第一行第二列保存原二维数组的列数,第一行第三列保存原二维数组非0数据的个数;2)稀疏数组一共有【原二维......
  • 二维逆运动学 – 数学
    如果您已经关注此博客一段时间,您可能已经注意到一些反复出现的主题。逆向运动学绝对是其中之一,我已经专门写了一整套关于如何将其应用于机械臂和触手的系列文章。如果您还没有读过它们,请不要担心:这个新系列将是独立的,因为它从一个新的角度回顾了逆运动学的问题。您可以在此处阅......
  • [转]IDEA 打开项目时,代码全被标记为红色
    在IntelliJIDEA中,代码被标记为红色通常表示存在编译错误或无法解析的引用等问题。要解决这个问题,请按照以下步骤进行排查和改进:检查项目结构:确保所有必需的源代码文件夹都被正确地包含在项目的模块设置中。确认项目SDK配置正确无误。在File->ProjectStructure(快捷键:Ctrl+......
  • python的代码发布到服务器上需要注意的事项
    1、服务器的python运行环境配置。从官网上 https://www.python.org/ 下载服务器操作系统对应的版本。 然后配置 python和pip命令运行的环境变量,这是windows下的 检查是否正常:  2、开发时引用的第三方库要在服务器上安装。   a.首先获取需要的第三方库......
  • 代码随想录训练营第44天 | 动态规划:完全背包理论基础、​​​​​​LeetCode 518.零钱
    目录动态规划:完全背包理论基础文章讲解:代码随想录(programmercarl.com)视频讲解:带你学透完全背包问题!_哔哩哔哩_bilibili思路​​​​​​LeetCode518.零钱兑换II文章讲解:代码随想录(programmercarl.com)视频讲解:518.零钱兑换II_哔哩哔哩_bilibili思路​​​​​​Le......
  • 代码随想录算法训练营第day46|139.单词拆分 、多重背包
    目录139.单词拆分多重背包 139.单词拆分力扣题目链接(opensnewwindow)给定一个非空字符串s和一个包含非空单词的列表wordDict,判定 s是否可以被空格拆分为一个或多个在字典中出现的单词。说明:拆分时可以重复使用字典中的单词。你可以假设字典中没有重复的单......
  • 代码随想录算法训练营第day17|110.平衡二叉树 、 257. 二叉树的所有路径 、404.左叶子
    目录a.110.平衡二叉树b.257.二叉树的所有路径 c.404.左叶子之和a.110.平衡二叉树力扣题目链接(opensnewwindow)给定一个二叉树,判断它是否是高度平衡的二叉树。本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点的左右两个子树的高度差的绝对值不超过1。示例1......
  • web漏洞:RCE代码及命令执行
    概述:RCE漏洞可以让攻击者直接向后台服务器远程注入操作命令或代码,从而控制后台系统,分为远程系统命令执行和远程代码执行。远程系统命令执行:(危害:执行系统命令)一般出现这种漏洞是因为应用系统从设计上需要给用户提供指定的远程命令操作的接口(比如路由器,防火墙,入侵检测等设......
  • 我的blog搭建代码
    下面是代码,可供抄袭,但注意信息改成自己的:博客侧边栏公告页面定制CSS代码(禁用模板默认CSS)#loading{bottom:0;left:0;position:fixed;right:0;top:0;z-index:9999;background-color:#f4f5f5;pointer-events:none;}.loader-inner{will-change:transform;width:40px;height:......
  • 代码随想录算法训练营第四十六天| 139.单词拆分 多重背包 背包问题总结篇!
    单词拆分 题目链接:139.单词拆分-力扣(LeetCode)思路:竟然真能转化为背包问题。classSolution{public:boolwordBreak(strings,vector<string>&wordDict){unordered_set<string>t(wordDict.begin(),wordDict.end());vector<bool>dp(s.size()+......