void updateBoardAlignment(int16_t roll, int16_t pitch) { const float sinAlignYaw = sin_approx(DECIDEGREES_TO_RADIANS(boardAlignment()->yawDeciDegrees)); const float cosAlignYaw = cos_approx(DECIDEGREES_TO_RADIANS(boardAlignment()->yawDeciDegrees)); boardAlignmentMutable()->rollDeciDegrees += -sinAlignYaw * pitch + cosAlignYaw * roll; boardAlignmentMutable()->pitchDeciDegrees += cosAlignYaw * pitch + sinAlignYaw * roll; initBoardAlignment(); }
static void imuCalculateEstimatedAttitude(float dT) { float courseOverGround = 0; int accWeight = 0; bool useMag = false; bool useCOG = false; accWeight = imuCalculateAccelerometerConfidence(); if (sensors(SENSOR_MAG) && isMagnetometerHealthy()) { useMag = true; } #if defined(GPS) else if (STATE(FIXED_WING) && sensors(SENSOR_GPS) && STATE(GPS_FIX) && gpsSol.numSat >= 5 && gpsSol.groundSpeed >= 300) { // In case of a fixed-wing aircraft we can use GPS course over ground to correct heading if (gpsHeadingInitialized) { courseOverGround = DECIDEGREES_TO_RADIANS(gpsSol.groundCourse); useCOG = true; } else { // Re-initialize quaternion from known Roll, Pitch and GPS heading imuComputeQuaternionFromRPY(attitude.values.roll, attitude.values.pitch, gpsSol.groundCourse); gpsHeadingInitialized = true; } } #endif imuMahonyAHRSupdate(dT, imuMeasuredRotationBF.A[X], imuMeasuredRotationBF.A[Y], imuMeasuredRotationBF.A[Z], accWeight, imuMeasuredGravityBF.A[X], imuMeasuredGravityBF.A[Y], imuMeasuredGravityBF.A[Z], useMag, magADC[X], magADC[Y], magADC[Z], useCOG, courseOverGround); imuUpdateEulerAngles(); }
void initBoardAlignment(void) { if (isBoardAlignmentStandard(boardAlignment())) { standardBoardAlignment = true; } else { fp_angles_t rotationAngles; standardBoardAlignment = false; rotationAngles.angles.roll = DECIDEGREES_TO_RADIANS(boardAlignment()->rollDeciDegrees ); rotationAngles.angles.pitch = DECIDEGREES_TO_RADIANS(boardAlignment()->pitchDeciDegrees); rotationAngles.angles.yaw = DECIDEGREES_TO_RADIANS(boardAlignment()->yawDeciDegrees ); rotationMatrixFromAngles(&boardRotMatrix, &rotationAngles); } }
static void imuCalculateEstimatedAttitude(void) { static biquad_t accLPFState[3]; static bool accStateIsSet; static uint32_t previousIMUUpdateTime; float rawYawError = 0; int32_t axis; bool useAcc = false; bool useMag = false; bool useYaw = false; uint32_t currentTime = micros(); uint32_t deltaT = currentTime - previousIMUUpdateTime; previousIMUUpdateTime = currentTime; // Smooth and use only valid accelerometer readings for (axis = 0; axis < 3; axis++) { if (imuRuntimeConfig->acc_cut_hz) { if (accStateIsSet) { accSmooth[axis] = lrintf(applyBiQuadFilter((float) accADC[axis], &accLPFState[axis])); } else { for (axis = 0; axis < 3; axis++) BiQuadNewLpf(imuRuntimeConfig->acc_cut_hz, &accLPFState[axis], 1000); accStateIsSet = true; } } else { accSmooth[axis] = accADC[axis]; } } if (imuIsAccelerometerHealthy()) { useAcc = true; } if (sensors(SENSOR_MAG) && isMagnetometerHealthy()) { useMag = true; } #if defined(GPS) else if (STATE(FIXED_WING) && sensors(SENSOR_GPS) && STATE(GPS_FIX) && GPS_numSat >= 5 && GPS_speed >= 300) { // In case of a fixed-wing aircraft we can use GPS course over ground to correct heading rawYawError = DECIDEGREES_TO_RADIANS(attitude.values.yaw - GPS_ground_course); useYaw = true; } #endif imuMahonyAHRSupdate(deltaT * 1e-6f, gyroADC[X] * gyroScale, gyroADC[Y] * gyroScale, gyroADC[Z] * gyroScale, useAcc, accSmooth[X], accSmooth[Y], accSmooth[Z], useMag, magADC[X], magADC[Y], magADC[Z], useYaw, rawYawError); imuUpdateEulerAngles(); imuCalculateAcceleration(deltaT); // rotate acc vector into earth frame }
void mavlinkSendAttitude(void) { uint16_t msgLength; mavlink_msg_attitude_pack(0, 200, &mavMsg, // time_boot_ms Timestamp (milliseconds since system boot) millis(), // roll Roll angle (rad) DECIDEGREES_TO_RADIANS(attitude.values.roll), // pitch Pitch angle (rad) DECIDEGREES_TO_RADIANS(-attitude.values.pitch), // yaw Yaw angle (rad) DECIDEGREES_TO_RADIANS(attitude.values.yaw), // rollspeed Roll angular speed (rad/s) 0, // pitchspeed Pitch angular speed (rad/s) 0, // yawspeed Yaw angular speed (rad/s) 0); msgLength = mavlink_msg_to_send_buffer(mavBuffer, &mavMsg); mavlinkSerialWrite(mavBuffer, msgLength); }
void applyFixedWingPitchRollThrottleController(navigationFSMStateFlags_t navStateFlags, uint32_t currentTime) { int16_t pitchCorrection = 0; // >0 climb, <0 dive int16_t rollCorrection = 0; // >0 right, <0 left int16_t throttleCorrection = 0; // raw throttle int16_t minThrottleCorrection = posControl.navConfig->fw_min_throttle - posControl.navConfig->fw_cruise_throttle; int16_t maxThrottleCorrection = posControl.navConfig->fw_max_throttle - posControl.navConfig->fw_cruise_throttle; // Mix Pitch/Roll/Throttle if (isPitchAdjustmentValid && (navStateFlags & NAV_CTL_ALT)) { pitchCorrection += posControl.rcAdjustment[PITCH]; throttleCorrection += DECIDEGREES_TO_DEGREES(posControl.rcAdjustment[PITCH]) * posControl.navConfig->fw_pitch_to_throttle; throttleCorrection = constrain(throttleCorrection, minThrottleCorrection, maxThrottleCorrection); } if (isRollAdjustmentValid && (navStateFlags & NAV_CTL_POS)) { pitchCorrection += ABS(posControl.rcAdjustment[ROLL]) * (posControl.navConfig->fw_roll_to_pitch / 100.0f); rollCorrection += posControl.rcAdjustment[ROLL]; } // Speed controller - only apply in POS mode if (navStateFlags & NAV_CTL_POS) { throttleCorrection += applyFixedWingMinSpeedController(currentTime); throttleCorrection = constrain(throttleCorrection, minThrottleCorrection, maxThrottleCorrection); } // Limit and apply if (isPitchAdjustmentValid && (navStateFlags & NAV_CTL_ALT)) { // PITCH angle is measured in opposite direction ( >0 - dive, <0 - climb) pitchCorrection = constrain(pitchCorrection, -DEGREES_TO_CENTIDEGREES(posControl.navConfig->fw_max_dive_angle), DEGREES_TO_CENTIDEGREES(posControl.navConfig->fw_max_climb_angle)); rcCommand[PITCH] = -pidAngleToRcCommand(pitchCorrection, posControl.pidProfile->max_angle_inclination[FD_PITCH]); } if (isRollAdjustmentValid && (navStateFlags & NAV_CTL_POS)) { rollCorrection = constrain(rollCorrection, -DEGREES_TO_CENTIDEGREES(posControl.navConfig->fw_max_bank_angle), DEGREES_TO_CENTIDEGREES(posControl.navConfig->fw_max_bank_angle)); rcCommand[ROLL] = pidAngleToRcCommand(rollCorrection, posControl.pidProfile->max_angle_inclination[FD_ROLL]); // Calculate coordinated turn rate based on velocity and banking angle if (posControl.actualState.velXY >= 300.0f) { float targetYawRateDPS = RADIANS_TO_DEGREES(tan_approx(DECIDEGREES_TO_RADIANS(-rollCorrection)) * GRAVITY_CMSS / posControl.actualState.velXY); rcCommand[YAW] = pidRateToRcCommand(targetYawRateDPS, currentControlRateProfile->rates[FD_YAW]); } else { rcCommand[YAW] = 0; } } if ((navStateFlags & NAV_CTL_ALT) || (navStateFlags & NAV_CTL_POS)) { uint16_t correctedThrottleValue = constrain(posControl.navConfig->fw_cruise_throttle + throttleCorrection, posControl.navConfig->fw_min_throttle, posControl.navConfig->fw_max_throttle); rcCommand[THROTTLE] = constrain(correctedThrottleValue, posControl.escAndServoConfig->minthrottle, posControl.escAndServoConfig->maxthrottle); } }
STATIC_UNIT_TESTED void imuComputeQuaternionFromRPY(int16_t initialRoll, int16_t initialPitch, int16_t initialYaw) { if (initialRoll > 1800) initialRoll -= 3600; if (initialPitch > 1800) initialPitch -= 3600; if (initialYaw > 1800) initialYaw -= 3600; float cosRoll = cos_approx(DECIDEGREES_TO_RADIANS(initialRoll) * 0.5f); float sinRoll = sin_approx(DECIDEGREES_TO_RADIANS(initialRoll) * 0.5f); float cosPitch = cos_approx(DECIDEGREES_TO_RADIANS(initialPitch) * 0.5f); float sinPitch = sin_approx(DECIDEGREES_TO_RADIANS(initialPitch) * 0.5f); float cosYaw = cos_approx(DECIDEGREES_TO_RADIANS(-initialYaw) * 0.5f); float sinYaw = sin_approx(DECIDEGREES_TO_RADIANS(-initialYaw) * 0.5f); q0 = cosRoll * cosPitch * cosYaw + sinRoll * sinPitch * sinYaw; q1 = sinRoll * cosPitch * cosYaw - cosRoll * sinPitch * sinYaw; q2 = cosRoll * sinPitch * cosYaw + sinRoll * cosPitch * sinYaw; q3 = cosRoll * cosPitch * sinYaw - sinRoll * sinPitch * cosYaw; imuComputeRotationMatrix(); }