123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458 |
- /*
- 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 <http://www.gnu.org/licenses/>.
- */
- /*
- * AP_MotorsHeli.cpp - ArduCopter motors library
- * Code by RandyMackay. DIYDrones.com
- *
- */
- #include <stdlib.h>
- #include <AP_HAL/AP_HAL.h>
- #include "AP_MotorsHeli.h"
- #include <GCS_MAVLink/GCS.h>
- extern const AP_HAL::HAL& hal;
- const AP_Param::GroupInfo AP_MotorsHeli::var_info[] = {
- // 1 was ROL_MAX which has been replaced by CYC_MAX
- // 2 was PIT_MAX which has been replaced by CYC_MAX
- // @Param: COL_MIN
- // @DisplayName: Collective Pitch Minimum
- // @Description: Lowest possible servo position in PWM microseconds for the swashplate
- // @Range: 1000 2000
- // @Units: PWM
- // @Increment: 1
- // @User: Standard
- AP_GROUPINFO("COL_MIN", 3, AP_MotorsHeli, _collective_min, AP_MOTORS_HELI_COLLECTIVE_MIN),
- // @Param: COL_MAX
- // @DisplayName: Collective Pitch Maximum
- // @Description: Highest possible servo position in PWM microseconds for the swashplate
- // @Range: 1000 2000
- // @Units: PWM
- // @Increment: 1
- // @User: Standard
- AP_GROUPINFO("COL_MAX", 4, AP_MotorsHeli, _collective_max, AP_MOTORS_HELI_COLLECTIVE_MAX),
- // @Param: COL_MID
- // @DisplayName: Collective Pitch Mid-Point
- // @Description: Swash servo position in PWM microseconds corresponding to zero collective pitch (or zero lift for Asymmetrical blades)
- // @Range: 1000 2000
- // @Units: PWM
- // @Increment: 1
- // @User: Standard
- AP_GROUPINFO("COL_MID", 5, AP_MotorsHeli, _collective_mid, AP_MOTORS_HELI_COLLECTIVE_MID),
- // @Param: SV_MAN
- // @DisplayName: Manual Servo Mode
- // @Description: Manual servo override for swash set-up. Do not set this manually!
- // @Values: 0:Disabled,1:Passthrough,2:Max collective,3:Mid collective,4:Min collective
- // @User: Standard
- AP_GROUPINFO("SV_MAN", 6, AP_MotorsHeli, _servo_mode, SERVO_CONTROL_MODE_AUTOMATED),
- // indices 7 and 8 were RSC parameters which were moved to RSC library. Do not use these indices in the future.
- // index 9 was LAND_COL_MIN. Do not use this index in the future.
- // indices 10-13 were RSC parameters which were moved to RSC library. Do not use these indices in the future.
- // index 14 was RSC_POWER_LOW. Do not use this index in the future.
- // index 15 was RSC_POWER_HIGH. Do not use this index in the future.
- // @Param: CYC_MAX
- // @DisplayName: Cyclic Pitch Angle Max
- // @Description: Maximum pitch angle of the swash plate
- // @Range: 0 18000
- // @Units: cdeg
- // @Increment: 100
- // @User: Advanced
- AP_GROUPINFO("CYC_MAX", 16, AP_MotorsHeli, _cyclic_max, AP_MOTORS_HELI_SWASH_CYCLIC_MAX),
- // @Param: SV_TEST
- // @DisplayName: Boot-up Servo Test Cycles
- // @Description: Number of cycles to run servo test on boot-up
- // @Range: 0 10
- // @Increment: 1
- // @User: Standard
- AP_GROUPINFO("SV_TEST", 17, AP_MotorsHeli, _servo_test, 0),
- // index 18 was RSC_POWER_NEGC. Do not use this index in the future.
- // index 19 was RSC_SLEWRATE and was moved to RSC library. Do not use this index in the future.
- // indices 20 to 24 was throttle curve. Do not use this index in the future.
- // @Group: RSC_
- // @Path: AP_MotorsHeli_RSC.cpp
- AP_SUBGROUPINFO(_main_rotor, "RSC_", 25, AP_MotorsHeli, AP_MotorsHeli_RSC),
- AP_GROUPEND
- };
- //
- // public methods
- //
- // init
- void AP_MotorsHeli::init(motor_frame_class frame_class, motor_frame_type frame_type)
- {
- // remember frame class and type
- _frame_type = frame_type;
- _frame_class = frame_class;
- // set update rate
- set_update_rate(_speed_hz);
- // load boot-up servo test cycles into counter to be consumed
- _servo_test_cycle_counter = _servo_test;
- // ensure inputs are not passed through to servos on start-up
- _servo_mode = SERVO_CONTROL_MODE_AUTOMATED;
- // initialise radio passthrough for collective to middle
- _throttle_radio_passthrough = 0.5f;
- // initialise Servo/PWM ranges and endpoints
- if (!init_outputs()) {
- // don't set initialised_ok
- return;
- }
- // calculate all scalars
- calculate_scalars();
- // record successful initialisation if what we setup was the desired frame_class
- _flags.initialised_ok = (frame_class == MOTOR_FRAME_HELI);
- // set flag to true so targets are initialized once aircraft is armed for first time
- _heliflags.init_targets_on_arming = true;
- }
- // set frame class (i.e. quad, hexa, heli) and type (i.e. x, plus)
- void AP_MotorsHeli::set_frame_class_and_type(motor_frame_class frame_class, motor_frame_type frame_type)
- {
- _flags.initialised_ok = (frame_class == MOTOR_FRAME_HELI);
- }
- // output_min - sets servos to neutral point with motors stopped
- void AP_MotorsHeli::output_min()
- {
- // move swash to mid
- move_actuators(0.0f,0.0f,0.5f,0.0f);
- update_motor_control(ROTOR_CONTROL_STOP);
- // override limits flags
- limit.roll = true;
- limit.pitch = true;
- limit.yaw = true;
- limit.throttle_lower = true;
- limit.throttle_upper = false;
- }
- // output - sends commands to the servos
- void AP_MotorsHeli::output()
- {
- // update throttle filter
- update_throttle_filter();
- // run spool logic
- output_logic();
- if (_flags.armed) {
- calculate_armed_scalars();
- if (!_flags.interlock) {
- output_armed_zero_throttle();
- } else {
- output_armed_stabilizing();
- }
- } else {
- output_disarmed();
- }
-
- output_to_motors();
- };
- // sends commands to the motors
- void AP_MotorsHeli::output_armed_stabilizing()
- {
- // if manual override active after arming, deactivate it and reinitialize servos
- if (_servo_mode != SERVO_CONTROL_MODE_AUTOMATED) {
- reset_flight_controls();
- }
- move_actuators(_roll_in, _pitch_in, get_throttle(), _yaw_in);
- }
- // output_armed_zero_throttle - sends commands to the motors
- void AP_MotorsHeli::output_armed_zero_throttle()
- {
- // if manual override active after arming, deactivate it and reinitialize servos
- if (_servo_mode != SERVO_CONTROL_MODE_AUTOMATED) {
- reset_flight_controls();
- }
- move_actuators(_roll_in, _pitch_in, get_throttle(), _yaw_in);
- }
- // output_disarmed - sends commands to the motors
- void AP_MotorsHeli::output_disarmed()
- {
- if (_servo_test_cycle_counter > 0){
- // perform boot-up servo test cycle if enabled
- servo_test();
- } else {
- // manual override (i.e. when setting up swash)
- switch (_servo_mode) {
- case SERVO_CONTROL_MODE_MANUAL_PASSTHROUGH:
- // pass pilot commands straight through to swash
- _roll_in = _roll_radio_passthrough;
- _pitch_in = _pitch_radio_passthrough;
- _throttle_filter.reset(_throttle_radio_passthrough);
- _yaw_in = _yaw_radio_passthrough;
- break;
- case SERVO_CONTROL_MODE_MANUAL_CENTER:
- // fixate mid collective
- _roll_in = 0.0f;
- _pitch_in = 0.0f;
- _throttle_filter.reset(_collective_mid_pct);
- _yaw_in = 0.0f;
- break;
- case SERVO_CONTROL_MODE_MANUAL_MAX:
- // fixate max collective
- _roll_in = 0.0f;
- _pitch_in = 0.0f;
- _throttle_filter.reset(1.0f);
- if (_frame_class == MOTOR_FRAME_HELI_DUAL ||
- _frame_class == MOTOR_FRAME_HELI_QUAD) {
- _yaw_in = 0;
- } else {
- _yaw_in = 1;
- }
- break;
- case SERVO_CONTROL_MODE_MANUAL_MIN:
- // fixate min collective
- _roll_in = 0.0f;
- _pitch_in = 0.0f;
- _throttle_filter.reset(0.0f);
- if (_frame_class == MOTOR_FRAME_HELI_DUAL ||
- _frame_class == MOTOR_FRAME_HELI_QUAD) {
- _yaw_in = 0;
- } else {
- _yaw_in = -1;
- }
- break;
- case SERVO_CONTROL_MODE_MANUAL_OSCILLATE:
- // use servo_test function from child classes
- servo_test();
- break;
- default:
- // no manual override
- break;
- }
- }
- // ensure swash servo endpoints haven't been moved
- init_outputs();
- // continuously recalculate scalars to allow setup
- calculate_scalars();
- // helicopters always run stabilizing flight controls
- move_actuators(_roll_in, _pitch_in, get_throttle(), _yaw_in);
- }
- // run spool logic
- void AP_MotorsHeli::output_logic()
- {
- // force desired and current spool mode if disarmed and armed with interlock enabled
- if (_flags.armed) {
- if (!_flags.interlock) {
- _spool_desired = DesiredSpoolState::GROUND_IDLE;
- } else {
- _heliflags.init_targets_on_arming = false;
- }
- } else {
- _heliflags.init_targets_on_arming = true;
- _spool_desired = DesiredSpoolState::SHUT_DOWN;
- _spool_state = SpoolState::SHUT_DOWN;
- }
- switch (_spool_state) {
- case SpoolState::SHUT_DOWN:
- // Motors should be stationary.
- // Servos set to their trim values or in a test condition.
- // make sure the motors are spooling in the correct direction
- if (_spool_desired != DesiredSpoolState::SHUT_DOWN) {
- _spool_state = SpoolState::GROUND_IDLE;
- break;
- }
- break;
- case SpoolState::GROUND_IDLE: {
- // Motors should be stationary or at ground idle.
- // Servos should be moving to correct the current attitude.
- if (_spool_desired == DesiredSpoolState::SHUT_DOWN){
- _spool_state = SpoolState::SHUT_DOWN;
- } else if(_spool_desired == DesiredSpoolState::THROTTLE_UNLIMITED) {
- _spool_state = SpoolState::SPOOLING_UP;
- } else { // _spool_desired == GROUND_IDLE
- }
- break;
- }
- case SpoolState::SPOOLING_UP:
- // Maximum throttle should move from minimum to maximum.
- // Servos should exhibit normal flight behavior.
- // make sure the motors are spooling in the correct direction
- if (_spool_desired != DesiredSpoolState::THROTTLE_UNLIMITED ){
- _spool_state = SpoolState::SPOOLING_DOWN;
- break;
- }
- if (_heliflags.rotor_runup_complete){
- _spool_state = SpoolState::THROTTLE_UNLIMITED;
- }
- break;
- case SpoolState::THROTTLE_UNLIMITED:
- // Throttle should exhibit normal flight behavior.
- // Servos should exhibit normal flight behavior.
- // make sure the motors are spooling in the correct direction
- if (_spool_desired != DesiredSpoolState::THROTTLE_UNLIMITED) {
- _spool_state = SpoolState::SPOOLING_DOWN;
- break;
- }
- break;
- case SpoolState::SPOOLING_DOWN:
- // Maximum throttle should move from maximum to minimum.
- // Servos should exhibit normal flight behavior.
- // make sure the motors are spooling in the correct direction
- if (_spool_desired == DesiredSpoolState::THROTTLE_UNLIMITED) {
- _spool_state = SpoolState::SPOOLING_UP;
- break;
- }
- if (!rotor_speed_above_critical()){
- _spool_state = SpoolState::GROUND_IDLE;
- }
- break;
- }
- }
- // parameter_check - check if helicopter specific parameters are sensible
- bool AP_MotorsHeli::parameter_check(bool display_msg) const
- {
- // returns false if RSC Mode is not set to a valid control mode
- if (_main_rotor._rsc_mode.get() <= (int8_t)ROTOR_CONTROL_MODE_DISABLED || _main_rotor._rsc_mode.get() > (int8_t)ROTOR_CONTROL_MODE_CLOSED_LOOP_POWER_OUTPUT) {
- if (display_msg) {
- gcs().send_text(MAV_SEVERITY_CRITICAL, "PreArm: H_RSC_MODE invalid");
- }
- return false;
- }
- // returns false if rsc_setpoint is out of range
- if ( _main_rotor._rsc_setpoint.get() > 100 || _main_rotor._rsc_setpoint.get() < 10){
- if (display_msg) {
- gcs().send_text(MAV_SEVERITY_CRITICAL, "PreArm: H_RSC_SETPOINT out of range");
- }
- return false;
- }
- // returns false if idle output is out of range
- if ( _main_rotor._idle_output.get() > 100 || _main_rotor._idle_output.get() < 0){
- if (display_msg) {
- gcs().send_text(MAV_SEVERITY_CRITICAL, "PreArm: H_RSC_IDLE out of range");
- }
- return false;
- }
- // returns false if _rsc_critical is not between 0 and 100
- if (_main_rotor._critical_speed.get() > 100 || _main_rotor._critical_speed.get() < 0) {
- if (display_msg) {
- gcs().send_text(MAV_SEVERITY_CRITICAL, "PreArm: H_RSC_CRITICAL out of range");
- }
- return false;
- }
- // returns false if RSC Runup Time is less than Ramp time as this could cause undesired behaviour of rotor speed estimate
- if (_main_rotor._runup_time.get() <= _main_rotor._ramp_time.get()){
- if (display_msg) {
- gcs().send_text(MAV_SEVERITY_CRITICAL, "PreArm: H_RUNUP_TIME too small");
- }
- return false;
- }
- // all other cases parameters are OK
- return true;
- }
- // reset_swash_servo
- void AP_MotorsHeli::reset_swash_servo(SRV_Channel::Aux_servo_function_t function)
- {
- // outputs are defined on a -500 to 500 range for swash servos
- SRV_Channels::set_range(function, 1000);
- // swash servos always use full endpoints as restricting them would lead to scaling errors
- SRV_Channels::set_output_min_max(function, 1000, 2000);
- }
- // update the throttle input filter
- void AP_MotorsHeli::update_throttle_filter()
- {
- _throttle_filter.apply(_throttle_in, 1.0f/_loop_rate);
- // constrain filtered throttle
- if (_throttle_filter.get() < 0.0f) {
- _throttle_filter.reset(0.0f);
- }
- if (_throttle_filter.get() > 1.0f) {
- _throttle_filter.reset(1.0f);
- }
- }
- // reset_flight_controls - resets all controls and scalars to flight status
- void AP_MotorsHeli::reset_flight_controls()
- {
- _servo_mode = SERVO_CONTROL_MODE_AUTOMATED;
- init_outputs();
- calculate_scalars();
- }
- // convert input in -1 to +1 range to pwm output for swashplate servo.
- // The value 0 corresponds to the trim value of the servo. Swashplate
- // servo travel range is fixed to 1000 pwm and therefore the input is
- // multiplied by 500 to get PWM output.
- void AP_MotorsHeli::rc_write_swash(uint8_t chan, float swash_in)
- {
- uint16_t pwm = (uint16_t)(1500 + 500 * swash_in);
- SRV_Channel::Aux_servo_function_t function = SRV_Channels::get_motor_function(chan);
- SRV_Channels::set_output_pwm_trimmed(function, pwm);
- }
|