【问题标题】:Determine new GPS position using roll, pitch, yaw and length使用横滚、俯仰、偏航和长度确定新的 GPS 位置
【发布时间】:2018-09-17 14:54:02
【问题描述】:

我正在使用 Reach RS+ 设备来捕获 GPS 位置数据以及 IMU 数据(滚动、俯仰和偏航);请参阅制造商website 上的“积分收集”图片。
我正在尝试确定底点的 GPS 坐标(接收器固定在杆的空端)。
为了能够以米为单位进行计算,我将经度 (X) 和纬度 (Y) 转换为 UTM,同时保持高度 (Z) 不变。
当杆直立时,XY 保持不变,而

Z1 = Z - ROD_LENGTH  

但是,当杆倾斜时,所有坐标都会受到影响,我需要一种方法来计算杆的末端位置。
我根据实验观察查看了旋转矩阵、三角方程、我自己的 sincos 公式,但我没有 3D 几何背景,我也没有确定要走哪条路线(例如,我不知道如何将杆长度与旋转矩阵一起使用)。
基本上我需要以下公式:

X1 = X + ROD_LENGTH * func_X(roll, pitch, yaw)
Y1 = Y + ROD_LENGTH * func_Y(roll, pitch, yaw)
Z1 = Z + ROD_LENGTH * func_Z(roll, pitch, yaw)

Roll、pitch 和 yaw 的值介于 -180° 和 180° 之间。

【问题讨论】:

  • 当杆直立、完全水平且 Reach 设备 LED 指向北方时的一些参考值:roll = 0, pitch = 0, yaw = 0
  • 设备或其 RTK 库肯定已经这样做了吗?这似乎是一个非常基本的要求。如果您找不到该信息,您是否询问过制造商?
  • 不幸的是,默认情况下该设备甚至不提供 IMU 数据(我必须手动编译 RTIMULib2 并使用 TCP 流提取数据)。这个问题在制造商的待办事项列表上已经有一段时间了。
  • 其实我想你在找this!。你的系统很可能会出现云台锁定问题,所以你要么走进第四维度,要么只是告诉大家不要把棍子拿直!祝你好运。
  • vector rotating in 3D space 没有解决我关于如何在矩阵中使用长度的问题。

标签: javascript math gps angle imu


【解决方案1】:

我必须说,这比我预期的要复杂得多。我想我已经完全正确了,但是如果有任何更正,请在 cmets 中告诉我,我会尽力修复。

