slerp插值曲线python

package test;
 
import test.EulerAngles;
import test.Quaternion;
import lombok.extern.slf4j.Slf4j;
 
import static java.lang.Math.abs;
 
/*************************************
 *Class Name: EulerAngle2QuatUtil
 *Description: <四元数与欧拉角互转>
 *@author: seminar
 *@create: 2021/5/24
 *@since 1.0.0
 *************************************/
@Slf4j
public class EulerAngle2QuatUtil {
     
 
    /**
     * 归一化
     *
     * @param x
     * @param y
     * @param z
     * @param w
     * @return
     */
    public Quaternion normalizeQuaternion(float w, float x, float y, float z) {
     
        double lengthD = 1.0f / (w * w + x * x + y * y + z * z);
        w *= lengthD;
        x *= lengthD;
        y *= lengthD;
        z *= lengthD;
        return new Quaternion(w, x, y, z);
    }
 
    /**
     * Slerp球面线性插值(Spherical Linear Interpolation)
     *
     * @param a 原始数据a
     * @param b 原始数据b
     * @param t 要插值的比例(中间插一个值1/2)
     * @return
     */
    public Quaternion makeInterpolated(Quaternion a, Quaternion b, double t) {
     
        Quaternion out = new Quaternion();
        double cosHalfTheta = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
        if (cosHalfTheta < 0.0F) {
     
            b = new Quaternion(b);
            cosHalfTheta = -cosHalfTheta;
            b.x = -b.x;
            b.y = -b.y;
            b.z = -b.z;
            b.w = -b.w;
        }
 
        double halfTheta = (double) Math.acos((double) cosHalfTheta);
        double sinHalfTheta = (double) Math.sqrt((double) (1.0F - cosHalfTheta * cosHalfTheta));
        double ratioA;
        double ratioB;
        if ((double) abs(sinHalfTheta) > 0.001D) {
     
            double oneOverSinHalfTheta = 1.0F / sinHalfTheta;
            ratioA = (double) Math.sin((double) ((1.0F - t) * halfTheta)) * oneOverSinHalfTheta;
            ratioB = (double) Math.sin((double) (t * halfTheta)) * oneOverSinHalfTheta;
        } else {
     
            ratioA = 1.0F - t;
            ratioB = t;
        }
 
        out.x = (float) (ratioA * a.x + ratioB * b.x);
        out.y = (float) (ratioA * a.y + ratioB * b.y);
        out.z = (float) (ratioA * a.z + ratioB * b.z);
        out.w = (float) (ratioA * a.w + ratioB * b.w);
 
        out = normalizeQuaternion(out.w, out.x, out.y, out.z);
        return out;
    }
 
    /**
     * 欧拉角(弧度)转四元数
     *
     * @param pitch
     * @param yaw
     * @param roll
     * @return
     */
    public Quaternion toQuaternion(double pitch, double yaw, double roll) {
     
        EulerAngles eu = new EulerAngles((float) Math.toRadians(pitch), (float) Math.toRadians(yaw), (float) Math.toRadians(roll)); // 角度转弧度
        return eu.toQuaternion();
    }
 
    /**
     * 四元数转欧拉角(弧度)
     *
     * @param quaternion
     * @return
     */
    public EulerAngles toEulerAngles(Quaternion quaternion) {
     
        return quaternion.toEulerAngles();
    }
 
    /**
     * 姿态角——即欧拉角转四元数,对俩个四元数进行球面插值,四元数转回欧拉角并返回
     *
     * @param pitch    位置一俯仰角 -180~180
     * @param yaw      位置一航向角 0~360
     * @param roll     位置一翻滚角 -180~180
     * @param pitch1   位置二俯仰角 -180~180
     * @param yaw1     位置二俯仰角 0~360°
     * @param roll1    位置二翻滚角 -180~180
     * @param t        位置一时间
     * @param t1       位置二时间
     * @param t_insert 要计算姿态角的位置对应时间
     * @return
     */
    public EulerAngles slerpInsert(float pitch, float yaw, float roll, float pitch1, float yaw1, float roll1, long t, long t1, long t_insert) {
     
        // 位置1 欧拉角转四元数
        // 位置2 欧拉角转四元数
        Quaternion p = toQuaternion(pitch, yaw, roll);
        Quaternion q = toQuaternion(pitch1, yaw1, roll1);
 
        // 计算插入的scale
        float scale = (float) ((t_insert - t) / ((t1 - t) * 1.0));
 
        // Slerp球面线性插值
        Quaternion r = makeInterpolated(q, p, scale);
 
        // 四元数转欧拉角
        EulerAngles eulerAngles = r.toEulerAngles();
        return eulerAngles;
    }
 
