Cesiumjs 从向量计算俯仰、偏航、航向

Arm*_*enB 6 javascript rotation linear-algebra quaternions cesiumjs

我将 Cesium 的模型之一加载到场景中,并且我想使用两个点来计算模型的方向,这是我创建的函数。

// calculate the direction which the model is facing
calculateOrientation({ position, nextPosition }) {

    let dir = new Cesium.Cartesian3();
    let normalizedDir = new Cesium.Cartesian3();

    Cesium.Cartesian3.subtract(nextPosition, position, dir);
    Cesium.Cartesian3.normalize(dir, normalizedDir);

    var heading = Math.acos(normalizedDir.x);
    var pitch = Math.acos(normalizedDir.y);
    var roll = 0;

    var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
    var orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr);
    return orientation;
}
Run Code Online (Sandbox Code Playgroud)

但我得到的轮换没有任何意义。我的数学错了吗?

更新

在@Keshet 给出第一个答案后,我查找了如何找到平面和向量之间的角度。我想如果我找到每个平面的法线和 -90 之间的角度,我应该得到正确的角度,但我不确定这是否正确。

另外我不知道Cesium Axis是如何工作的,也找不到任何描述它的文档。例如XY平面等。

    let dir = new Cesium.Cartesian3();
    let xyNormal = new Cesium.Cartesian3(0,0,1);
    let xzNormal = new Cesium.Cartesian3(0,1,0);
    let yzNormal = new Cesium.Cartesian3(1,0,0);

    Cesium.Cartesian3.subtract(nextPosition, position, dir);

    let xyAngle = Cesium.Math.PI_OVER_TWO - Cesium.Cartesian3.angleBetween(dir, xyNormal);
    let xzAngle = Cesium.Math.PI_OVER_TWO - Cesium.Cartesian3.angleBetween(dir, xzNormal);
    let yzAngle = Cesium.Math.PI_OVER_TWO - Cesium.Cartesian3.angleBetween(dir, yzNormal);
Run Code Online (Sandbox Code Playgroud)

更新2

遵循使用 atan2 的 @IIan 建议,代码如下:

    Cesium.Cartesian3.subtract(position, nextPosition, dir);

    // create the mapped to plane vectors, and get the 
    // normalized versions
    let xyMappedVectorNormalized = new Cesium.Cartesian3(0, 0, 0);
    let xyMappedVector = new Cesium.Cartesian3(dir.x, dir.y, 0);

    let xzMappedVectorNormalized = new Cesium.Cartesian3(0, 0, 0);
    let xzMappedVector = new Cesium.Cartesian3(dir.x, 0, dir.z);

    let yzMappedVectorNormalized = new Cesium.Cartesian3(0, 0, 0);
    let yzMappedVector = new Cesium.Cartesian3(0, dir.y, dir.z);

    Cesium.Cartesian3.normalize(xyMappedVector, xyMappedVectorNormalized);
    Cesium.Cartesian3.normalize(xzMappedVector, xzMappedVectorNormalized);
    Cesium.Cartesian3.normalize(yzMappedVector, yzMappedVectorNormalized);

    // calculate the angles
    let xyAngle = Math.atan2(xyMappedVectorNormalized.y, xyMappedVectorNormalized.x);
    let xzAngle = Math.atan2(xzMappedVectorNormalized.z, xzMappedVectorNormalized.x);
    let yzAngle = Math.atan2(yzMappedVectorNormalized.z, yzMappedVectorNormalized.y);
Run Code Online (Sandbox Code Playgroud)

Ila*_*het 0

首先,我们需要解释一下航向角、俯仰角和横滚角代表什么。

  1. 航向角表示相对于 XY 平面的角度(以弧度表示)
  2. 俯仰角表示相对于 XZ 平面的角度(以弧度表示)
  3. 滚动角表示相对于 YZ 平面的角度(以弧度表示)

您不能简单地使用 x / y 来计算航向/俯仰

var heading = Math.acos(normalizedDir.x);
var pitch = Math.acos(normalizedDir.y);
Run Code Online (Sandbox Code Playgroud)

您需要获得每个平面上的总角度。

在 XY 平面上,您将使用归一化的 |(X, Y)| 在 XZ 平面上,您将使用归一化 |(X, Z)| 在 YZ 平面上,您将使用归一化 |(Y, Z)|


更新

|(X,Y)| 表示单位圆上的一个点。

其中 (sin(theta), cos(theta)) = |(X, Y)|

即当 theta = 0 时,|(X, Y)| = (1, 0) 当 theta = PI/2 时,|(X, Y)| = (0, 1)

这个 theta 将是您用于航向的角度


然后,您可以调用 arctan2 函数来计算相对于平面的角度。

atan2(y, x) = theta 其中 [x, y] 是使用相应的归一化二维向量从上面计算出来的

注意:atan2 给出以 (-PI, PI] 为界的角度

例子

如果您的 3D 矢量是 (1, 2, 3) - 在 XY 平面上 X = 1,Y = 2。

那么如果你标准化 (1, 2) => (1 / sqrt(5), 2 / sqrt(5))

然后您可以使用 atan2(2 / sqrt(5), 1 / sqrt(5) 计算航向的角度(以弧度为单位)