Line data Source code
1 : /* 2 : This program is free software: you can redistribute it and/or modify 3 : it under the terms of the GNU General Public License as published by 4 : the Free Software Foundation, either version 3 of the License, or 5 : (at your option) any later version. 6 : 7 : This program is distributed in the hope that it will be useful, 8 : but WITHOUT ANY WARRANTY; without even the implied warranty of 9 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 : GNU General Public License for more details. 11 : 12 : You should have received a copy of the GNU General Public License 13 : along with this program. If not, see <http://www.gnu.org/licenses/>. 14 : */ 15 : 16 : #include <AP_HAL/AP_HAL.h> 17 : #include <AP_Math/AP_Math.h> 18 : #include "AP_MotorsCoax.h" 19 : #include <GCS_MAVLink/GCS.h> 20 : #include <SRV_Channel/SRV_Channel.h> 21 : 22 : extern const AP_HAL::HAL& hal; 23 : 24 : // init 25 1 : void AP_MotorsCoax::init(motor_frame_class frame_class, motor_frame_type frame_type) 26 : { 27 : // make sure 6 output channels are mapped 28 7 : for (uint8_t i = 0; i < 6; i++) { 29 6 : add_motor_num(CH_1 + i); 30 : } 31 : 32 : // set the motor_enabled flag so that the main ESC can be calibrated like other frame types 33 1 : motor_enabled[AP_MOTORS_MOT_5] = true; 34 1 : motor_enabled[AP_MOTORS_MOT_6] = true; 35 : 36 : // setup actuator scaling 37 5 : for (uint8_t i = 0; i < NUM_ACTUATORS; i++) { 38 4 : SRV_Channels::set_angle(SRV_Channels::get_motor_function(i), AP_MOTORS_COAX_SERVO_INPUT_RANGE); 39 : } 40 : 41 1 : _mav_type = MAV_TYPE_COAXIAL; 42 : 43 : // record successful initialisation if what we setup was the desired frame_class 44 1 : set_initialised_ok(frame_class == MOTOR_FRAME_COAX); 45 1 : } 46 : 47 : // set frame class (i.e. quad, hexa, heli) and type (i.e. x, plus) 48 41 : void AP_MotorsCoax::set_frame_class_and_type(motor_frame_class frame_class, motor_frame_type frame_type) 49 : { 50 41 : set_initialised_ok(frame_class == MOTOR_FRAME_COAX); 51 41 : } 52 : 53 : // set update rate to motors - a value in hertz 54 1 : void AP_MotorsCoax::set_update_rate(uint16_t speed_hz) 55 : { 56 : // record requested speed 57 1 : _speed_hz = speed_hz; 58 : 59 1 : uint32_t mask = 60 : 1U << AP_MOTORS_MOT_5 | 61 : 1U << AP_MOTORS_MOT_6 ; 62 1 : rc_set_freq(mask, _speed_hz); 63 1 : } 64 : 65 50370 : void AP_MotorsCoax::output_to_motors() 66 : { 67 50370 : switch (_spool_state) { 68 17769 : case SpoolState::SHUT_DOWN: 69 : // sends minimum values out to the motors 70 17769 : rc_write_angle(AP_MOTORS_MOT_1, _roll_radio_passthrough * AP_MOTORS_COAX_SERVO_INPUT_RANGE); 71 17769 : rc_write_angle(AP_MOTORS_MOT_2, _pitch_radio_passthrough * AP_MOTORS_COAX_SERVO_INPUT_RANGE); 72 17769 : rc_write_angle(AP_MOTORS_MOT_3, -_roll_radio_passthrough * AP_MOTORS_COAX_SERVO_INPUT_RANGE); 73 17769 : rc_write_angle(AP_MOTORS_MOT_4, -_pitch_radio_passthrough * AP_MOTORS_COAX_SERVO_INPUT_RANGE); 74 17769 : rc_write(AP_MOTORS_MOT_5, output_to_pwm(0)); 75 17769 : rc_write(AP_MOTORS_MOT_6, output_to_pwm(0)); 76 17769 : break; 77 202 : case SpoolState::GROUND_IDLE: 78 : // sends output to motors when armed but not flying 79 1010 : for (uint8_t i = 0; i < NUM_ACTUATORS; i++) { 80 808 : rc_write_angle(AP_MOTORS_MOT_1 + i, _spin_up_ratio * _actuator_out[i] * AP_MOTORS_COAX_SERVO_INPUT_RANGE); 81 : } 82 202 : set_actuator_with_slew(_actuator[AP_MOTORS_MOT_5], actuator_spin_up_to_ground_idle()); 83 202 : set_actuator_with_slew(_actuator[AP_MOTORS_MOT_6], actuator_spin_up_to_ground_idle()); 84 202 : rc_write(AP_MOTORS_MOT_5, output_to_pwm(_actuator[AP_MOTORS_MOT_5])); 85 202 : rc_write(AP_MOTORS_MOT_6, output_to_pwm(_actuator[AP_MOTORS_MOT_6])); 86 202 : break; 87 32399 : case SpoolState::SPOOLING_UP: 88 : case SpoolState::THROTTLE_UNLIMITED: 89 : case SpoolState::SPOOLING_DOWN: 90 : // set motor output based on thrust requests 91 161995 : for (uint8_t i = 0; i < NUM_ACTUATORS; i++) { 92 129596 : rc_write_angle(AP_MOTORS_MOT_1 + i, _actuator_out[i] * AP_MOTORS_COAX_SERVO_INPUT_RANGE); 93 : } 94 32399 : set_actuator_with_slew(_actuator[AP_MOTORS_MOT_5], thr_lin.thrust_to_actuator(_thrust_yt_ccw)); 95 32399 : set_actuator_with_slew(_actuator[AP_MOTORS_MOT_6], thr_lin.thrust_to_actuator(_thrust_yt_cw)); 96 32399 : rc_write(AP_MOTORS_MOT_5, output_to_pwm(_actuator[AP_MOTORS_MOT_5])); 97 32399 : rc_write(AP_MOTORS_MOT_6, output_to_pwm(_actuator[AP_MOTORS_MOT_6])); 98 32399 : break; 99 : } 100 50370 : } 101 : 102 : // get_motor_mask - returns a bitmask of which outputs are being used for motors or servos (1 means being used) 103 : // this can be used to ensure other pwm outputs (i.e. for servos) do not conflict 104 44 : uint32_t AP_MotorsCoax::get_motor_mask() 105 : { 106 44 : uint32_t motor_mask = 107 : 1U << AP_MOTORS_MOT_5 | 108 : 1U << AP_MOTORS_MOT_6; 109 44 : uint32_t mask = motor_mask_to_srv_channel_mask(motor_mask); 110 : 111 : // add parent's mask 112 44 : mask |= AP_MotorsMulticopter::get_motor_mask(); 113 : 114 44 : return mask; 115 : } 116 : 117 : // sends commands to the motors 118 50370 : void AP_MotorsCoax::output_armed_stabilizing() 119 : { 120 : float roll_thrust; // roll thrust input value, +/- 1.0 121 : float pitch_thrust; // pitch thrust input value, +/- 1.0 122 : float yaw_thrust; // yaw thrust input value, +/- 1.0 123 : float throttle_thrust; // throttle thrust input value, 0.0 - 1.0 124 : float throttle_avg_max; // throttle thrust average maximum value, 0.0 - 1.0 125 : float thrust_min_rpy; // the minimum throttle setting that will not limit the roll and pitch output 126 : float thr_adj; // the difference between the pilot's desired throttle and throttle_thrust_best_rpy 127 : float thrust_out; // 128 50370 : float rp_scale = 1.0f; // this is used to scale the roll, pitch and yaw to fit within the motor limits 129 50370 : float actuator_allowed = 0.0f; // amount of yaw we can fit in 130 : 131 : // apply voltage and air pressure compensation 132 50370 : const float compensation_gain = thr_lin.get_compensation_gain(); 133 50370 : roll_thrust = (_roll_in + _roll_in_ff) * compensation_gain; 134 50370 : pitch_thrust = (_pitch_in + _pitch_in_ff) * compensation_gain; 135 50370 : yaw_thrust = (_yaw_in + _yaw_in_ff) * compensation_gain; 136 50370 : throttle_thrust = get_throttle() * compensation_gain; 137 50370 : throttle_avg_max = _throttle_avg_max * compensation_gain; 138 : 139 : // sanity check throttle is above zero and below current limited throttle 140 50370 : if (throttle_thrust <= 0.0f) { 141 18574 : throttle_thrust = 0.0f; 142 18574 : limit.throttle_lower = true; 143 : } 144 50370 : if (throttle_thrust >= _throttle_thrust_max) { 145 18083 : throttle_thrust = _throttle_thrust_max; 146 18083 : limit.throttle_upper = true; 147 : } 148 : 149 50370 : throttle_avg_max = constrain_float(throttle_avg_max, throttle_thrust, _throttle_thrust_max); 150 : 151 50370 : float rp_thrust_max = MAX(fabsf(roll_thrust), fabsf(pitch_thrust)); 152 : 153 : // calculate how much roll and pitch must be scaled to leave enough range for the minimum yaw 154 50370 : if (is_zero(rp_thrust_max)) { 155 1 : rp_scale = 1.0f; 156 : } else { 157 50369 : rp_scale = constrain_float((1.0f - MIN(fabsf(yaw_thrust), 0.5f * (float)_yaw_headroom * 0.001f)) / rp_thrust_max, 0.0f, 1.0f); 158 50369 : if (rp_scale < 1.0f) { 159 0 : limit.roll = true; 160 0 : limit.pitch = true; 161 : } 162 : } 163 : 164 50370 : actuator_allowed = 2.0f * (1.0f - rp_scale * rp_thrust_max); 165 50370 : if (fabsf(yaw_thrust) > actuator_allowed) { 166 5526 : yaw_thrust = constrain_float(yaw_thrust, -actuator_allowed, actuator_allowed); 167 5526 : limit.yaw = true; 168 : } 169 : 170 : // calculate the minimum thrust that doesn't limit the roll, pitch and yaw forces 171 50370 : thrust_min_rpy = MAX(fabsf(rp_scale * rp_thrust_max), fabsf(yaw_thrust)); 172 : 173 50370 : thr_adj = throttle_thrust - throttle_avg_max; 174 50370 : if (thr_adj < (thrust_min_rpy - throttle_avg_max)) { 175 : // Throttle can't be reduced to the desired level because this would reduce airflow over 176 : // the control surfaces preventing roll and pitch reaching the desired level. 177 44173 : thr_adj = MIN(thrust_min_rpy, throttle_avg_max) - throttle_avg_max; 178 : } 179 : 180 : // calculate the throttle setting for the lift fan 181 50370 : thrust_out = throttle_avg_max + thr_adj; 182 : // compensation_gain can never be zero 183 50370 : _throttle_out = thrust_out / compensation_gain; 184 : 185 50370 : if (fabsf(yaw_thrust) > thrust_out) { 186 23473 : yaw_thrust = constrain_float(yaw_thrust, -thrust_out, thrust_out); 187 23473 : limit.yaw = true; 188 : } 189 : 190 50370 : _thrust_yt_ccw = thrust_out + 0.5f * yaw_thrust; 191 50370 : _thrust_yt_cw = thrust_out - 0.5f * yaw_thrust; 192 : 193 : // limit thrust out for calculation of actuator gains 194 50370 : float thrust_out_actuator = constrain_float(MAX(_throttle_hover * 0.5f, thrust_out), 0.5f, 1.0f); 195 : 196 50370 : if (is_zero(thrust_out)) { 197 17972 : limit.roll = true; 198 17972 : limit.pitch = true; 199 : } 200 : // force of a lifting surface is approximately equal to the angle of attack times the airflow velocity squared 201 : // static thrust is proportional to the airflow velocity squared 202 : // therefore the torque of the roll and pitch actuators should be approximately proportional to 203 : // the angle of attack multiplied by the static thrust. 204 50370 : _actuator_out[0] = roll_thrust / thrust_out_actuator; 205 50370 : _actuator_out[1] = pitch_thrust / thrust_out_actuator; 206 50370 : if (fabsf(_actuator_out[0]) > 1.0f) { 207 15047 : limit.roll = true; 208 15047 : _actuator_out[0] = constrain_float(_actuator_out[0], -1.0f, 1.0f); 209 : } 210 50370 : if (fabsf(_actuator_out[1]) > 1.0f) { 211 15009 : limit.pitch = true; 212 15009 : _actuator_out[1] = constrain_float(_actuator_out[1], -1.0f, 1.0f); 213 : } 214 50370 : _actuator_out[2] = -_actuator_out[0]; 215 50370 : _actuator_out[3] = -_actuator_out[1]; 216 50370 : } 217 : 218 : // output_test_seq - spin a motor at the pwm value specified 219 : // motor_seq is the motor's sequence number from 1 to the number of motors on the frame 220 : // pwm value is an actual pwm value that will be output, normally in the range of 1000 ~ 2000 221 0 : void AP_MotorsCoax::_output_test_seq(uint8_t motor_seq, int16_t pwm) 222 : { 223 : // output to motors and servos 224 0 : switch (motor_seq) { 225 0 : case 1: 226 : // flap servo 1 227 : case 2: 228 : // flap servo 2 229 : case 3: 230 : // flap servo 3 231 : case 4: 232 : // flap servo 4 233 : case 5: 234 : // motor 1 235 : case 6: 236 : // motor 2 237 0 : rc_write(motor_seq - 1u, pwm); 238 0 : break; 239 0 : default: 240 : // do nothing 241 0 : break; 242 : } 243 0 : }