我想使用 IMU(加速度计和陀螺仪)读数通过神经网络计算姿态。输入为
input_shape = (time steps, 6)
,输出为四元数形式 output_shape = (time steps,4)
。
根据数学计算,参考四元数与预测四元数之间的误差为
y_pred[i,]=w0,x0,y0,z0
y_true[i,]=w1,x1,y1,z1
w = w0*w1 - x0*x1 - y0*y1 - z0*z1
x = w0*x1 + x0*w1 + y0*z1 - z0*y1
y = w0*y1 - x0*z1 + y0*w1 + z0*z1
z = w0*z1 + x0*y1 - y0*x1 + z0*w1
error_quaternion = [w, x, y, z]
为了最小化误差,误差四元数 (w) 的缩放部分必须最小化。 (请忽略数学) 因此,为了达到最佳预测,必须最小化
w
(w 是预测姿态和参考姿态之间的最短角度)-
参考 = {Markley、F. Landis 和 John L. Crassidis。基本原理 航天器姿态确定与控制。卷。 1286.纽约, 美国纽约::Springer 纽约,2014 年。}
我写了这个损失函数
def LossQuat2(y_true, y_pred):
a, b = y_true.get_shape()
error = []
for i in range(a):
w0,x0,y0,z0 = tf.unstack(y_pred[i,])
w1,x1,y1,z1 = tf.unstack(y_true[i,])
x1 = -x1
y1 = -y1
z1 = -z1
w = w0*w1 - x0*x1 - y0*y1 - z0*z1
error.append(2*tf.math.acos(K.clip(tf.math.sqrt(w*w), -1., 1.)))
return tf.reduce_mean(error)
为了验证它确实计算了错误,我尝试了这段代码并精确计算了错误
w0,x0,y0,z0 = y_pred[i,]
w1,x1,y1,z1 = y_true[i,]
x1 = -x1
y1 = -y1
z1 = -z1
w = w0*w1 - x0*x1 - y0*y1 - z0*z1
error = 2*math.acos(K.clip(np.sqrt(w*w), -1., 1.))
但是使用这个损失函数训练模型后,输出误差比MSE损失函数大得多。而且,它比 MSE 太慢了。
更新:
数学
四元数:
四元数是具有 4 个元素的态度表示 q=[w x y z]
w
是标量部分或实部
x y z
是矢量部分或虚部
此外,四元数可以写成:
q = [cos(theta/2) e*sin(theta/2)] , e is a unit vector (e=[i j k]
四元数逆
四元数逆或四元数共轭可以通过以下方式计算:
quaternion = [w x y z]
inverse(quaternion) = [w -x -y -z]
四元数乘法
为了找到估计姿态和真实(参考)姿态之间的差异,必须使用四元数乘法将估计姿态(NN 输出)乘以四元数参考。
四元数乘法:
q_m = q1 * inverse(q2)
或
q_m = q2 * inverse(q1)
两者是一样的。
如果
q1=w0,x0,y0,z0
q2=w1,x1,y1,z1
然后
q_m = [w x y z]
可以通过以下方式计算:
w = w0*w1 - x0*x1 - y0*y1 - z0*z1
x = w0*x1 + x0*w1 + y0*z1 - z0*y1
y = w0*y1 - x0*z1 + y0*w1 + z0*z1
z = w0*z1 + x0*y1 - y0*x1 + z0*w1
q1 和 q2 之间的最短角度是 theta:
Theta = 2*acos(sqrt(w*w))
我需要的是写一个损失函数来最小化
theta
,如果theta=0,w
将等于1,所以,最优的q_m是:
q_m=[1 0 0 0]
非常感谢大卫哈里斯@david-harris:
def loss(y_true, y_pred):
z = y_true * y_pred * tf.constant([[1., -1., -1., -1.]])
wtot = tf.reduce_sum(z, axis=1)
return tf.reduce_mean(2*tf.math.acos(tf.math.sqrt(wtot*wtot)))
它快得多,但似乎它减少了四元数的所有值,因此它无法正常工作。
**
**
根据大卫建议的代码,我写了这个:
def loss(y_true, y_pred):
z = y_true * (y_pred * tf.constant([1., -1., -1., -1.000000000]))
wtot = tf.reduce_sum(z,1)
return tf.reduce_mean(2*tf.math.acos(K.clip(tf.math.sqrt(wtot*wtot), -1.,1.)))
这段代码减少了损失,但 MSE 呈指数增长。我知道这段代码不会直接针对 MSE 进行优化,但由于数学原因,MSE 也必须降低。 10 个纪元后
loss: 0.0124 - mse: 227.4045
橙色=参考
蓝色 = 由 NN 估计
您应该能够使用这种方法对计算进行矢量化(并加速)。 (我不确定所有标志都正确 - 不明白为什么你的行 'x1 = -x1' 在那里。我已经暂时删除了 'clip' 部分,如果你想要它)
def loss(y_true, y_pred):
z = y_true * y_pred * tf.constant([[1., -1., -1., -1.]])
wtot = tf.reduce_sum(z, axis=1)
return tf.reduce_mean(2*tf.math.acos(tf.math.sqrt(wtot*wtot)))
看不出数学错误是什么,抱歉
你的公式有错误:
y = w0*y1 - x0*z1 + y0*w1 + z0*z1
应该是这样
y = w0*y1 - x0*z1 + y0*w1 + z0*x1