mirror of
https://github.com/ArduPilot/ardupilot
synced 2025-01-03 06:28:27 -04:00
b2898ca1d3
The slowdown calculations should apply only to the relative velocity, not the absolute one. Thus the target baseline velocity should be added afterwards. Naturally the absolute velocity limits should be applied afterwards.
176 lines
6.8 KiB
C++
176 lines
6.8 KiB
C++
#include "Copter.h"
|
|
|
|
#if MODE_FOLLOW_ENABLED == ENABLED
|
|
|
|
/*
|
|
* mode_follow.cpp - follow another mavlink-enabled vehicle by system id
|
|
*
|
|
* TODO: stick control to move around on sphere
|
|
* TODO: stick control to change sphere diameter
|
|
* TODO: "channel 7 option" to lock onto "pointed at" target
|
|
* TODO: do better in terms of loitering around the moving point; may need a PID? Maybe use loiter controller somehow?
|
|
* TODO: extrapolate target vehicle position using its velocity and acceleration
|
|
* TODO: ensure AC_AVOID_ENABLED is true because we rely on it velocity limiting functions
|
|
*/
|
|
|
|
// initialise follow mode
|
|
bool ModeFollow::init(const bool ignore_checks)
|
|
{
|
|
if (!g2.follow.enabled()) {
|
|
gcs().send_text(MAV_SEVERITY_WARNING, "Set FOLL_ENABLE = 1");
|
|
return false;
|
|
}
|
|
|
|
#if HAL_MOUNT_ENABLED
|
|
AP_Mount *mount = AP_Mount::get_singleton();
|
|
// follow the lead vehicle using sysid
|
|
if (g2.follow.option_is_enabled(AP_Follow::Option::MOUNT_FOLLOW_ON_ENTER) && mount != nullptr) {
|
|
mount->set_target_sysid(g2.follow.get_target_sysid());
|
|
}
|
|
#endif
|
|
|
|
// re-use guided mode
|
|
return ModeGuided::init(ignore_checks);
|
|
}
|
|
|
|
// perform cleanup required when leaving follow mode
|
|
void ModeFollow::exit()
|
|
{
|
|
g2.follow.clear_offsets_if_required();
|
|
}
|
|
|
|
void ModeFollow::run()
|
|
{
|
|
// if not armed set throttle to zero and exit immediately
|
|
if (is_disarmed_or_landed()) {
|
|
make_safe_ground_handling();
|
|
return;
|
|
}
|
|
|
|
// set motors to full range
|
|
motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
|
|
|
|
// re-use guided mode's velocity controller
|
|
// Note: this is safe from interference from GCSs and companion computer's whose guided mode
|
|
// position and velocity requests will be ignored while the vehicle is not in guided mode
|
|
|
|
// variables to be sent to velocity controller
|
|
Vector3f desired_velocity_neu_cms;
|
|
bool use_yaw = false;
|
|
float yaw_cd = 0.0f;
|
|
|
|
Vector3f dist_vec; // vector to lead vehicle
|
|
Vector3f dist_vec_offs; // vector to lead vehicle + offset
|
|
Vector3f vel_of_target; // velocity of lead vehicle
|
|
if (g2.follow.get_target_dist_and_vel_ned(dist_vec, dist_vec_offs, vel_of_target)) {
|
|
// convert dist_vec_offs to cm in NEU
|
|
const Vector3f dist_vec_offs_neu(dist_vec_offs.x * 100.0f, dist_vec_offs.y * 100.0f, -dist_vec_offs.z * 100.0f);
|
|
|
|
// calculate desired relative velocity vector in cm/s in NEU
|
|
const float kp = g2.follow.get_pos_p().kP();
|
|
desired_velocity_neu_cms = dist_vec_offs_neu * kp;
|
|
|
|
// create horizontal desired velocity vector (required for slow down calculations)
|
|
Vector2f desired_velocity_xy_cms(desired_velocity_neu_cms.x, desired_velocity_neu_cms.y);
|
|
|
|
// create horizontal unit vector towards target (required for slow down calculations)
|
|
Vector2f dir_to_target_xy(desired_velocity_xy_cms.x, desired_velocity_xy_cms.y);
|
|
if (!dir_to_target_xy.is_zero()) {
|
|
dir_to_target_xy.normalize();
|
|
}
|
|
|
|
// slow down horizontally as we approach target (use 1/2 of maximum deceleration for gentle slow down)
|
|
const float dist_to_target_xy = Vector2f(dist_vec_offs_neu.x, dist_vec_offs_neu.y).length();
|
|
copter.avoid.limit_velocity_2D(pos_control->get_pos_xy_p().kP().get(), pos_control->get_max_accel_xy_cmss() * 0.5f, desired_velocity_xy_cms, dir_to_target_xy, dist_to_target_xy, copter.G_Dt);
|
|
// copy horizontal velocity limits back to 3d vector
|
|
desired_velocity_neu_cms.xy() = desired_velocity_xy_cms;
|
|
|
|
// limit vertical desired_velocity_neu_cms to slow as we approach target (we use 1/2 of maximum deceleration for gentle slow down)
|
|
const float des_vel_z_max = copter.avoid.get_max_speed(pos_control->get_pos_z_p().kP().get(), pos_control->get_max_accel_z_cmss() * 0.5f, fabsf(dist_vec_offs_neu.z), copter.G_Dt);
|
|
desired_velocity_neu_cms.z = constrain_float(desired_velocity_neu_cms.z, -des_vel_z_max, des_vel_z_max);
|
|
|
|
// Add the target velocity baseline.
|
|
desired_velocity_neu_cms.xy() += vel_of_target.xy() * 100.0f;
|
|
desired_velocity_neu_cms.z += -vel_of_target.z * 100.0f;
|
|
|
|
// scale desired velocity to stay within horizontal speed limit
|
|
desired_velocity_neu_cms.xy().limit_length(pos_control->get_max_speed_xy_cms());
|
|
|
|
// limit desired velocity to be between maximum climb and descent rates
|
|
desired_velocity_neu_cms.z = constrain_float(desired_velocity_neu_cms.z, -fabsf(pos_control->get_max_speed_down_cms()), pos_control->get_max_speed_up_cms());
|
|
|
|
// limit the velocity for obstacle/fence avoidance
|
|
copter.avoid.adjust_velocity(desired_velocity_neu_cms, pos_control->get_pos_xy_p().kP().get(), pos_control->get_max_accel_xy_cmss(), pos_control->get_pos_z_p().kP().get(), pos_control->get_max_accel_z_cmss(), G_Dt);
|
|
|
|
// calculate vehicle heading
|
|
switch (g2.follow.get_yaw_behave()) {
|
|
case AP_Follow::YAW_BEHAVE_FACE_LEAD_VEHICLE: {
|
|
if (dist_vec.xy().length_squared() > 1.0) {
|
|
yaw_cd = get_bearing_cd(Vector2f{}, dist_vec.xy());
|
|
use_yaw = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case AP_Follow::YAW_BEHAVE_SAME_AS_LEAD_VEHICLE: {
|
|
float target_hdg = 0.0f;
|
|
if (g2.follow.get_target_heading_deg(target_hdg)) {
|
|
yaw_cd = target_hdg * 100.0f;
|
|
use_yaw = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case AP_Follow::YAW_BEHAVE_DIR_OF_FLIGHT: {
|
|
if (desired_velocity_neu_cms.xy().length_squared() > (100.0 * 100.0)) {
|
|
yaw_cd = get_bearing_cd(Vector2f{}, desired_velocity_neu_cms.xy());
|
|
use_yaw = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case AP_Follow::YAW_BEHAVE_NONE:
|
|
default:
|
|
// do nothing
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
// log output at 10hz
|
|
uint32_t now = AP_HAL::millis();
|
|
bool log_request = false;
|
|
if ((now - last_log_ms >= 100) || (last_log_ms == 0)) {
|
|
log_request = true;
|
|
last_log_ms = now;
|
|
}
|
|
// re-use guided mode's velocity controller (takes NEU)
|
|
ModeGuided::set_velocity(desired_velocity_neu_cms, use_yaw, yaw_cd, false, 0.0f, false, log_request);
|
|
|
|
ModeGuided::run();
|
|
}
|
|
|
|
uint32_t ModeFollow::wp_distance() const
|
|
{
|
|
return g2.follow.get_distance_to_target() * 100;
|
|
}
|
|
|
|
int32_t ModeFollow::wp_bearing() const
|
|
{
|
|
return g2.follow.get_bearing_to_target() * 100;
|
|
}
|
|
|
|
/*
|
|
get target position for mavlink reporting
|
|
*/
|
|
bool ModeFollow::get_wp(Location &loc) const
|
|
{
|
|
float dist = g2.follow.get_distance_to_target();
|
|
float bearing = g2.follow.get_bearing_to_target();
|
|
loc = copter.current_loc;
|
|
loc.offset_bearing(bearing, dist);
|
|
return true;
|
|
}
|
|
|
|
#endif // MODE_FOLLOW_ENABLED == ENABLED
|