LCOV - code coverage report
Current view: top level - home/pbarker/rc/ardupilot/ArduCopter - mode_follow.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 62 75 82.7 %
Date: 2026-09-09 11:02:08 Functions: 7 7 100.0 %

          Line data    Source code
       1             : #include "Copter.h"
       2             : 
       3             : #if MODE_FOLLOW_ENABLED
       4             : 
       5             : /*
       6             :  * mode_follow.cpp - follow another mavlink-enabled vehicle by system id
       7             :  *
       8             :  * TODO: stick control to move around on sphere
       9             :  * TODO: stick control to change sphere diameter
      10             :  * TODO: "channel 7 option" to lock onto "pointed at" target
      11             :  * TODO: do better in terms of loitering around the moving point; may need a PID?  Maybe use loiter controller somehow?
      12             :  * TODO: extrapolate target vehicle position using its velocity and acceleration
      13             :  * TODO: ensure AP_AVOIDANCE_ENABLED is true because we rely on it velocity limiting functions
      14             :  */
      15             : 
      16             : // Return true if this mode is enabled, used by MAVLink available modes
      17       75230 : bool ModeFollow::enabled() const
      18             : {
      19       75230 :     return g2.follow.enabled();
      20             : }
      21             : 
      22             : // initialise follow mode
      23           7 : bool ModeFollow::init(const bool ignore_checks)
      24             : {
      25           7 :     if (!enabled()) {
      26           0 :         gcs().send_text(MAV_SEVERITY_WARNING, "Set FOLL_ENABLE = 1");
      27           0 :         return false;
      28             :     }
      29             : 
      30             : #if HAL_MOUNT_ENABLED
      31           7 :     AP_Mount *mount = AP_Mount::get_singleton();
      32             :     // follow the lead vehicle using sysid
      33           7 :     if (g2.follow.option_is_enabled(AP_Follow::Option::MOUNT_FOLLOW_ON_ENTER) && mount != nullptr) {
      34           0 :         mount->set_target_sysid(g2.follow.get_target_sysid());
      35             :     }
      36             : #endif
      37             : 
      38             :     // initialise horizontal speed, acceleration
      39           7 :     pos_control->NE_set_max_speed_accel_m(wp_nav->get_default_speed_NE_ms(), wp_nav->get_wp_acceleration_mss());
      40           7 :     pos_control->NE_set_correction_speed_accel_m(wp_nav->get_default_speed_NE_ms(), wp_nav->get_wp_acceleration_mss());
      41             : 
      42             :     // initialize vertical speeds and acceleration
      43           7 :     pos_control->D_set_max_speed_accel_m(wp_nav->get_default_speed_down_ms(), wp_nav->get_default_speed_up_ms(), wp_nav->get_accel_D_mss());
      44           7 :     pos_control->D_set_correction_speed_accel_m(wp_nav->get_default_speed_down_ms(), wp_nav->get_default_speed_up_ms(), wp_nav->get_accel_D_mss());
      45             : 
      46             :     // initialise velocity controller
      47           7 :     pos_control->D_init_controller();
      48           7 :     pos_control->NE_init_controller();
      49             : 
      50             :     // initialise yaw
      51           7 :     auto_yaw.set_mode_to_default(false);
      52             : 
      53           7 :     return true;
      54             : }
      55             : 
      56             : // perform cleanup required when leaving follow mode
      57           7 : void ModeFollow::exit()
      58             : {
      59           7 :     g2.follow.clear_offsets_if_required();
      60           7 : }
      61             : 
      62       16085 : void ModeFollow::run()
      63             : {
      64             :     // if not armed set throttle to zero and exit immediately
      65       16085 :     if (is_disarmed_or_landed()) {
      66         648 :         make_safe_ground_handling();
      67         648 :         return;
      68             :     }
      69             : 
      70             :     // Initialize follow offset if not yet set.
      71             :     // Prevents vehicle from starting directly on top of the lead vehicle.
      72       15437 :     g2.follow.init_offsets_if_required();
      73             : 
      74             :     // set motors to full range
      75       15437 :     motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
      76             : 
      77       15437 :     float yaw_rad = attitude_control->get_att_target_euler_rad().z;
      78       15437 :     float yaw_rate_rads = 0.0f;
      79             : 
      80       15437 :     Vector3p pos_ofs_ned_m;  // vector to lead vehicle + offset
      81       15437 :     Vector3f vel_ofs_ned_ms;  // velocity of lead vehicle + offset
      82       15437 :     Vector3f accel_ofs_ned_mss;  // accel of lead vehicle + offset
      83       15437 :     if (g2.follow.get_ofs_pos_vel_accel_NED_m(pos_ofs_ned_m, vel_ofs_ned_ms, accel_ofs_ned_mss)) {
      84             : 
      85       15272 :         float target_heading_deg = 0.0f;
      86       15272 :         float target_heading_rate_degs = 0.0f;
      87       15272 :         g2.follow.get_target_heading_deg(target_heading_deg);
      88       15272 :         g2.follow.get_target_heading_rate_degs(target_heading_rate_degs);
      89             : 
      90       15272 :         pos_control->input_pos_vel_accel_NE_m(pos_ofs_ned_m.xy(), vel_ofs_ned_ms.xy(), accel_ofs_ned_mss.xy(), false);
      91             : 
      92       15272 :         float pos_ofs_d_m = pos_ofs_ned_m.z;
      93       15272 :         pos_control->input_pos_vel_accel_D_m(pos_ofs_d_m, vel_ofs_ned_ms.z, accel_ofs_ned_mss.z, false);
      94             : 
      95             :         // Determine desired yaw behavior based on configured follow mode
      96       15272 :         switch (g2.follow.get_yaw_behave()) {
      97       15272 :             case AP_Follow::YAW_BEHAVE_FACE_LEAD_VEHICLE: {
      98             :                 // Face the target directly
      99       15272 :                 Vector3p pos_ned_m;  // vector to lead vehicle
     100       15272 :                 Vector3f vel_ned_ms;  // velocity of lead vehicle
     101       15272 :                 Vector3f accel_ned_mss;  // accel of lead vehicle
     102       15272 :                 if (g2.follow.get_target_pos_vel_accel_NED_m(pos_ned_m, vel_ned_ms, accel_ned_mss)) {
     103       15272 :                     const Vector2p vec_to_lead_ne_m = pos_ned_m.xy() - pos_control->get_pos_target_NED_m().xy();
     104       15272 :                     if (vec_to_lead_ne_m.length_squared() > 1.0) {
     105       15241 :                         yaw_rad = vec_to_lead_ne_m.angle();
     106             :                     }
     107             :                 }
     108       15272 :                 break;
     109             :             }
     110             : 
     111           0 :             case AP_Follow::YAW_BEHAVE_SAME_AS_LEAD_VEHICLE: {
     112             :                 // Match the heading of the lead vehicle
     113           0 :                 yaw_rad = radians(target_heading_deg);
     114           0 :                 yaw_rate_rads = radians(target_heading_rate_degs);
     115           0 :                 break;
     116             :             }
     117             : 
     118           0 :             case AP_Follow::YAW_BEHAVE_DIR_OF_FLIGHT: {
     119             :                 // Face the direction of travel
     120           0 :                 if (vel_ofs_ned_ms.xy().length_squared() > 1.0) {
     121           0 :                     yaw_rad = vel_ofs_ned_ms.xy().angle();
     122             :                 }
     123           0 :                 break;
     124             :             }
     125             : 
     126           0 :             case AP_Follow::YAW_BEHAVE_NONE:
     127             :             default:
     128             :                 // do nothing
     129           0 :                break;
     130             : 
     131             :         }
     132             :     } else {
     133             :         // Target data is invalid; hold position using zero velocity and acceleration inputs
     134         165 :         Vector2f vel_ne_zero;
     135         165 :         Vector2f accel_ne_zero;
     136         165 :         pos_control->input_vel_accel_NE_m(vel_ne_zero, accel_ne_zero, false);
     137         165 :         float vel_d_zero = 0.0;
     138         165 :         pos_control->input_vel_accel_D_m(vel_d_zero, 0.0, false);
     139         165 :         yaw_rate_rads = 0.0f;
     140             :     }
     141             : 
     142             :     // update the position controller
     143       15437 :     pos_control->NE_update_controller();
     144       15437 :     pos_control->D_update_controller();
     145             : 
     146             :     // call attitude controller
     147       15437 :     attitude_control->input_thrust_vector_heading_rad(pos_control->get_thrust_vector(), yaw_rad, yaw_rate_rads);
     148             : }
     149             : 
     150         243 : float ModeFollow::wp_distance_m() const
     151             : {
     152         243 :     return g2.follow.get_distance_to_target_m();
     153             : }
     154             : 
     155         243 : float ModeFollow::wp_bearing_deg() const
     156             : {
     157         243 :     return g2.follow.get_bearing_to_target_deg();
     158             : }
     159             : 
     160             : // Returns target location with offset applied, for MAVLink reporting
     161         201 : bool ModeFollow::get_wp(Location &loc) const
     162             : {
     163         201 :     Vector3f vel_ned_ms;
     164         201 :     return g2.follow.get_target_location_and_velocity_ofs(loc, vel_ned_ms);
     165             : }
     166             : 
     167             : #endif // MODE_FOLLOW_ENABLED

Generated by: LCOV version 1.14