/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* * AP_MotorsMatrix.cpp - ArduCopter motors library * Code by RandyMackay. DIYDrones.com * */ #include #include "AP_MotorsMatrix.h" extern const AP_HAL::HAL& hal; // init void AP_MotorsMatrix::init(motor_frame_class frame_class, motor_frame_type frame_type) { // record requested frame class and type _last_frame_class = frame_class; _last_frame_type = frame_type; // setup the motors setup_motors(frame_class, frame_type); // enable fast channels or instant pwm set_update_rate(_speed_hz); } // set update rate to motors - a value in hertz void AP_MotorsMatrix::set_update_rate( uint16_t speed_hz ) { // record requested speed _speed_hz = speed_hz; uint16_t mask = 0; for (uint8_t i=0; i= throttle_thrust_max) { throttle_thrust = throttle_thrust_max; limit.throttle_upper = true; } // ensure that throttle_avg_max is between the input throttle and the maximum throttle throttle_avg_max = constrain_float(throttle_avg_max, throttle_thrust, throttle_thrust_max); // calculate throttle that gives most possible room for yaw which is the lower of: // 1. 0.5f - (rpy_low+rpy_high)/2.0 - this would give the maximum possible margin above the highest motor and below the lowest // 2. the higher of: // a) the pilot's throttle input // b) the point _throttle_rpy_mix between the pilot's input throttle and hover-throttle // Situation #2 ensure we never increase the throttle above hover throttle unless the pilot has commanded this. // Situation #2b allows us to raise the throttle above what the pilot commanded but not so far that it would actually cause the copter to rise. // We will choose #1 (the best throttle for yaw control) if that means reducing throttle to the motors (i.e. we favor reducing throttle *because* it provides better yaw control) // We will choose #2 (a mix of pilot and hover throttle) only when the throttle is quite low. We favor reducing throttle instead of better yaw control because the pilot has commanded it // Under the motor lost condition we remove the highest motor output from our calculations and let that motor go greater than 1.0 // To ensure control and maximum righting performance Hex and Octo have some optimal settings that should be used // Y6 : MOT_YAW_HEADROOM = 350, ATC_RAT_RLL_IMAX = 1.0, ATC_RAT_PIT_IMAX = 1.0, ATC_RAT_YAW_IMAX = 0.5 // Octo-Quad (x8) x : MOT_YAW_HEADROOM = 300, ATC_RAT_RLL_IMAX = 0.375, ATC_RAT_PIT_IMAX = 0.375, ATC_RAT_YAW_IMAX = 0.375 // Octo-Quad (x8) + : MOT_YAW_HEADROOM = 300, ATC_RAT_RLL_IMAX = 0.75, ATC_RAT_PIT_IMAX = 0.75, ATC_RAT_YAW_IMAX = 0.375 // Usable minimums below may result in attitude offsets when motors are lost. Hex aircraft are only marginal and must be handles with care // Hex : MOT_YAW_HEADROOM = 0, ATC_RAT_RLL_IMAX = 1.0, ATC_RAT_PIT_IMAX = 1.0, ATC_RAT_YAW_IMAX = 0.5 // Octo-Quad (x8) x : MOT_YAW_HEADROOM = 300, ATC_RAT_RLL_IMAX = 0.25, ATC_RAT_PIT_IMAX = 0.25, ATC_RAT_YAW_IMAX = 0.25 // Octo-Quad (x8) + : MOT_YAW_HEADROOM = 300, ATC_RAT_RLL_IMAX = 0.5, ATC_RAT_PIT_IMAX = 0.5, ATC_RAT_YAW_IMAX = 0.25 // Quads cannot make use of motor loss handling because it doesn't have enough degrees of freedom. // calculate amount of yaw we can fit into the throttle range // this is always equal to or less than the requested yaw from the pilot or rate controller float rp_low = 1.0f; // lowest thrust value float rp_high = -1.0f; // highest thrust value for (i=0; i rp_high && (!_thrust_boost || i != _motor_lost_index)) { rp_high = _thrust_rpyt_out[i]; } } } // include the lost motor scaled by _thrust_boost_ratio if (_thrust_boost && motor_enabled[_motor_lost_index]) { // record highest roll+pitch command if (_thrust_rpyt_out[_motor_lost_index] > rp_high) { rp_high = _thrust_boost_ratio*rp_high + (1.0f-_thrust_boost_ratio)*_thrust_rpyt_out[_motor_lost_index]; } } // check for roll and pitch saturation if (rp_high-rp_low > 1.0f || throttle_avg_max < -rp_low) { // Full range is being used by roll and pitch. limit.roll_pitch = true; } // calculate the highest allowed average thrust that will provide maximum control range throttle_thrust_best_rpy = MIN(0.5f, throttle_avg_max); // calculate the maximum yaw control that can be used // todo: make _yaw_headroom 0 to 1 yaw_allowed = (float)_yaw_headroom / 1000.0f; yaw_allowed = _thrust_boost_ratio*0.5f + (1.0f - _thrust_boost_ratio) * yaw_allowed; yaw_allowed = MAX(MIN(throttle_thrust_best_rpy+rp_low, 1.0f - (throttle_thrust_best_rpy + rp_high)), yaw_allowed); if (fabsf(yaw_thrust) > yaw_allowed) { // not all commanded yaw can be used yaw_thrust = constrain_float(yaw_thrust, -yaw_allowed, yaw_allowed); limit.yaw = true; } // add yaw control to thrust outputs float rpy_low = 1.0f; // lowest thrust value float rpy_high = -1.0f; // highest thrust value for (i=0; i rpy_high && (!_thrust_boost || i != _motor_lost_index)) { rpy_high = _thrust_rpyt_out[i]; } } } // include the lost motor scaled by _thrust_boost_ratio if (_thrust_boost) { // record highest roll+pitch+yaw command if (_thrust_rpyt_out[_motor_lost_index] > rpy_high && motor_enabled[_motor_lost_index]) { rpy_high = _thrust_boost_ratio*rpy_high + (1.0f-_thrust_boost_ratio)*_thrust_rpyt_out[_motor_lost_index]; } } // calculate any scaling needed to make the combined thrust outputs fit within the output range if (rpy_high-rpy_low > 1.0f) { rpy_scale = 1.0f / (rpy_high-rpy_low); } if (is_negative(rpy_low)) { rpy_scale = MIN(rpy_scale, -throttle_avg_max / rpy_low); } // calculate how close the motors can come to the desired throttle rpy_high *= rpy_scale; rpy_low *= rpy_scale; throttle_thrust_best_rpy = -rpy_low; thr_adj = throttle_thrust - throttle_thrust_best_rpy; if (rpy_scale < 1.0f) { // Full range is being used by roll, pitch, and yaw. limit.roll_pitch = true; limit.yaw = true; if (thr_adj > 0.0f) { limit.throttle_upper = true; } thr_adj = 0.0f; } else { if (thr_adj < 0.0f) { // Throttle can't be reduced to desired value // todo: add lower limit flag and ensure it is handled correctly in altitude controller thr_adj = 0.0f; } else if (thr_adj > 1.0f - (throttle_thrust_best_rpy + rpy_high)) { // Throttle can't be increased to desired value thr_adj = 1.0f - (throttle_thrust_best_rpy + rpy_high); limit.throttle_upper = true; } } // add scaled roll, pitch, constrained yaw and throttle for each motor for (i=0; i rpyt_high) { rpyt_high = _thrust_rpyt_out_filt[i]; // hold motor lost index constant while thrust balance is true if (_thrust_balanced) { _motor_lost_index = i; } } } } float thrust_balance = 1.0f; if (rpyt_sum > 0.1f) { thrust_balance = rpyt_high * number_motors / rpyt_sum; } // ensure thrust balance does not activate for multirotors with less than 6 motors if (number_motors >= 6 && thrust_balance >= 1.5f && _thrust_balanced) { _thrust_balanced = false; } if (thrust_balance <= 1.25f && !_thrust_balanced) { _thrust_balanced = true; } // check to see if thrust boost is using more throttle than _throttle_thrust_max if (_throttle_thrust_max > throttle_thrust_best_plus_adj && rpyt_high < 0.9f && _thrust_balanced) { _thrust_boost = false; } } // output_test_seq - spin a motor at the pwm value specified // motor_seq is the motor's sequence number from 1 to the number of motors on the frame // pwm value is an actual pwm value that will be output, normally in the range of 1000 ~ 2000 void AP_MotorsMatrix::output_test_seq(uint8_t motor_seq, int16_t pwm) { // exit immediately if not armed if (!armed()) { return; } // loop through all the possible orders spinning any motors that match that description for (uint8_t i=0; i AP_MOTORS_MAX_NUM_MOTORS -1) { return false; } // Is motor enabled? if (!motor_enabled[output_channel]) { return false; } rc_write(output_channel, pwm); // output return true; } // add_motor void AP_MotorsMatrix::add_motor_raw(int8_t motor_num, float roll_fac, float pitch_fac, float yaw_fac, uint8_t testing_order) { // ensure valid motor number is provided if( motor_num >= 0 && motor_num < AP_MOTORS_MAX_NUM_MOTORS ) { // increment number of motors if this motor is being newly motor_enabled if( !motor_enabled[motor_num] ) { motor_enabled[motor_num] = true; } // set roll, pitch, thottle factors and opposite motor (for stability patch) _roll_factor[motor_num] = roll_fac; _pitch_factor[motor_num] = pitch_fac; _yaw_factor[motor_num] = yaw_fac; // set order that motor appears in test _test_order[motor_num] = testing_order; // call parent class method add_motor_num(motor_num); } } // add_motor using just position and prop direction - assumes that for each motor, roll and pitch factors are equal void AP_MotorsMatrix::add_motor(int8_t motor_num, float angle_degrees, float yaw_factor, uint8_t testing_order) { add_motor(motor_num, angle_degrees, angle_degrees, yaw_factor, testing_order); } // add_motor using position and prop direction. Roll and Pitch factors can differ (for asymmetrical frames) void AP_MotorsMatrix::add_motor(int8_t motor_num, float roll_factor_in_degrees, float pitch_factor_in_degrees, float yaw_factor, uint8_t testing_order) { add_motor_raw( motor_num, cosf(radians(roll_factor_in_degrees + 90)), cosf(radians(pitch_factor_in_degrees)), yaw_factor, testing_order); } // remove_motor - disabled motor and clears all roll, pitch, throttle factors for this motor void AP_MotorsMatrix::remove_motor(int8_t motor_num) { // ensure valid motor number is provided if( motor_num >= 0 && motor_num < AP_MOTORS_MAX_NUM_MOTORS ) { // disable the motor, set all factors to zero motor_enabled[motor_num] = false; _roll_factor[motor_num] = 0; _pitch_factor[motor_num] = 0; _yaw_factor[motor_num] = 0; } } void AP_MotorsMatrix::setup_motors(motor_frame_class frame_class, motor_frame_type frame_type) { // remove existing motors for (int8_t i=0; i