刚体运动学-四元数插值

前言

之前对写了一篇关于刚体运动学相关知识博客:刚体运动学——欧拉角、四元数、旋转矩阵,本篇博客就举例来说明,如何在运动捕捉数据中进行四元数插值。

国际惯例,参考博客:

探讨:向量(方向)之间的插值-四元数法VS.旋转矩阵法的性能比较

书籍《3D数学基础:图形与游戏开发》

插值理论

问题:3D空间中,在等长度的两个交角为θ\thetaθ的向量V1(x1,y1,z1)V_1(x_1,y_1,z_1)V1(x1,y1,z1)V2(x2,y2,z2)V_2(x_2,y_2,z_2)V2(x2,y2,z2)

实例:行星绕太阳转动,找到旋转过程的两个位置p1,p2p_1,p_2p1,p2,现在模拟从p1p_1p1p2p_2p2的过程。

思路:

1 .一般线性插值

线性插值方法:

这里写图片描述

这里可以看出,插值的部分就是向量V3V_3V3.下面来证明V3V_3V3ttt的关系V2−V1V_2-V_1V2V1得到V1V_1V1指向V2V_2V2的向量,再乘以ttt就是V1V_1V1指向V3V_3V3的向量了,最后加上向量V1V_1V1就是向量V3V_3V3了,公式为:
v(t)=v1+t∗(v2−v1)(0≤t≤1)v(t) = v_1 + t*(v_2-v_1)(0\leq t\leq1) v(t)=v1+t(v2v1)0t1)
【注:可以看出一般线性插值长度变化了,不满足要求,用球面线性插值就不会变化】

2.一般球面线性插值

这里写图片描述

将插值结果放大一个放大系数k(t)k(t)k(t),使其长度放大到∣v1∣|v_1|v1或者∣v2∣|v_2|v2(简单的说就是保持长度不变)。
v(t)=k(t)(v1+t(v2−v1))v(t) = k(t)(v_1 + t(v_2-v_1)) v(t)=k(t)(v1+t(v2v1))
其中k(t)=∣v1∣∣v(t)∣=∣v1∣∣v1+t∗(v2−v1)∣k(t) =\frac{|v_1|}{|v(t)|}=\frac{|v_1|}{|v_1+t*(v_2-v_1)|}k(t)=v(t)v1=v1+t(v2v1)v1.

这样,插值向量v(t)v(t)v(t)的端点就会沿着v1v_1v1v2v_2v2端点构成的圆弧进行,v1v_1v1v2v_2v2是等长的,圆弧实际位于v1和v_1和v1v2v_2v2构成的曲面上的一段,所以又叫球面线性插值。

这个插值解决了3D空间中旋转的插值,在关键帧动画中可以用来计算两个关键帧之间的动画。但是,由于它的插值不是等角速度的,而是变速的,所以如果用来实现案例中的效果的话还需进一步处理。

【注】一般球面线性插值v(t)v(t)v(t)v1v_1v1的夹角θ(t)\theta(t)θ(t)不是t的线性函数。

证明过程如下(我滴妈呀,我的字好丑o(╯□╰)o):

这里写图片描述

3.改进的球面线性插值,有两种方法:

1> 四元数工具
变换方法:

