Skip to content

Commit

Permalink
ArduPlane: Support a set of offboard MAVLink guided controls with rates
Browse files Browse the repository at this point in the history
+ 3 rounds of fixes
  • Loading branch information
davidbuzz committed May 16, 2020
1 parent 0c94338 commit b45682f
Show file tree
Hide file tree
Showing 13 changed files with 455 additions and 2 deletions.
4 changes: 3 additions & 1 deletion ArduPlane/ArduPlane.cpp
Expand Up @@ -221,8 +221,10 @@ void Plane::update_logging2(void)
#endif
}

if (should_log(MASK_LOG_NTUN))
if (should_log(MASK_LOG_NTUN)) {
Log_Write_Nav_Tuning();
Log_Write_Guided();
}

if (should_log(MASK_LOG_RC))
Log_Write_RC();
Expand Down
30 changes: 30 additions & 0 deletions ArduPlane/Attitude.cpp
Expand Up @@ -588,6 +588,36 @@ void Plane::calc_nav_roll()
plane.guided_state.last_forced_rpy_ms.x > 0 &&
millis() - plane.guided_state.last_forced_rpy_ms.x < 3000) {
commanded_roll = plane.guided_state.forced_rpy_cd.x;
#if OFFBOARD_GUIDED == ENABLED
// guided_state.target_heading is radians at this point between -pi and pi ( defaults to -4 )
} else if ((control_mode == &mode_guided) && (guided_state.target_heading_type != GUIDED_HEADING_NONE) ) {
uint32_t tnow = AP_HAL::millis();
float delta = (tnow - guided_state.target_heading_time_ms) * 1e-3f;
guided_state.target_heading_time_ms = tnow;

float error = 0.0f;
if (guided_state.target_heading_type == GUIDED_HEADING_HEADING) {
error = wrap_PI(guided_state.target_heading - AP::ahrs().yaw);
} else {
Vector2f groundspeed = AP::ahrs().groundspeed_vector();
error = wrap_PI(guided_state.target_heading - atan2f(-groundspeed.y, -groundspeed.x) + M_PI);
}

float bank_limit = degrees(atanf(guided_state.target_heading_accel_limit/GRAVITY_MSS)) * 1e2f;

g2.guidedHeading.update_error(error); // push error into AC_PID , possible improvement is to use update_all instead.?
g2.guidedHeading.set_dt(delta);

float i = g2.guidedHeading.get_i(); // get integrator TODO
if (((is_negative(error) && !guided_state.target_heading_limit_low) || (is_positive(error) && !guided_state.target_heading_limit_high))) {
i = g2.guidedHeading.get_i();
}

float desired = g2.guidedHeading.get_p() + i + g2.guidedHeading.get_d();
guided_state.target_heading_limit_low = (desired <= -bank_limit);
guided_state.target_heading_limit_high = (desired >= bank_limit);
commanded_roll = constrain_float(desired, -bank_limit, bank_limit);
#endif // OFFBOARD_GUIDED == ENABLED
}

nav_roll_cd = constrain_int32(commanded_roll, -roll_limit_cd, roll_limit_cd);
Expand Down
157 changes: 157 additions & 0 deletions ArduPlane/GCS_Mavlink.cpp
Expand Up @@ -794,13 +794,170 @@ MAV_RESULT GCS_MAVLINK_Plane::handle_command_int_do_reposition(const mavlink_com
return MAV_RESULT_FAILED;
}