在查看下面的代码之前,请注意!这些假设很重要,您需要验证它们在您的情况下是否正确。有几十种(至少!)定义空间方向和位置的方法。您需要确保与您的设备对齐的主要假设是我们正在操作的空间框架。 This article 会让你明白为什么这如此重要!最明显的是我们如何标记我们的轴,哪个方向向上(正 Z,正如我在下面选择的,但如果我们谈论潜艇,例如,我们可能会选择负 Z)。

  1. 框架假设:想象一架飞机(我知道它不是飞机,但这样更容易解释),一根长杆直下垂。我们将 Z 轴定义为向上(正)和向下(负)。 X 轴指向前(正)和后(负)。 Y 轴是围绕机翼的旋转轴,左翼为正,右翼为负 - 这是“right handed coordinate system”。因此,轴相交在飞机的中间,大致位于机翼连接的位置。旋转被定义为绕轴逆时针为正角,顺时针为负角。所以...

    • “偏航”表示绝对航向的变化(因此,即使您被俯仰和滚动,这也是您所指向的相对于地球实际的方向。
    • “pitch”表示机翼周围的角度 - 基本上是鼻子朝上还是朝下。
    • “横滚”代表飞机的倾斜 - 机翼轴线是平行于地球表面还是围绕机身倾斜。

    正确处理这一切很重要,尤其是与您的角度相关的符号 (+/-) - 尝试将其倾斜并滚动大约 30 度,并确保结果与输出一致 - 否则更改符号角度。对于偏航,您将需要更改航向以及俯仰和滚动,因为航向本身不会影响杆末端的位置,如果它是直线向上和向下的。 您描述“飞机”的数据是位置(三个数字),位于与上述相同的 XYZ 框架中,以及三个角度(以度为单位,-180 到 180),如上所述。

  2. 设备假设:这些是您可能需要与供应商核实的事项。如果这些数字相对于预期(或允许)的 GPS 误差较小,则可能无关紧要。
    • 代码假定所有轴都在设备底部相交,并且杆从该点垂直向下悬挂。例如,如果杆长 2 米,并且轴实际上在连接点上方 3 厘米处相交,则可以将杆长度调整为 2.03 米。如果杆实际上连接到轴相交点下方不完全的点,则需要稍微更改软件以说明末端不在其正下方。同样,从宏观角度来看,几毫米对您来说可能并不重要。
    • 代码假定设备声称的位置实际上是轴相交的位置。如果没有,您将需要调整该位置(或更改软件以允许该位置)。
    • 您需要以与设备位置相同的单位指定杆长度。
  3. 其他假设:
    • 这不涉及地球曲率 - 除非您的杆非常长,否则这应该无关紧要,如果您将其竖直(或几乎如此)举起,则根本不重要。

代码:

我留下了一些不必要的东西(如果你需要重组它,你可能需要这些东西)并且也没有尝试让它更有效(例如不断重新计算相同的正弦和余弦)来使它清楚一点。我留下了闭包编译器类型,既是为了一些文档,也是为了以后你想缩小它。 rodloc是你想要的功能……

function presentresult(location, length, yaw, pitch, roll) {
    console.log("Starting point");
    console.log(location);
    console.log("Rod length = " + length);
    console.log("Yaw = " + yaw + ", Pitch = " + pitch + ", Roll = " + roll);
    console.log("Result:");
    console.log(rodloc(location, length, yaw, pitch, roll));
}

presentresult([100, 100, 100], 2, 0, 0, 0); // Result:  [100, 100, 98] (3)
presentresult([100, 100, 100], 2, 30, 0, 0); // Result:  [100, 100, 98] (3)
presentresult([100, 100, 100], 2, -30, 0, 0); // Result:  [100, 100, 98] (3)
presentresult([100, 100, 100], 2, 0, 30, 0); // Result:  [99, 100, 98.26794919243112] (3)
presentresult([100, 100, 100], 2, 0, -30, 0); // Result:  [101, 100, 98.26794919243112] (3)
presentresult([100, 100, 100], 2, 0, 0, 30); // Result:  [100, 101, 98.26794919243112] (3)
presentresult([100, 100, 100], 2, 0, 0, -30); // Result:  [100, 99, 98.26794919243112] (3)
presentresult([100, 100, 100], 2, 30, 30, 30); // Result:  [98.75, 100.43301270189222, 98.5] (3)
presentresult([100, 100, 100], 2, -30, -30, -30); // Result:  [100.25, 98.70096189432334, 98.5] (3)
presentresult([100, 100, 100], 2, -30, 30, -30); // Result:  [98.75, 99.56698729810778, 98.5] (3)

/** @typedef {Array<number,number,number>} */ var Vector3D;
/** @typedef {Array<Vector3D,vector3D,Vector3D>} */ var Matrix3D;

/**
 * @param {Vector3D} location - The location (3 coordinates) of the "plane"
 * @param {number} length - The length of the rod
 * @param {number} yaw - the yaw (heading) in degrees
 * @param {number} pitch - the pitch in degrees
 * @param {number} roll - the roll in degrees
 * @returns {Vector3D} - the location of the end of the rod
 */
function rodloc(location, length, yaw, pitch, roll) {
    let ryaw = yaw * Math.PI / 180.0;       // Convert to radians
    let rpitch = pitch * Math.PI / 180.0;
    let rroll = roll * Math.PI / 180.0;

    // This is where our axes start
    let x = [1, 0, 0];
    let y = [0, 1, 0];
    let z = [0, 0, 1];

    // NOTE:  ORDER MATTERS - your data may mean different things (see
    //        assumptions in answer!
    // Rotate axes around z by yaw
    let yprime = rotatearound([0, 1, 0], [0, 0, 1], ryaw);
    let xprime = rotatearound([1, 0, 0], [0, 0, 1], ryaw);
    let zprime = z;     // rotating around itself

    // Next we need to rotate for pitch (around the Y axis...)
    let x2prime = rotatearound(xprime, yprime, rpitch); 
    let y2prime = yprime; // dont need this
    let z2prime = rotatearound(zprime, yprime, rpitch);

    // Now we need to roll around the new x axis...
    let x3prime = x2prime   // dont need this
    let y3prime = rotatearound(y2prime, x2prime, rroll); // dont need this
    let z3prime = rotatearound(z2prime, x2prime, rroll);

    // now take what started out as [0, 0, 1] and place the end of the rod
    // (at what started out as [0, 0, -length])
    let rotend = [0,1,2].map(n=>-length*z3prime[n]);

    // now take that and add it to the original location of the plane 
    // and return it as the result
    return [0,1,2].map(n=>location[n]+rotend[n]);
}

/** Multiply a vector times a matrix
 * @param {Vector3D} offset - The vector of the offset
 * @param {Matrix3D} rotate - The rotation vector
 * @returns {Vector3D} - The new offset vector
 */
function vmmult(offset, rotate) {
    return [0,1,2].map(x=>xmult(offset,rotate[x]));
}

/** dot product of two vectors
 * @param {Vector3D} col
 * @param {Vector3D} row
 * @returns {number}
 */
function xmult(col, row) {
    return [0,1,2].reduce((a,c)=>a+col[c]*row[c],0);
}

/** Rotate a point around a vector projecting from the origin
 * @param {Vector3D} point - the we want to rotate
 * @param {Vector3D} vec - the vector (from origin to here) to rotate around
 * @param {number} angle - the angle (in radians) to rotate
 * @returns {Vector3D} - the new point location
 */
function rotatearound(point, vec, angle) {
    let rotmat = setuprotationmatrix(angle, vec);
    return vmmult(point, rotmat);
}

/**
 * Adapted from C courtesy of Bibek Subedi
 * https://www.programming-techniques.com/2012/03/3d-rotation-algorithm-about-arbitrary.html
 * @param {number} angle - the angle to rotate around the vector
 * @param {Vector3D} vec - the vector around which to rotate
 * @returns {Matrix3D} - the rotation matrix
 */
function setuprotationmatrix(angle, vec) {
    // Leaving L in for reusability, but it should always be 1 in our case
    let u = vec[0], v = vec[1], w = vec[2]; 
    let L = (u*u + v * v + w * w);
    let u2 = u * u;
    let v2 = v * v;
    let w2 = w * w; 

    let rotmat = [[],[],[]];
    rotmat[0][0] = (u2 + (v2 + w2) * Math.cos(angle)) / L;
    rotmat[0][1] = (u * v * (1 - Math.cos(angle)) - w * Math.sqrt(L) * Math.sin(angle)) / L;
    rotmat[0][2] = (u * w * (1 - Math.cos(angle)) + v * Math.sqrt(L) * Math.sin(angle)) / L;

    rotmat[1][0] = (u * v * (1 - Math.cos(angle)) + w * Math.sqrt(L) * Math.sin(angle)) / L;
    rotmat[1][1] = (v2 + (u2 + w2) * Math.cos(angle)) / L;
    rotmat[1][2] = (v * w * (1 - Math.cos(angle)) - u * Math.sqrt(L) * Math.sin(angle)) / L;

    rotmat[2][0] = (u * w * (1 - Math.cos(angle)) - v * Math.sqrt(L) * Math.sin(angle)) / L;
    rotmat[2][1] = (v * w * (1 - Math.cos(angle)) + u * Math.sqrt(L) * Math.sin(angle)) / L;
    rotmat[2][2] = (w2 + (u2 + v2) * Math.cos(angle)) / L;
    return rotmat;
} 

【讨论】:

  • 嘿,非常感谢您为这个详细的答案付出的努力!目前我正在用我自己的解决方案测试您的解决方案(基于three.js - 我将在此处发布该解决方案的代码作为答案)。
  • 太棒了!尘埃落定后给我发消息告诉我结果如何!
【解决方案2】:

目前我正在测试一个基于 three.js 的解决方案,该解决方案的工作原理如下:

function getCorrectedPosition(x, y, z, dist, roll, pitch, yaw) {
    let matrix = new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(toRadians(pitch), toRadians(roll), toRadians(yaw)));
    let moveVector = new THREE.Vector3(0, 0, -dist);
    moveVector.applyMatrix4(matrix);
    let position = new THREE.Vector3(z, y, x).add(moveVector);
    return [position.x, position.y, position.z]
}

我会在获得结果后发布更新。

【讨论】:

    【解决方案3】:

    我不认为您的问题恰好是云台锁定,因为您说杆直立时X和Y相同。您确定将 IMU 放置在与地面平行的位置吗?我建议首先尝试仅测量 IMU 的结果,当您确定它给出准确的结果时,然后将其与 RS 连接。

    如果您想测量 GPS 坐标和偏航俯仰滚动,那么我建议您使用 GPS 模块和 MPU-6050 与 Arduino mini。它小到可以挂在任何东西上,而且与非常昂贵的 RS+ 相比也便宜很多。此外,有了这样的小工具,您会发现比使用 RS+ 更多的支持。

    【讨论】:

    • 我从 IMU 获得的值是正常的(我也执行了一些校准步骤)。
    • 我不寻找其他硬件仅仅是因为 GPS 读数需要比普通接收器提供的更准确;对于不太准确的接收器,滚动俯仰计算将不相关。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多