构造四元数q(cos⁡θ,sin⁡θ∗v1’),r(cos⁡θ,sin⁡θ∗v2’)q(\cos \theta,\sin\theta *v_1’),r(\cos \theta,\sin \theta *v_2’)q(cosθsinθv1)r(cosθsinθv2)(v1’,v2’v_1’,v_2’v1v2为单位v1,v2v_1,v_2v1,v2向量),以及参数t(0≤t≤1)t(0\leq t\leq1)t(0t1),则构造四元数变换:

  • 四元数s(w,v’)=r∗(q−1)t∗qs(w,v’)=r*(q-1)t*qs(w,v)=r(q1)tq即为球面线性插值变换。其中,s的虚部v1’v_1’v1v2’v_2’v2间的插值向量,乘以长度x2+y2+z2\sqrt{x^2+y^2+z^2}x2+y2+z2,即得到v1,v2v_1,v_2v1,v2间插值向量vvv

  • 另一种变换形式是对四元数进行插值变换
    s(w,v′)=a∗q+b∗rs(w,v')=a*q+b*r s(w,v)=aq+br
    其中a=sin⁡(α(1−t))sin⁡α,b=sin(αt)sin⁡α,cos⁡α=x1∗x2+y1∗y2+w1∗w2a=\frac{\sin(\alpha(1-t))}{\sin\alpha},b=\frac{\\sin(\alpha t)}{\sin\alpha},\cos\alpha=x_1*x_2+y_1*y_2+w_1*w_2a=sinαsin(α(1t)),b=sinαsin(αt),cosα=x1x2+y1y2+w1w2

    S的虚部v′v'v即为v1′v_1'v1v2′v_2'v2间的插值向量,乘以长度x2+y2+z2\sqrt{x^2+y^2+z^2}x2+y2+z2,即得v1,v2v_1,v_2v1,v2间插值向量vvv

2> 利用旋转矩阵

变换方法:v=v1∗Trotv=v1*Trotv=v1Trot

其中,TrotTrotTrot即绕任意轴旋转的矩阵变换矩阵,因为v1v_1v1v2v_2v2间的插值可以看成是v1v_1v1绕垂直于v1,v2v_1,v_2v1,v2组成的平面的向量的旋转,所以实际上是绕轴旋转的问题,不过相应参数变成 θ=t∗θ\theta=t*\thetaθ=tθ,轴q(q1,q2,q3)q(q1,q2,q3)q(q1,q2,q3)变成向量v1×v2∣v1×v2∣=y1z2−z1y2,z1x2−x1z2,x1y2−y1x2sin⁡θ\frac{v_1\times v_2}{|v_1\times v_2|}=\frac{y_1z_2-z_1y_2,z_1x_2-x_1z_2,x_1y_2-y_1x_2}{\sin\theta}v1×v2v1×v2=sinθy1z2z1y2,z1x2x1z2,x1y2y1x2

四元数插值

##第一种插值方法

四元数比较重要的一个用途就是球面线性插值(Spherical Linear Interpolation),可以在两个四元数之间平滑插值。

插值步骤:

① 计算两个值的差:q0q_0q0q1q_1q1的角位移由Δq=q0−1q1\Delta q=q_0^{-1}q_1Δq=q01q1给出

② 计算差的一部分,四元数求幂可以做到,差的一部分由Δqt\Delta q_tΔqt给出

③ 在开始值上加上差的一部分,用四元数乘法组合角位移q0Δqtq_0\Delta q_tq0Δqt

这样就可以得到slerp公式:
slerp(q0,q1,t)=q0(q0−1q1)tslerp(q_0,q_1,t)=q_0(q_0^{-1}q_1)^t slerp(q0,q1,t)=q0(q01q1)t
看看matlab中的函数实现:

function y = slerp(q1, q2, t)
% The third parameter, t, gives the 'distance' along the 'arc' between the
% quaternions, 0 representing q1 and 1 representing q2. If q1 and q2 are
% unit pure quaternions, the interpolation is along a great circle of the
% sphere between the points represented by q1 and q2. If q1 and q2 are unit
% full quaternions, the interpolation is along the 'arc' on the 4-sphere:
% this means the result is a quaternion which represents a rotation
% intermediate between the two rotations represented by q1 and q2. If the
% first two parameters are not unit quaternions, then there is also
% interpolation in modulus.error(nargchk(3, 3, nargin)), error(nargoutchk(0, 1, nargout))if ~isnumeric(t) || ~isreal(t)error('Third parameter must be real and numeric.');
endif any(any(t < 0.0)) || any(any(t > 1.0))error('Third parameter must have values between 0 and 1 inclusive.');
endif ~(all(size(q1) == size(q2)) || isscalar(q1) || isscalar(q2))error(['First two parameters cannot be of different sizes unless' ...' one is a scalar.']);
endif ~isscalar(t)    if ~(all(size(q1) == size(t)) || all(size(q2) == size(t)) || ...(isscalar(q1) && isscalar(q2)) ...)error(['Third parameter cannot be an array unless' ...' the first two are scalars, or it has the'...' same size as one of the first two parameters.']);end
endy = q1 .* (q1.^-1 .* q2).^t;

然后使用此函数尝试在运动捕捉数据中进行插值:

%方法一:matlab自带函数slerp
clear
clc
close all
addpath(genpath('.'))%读取两个运动数据skel,A,B
load sample.mat
% skelPlayDataA(skel,[A;B])
%将欧拉角转换为四元数
quatA=joint_euler2quat(skel,A);
quatB=joint_euler2quat(skel,B);
%执行四元数插值,插20帧
internum=20;
temp_quat=zeros(31,4);%31个关节,每个关节一个四元数
newMotion=zeros(internum,62);%20帧,每帧62维
for i=1:internumt=i/internum;%对于角度采用四元数插值for j=1:size(quatA,1)temp_quat(j,:)=slerp(quatA(j,:),quatB(j,:),t);        endtemp_quat(find(isnan(temp_quat)))=0;temp_quat=real(temp_quat);newMotion(i,:)=joint_quat2euler(temp_quat);%对于位置采用线性插值posA=A(1,1:3);posB=B(1,1:3);newMotion(i,1:3)=(1-t)*posA+t*posB;
end
newMotion(find(isnan(newMotion)))=0;
skelPlayDataA(skel,[A;newMotion;B])

结果

这里写图片描述

第二种插值方法

Slerp的思想就是沿着4D4D4D球面上连接两个四元数的弧插值。

先看平面上的两个2D2D2D向量v0v_0v0v1v_1v1都是单位向量,我们需要计算vtv_tvt它是沿着v0v_0v0v1v_1v1弧的平滑插值。设wwwv0v_0v0vtv_tvt弧所截的角,那么vtv_tvt就是v1v_1v1沿弧旋转twtwtw的结果。
这里写图片描述

需要考虑两点问题:一是四元数qqq−q-qq代表同一方位,但是作为slerp的参数时,可能有不一样的结果,是因为4D4D4D球面不是欧式空间的直接扩展,而这种现象在2D3D2D 3D2D3D空间是不会发生的。解决方法是选择q0q_0q0q1q_1q1的符号使得点乘q0⋅q1q_0\cdot q_1q0q1的结果是非负。第二就是如果q0q_0q0q1q_1q1非常接近,sin⁡θ\sin\thetasinθ会非常小,这时除法会出现问题,解决方法是此时采用线性插值。

在论文《从运动捕获数据中提取关键帧》也有介绍到这种四元数插值方法,这里直接贴过来,有兴趣去看看论文:

q1=[w1,x1,y1,z1]q_1=[w_1,x_1,y_1,z_1]q1=[w1,x1,y1,z1]q2=[w2,x2,y2,z2]q_2=[w_2,x_2,y_2,z_2]q2=[w2,x2,y2,z2]为两个单位四元数,它们之间的球面线性插值为
slerp(q1,q2;t)=sin⁡(1−t)θsin⁡θq1+sin⁡tθsin⁡θq2slerp(q_1,q_2;t)=\frac{\sin(1-t)\theta}{\sin\theta}q_1+\frac{\sin t\theta}{\sin\theta}q_2 slerp(q1,q2;t)=sinθsin(1t)θq1+sinθsintθq2
其中θ=arccos⁡(w1w2+x1x2+y1y2+z1z2)\theta=\arccos(w_1w_2+x_1x_2+y_1y_2+z_1z_2)θ=arccos(w1w2+x1x2+y1y2+z1z2)

直接撸代码:

function [ q3 ] = jointslerp( q1, q2, t )
%SLERP quaternion slerp
%   computes the slerp of value t between quaternions q1 and q2
%https://gist.github.com/simonlynen/5349167
q1 = q1 ./ norm(q1);
q2 = q2 ./ norm(q2);one = 1.0 - eps;
d = q1'*q2;
absD = abs(d);if(absD >= one)scale0 = 1 - t;scale1 = t;
else% theta is the angle between the 2 quaternionstheta = acos(absD);sinTheta = sin(theta);scale0 = sin( ( 1.0 - t ) * theta) / sinTheta;scale1 = sin( ( t * theta) ) / sinTheta;
end
if(d < 0)scale1 = -scale1;
endq3 = scale0 * q1 + scale1 * q2;
q3 = q3 ./ norm(q3);
end

同样使用此算法对运动捕捉数据进行插值:

%第二个插值方法
clear
clc
close all
addpath(genpath('.'))%读取两个运动数据skel,A,B
load sample.mat
% skelPlayDataA(skel,[A;B])
%将欧拉角转换为四元数
quatA=joint_euler2quat(skel,A);
quatB=joint_euler2quat(skel,B);
%执行四元数插值,插20帧
internum=20;
temp_quat=zeros(31,4);%31个关节,每个关节一个四元数
newMotion=zeros(internum,62);%20帧,每帧62维
for i=1:internumt=i/internum;%对于角度采用四元数插值for j=1:size(quatA,1)temp_quat(j,:)=jointslerp(quatA(j,:)',quatB(j,:)',t);        endnewMotion(i,:)=joint_quat2euler(temp_quat);%对于位置采用线性插值posA=A(1,1:3);posB=B(1,1:3);newMotion(i,1:3)=(1-t)*posA+t*posB;
end
newMotion(find(isnan(newMotion)))=0;
skelPlayDataA(skel,[A;newMotion;B])

结果:

这里写图片描述

后记

其实之前写过类似博客,但是不是用markdown写的,排版真的好丑,我就把它们删掉,写到此博客了。代码连接:链接:https://pan.baidu.com/s/1uLadyPL8yPlQWdPpLSWVrw 密码:asph

代码也可以到我个人的CSDN上传空间去找,或者微信公众号个人简介中的GitHub。此博客已同步更新至微信公众号
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/246611.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【TensorFlow-windows】学习笔记一——基础理解

前言 因为Theano已经停止更新了&#xff0c;所以在前面学完Theano搭建RBM,CNN,RNN相关结构以后&#xff0c;还是得选择一个主流框架的&#xff0c;由于我自身的学习最终是向强化学习靠近&#xff0c;可能用到的仿真环境是openai gym&#xff0c;所以选择了继续学习TensorFlow&…

【TensorFlow-windows】学习笔记二——低级API

前言 上一篇博客初步了解了tensorflow中建立机器学习模型的方法&#xff1a;可以使用eager execution和graph execution两种模式&#xff0c;可以使用高级API estimator中已经封装好的模型&#xff0c;也可以自己创建estimator&#xff0c;更重要的是我们也可以使用低级API自行…

【TensorFlow-windows】学习笔记五——自编码器

前言 上一篇博客介绍的是构建简单的CNN去识别手写数字&#xff0c;这一篇博客折腾一下自编码&#xff0c;理论很简单&#xff0c;就是实现对输入数据的重构&#xff0c;具体理论可以看我前面的【theano-windows】学习笔记十三——去噪自编码器 国际惯例&#xff0c;参考博客&…

【TensorFlow-windows】学习笔记六——变分自编码器

#前言 对理论没兴趣的直接看代码吧&#xff0c;理论一堆&#xff0c;而且还有点复杂&#xff0c;我自己的描述也不一定准确&#xff0c;但是代码就两三句话搞定了。 国际惯例&#xff0c;参考博文 论文&#xff1a;Tutorial on Variational Autoencoders 【干货】一文读懂…

【TensorFlow-windows】学习笔记七——生成对抗网络

前言 既然学习了变分自编码(VAE)&#xff0c;那也必须来一波生成对抗网络(GAN)。 国际惯例&#xff0c;参考网址&#xff1a; 论文: Generative Adversarial Nets PPT:Generative Adversarial Networks (GANs) Generative Adversarial Nets in TensorFlow GAN原理学习笔记…

Openpose——windows编译(炒鸡简单)

前言 最近准备看看rtpose的代码&#xff0c;发现已经由openpose这个项目维护着了&#xff0c;由于经常在windows下调试代码&#xff0c;所以尝试了一下如何在windows下编译openpose源码&#xff0c;整体来说非常简单的。 国际惯例&#xff0c;参考博客&#xff1a; [OpenPos…

强化学习——Qlearning

前言 在控制决策领域里面强化学习还是占很重比例的&#xff0c;最近出了几篇角色控制的论文需要研究&#xff0c;其中部分涉及到强化学习&#xff0c;都有开源&#xff0c;有兴趣可以点开看看&#xff1a; A Deep Learning Framework For Character Motion Synthesis and Edit…

【TensorFlow-windows】keras接口学习——线性回归与简单的分类

前言 之前有写过几篇TensorFlow相关文章&#xff0c;但是用的比较底层的写法&#xff0c;比如tf.nn和tf.layers&#xff0c;也写了部分基本模型如自编码和对抗网络等&#xff0c;感觉写起来不太舒服&#xff0c;最近看官方文档发现它的教程基本都使用的keras API&#xff0c;这…

【TensorFlow-windows】keras接口——卷积手写数字识别,模型保存和调用

前言 上一节学习了以TensorFlow为底端的keras接口最简单的使用&#xff0c;这里就继续学习怎么写卷积分类模型和各种保存方法(仅保存权重、权重和网络结构同时保存) 国际惯例&#xff0c;参考博客&#xff1a; 官方教程 【注】其实不用看博客&#xff0c;直接翻到文末看我的c…

【TensorFlow-windows】keras接口——BatchNorm和ResNet

前言 之前学习利用Keras简单地堆叠卷积网络去构建分类模型的方法&#xff0c;但是对于很深的网络结构很难保证梯度在各层能够正常传播&#xff0c;经常发生梯度消失、梯度爆炸或者其它奇奇怪怪的问题。为了解决这类问题&#xff0c;大佬们想了各种办法&#xff0c;比如最原始的…

【TensorFlow-windows】keras接口——卷积核可视化

前言 在机器之心上看到了关于卷积核可视化相关理论&#xff0c;但是作者的源代码是基于fastai写的&#xff0c;而fastai的底层是pytorch&#xff0c;本来准备自己用Keras复现一遍的&#xff0c;但是尴尬地发现Keras还没玩熟练&#xff0c;随后发现了一个keras-vis包可以用于做…

【TensorFlow-windows】投影变换

前言 没什么重要的&#xff0c;就是想测试一下tensorflow的投影变换函数tf.contrib.image.transform中每个参数的含义 国际惯例&#xff0c;参考文档 官方文档 描述 调用方法与默认参数&#xff1a; tf.contrib.image.transform(images,transforms,interpolationNEAREST,…

【TensorFlow-windows】扩展层之STN

前言 读TensorFlow相关代码看到了STN的应用&#xff0c;搜索以后发现可替代池化&#xff0c;增强网络对图像变换(旋转、缩放、偏移等)的抗干扰能力&#xff0c;简单说就是提高卷积神经网络的空间不变性。 国际惯例&#xff0c;参考博客&#xff1a; 理解Spatial Transformer…

【TensorFlow-windows】MobileNet理论概览与实现

前言 轻量级神经网络中&#xff0c;比较重要的有MobileNet和ShuffleNet&#xff0c;其实还有其它的&#xff0c;比如SqueezeNet、Xception等。 本博客为MobileNet的前两个版本的理论简介与Keras中封装好的模块的对应实现方案。 国际惯例&#xff0c;参考博客&#xff1a; 纵…

【TensorFlow-windows】keras接口——ImageDataGenerator裁剪

前言 Keras中有一个图像数据处理器ImageDataGenerator&#xff0c;能够很方便地进行数据增强&#xff0c;并且从文件中批量加载图片&#xff0c;避免数据集过大时&#xff0c;一下子加载进内存会崩掉。但是从官方文档发现&#xff0c;并没有一个比较重要的图像增强方式&#x…

【TensorFlow-windows】TensorBoard可视化

前言 紧接上一篇博客&#xff0c;学习tensorboard可视化训练过程。 国际惯例&#xff0c;参考博客&#xff1a; MNIST机器学习入门 Tensorboard 详解&#xff08;上篇&#xff09; Tensorboard 可视化好帮手 2 tf-dev-summit-tensorboard-tutorial tensorflow官方mnist_…

深度学习特征归一化方法——BN、LN、IN、GN

前言 最近看到Group Normalization的论文&#xff0c;主要提到了四个特征归一化方法&#xff1a;Batch Norm、Layer Norm、Instance Norm、Group Norm。此外&#xff0c;论文还提到了Local Response Normalization(LRN)、Weight Normalization(WN)、Batch Renormalization(BR)…

【TensorFlow-windows】keras接口——利用tensorflow的方法加载数据

前言 之前使用tensorflow和keras的时候&#xff0c;都各自有一套数据读取方法&#xff0c;但是遇到一个问题就是&#xff0c;在训练的时候&#xff0c;GPU的利用率忽高忽低&#xff0c;极大可能是由于训练过程中读取每个batch数据造成的&#xff0c;所以又看了tensorflow官方的…

骨骼动画——论文与代码精读《Phase-Functioned Neural Networks for Character Control》

前言 最近一直玩CV&#xff0c;对之前学的动捕知识都忘得差不多了&#xff0c;最近要好好总结一下一直以来学习的内容&#xff0c;不能学了忘。对2017年的SIGGRAPH论文《Phase-Functioned Neural Networks for Character Control》进行一波深入剖析吧&#xff0c;结合源码。 额…

颜色协调模型Color Harmoniztion

前言 最近做换脸&#xff0c;在肤色调整的那一块&#xff0c;看到一个有意思的文章&#xff0c;复现一波玩玩。不过最后一步掉链子了&#xff0c;有兴趣的可以一起讨论把链子补上。 主要是github上大佬的那个复现代码和原文有点差异&#xff0c;而且代码复杂度过高&#xff0…