// these are GUIDED mode commands that are RATE or slew enabled, so you can have more powerful control than default controls.
MAV_RESULT GCS_MAVLINK_Plane::handle_command_int_guided_slew_commands(const mavlink_command_int_t &packet)
{
switch(packet.command) {

#if OFFBOARD_GUIDED == ENABLED
case MAV_CMD_GUIDED_CHANGE_SPEED: {
// command is only valid in guided mode
if (plane.control_mode != &plane.mode_guided) {
return MAV_RESULT_FAILED;
}

// only airspeed commands are supported right now...
if (int(packet.param1) != SPEED_TYPE_AIRSPEED) { // since SPEED_TYPE is int in range 0-1 and packet.param1 is a *float* this works.
return MAV_RESULT_DENIED;
}

// reject airspeeds that are outside of the tuning envelope
if (packet.param2 > plane.aparm.airspeed_max || packet.param2 < plane.aparm.airspeed_min) {
return MAV_RESULT_DENIED;
}

// no need to process any new packet/s with the
// same airspeed any further, if we are already doing it.
float new_target_airspeed_cm = packet.param2 * 100;
if ( is_equal(new_target_airspeed_cm,plane.guided_state.target_airspeed_cm)) {
return MAV_RESULT_ACCEPTED;
}
plane.guided_state.target_airspeed_cm = new_target_airspeed_cm;
plane.guided_state.target_airspeed_time_ms = AP_HAL::millis();

if (is_zero(packet.param3)) {
// the user wanted /maximum acceleration, pick a large value as close enough
plane.guided_state.target_airspeed_accel = 1000.0f;
} else {
plane.guided_state.target_airspeed_accel = fabsf(packet.param3);
}

// assign an acceleration direction
if (plane.guided_state.target_airspeed_cm < plane.target_airspeed_cm) {
plane.guided_state.target_airspeed_accel *= -1.0f;
}
return MAV_RESULT_ACCEPTED;
}

case MAV_CMD_GUIDED_CHANGE_ALTITUDE: {
// command is only valid in guided
if (plane.control_mode != &plane.mode_guided) {
return MAV_RESULT_FAILED;
}

// disallow default value of -1 and dangerous value of zero
if (is_equal(packet.z, -1.0f) || is_equal(packet.z, 0.0f)){
return MAV_RESULT_DENIED;
}

// the requested alt data might be relative or absolute
float new_target_alt = packet.z * 100;
float new_target_alt_rel = packet.z * 100 + plane.home.alt;

// only global/relative/terrain frames are supported
switch(packet.frame) {
case MAV_FRAME_GLOBAL_RELATIVE_ALT: {
if (is_equal(plane.guided_state.target_alt,new_target_alt_rel) ) { // compare two floats as near-enough
// no need to process any new packet/s with the same ALT any further, if we are already doing it.
return MAV_RESULT_ACCEPTED;
}
plane.guided_state.target_alt = new_target_alt_rel;
break;
}
case MAV_FRAME_GLOBAL: {
if (is_equal(plane.guided_state.target_alt,new_target_alt) ) { // compare two floats as near-enough
// no need to process any new packet/s with the same ALT any further, if we are already doing it.
return MAV_RESULT_ACCEPTED;
}
plane.guided_state.target_alt = new_target_alt;
break;
}
default:
// MAV_RESULT_DENIED means Command is invalid (is supported but has invalid parameters).
return MAV_RESULT_DENIED;
}

plane.guided_state.target_alt_frame = packet.frame;
plane.guided_state.last_target_alt = plane.current_loc.alt; // FIXME: Reference frame is not corrected for here
plane.guided_state.target_alt_time_ms = AP_HAL::millis();

if (is_zero(packet.param3)) {
// the user wanted /maximum acceleration, pick a large value as close enough
plane.guided_state.target_alt_accel = 1000.0;
} else {
plane.guided_state.target_alt_accel = fabsf(packet.param3);
}

// assign an acceleration direction
if (plane.guided_state.target_alt < plane.current_loc.alt) {
plane.guided_state.target_alt_accel *= -1.0f;
}
return MAV_RESULT_ACCEPTED;
}

case MAV_CMD_GUIDED_CHANGE_HEADING: {

// command is only valid in guided mode
if (plane.control_mode != &plane.mode_guided) {
return MAV_RESULT_FAILED;
}

// don't accept packets outside of [0-360] degree range
if (packet.param2 < 0.0f || packet.param2 >= 360.0f) {
return MAV_RESULT_DENIED;
}

float new_target_heading = radians(wrap_180(packet.param2));

// if packet is requesting us to go to the heading we are already going to, we-re already on it.
if ( (is_equal(new_target_heading,plane.guided_state.target_heading))) { // compare two floats as near-enough
return MAV_RESULT_ACCEPTED;
}

// course over ground
if ( int(packet.param1) == HEADING_TYPE_COURSE_OVER_GROUND) { // compare as nearest int
plane.guided_state.target_heading_type = GUIDED_HEADING_COG;
plane.prev_WP_loc = plane.current_loc;
// normal vehicle heading
} else if (int(packet.param1) == HEADING_TYPE_HEADING) { // compare as nearest int
plane.guided_state.target_heading_type = GUIDED_HEADING_HEADING;
} else {
// MAV_RESULT_DENIED means Command is invalid (is supported but has invalid parameters).
return MAV_RESULT_DENIED;
}

plane.g2.guidedHeading.reset_I();

plane.guided_state.target_heading = new_target_heading;
plane.guided_state.target_heading_accel_limit = MAX(packet.param3, 0.05f);
plane.guided_state.target_heading_time_ms = AP_HAL::millis();
return MAV_RESULT_ACCEPTED;
}
#endif // OFFBOARD_GUIDED == ENABLED


}
// anything else ...
return MAV_RESULT_UNSUPPORTED;

}

MAV_RESULT GCS_MAVLINK_Plane::handle_command_int_packet(const mavlink_command_int_t &packet)
{

plane.Log_Write_MavCmdI(packet);

switch(packet.command) {

case MAV_CMD_DO_REPOSITION:
return handle_command_int_do_reposition(packet);

// special 'slew-enabled' guided commands here... for speed,alt, and direction commands
case MAV_CMD_GUIDED_CHANGE_SPEED:
case MAV_CMD_GUIDED_CHANGE_ALTITUDE:
case MAV_CMD_GUIDED_CHANGE_HEADING:
return handle_command_int_guided_slew_commands(packet);

default:
return GCS_MAVLINK::handle_command_int_packet(packet);
}
Expand Down
1 change: 1 addition & 0 deletions ArduPlane/GCS_Mavlink.h
Expand Up @@ -51,6 +51,7 @@ class GCS_MAVLINK_Plane : public GCS_MAVLINK
void handle_change_alt_request(AP_Mission::Mission_Command &cmd) override;
void handle_rc_channels_override(const mavlink_message_t &msg) override;
MAV_RESULT handle_command_int_do_reposition(const mavlink_command_int_t &packet);
MAV_RESULT handle_command_int_guided_slew_commands(const mavlink_command_int_t &packet);


bool try_send_message(enum ap_message id) override;
Expand Down

0 comments on commit b45682f

Please sign in to comment.