使用循环坐标下降(CCD)算法解算反向运动学(IK)

更新时间:2023-06-04 13:37:30 阅读: 评论:0

使⽤循环坐标下降(CCD)算法解算反向运动学(IK)使⽤循环坐标下降(CCD)算法解算反向运动学(IK)
周五的时间总是美好的,今天也将⾃⼰制作的演⽰程序收官了。这篇⽂章是的继续,向⼤家介绍⼀下如何使⽤循环坐标下降(cyclic coordinatedecent,CCD)算法来求解反向运动学问题。
注意到了没有?我们要解决的问题属于刚体运动学范畴,所以⼀切涉及到形变的因素都不⽤考虑,因此我们既可以使⽤四元数和⼀个向量表⽰⾻骼的位置和平移,也可以将两者结合起来使⽤⼀个矩阵表⽰⾻骼的所有变换。学过计算机图形学的同学都知道,旋转分量和平移分量是互不影响的,所以可以简单地分别将旋转和平移叠加来表⽰累次旋转和累次平移。
原创⽂章,反对未声明的引⽤。原博客地址:blog.csdn/gamesdev/article/details/14047265
⾸先让我们看⼀下同样的场景,当使⽤反向运动学和不使⽤反向运动学时的差别:
这是使⽤了反向运动学的
这是未使⽤反向运动学的,可见初⾳ミク的腿⽆法弯曲。
了解了反向运动学的效果之后,我们再看如何⽤CCD算法来实现反向运动学。
⾸先让我们来处理最简单的情况:只有两根⾻骼、两个关节,这样的情况⾮常好处理,设原点为origin、⽬标点为target,⽤
target.finalPos = origin.finalPos + ation * lativePos
现在反过来,已知target.finalPos和 origin.finalPos,显⽽易见,有:
origin.finalPos = target.finalPos – ation-1 *lativePos
解⽅程,求出ation即可。
下⾯来看⼀下三根⾻骼、三个关节的问题怎么求。这种情况多见于⼈类的⼿臂部分和腿部分,求解的⽅法,⽼实说有两种——使⽤余弦定理和⼀般迭代⽅法。下⾯我就讲讲使⽤这两种⽅式的感受。
使⽤余弦定理求解见于《Character AnimationWith Direct3D》这本书,余弦定理的公式是C2 =A2+B2-2ABcos(∠C),拿胳膊打⽐⽅,当你的胳膊从⾃然伸直转向弯曲的时候,肘部与肩膀和⼿掌形成的夹⾓在缩⼩。我们拿两个关键帧s和t来进⾏对⽐,⾸先我们知道s 帧时⼿臂的所有位置和⽅位,这个时候我们可以使⽤向量点击求出肘部形成的夹⾓α,其次我们知道t帧时肩膀和⼿掌的位置,我们需要求出肘部的位置,这时我们可以利⽤余弦定理,已知两条边长(上臂低位的长度和⾼位的长度)以及另⼀条边长(可由⼿掌的坐标减去肩膀的位置求模得到)以及两个顶点的位置,求出第三个顶点
与两个顶点形成的⾓度β。我们可以计算出δ=β-α。然后根据叉积公式求出旋转轴,将⼿掌绕着这个旋转轴反向旋转δ就可以近似地到达关键帧t中肘部的位置了。
可是余弦定理只能处理三根⾻骼、三个关节的问题,如果是多跟⾻骼、多个关节(例如蜈蚣那样),由于概念上的缺陷⽽⽆法处理。      ⼀般的迭代⽅法是这么处理的:
1、从末端关节连接的⽗关节A开始,计算末端关节与⽬标末端关节位置与该关节形成的夹⾓,旋转之;
2、如果旋转后的末端关节未达到⽬标,则以A的⽗关节开始重复步骤1;
3、如果到达根关节仍未将末端关节达到⽬标,则结束⼀次迭代,重复步骤1、2,进⼊下⼀次迭代。
使⽤迭代的⽅法是⼀种传统的解决IK问题的⽅法,⽬前出现了⼏种改进的⽅法,都是以这种⽅法为基本,分析其缺陷并加以改进⽽成的。
下⾯是⼀张演⽰程序截图,其中初⾳ミク模型的蓝⾊⾻骼部分是属于正向运动学的,⽽黄⾊部分,也就是长发、腿部、脚部和领带则属
于反向运动学,受反向运动学影响的⾻骼,都受控制端(以同⼼⽅块作为标识)的影响。
下⾯是其中的⼀帧截图,我们发现,⾓⾊的腿部和头发部分受到反向运动学的影响⽽发⽣弯曲。
下⾯是使⽤⼀般迭代⽅法处理反向运动学的部分源代码:
/*---------------------------------------------------------------------------*/
void MMDRenderHandler::CalculateInverKinematics( void )// 计算反向运动学
{
// 遍历每个IK
foreach ( const IK& _IK, m_IKs )
{
Bone& destBone = m_Bones[_IK.destIndex];        // ⼀般是IK⾻骼
Bone& targetBone = m_Bones[_IK.targetIndex];    // ⼀般是与IK⼀端连接的⾻骼
for ( int i = 0; i < _IK.iteration; ++i )
身上发冷是怎么回事{
for ( int j = 0; j < _IK.bones.size( ); ++j )
{
quint16 index = _IK.bones[j];
Bone& joint = m_Bones[index];
CCDIKSolve( joint,
targetBone,
destBone,
_IK.limitAngle,
i );
CalculateBonesFinalPos( index );
}
if ( qFuzzyIsNull( ( targetBone.finalPos -怎么登陆微信
destBone.finalPos ).SquareLength( ) ) )
{
{
break;// ⽬的达到了,结束迭代
}
}
}
}
冷战的婚姻
/*---------------------------------------------------------------------------*/
void MMDRenderHandler::CCDIKSolve( Bone& joint, // 想象成肘部
Bone& target, // ⽬标位置
Bone& end, // 末端效应器
float limitAngle,// 单位限制⾓度
随枣会战
int iterNum )// 迭代次数
{
// 使⽤循环坐标下降算法(cyclic coordinate decent,CCD)
// 计算在绝对旋转后的连接点和⽬标位置以及末端效应器的相对位置
Vector3F absJoint2End = end.finalPos - joint.finalPos;
Vector3F absJoint2Target = target.finalPos - joint.finalPos;
同一个梦Quaternion invRotation = joint.absRotation.Conjugate( );// 求出四元数的共轭四元数
// 转为本地坐标系(平移因素在第⼀阶段已剔除)台湾简介
Vector3F localJoint2End = invRotation.RotatedVector( absJoint2End );
Vector3F localJoint2Target = invRotation.RotatedVector( absJoint2Target );
// 计算应该旋转的⾓度
float deltaAngle = acosf( Vector3F::DotProduct(
localJoint2End.Normalized( ),
localJoint2Target.Normalized( ) ) );
if ( std::isnan( deltaAngle ) ||
qFuzzyIsNull( deltaAngle ) )// ⾓度计算出错或⾓度太⼩(⼀般是向量太接近)
{
return;// 不处理,直接返回
}
/
/ 限制⾓度为[-limitAngle, limitAngle]
deltaAngle = qBound( -limitAngle, deltaAngle, limitAngle );
// 求出旋转轴
Vector3F rotateAxis = Vector3F::CrossProduct( localJoint2Target,
localJoint2End );
// 构造旋转四元数卤豆腐的做法
Quaternion deltaRotation = Quaternion::FromRotation(
rotateAxis, deltaAngle );
joint.absRotation = m_Bones[joint.parent].absRotation * ation;
}
/
*---------------------------------------------------------------------------*/
演⽰程序下载地址:
山乡村野

本文发布于:2023-06-04 13:37:30,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/82/860608.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:关节   位置   旋转   处理
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图