LCOV - code coverage report
Current view: top level - home/pbarker/rc/ardupilot/ArduSub - mode_poshold.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 51 58 87.9 %
Date: 2026-09-09 11:02:08 Functions: 3 3 100.0 %

          Line data    Source code
       1             : // ArduSub position hold flight mode
       2             : // GPS required
       3             : // Jacob Walser August 2016
       4             : 
       5             : #include "Sub.h"
       6             : 
       7             : #if POSHOLD_ENABLED
       8             : 
       9             : // poshold_init - initialise PosHold controller
      10           6 : bool ModePoshold::init(bool ignore_checks)
      11             : {
      12             :     // fail to initialise PosHold mode if no GPS lock
      13           6 :     if (!sub.position_ok()) {
      14           0 :         return false;
      15             :     }
      16             : 
      17             :     // initialize vertical speeds and acceleration
      18             :     // All limits must be positive
      19           6 :     position_control->NE_set_max_speed_accel_cm(g.pilot_speed, g.pilot_accel_z);
      20           6 :     position_control->NE_set_correction_speed_accel_cm(g.pilot_speed, g.pilot_accel_z);
      21           6 :     position_control->D_set_max_speed_accel_cm(sub.get_pilot_speed_dn(), g.pilot_speed_up, g.pilot_accel_z);
      22           6 :     position_control->D_set_correction_speed_accel_cm(sub.get_pilot_speed_dn(), g.pilot_speed_up, g.pilot_accel_z);
      23             : 
      24             :     // initialise position and desired velocity
      25           6 :     position_control->NE_init_controller_stopping_point();
      26           6 :     position_control->D_init_controller();
      27             : 
      28             :     // Stop all thrusters
      29           6 :     attitude_control->set_throttle_out(NEUTRAL_THROTTLE ,true, g.throttle_filt);
      30           6 :     attitude_control->relax_attitude_controllers();
      31           6 :     position_control->D_relax_controller(0.5f);
      32             : 
      33           6 :     sub.last_pilot_heading_rad = ahrs.get_yaw_rad();
      34             : 
      35           6 :     return true;
      36             : }
      37             : 
      38             : // poshold_run - runs the PosHold controller
      39             : // should be called at 100hz or more
      40       86087 : void ModePoshold::run()
      41             : {
      42       86087 :     uint32_t tnow = AP_HAL::millis();
      43             :     // When unarmed, disable motors and stabilization
      44       86087 :     if (!motors.armed()) {
      45        6058 :         motors.set_desired_spool_state(AP_Motors::DesiredSpoolState::GROUND_IDLE);
      46             :         // Sub vehicles do not stabilize roll/pitch/yaw when not auto-armed (i.e. on the ground, pilot has never raised throttle)
      47        6058 :         attitude_control->set_throttle_out(NEUTRAL_THROTTLE ,true, g.throttle_filt);
      48        6058 :         attitude_control->relax_attitude_controllers();
      49        6058 :         position_control->NE_init_controller_stopping_point();
      50        6058 :         position_control->D_relax_controller(0.5f);
      51        6058 :         sub.last_pilot_heading_rad = ahrs.get_yaw_rad();
      52        6058 :         return;
      53             :     }
      54             : 
      55             :     // set motors to full range
      56       80029 :     motors.set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
      57             : 
      58             :     /////////////////////
      59             :     // Update attitude //
      60             : 
      61             :     // get pilot's desired yaw rate
      62       80029 :     float yaw_input = channel_yaw->pwm_to_angle_dz_trim(channel_yaw->get_dead_zone() * sub.gain, channel_yaw->get_radio_trim());
      63       80029 :     float target_yaw_rate = sub.get_pilot_desired_yaw_rate(yaw_input);
      64             : 
      65             :     // convert pilot input to lean angles
      66             :     // To-Do: convert get_pilot_desired_lean_angles to return angles as floats
      67             :     float target_roll, target_pitch;
      68       80029 :     sub.get_pilot_desired_lean_angles(channel_roll->get_control_in(), channel_pitch->get_control_in(), target_roll, target_pitch, attitude_control->lean_angle_max_cd());
      69             : 
      70             :     // update attitude controller targets
      71       80029 :     if (!is_zero(target_yaw_rate)) { // call attitude controller with rate yaw determined by pilot input
      72           0 :         attitude_control->input_euler_angle_roll_pitch_euler_rate_yaw_cd(target_roll, target_pitch, target_yaw_rate);
      73           0 :         sub.last_pilot_heading_rad = ahrs.get_yaw_rad();
      74           0 :         sub.last_pilot_yaw_input_ms = tnow; // time when pilot last changed heading
      75             : 
      76             :     } else { // hold current heading
      77             : 
      78             :         // this check is required to prevent bounce back after very fast yaw manoeuvres
      79             :         // the inertia of the vehicle causes the heading to move slightly past the point when pilot input actually stopped
      80       80029 :         if (tnow < sub.last_pilot_yaw_input_ms + 250) { // give 250ms to slow down, then set target heading
      81           0 :             target_yaw_rate = 0; // Stop rotation on yaw axis
      82             : 
      83             :             // call attitude controller with target yaw rate = 0 to decelerate on yaw axis
      84           0 :             attitude_control->input_euler_angle_roll_pitch_euler_rate_yaw_cd(target_roll, target_pitch, target_yaw_rate);
      85           0 :             sub.last_pilot_heading_rad = ahrs.get_yaw_rad(); // update heading to hold
      86             : 
      87             :         } else { // call attitude controller holding absolute bearing
      88       80029 :             attitude_control->input_euler_angle_roll_pitch_yaw_cd(target_roll, target_pitch, rad_to_cd(sub.last_pilot_heading_rad), true);
      89             :         }
      90             :     }
      91             : 
      92             :     // update z axis
      93       80029 :     control_depth();
      94             : 
      95             :     // update xy axis
      96             :     // call this after Sub::get_pilot_desired_climb_rate is called so that THR_DZ is reasonable
      97       80029 :     control_horizontal();
      98             : }
      99             : 
     100       80029 : void ModePoshold::control_horizontal() {
     101       80029 :     float lateral_out = 0;
     102       80029 :     float forward_out = 0;
     103             : 
     104             :     // get desired rates in the body frame
     105             :     Vector2f body_rates_cms = {
     106       80029 :         sub.get_pilot_desired_horizontal_rate(channel_forward),
     107       80029 :         sub.get_pilot_desired_horizontal_rate(channel_lateral)
     108       80029 :     };
     109             : 
     110       80029 :     if (sub.position_ok()) {
     111       73639 :         if (!position_control->NE_is_active()) {
     112             :             // the xy controller timed out, re-initialize
     113           1 :             position_control->NE_init_controller_stopping_point();
     114             :         }
     115             : 
     116             :         // convert to the earth frame and set target rates
     117       73639 :         auto earth_rates_cms = ahrs.body_to_earth2D(body_rates_cms);
     118       73639 :         position_control->input_vel_accel_NE_cm(earth_rates_cms, {0, 0});
     119             : 
     120             :         // convert pos control roll and pitch angles back to lateral and forward efforts
     121       73639 :         sub.translate_pos_control_rp(lateral_out, forward_out);
     122             : 
     123             :         // update the xy controller
     124       73639 :         position_control->NE_update_controller();
     125        6390 :     } else if (g.pilot_speed > 0) {
     126             :         // allow the pilot to reposition manually
     127        6390 :         forward_out = body_rates_cms.x / (float)g.pilot_speed;
     128        6390 :         lateral_out = body_rates_cms.y / (float)g.pilot_speed;
     129             :     }
     130             : 
     131       80029 :     motors.set_forward(forward_out);
     132       80029 :     motors.set_lateral(lateral_out);
     133       80029 : }
     134             : #endif  // POSHOLD_ENABLED

Generated by: LCOV version 1.14