AC_Circle.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. #include <AP_HAL/AP_HAL.h>
  2. #include "AC_Circle.h"
  3. #include <AP_Math/AP_Math.h>
  4. extern const AP_HAL::HAL& hal;
  5. const AP_Param::GroupInfo AC_Circle::var_info[] = {
  6. // @Param: RADIUS
  7. // @DisplayName: Circle Radius
  8. // @Description: Defines the radius of the circle the vehicle will fly when in Circle flight mode
  9. // @Units: cm
  10. // @Range: 0 10000
  11. // @Increment: 100
  12. // @User: Standard
  13. AP_GROUPINFO("RADIUS", 0, AC_Circle, _radius, AC_CIRCLE_RADIUS_DEFAULT),
  14. // @Param: RATE
  15. // @DisplayName: Circle rate
  16. // @Description: Circle mode's turn rate in deg/sec. Positive to turn clockwise, negative for counter clockwise
  17. // @Units: deg/s
  18. // @Range: -90 90
  19. // @Increment: 1
  20. // @User: Standard
  21. AP_GROUPINFO("RATE", 1, AC_Circle, _rate, AC_CIRCLE_RATE_DEFAULT),
  22. AP_GROUPEND
  23. };
  24. // Default constructor.
  25. // Note that the Vector/Matrix constructors already implicitly zero
  26. // their values.
  27. //
  28. AC_Circle::AC_Circle(const AP_InertialNav& inav, const AP_AHRS_View& ahrs, AC_PosControl& pos_control) :
  29. _inav(inav),
  30. _ahrs(ahrs),
  31. _pos_control(pos_control),
  32. _yaw(0.0f),
  33. _angle(0.0f),
  34. _angle_total(0.0f),
  35. _angular_vel(0.0f),
  36. _angular_vel_max(0.0f),
  37. _angular_accel(0.0f)
  38. {
  39. AP_Param::setup_object_defaults(this, var_info);
  40. // init flags
  41. _flags.panorama = false;
  42. }
  43. /// init - initialise circle controller setting center specifically
  44. /// caller should set the position controller's x,y and z speeds and accelerations before calling this
  45. void AC_Circle::init(const Vector3f& center)
  46. {
  47. _center = center;
  48. // initialise position controller (sets target roll angle, pitch angle and I terms based on vehicle current lean angles)
  49. _pos_control.set_desired_accel_xy(0.0f,0.0f);
  50. _pos_control.set_desired_velocity_xy(0.0f,0.0f);
  51. _pos_control.init_xy_controller();
  52. // set initial position target to reasonable stopping point
  53. _pos_control.set_target_to_stopping_point_xy();
  54. _pos_control.set_target_to_stopping_point_z();
  55. // calculate velocities
  56. calc_velocities(true);
  57. // set start angle from position
  58. init_start_angle(false);
  59. }
  60. /// init - initialise circle controller setting center using stopping point and projecting out based on the copter's heading
  61. /// caller should set the position controller's x,y and z speeds and accelerations before calling this
  62. void AC_Circle::init()
  63. {
  64. // initialise position controller (sets target roll angle, pitch angle and I terms based on vehicle current lean angles)
  65. _pos_control.set_desired_accel_xy(0.0f,0.0f);
  66. _pos_control.set_desired_velocity_xy(0.0f,0.0f);
  67. _pos_control.init_xy_controller();
  68. // set initial position target to reasonable stopping point
  69. _pos_control.set_target_to_stopping_point_xy();
  70. _pos_control.set_target_to_stopping_point_z();
  71. // get stopping point
  72. const Vector3f& stopping_point = _pos_control.get_pos_target();
  73. // set circle center to circle_radius ahead of stopping point
  74. _center.x = stopping_point.x + _radius * _ahrs.cos_yaw();
  75. _center.y = stopping_point.y + _radius * _ahrs.sin_yaw();
  76. _center.z = stopping_point.z;
  77. // calculate velocities
  78. calc_velocities(true);
  79. // set starting angle from vehicle heading
  80. init_start_angle(true);
  81. }
  82. /// set_circle_rate - set circle rate in degrees per second
  83. void AC_Circle::set_rate(float deg_per_sec)
  84. {
  85. if (!is_equal(deg_per_sec, _rate.get())) {
  86. _rate = deg_per_sec;
  87. calc_velocities(false);
  88. }
  89. }
  90. /// update - update circle controller
  91. void AC_Circle::update()
  92. {
  93. // calculate dt
  94. float dt = _pos_control.time_since_last_xy_update();
  95. if (dt >= 0.2f) {
  96. dt = 0.0f;
  97. }
  98. // ramp angular velocity to maximum
  99. if (_angular_vel < _angular_vel_max) {
  100. _angular_vel += fabsf(_angular_accel) * dt;
  101. _angular_vel = MIN(_angular_vel, _angular_vel_max);
  102. }
  103. if (_angular_vel > _angular_vel_max) {
  104. _angular_vel -= fabsf(_angular_accel) * dt;
  105. _angular_vel = MAX(_angular_vel, _angular_vel_max);
  106. }
  107. // update the target angle and total angle traveled
  108. float angle_change = _angular_vel * dt;
  109. _angle += angle_change;
  110. _angle = wrap_PI(_angle);
  111. _angle_total += angle_change;
  112. // if the circle_radius is zero we are doing panorama so no need to update loiter target
  113. if (!is_zero(_radius)) {
  114. // calculate target position
  115. Vector3f target;
  116. target.x = _center.x + _radius * cosf(-_angle);
  117. target.y = _center.y - _radius * sinf(-_angle);
  118. target.z = _pos_control.get_alt_target();
  119. // update position controller target
  120. _pos_control.set_xy_target(target.x, target.y);
  121. // heading is from vehicle to center of circle
  122. _yaw = get_bearing_cd(_inav.get_position(), _center);
  123. } else {
  124. // set target position to center
  125. Vector3f target;
  126. target.x = _center.x;
  127. target.y = _center.y;
  128. target.z = _pos_control.get_alt_target();
  129. // update position controller target
  130. _pos_control.set_xy_target(target.x, target.y);
  131. // heading is same as _angle but converted to centi-degrees
  132. _yaw = _angle * DEGX100;
  133. }
  134. // update position controller
  135. _pos_control.update_xy_controller();
  136. }
  137. // get_closest_point_on_circle - returns closest point on the circle
  138. // circle's center should already have been set
  139. // closest point on the circle will be placed in result
  140. // result's altitude (i.e. z) will be set to the circle_center's altitude
  141. // if vehicle is at the center of the circle, the edge directly behind vehicle will be returned
  142. void AC_Circle::get_closest_point_on_circle(Vector3f &result)
  143. {
  144. // return center if radius is zero
  145. if (_radius <= 0) {
  146. result = _center;
  147. return;
  148. }
  149. // get current position
  150. Vector3f stopping_point;
  151. _pos_control.get_stopping_point_xy(stopping_point);
  152. // calc vector from stopping point to circle center
  153. Vector2f vec; // vector from circle center to current location
  154. vec.x = (stopping_point.x - _center.x);
  155. vec.y = (stopping_point.y - _center.y);
  156. float dist = norm(vec.x, vec.y);
  157. // if current location is exactly at the center of the circle return edge directly behind vehicle
  158. if (is_zero(dist)) {
  159. result.x = _center.x - _radius * _ahrs.cos_yaw();
  160. result.y = _center.y - _radius * _ahrs.sin_yaw();
  161. result.z = _center.z;
  162. return;
  163. }
  164. // calculate closest point on edge of circle
  165. result.x = _center.x + vec.x / dist * _radius;
  166. result.y = _center.y + vec.y / dist * _radius;
  167. result.z = _center.z;
  168. }
  169. // calc_velocities - calculate angular velocity max and acceleration based on radius and rate
  170. // this should be called whenever the radius or rate are changed
  171. // initialises the yaw and current position around the circle
  172. void AC_Circle::calc_velocities(bool init_velocity)
  173. {
  174. // if we are doing a panorama set the circle_angle to the current heading
  175. if (_radius <= 0) {
  176. _angular_vel_max = ToRad(_rate);
  177. _angular_accel = MAX(fabsf(_angular_vel_max),ToRad(AC_CIRCLE_ANGULAR_ACCEL_MIN)); // reach maximum yaw velocity in 1 second
  178. }else{
  179. // calculate max velocity based on waypoint speed ensuring we do not use more than half our max acceleration for accelerating towards the center of the circle
  180. float velocity_max = MIN(_pos_control.get_max_speed_xy(), safe_sqrt(0.5f*_pos_control.get_max_accel_xy()*_radius));
  181. // angular_velocity in radians per second
  182. _angular_vel_max = velocity_max/_radius;
  183. _angular_vel_max = constrain_float(ToRad(_rate),-_angular_vel_max,_angular_vel_max);
  184. // angular_velocity in radians per second
  185. _angular_accel = MAX(_pos_control.get_max_accel_xy()/_radius, ToRad(AC_CIRCLE_ANGULAR_ACCEL_MIN));
  186. }
  187. // initialise angular velocity
  188. if (init_velocity) {
  189. _angular_vel = 0;
  190. }
  191. }
  192. // init_start_angle - sets the starting angle around the circle and initialises the angle_total
  193. // if use_heading is true the vehicle's heading will be used to init the angle causing minimum yaw movement
  194. // if use_heading is false the vehicle's position from the center will be used to initialise the angle
  195. void AC_Circle::init_start_angle(bool use_heading)
  196. {
  197. // initialise angle total
  198. _angle_total = 0;
  199. // if the radius is zero we are doing panorama so init angle to the current heading
  200. if (_radius <= 0) {
  201. _angle = _ahrs.yaw;
  202. return;
  203. }
  204. // if use_heading is true
  205. if (use_heading) {
  206. _angle = wrap_PI(_ahrs.yaw-M_PI);
  207. } else {
  208. // if we are exactly at the center of the circle, init angle to directly behind vehicle (so vehicle will backup but not change heading)
  209. const Vector3f &curr_pos = _inav.get_position();
  210. if (is_equal(curr_pos.x,_center.x) && is_equal(curr_pos.y,_center.y)) {
  211. _angle = wrap_PI(_ahrs.yaw-M_PI);
  212. } else {
  213. // get bearing from circle center to vehicle in radians
  214. float bearing_rad = atan2f(curr_pos.y-_center.y,curr_pos.x-_center.x);
  215. _angle = wrap_PI(bearing_rad);
  216. }
  217. }
  218. }