@@ -106,8 +106,18 @@ void ModeGuided::run()
106106 case SubMode::Angle:
107107 angle_control_run ();
108108 break ;
109+
110+ #if AC_COPTER_MODEGUIDED_ORBIT_ENABLED
111+ case SubMode::Orbit:
112+ if (_circle_moving_to_edge) {
113+ wp_control_run ();
114+ } else {
115+ orbit_run ();
116+ }
117+ break ;
118+ #endif // AC_COPTER_MODEGUIDED_ORBIT_ENABLED
109119 }
110- }
120+ }
111121
112122// returns true if the Guided-mode-option is set (see GUID_OPTIONS)
113123bool ModeGuided::option_is_enabled (Option option) const
@@ -147,6 +157,9 @@ bool ModeGuided::move_vehicle_on_ekf_reset() const
147157 case SubMode::WP:
148158 case SubMode::Pos:
149159 case SubMode::PosVelAccel:
160+ #if AC_COPTER_MODEGUIDED_ORBIT_ENABLED
161+ case SubMode::Orbit:
162+ #endif
150163 // these submodes have absolute position targets so we smoothly slew the target upon an ekf reset
151164 return true ;
152165 }
@@ -241,6 +254,20 @@ void ModeGuided::wp_control_run()
241254 // run waypoint controller
242255 copter.failsafe_terrain_set_status (wp_nav->update_wpnav ());
243256
257+ // if moving to circle edge, check if we've arrived
258+ #if AC_COPTER_MODEGUIDED_ORBIT_ENABLED
259+ if (guided_mode == SubMode::Orbit && _circle_moving_to_edge && wp_nav->reached_wp_destination ()) {
260+ // initialise circle controller and switch to circle submode
261+ copter.circle_nav ->init_NED_m (
262+ copter.circle_nav ->get_center_NED_m (),
263+ copter.circle_nav ->center_is_terrain_alt (),
264+ _orbit_rate_degs);
265+ _circle_moving_to_edge = false ;
266+ orbit_apply_yaw_behaviour ();
267+ guided_mode = SubMode::Orbit;
268+ }
269+ #endif // AP_MAVLINK_MAV_CMD_DO_ORBIT_ENABLED
270+
244271 // call z-axis position controller (wpnav should have already updated it's alt target)
245272 pos_control->D_update_controller ();
246273
@@ -449,6 +476,21 @@ bool ModeGuided::get_wp(Location& destination) const
449476 case SubMode::Pos:
450477 destination = Location::from_ekf_offset_NED_m (guided_pos_target_ned_m, guided_is_terrain_alt ? Location::AltFrame::ABOVE_TERRAIN : Location::AltFrame::ABOVE_ORIGIN);
451478 return true ;
479+ #if AC_COPTER_MODEGUIDED_ORBIT_ENABLED
480+ case SubMode::Orbit:
481+ if (_circle_moving_to_edge) {
482+ return wp_nav->get_oa_wp_destination (destination);
483+ }
484+ // already orbiting - return circle center
485+ {
486+ Location circle_center;
487+ if (!AP::ahrs ().get_location_from_origin_offset_NED (circle_center, copter.circle_nav ->get_center_NED_m ())) {
488+ return false ;
489+ }
490+ destination = circle_center;
491+ return true ;
492+ }
493+ #endif // AP_MAVLINK_MAV_CMD_DO_ORBIT_ENABLED
452494 case SubMode::Angle:
453495 case SubMode::TakeOff:
454496 case SubMode::Accel:
@@ -1147,6 +1189,12 @@ float ModeGuided::wp_distance_m() const
11471189 return get_horizontal_distance (pos_control->get_pos_estimate_NED_m ().xy ().tofloat (), guided_pos_target_ned_m.xy ().tofloat ());
11481190 case SubMode::PosVelAccel:
11491191 return pos_control->get_pos_error_NE_m ();
1192+ #if AC_COPTER_MODEGUIDED_ORBIT_ENABLED
1193+ case SubMode::Orbit:
1194+ return _circle_moving_to_edge ?
1195+ wp_nav->get_wp_distance_to_destination_m () :
1196+ copter.circle_nav ->get_distance_to_target_m ();
1197+ #endif
11501198 default :
11511199 return 0 .0f ;
11521200 }
@@ -1161,6 +1209,12 @@ float ModeGuided::wp_bearing_deg() const
11611209 return degrees (get_bearing_rad (pos_control->get_pos_estimate_NED_m ().xy ().tofloat (), guided_pos_target_ned_m.xy ().tofloat ()));
11621210 case SubMode::PosVelAccel:
11631211 return degrees (pos_control->get_bearing_to_target_rad ());
1212+ #if AC_COPTER_MODEGUIDED_ORBIT_ENABLED
1213+ case SubMode::Orbit:
1214+ return _circle_moving_to_edge ?
1215+ degrees (wp_nav->get_wp_bearing_to_destination_rad ()) :
1216+ degrees (copter.circle_nav ->get_bearing_to_target_rad ());
1217+ #endif
11641218 case SubMode::TakeOff:
11651219 case SubMode::Accel:
11661220 case SubMode::VelAccel:
@@ -1183,6 +1237,11 @@ float ModeGuided::crosstrack_error_m() const
11831237 case SubMode::VelAccel:
11841238 case SubMode::PosVelAccel:
11851239 return pos_control->crosstrack_error_m ();
1240+ #if AC_COPTER_MODEGUIDED_ORBIT_ENABLED
1241+ case SubMode::Orbit:
1242+ return _circle_moving_to_edge ?
1243+ wp_nav->crosstrack_error_m () : 0 ;
1244+ #endif
11861245 case SubMode::Angle:
11871246 // no track to have a crosstrack to
11881247 return 0 ;
@@ -1211,4 +1270,110 @@ bool ModeGuided::resume()
12111270 return true ;
12121271}
12131272
1214- #endif
1273+ #if AC_COPTER_MODEGUIDED_ORBIT_ENABLED
1274+ // orbit_start - initialise guided controller to fly a circle around a specified location
1275+ void ModeGuided::orbit_start (const Location &circle_center, float radius_m, bool ccw, float speed_ms, bool update_turns, float turns, ORBIT_YAW_BEHAVIOUR yaw_behaviour)
1276+ {
1277+ // set circle center, radius and direction
1278+ copter.circle_nav ->set_center (circle_center);
1279+ // configure position controller speeds and accelerations
1280+ pos_control->NE_set_max_speed_accel_m (wp_nav->get_default_speed_NE_ms (), wp_nav->get_wp_acceleration_mss ());
1281+ pos_control->NE_set_correction_speed_accel_m (wp_nav->get_default_speed_NE_ms (), wp_nav->get_wp_acceleration_mss ());
1282+ 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 ());
1283+ 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 ());
1284+ copter.circle_nav ->set_radius_m (radius_m);
1285+
1286+ // convert tangential speed to angular rate, or use default rate with correct direction
1287+ if (is_positive (speed_ms) && is_positive (radius_m)) {
1288+ _orbit_rate_degs = degrees (speed_ms / radius_m);
1289+ } else {
1290+ _orbit_rate_degs = fabsf (copter.circle_nav ->get_rate_degs ());
1291+ }
1292+ if (ccw) {
1293+ _orbit_rate_degs = -_orbit_rate_degs;
1294+ }
1295+ copter.circle_nav ->set_rate_degs (_orbit_rate_degs);
1296+ // store requested number of turns (0 = orbit forever)
1297+ if (update_turns) {
1298+ _orbit_turns = turns;
1299+ _orbit_angle_total_at_start = copter.circle_nav ->get_angle_total_rad ();
1300+ }
1301+ _orbit_yaw_behaviour = yaw_behaviour;
1302+
1303+ // check distance to edge of circle
1304+ Vector3p circle_edge_ned_m;
1305+ float dist_to_edge_m;
1306+ copter.circle_nav ->get_closest_point_on_circle_NED_m (circle_edge_ned_m, dist_to_edge_m);
1307+
1308+ // if more than 3m away, fly to edge first
1309+ if (dist_to_edge_m > 3 .0f ) {
1310+ Location circle_edge = Location::from_ekf_offset_NED_m (circle_edge_ned_m, Location::AltFrame::ABOVE_ORIGIN);
1311+ circle_edge.copy_alt_from (circle_center);
1312+ if (!wp_nav->set_wp_destination_loc (circle_edge)) {
1313+ // terrain data missing
1314+ copter.failsafe_terrain_on_event ();
1315+ return ;
1316+ }
1317+ // during approach look in direction of travel (default WP yaw behaviour)
1318+ auto_yaw.set_mode_to_default (false );
1319+ guided_mode = SubMode::Orbit;
1320+ _circle_moving_to_edge = true ;
1321+ } else {
1322+ // already at edge - start circling immediately
1323+ copter.circle_nav ->init_NED_m (
1324+ copter.circle_nav ->get_center_NED_m (),
1325+ copter.circle_nav ->center_is_terrain_alt (),
1326+ _orbit_rate_degs);
1327+ // set yaw behaviour
1328+ orbit_apply_yaw_behaviour ();
1329+ guided_mode = SubMode::Orbit;
1330+ }
1331+ }
1332+
1333+ // orbit_apply_yaw_behaviour - set yaw mode based on ORBIT_YAW_BEHAVIOUR parameter
1334+ void ModeGuided::orbit_apply_yaw_behaviour ()
1335+ {
1336+ switch ((ORBIT_YAW_BEHAVIOUR)_orbit_yaw_behaviour) {
1337+ case ORBIT_YAW_BEHAVIOUR_HOLD_FRONT_TO_CIRCLE_CENTER:
1338+ default :
1339+ auto_yaw.set_mode (AutoYaw::Mode::CIRCLE);
1340+ break ;
1341+ case ORBIT_YAW_BEHAVIOUR_HOLD_INITIAL_HEADING:
1342+ auto_yaw.set_fixed_yaw_rad (copter.ahrs .get_yaw_rad (), 0 .0f , 0 , false );
1343+ break ;
1344+ case ORBIT_YAW_BEHAVIOUR_UNCONTROLLED:
1345+ auto_yaw.set_mode (AutoYaw::Mode::HOLD);
1346+ break ;
1347+ case ORBIT_YAW_BEHAVIOUR_HOLD_FRONT_TANGENT_TO_CIRCLE:
1348+ auto_yaw.set_mode (AutoYaw::Mode::LOOK_AHEAD);
1349+ break ;
1350+ case ORBIT_YAW_BEHAVIOUR_RC_CONTROLLED:
1351+ auto_yaw.set_mode (AutoYaw::Mode::PILOT_RATE);
1352+ break ;
1353+ case ORBIT_YAW_BEHAVIOUR_UNCHANGED:
1354+ // do not change yaw mode
1355+ break ;
1356+ }
1357+ }
1358+
1359+ // orbit_run - run circle controller
1360+ void ModeGuided::orbit_run ()
1361+ {
1362+ // call circle controller
1363+ copter.failsafe_terrain_set_status (copter.circle_nav ->update_ms (0 .0f ));
1364+ // check if we've completed the requested number of turns
1365+ if (is_positive (_orbit_turns)) {
1366+ const float turns_completed = fabsf (copter.circle_nav ->get_angle_total_rad () / float (M_2PI));
1367+ if (turns_completed >= _orbit_turns) {
1368+ // orbit complete - hold position and maintain yaw behaviour
1369+ hold_position ();
1370+ orbit_apply_yaw_behaviour ();
1371+ return ;
1372+ }
1373+ }
1374+ // call attitude, position and yaw controllers
1375+ pos_control->D_update_controller ();
1376+ attitude_control->input_thrust_vector_heading (pos_control->get_thrust_vector (), auto_yaw.get_heading ());
1377+ }
1378+ #endif // AC_COPTER_MODEGUIDED_ORBIT_ENABLED
1379+ #endif // MODE_GUIDED_ENABLED
0 commit comments