我必须说,事实证明这比我预期的要复杂得多。我想我已经完全正确了,但是请在任何更正的评论中告诉我,我会尝试修复。
在查看下面的代码之前,请注意!这些假设很重要,您需要验证它们在您的情况下是否正确。有几十种(至少!)定义空间方向和位置的方法。您需要确保与您的设备对齐的主要假设是我们正在操作的空间框架。 本文将让您了解为什么这如此重要!最明显的是我们如何标记我们的轴,哪个方向是向上的(正 Z,正如我在下面选择的,但如果我们谈论潜艇,例如,我们可能会选择负 Z)。
框架假设:想象一架飞机(我知道它不是飞机,但这样解释更容易),一根长杆直下垂。我们将 Z 轴定义为向上(正)和向下(负)。X 轴指向前(正)和后(负)。Y 轴是围绕机翼的旋转轴,左翼为正,右翼为负 - 这是一个“右手坐标系”。因此,轴相交在飞机的中间,大致位于机翼连接的位置。旋转被定义为绕轴逆时针为正角,顺时针为负角。所以...
- “偏航”表示绝对航向的变化(因此,即使您被俯仰和滚动,这也是您相对于地球实际指向的方向。
- “俯仰”代表机翼周围的角度——基本上是鼻子是朝上还是朝下。
- “横滚”代表飞机的倾斜 - 因此机翼轴线是平行于地球表面还是围绕机身倾斜。
正确处理这一切很重要,尤其是与角度相关的符号 (+/-) - 尝试将其倾斜并滚动约 30 度,并确保结果与输出一致 - 否则更改角度的符号。对于偏航,您将需要更改航向以及俯仰和滚动,因为航向本身不会影响杆末端的位置,如果它是直线向上和向下的。您描述“飞机”的数据是位置(三个数字),在与上述相同的 XYZ 框架中,以及三个角度(以度为单位,-180 到 180),如上所述。
- 设备假设:这些是您可能需要与供应商核实的事项。如果这些数字相对于预期(或允许)的 GPS 误差较小,则可能无关紧要。
- 代码假定轴都在设备底部相交,并且杆从该点垂直向下悬挂。例如,如果杆长 2 米,并且轴实际上在连接点上方 3 厘米处相交,则可以将杆长度调整为 2.03 米。如果杆实际上连接到轴相交点下方不完全的点,则需要稍微更改软件以说明末端不在其正下方。同样,在宏伟的计划中,几毫米对您来说可能并不重要。
- 该代码假定设备声称的位置实际上是轴相交的位置。如果没有,您将需要调整该位置(或更改软件以允许该位置)。
- 您需要以与设备位置相同的单位指定杆长度。
- 其他假设:
- 这不涉及地球曲率 - 除非您的杆异常长,否则这无关紧要,如果您将其笔直(或几乎如此)举起,则根本无关紧要。
编码:
我留下了一些不必要的东西(如果你需要重组它,你可能需要这些东西)并且也没有试图让它更有效(例如不断重新计算相同的正弦和余弦)以使它更清晰一点. 我留下了闭包编译器类型,既是为了一些文档,也是为了以后你想缩小它。 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;
}