    public static void main(String[] args) {
     
//        示例,中间1615609866585L的插值不太对
//                         Roll    Pitch    Heading
// 1615609866544L         -0.9    -0.405   358.809
// 1615609866585L         -0.942   -0.362  314.489
// 1615609866625L         -0.956   -0.331  0.178
 
//        正确结果
//                         Roll    Pitch    Heading
// 1615609866544L         -0.9,    -0.405,  358.809
// 1615609866585L         -0.929, -0.368, 359.502
// 1615609866625L         -0.956,  -0.331,  0.178
 
        // 调用EulerAngle2QuatUtil实现姿态角插值的获取
        float roll = -0.9f, pitch = -0.405f, yaw = 358.809f;
        EulerAngle2QuatUtil eq = new EulerAngle2QuatUtil();
        Quaternion p = eq.toQuaternion(pitch, yaw, roll);
        log.info("p: {} {} {} {}", p.w, p.x, p.y, p.z);
 
        float roll1 = -0.956f, pitch1 = -0.331f, yaw1 = 0.178f;
        Quaternion q = eq.toQuaternion(pitch1, yaw1, roll1);
        log.info("q: {} {} {} {}", q.w, q.x, q.y, q.z);
 
        long t = 1615609866544L;
        long t1 = 1615609866625L;
        long t_insert = 1615609866585L;
        float scale = (float) ((t_insert - t) / ((t1 - t) * 1.0));
 
        // Slerp球面线性插值
        Quaternion r = eq.makeInterpolated(q, p, scale);
        EulerAngles eulerAngles = r.toEulerAngles();
        float roll2 = (float) Math.toDegrees(eulerAngles.roll); // 弧度转回角度
        float pitch2 = (float) Math.toDegrees(eulerAngles.pitch); // 弧度转回角度
        float heading2 = (float) (Math.toDegrees(eulerAngles.yaw) > 0 ? Math.toDegrees(eulerAngles.yaw) : Math.toDegrees(eulerAngles.yaw) + 360); // 弧度转回角度(航向角0~360°)
 
        log.info("{} {} {}", Double.parseDouble(String.format("%.3f", roll2)), Double.parseDouble(String.format("%.3f", pitch2)), Double.parseDouble(String.format("%.3f", heading2)));
 
        testSlerpInsert(pitch, yaw, roll, pitch1, yaw1, roll1, t, t1, t_insert);
 
//  0.000     -8.523      0.000
//  0.000     -0.432     93.112
        testSlerpInsert(-8.523f, 0.00f, 0.00f, -0.432f, 93.112f, 0.00f, t, t1, t_insert);
//        0.000      1.054     66.847
//        1.237     -1.956     62.336
        testSlerpInsert(1.054f, 66.847f, 0.00f, -1.956f, 62.336f, 1.237f, t, t1, t_insert);
 
//        0.411      5.393    338.058
//        0.402      5.395    338.063
        testSlerpInsert(5.393f, 338.058f, 0.411f, 5.395f, 338.063f, 0.402f, t, t1, t_insert);
    }
 
    private static void testSlerpInsert(float pitch, float yaw, float roll, float pitch1, float yaw1, float roll1, long t, long t1, long t_insert) {
     
        log.info("==================testSlerpInsert start===============");
        EulerAngle2QuatUtil eq = new EulerAngle2QuatUtil();
        EulerAngles eulerAngles = eq.slerpInsert(pitch, yaw, roll, pitch1, yaw1, roll1, t, t1, t_insert);
        float roll2 = (float) Math.toDegrees(eulerAngles.roll); // 弧度转回角度
        float pitch2 = (float) Math.toDegrees(eulerAngles.pitch); // 弧度转回角度
        float heading2 = (float) (Math.toDegrees(eulerAngles.yaw) > 0 ? Math.toDegrees(eulerAngles.yaw) : Math.toDegrees(eulerAngles.yaw) + 360); // 弧度转回角度(航向角0~360°)
 
        log.info("slerpInsert {} {} {}", Double.parseDouble(String.format("%.3f", roll2)), Double.parseDouble(String.format("%.3f", pitch2)), Double.parseDouble(String.format("%.3f", heading2)));
        log.info("==================testSlerpInsert end=================");
    }
 
    private static Quaternion getQuaternion(float roll, float pitch, float yaw) {
     
        EulerAngle2QuatUtil eq = new EulerAngle2QuatUtil();
        EulerAngles eu = new EulerAngles((float) Math.toRadians(pitch), (float) Math.toRadians(yaw), (float) Math.toRadians(roll));
        Quaternion quaternion = eu.toQuaternion();
        EulerAngles eulerAngles = quaternion.toEulerAngles();
        float roll2 = (float) Math.toDegrees(eulerAngles.roll); // 弧度转回角度
        float pitch2 = (float) Math.toDegrees(eulerAngles.pitch); // 弧度转回角度
        float heading2 = (float) (Math.toDegrees(eulerAngles.yaw) > 0 ? Math.toDegrees(eulerAngles.yaw) : Math.toDegrees(eulerAngles.yaw) + 360); // 弧度转回角度(航向角0~360°)
        log.info("toDegree: {} {} {}", Double.parseDouble(String.format("%.3f", roll2)), Double.parseDouble(String.format("%.3f", pitch2)), Double.parseDouble(String.format("%.3f", heading2)));
        return quaternion;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值