fix: IMU axis mapping for CW270 orientation (#15) #20
@ -60,21 +60,29 @@ void mpu6000_read(IMUData *data) {
|
|||||||
float az = raw.az * ACCEL_SCALE; /* g */
|
float az = raw.az * ACCEL_SCALE; /* g */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gyro axes with CW270 alignment:
|
* CW270 alignment transform (Betaflight convention, R_CW270):
|
||||||
* pitch rate = gx, roll rate = gy, yaw rate = gz
|
*
|
||||||
* Signs may need inverting depending on mounting orientation.
|
* R = [[0, 1, 0], [-1, 0, 0], [0, 0, 1]]
|
||||||
|
* board_forward = sensor_Y → pitch accel uses ay
|
||||||
|
* board_right = -sensor_X → roll accel uses -ax
|
||||||
|
* board_gx (roll rate) = sensor_gy (board_gx = R[0,·]·omega_sensor)
|
||||||
|
* board_gy (pitch rate) = -sensor_gx (board_gy = R[1,·]·omega_sensor)
|
||||||
|
* board_gz (yaw rate) = sensor_gz (unchanged)
|
||||||
|
*
|
||||||
|
* Convention: pitch+ = nose up, roll+ = right bank, yaw+ = CW from above.
|
||||||
*/
|
*/
|
||||||
float gyro_pitch_rate = raw.gx * GYRO_SCALE; /* °/s */
|
float gyro_pitch_rate = -raw.gx * GYRO_SCALE; /* °/s board_gy = -sensor_gx */
|
||||||
float gyro_roll_rate = raw.gy * GYRO_SCALE; /* °/s */
|
float gyro_roll_rate = raw.gy * GYRO_SCALE; /* °/s board_gx = sensor_gy */
|
||||||
float gyro_yaw_rate = raw.gz * GYRO_SCALE; /* °/s */
|
float gyro_yaw_rate = raw.gz * GYRO_SCALE; /* °/s unchanged */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Accel-derived pitch and roll (degrees).
|
* Accel-derived angles after CW270 transform.
|
||||||
* CW270 alignment: pitch = atan2(ax, az), roll = atan2(ay, az).
|
* board_ax (forward) = sensor_ay → pitch = atan2(ay, az)
|
||||||
|
* board_ay (right) = -sensor_ax → roll = atan2(-ax, az)
|
||||||
* Valid while total accel ≈ 1g (low linear acceleration).
|
* Valid while total accel ≈ 1g (low linear acceleration).
|
||||||
*/
|
*/
|
||||||
float accel_pitch = atan2f(ax, az) * (180.0f / 3.14159265358979f);
|
float accel_pitch = atan2f( ay, az) * (180.0f / 3.14159265358979f);
|
||||||
float accel_roll = atan2f(ay, az) * (180.0f / 3.14159265358979f);
|
float accel_roll = atan2f(-ax, az) * (180.0f / 3.14159265358979f);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Complementary filter for pitch and roll:
|
* Complementary filter for pitch and roll:
|
||||||
|
|||||||
@ -186,13 +186,15 @@ window.updateIMU = function(data) {
|
|||||||
const motorCmd = data.m || 0;
|
const motorCmd = data.m || 0;
|
||||||
const state = data.s || 0;
|
const state = data.s || 0;
|
||||||
|
|
||||||
// Three.js rotation targets (radians):
|
// Three.js rotation targets (radians) — CW270 IMU mount on MAMBA F722S:
|
||||||
// pitch → rotation.x (tipping forward/back around left-right axis)
|
// pitch → rotation.x positive = nose up (Three.js +x rotates -Z end upward ✓)
|
||||||
// roll → rotation.z (banking left/right around forward axis)
|
// roll → -rotation.z positive = right bank (Three.js +z is CCW from camera = left bank,
|
||||||
// yaw → rotation.y (spinning on vertical axis)
|
// so negate to match right-bank-positive convention)
|
||||||
|
// yaw → -rotation.y positive = CW from above (Three.js +y is CCW, sensor Z points down
|
||||||
|
// so gz+ = CW physical; negate so model spins correctly)
|
||||||
targetPitch = pitch * Math.PI / 180;
|
targetPitch = pitch * Math.PI / 180;
|
||||||
targetRoll = roll * Math.PI / 180;
|
targetRoll = -roll * Math.PI / 180; // negate: Three.js +z = left bank, we want right bank+
|
||||||
targetYaw = yaw * Math.PI / 180;
|
targetYaw = -yaw * Math.PI / 180; // negate: Three.js +y = CCW, sensor gz+ = CW
|
||||||
|
|
||||||
document.getElementById('v-pitch').textContent = pitch.toFixed(1);
|
document.getElementById('v-pitch').textContent = pitch.toFixed(1);
|
||||||
document.getElementById('v-roll').textContent = roll.toFixed(1);
|
document.getElementById('v-roll').textContent = roll.toFixed(1);
|
||||||
@ -245,8 +247,9 @@ function animate() {
|
|||||||
animate();
|
animate();
|
||||||
|
|
||||||
window.resetYaw = function() {
|
window.resetYaw = function() {
|
||||||
// Capture current firmware yaw as new zero reference
|
// Capture current raw firmware yaw (before negate) as new zero reference.
|
||||||
const currentFirmwareYaw = targetYaw * 180 / Math.PI + yawOffset;
|
// targetYaw = -(yawRaw - yawOffset) * pi/180, so yawRaw = yawOffset - targetYaw*180/pi
|
||||||
|
const currentFirmwareYaw = yawOffset - targetYaw * 180 / Math.PI;
|
||||||
yawOffset = currentFirmwareYaw;
|
yawOffset = currentFirmwareYaw;
|
||||||
targetYaw = 0;
|
targetYaw = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user