LCOV - code coverage report
Current view: top level - AP_Math - SCurve.h (source / functions) Hit Total Coverage
Test: lcov.info Lines: 4 4 100.0 %
Date: 2026-09-09 11:02:08 Functions: 4 4 100.0 %

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <AP_Common/AP_Common.h>
       4             : 
       5             : /*
       6             :  * SCurves calculate paths between waypoints (including the corners) using specified speed, acceleration and jerk limits
       7             :  *
       8             :  * How to use:
       9             :  *  1. create three SCurve objects called something like "prev_leg", "this_leg" and "next_leg"
      10             :  *  2. call this_leg.calculate_track() to calculate the path from the origin to the destination for the given speed, accel and jerk limits
      11             :  *  3. if the vehicle will fly past the destination to another "next destination":
      12             :  *        a) call next_leg.calculate_track() with the appropriate arguments
      13             :  *        b) set a "fast_waypoint" boolean to true.  this will be passed into "advance_target_along_track()" in the next step
      14             :  *     if there is no "next destination"
      15             :  *        a) call next_leg.init()
      16             :  *        b) set the "fast_waypoint" boolean to false
      17             :  *  4. call this_leg.advance_target_along_track() with a small "dt" value and retrieve the resulting target position, velocity and acceleration
      18             :  *     Note: the target_pos should be set to the segments's earth frame origin before this function is called
      19             :  *  5. pass the target position, velocity and acceleration into the position controller
      20             :  *  6. repeat steps 4 and 5 until finished() returns true
      21             :  *  7. promote the legs:
      22             :  *        a) set prev_leg = this_leg
      23             :  *        b) set this_leg = next_leg
      24             :  *        c) jump back to step 3
      25             :  *
      26             :  * Other features:
      27             :  *  1. set_speed_max() allows changing the max speeds mid path.  The path will be recalculated
      28             :  *  2. set_origin_speed_max() and set_destination_speed_max() allows setting the speed along the path at the beginning and end of the leg
      29             :  *     this is used to smoothly integrate with spline segments
      30             :  *
      31             :  * This library works with any units (meters, cm, etc) as long as they are used consistently.
      32             :  *    e.g. if origin and destination are meters, speeds should be in m/s, accel in m/s/s, etc.
      33             :  *
      34             :  * Terminology:
      35             :  *    position: a point in space
      36             :  *    velocity: rate of change of position.  aka speed
      37             :  *    acceleration: rate of change of speed
      38             :  *    jerk: rate of change of acceleration
      39             :  *    snap: rate of change of jerk
      40             :  *    jerk time: the time (in seconds) for jerk to increase from zero to its maximum value
      41             :  *    track: 3D path that the vehicle will follow
      42             :  *    path: position, velocity, accel and jerk kinematic profile that this library generates
      43             :  */
      44             : 
      45             : class SCurve {
      46             : 
      47             : public:
      48             : 
      49             :     // constructor
      50             :     SCurve();
      51             : 
      52             :     // initialise and clear the path
      53             :     void init();
      54             : 
      55             :     // generate a trigonometric track in 3D space that moves over a straight line
      56             :     // between two points defined by the origin and destination
      57             :     void calculate_track(const Vector3p &origin, const Vector3p &destination, float arc_rad,
      58             :                          float speed_xy, float speed_up, float speed_down,
      59             :                          float accel_xy, float accel_z, float accel_c,
      60             :                          float snap_maximum, float jerk_maximum);
      61             : 
      62             :     // generate a trigonometric track that follows a circular arc about center_ne
      63             :     // total_angle_rad is the signed swept angle and may exceed 2*pi for multiple turns
      64             :     // climb_d_m is the net change along the down (D) axis, applied linearly over the arc
      65             :     // the arc radius is taken as the distance from origin to center, so the origin lies on the circle
      66             :     void calculate_circle_track(const Vector3p &origin, const Vector2f &center_ne,
      67             :                                 float total_angle_rad, float climb_d_m,
      68             :                                 float speed_xy, float speed_up, float speed_down,
      69             :                                 float accel_xy, float accel_z, float accel_c,
      70             :                                 float snap_maximum, float jerk_maximum);
      71             : 
      72             :     // set maximum velocity and re-calculate the path using these limits
      73             :     void set_speed_max(float speed_xy, float speed_up, float speed_down);
      74             : 
      75             :     // set the maximum vehicle speed at the origin
      76             :     // returns the expected speed at the origin which will always be equal or lower than speed
      77             :     float set_origin_speed_max(float speed);
      78             : 
      79             :     // set the maximum vehicle speed at the destination
      80             :     void set_destination_speed_max(float speed);
      81             : 
      82             :     // move target location along path from origin to destination
      83             :     // prev_leg and next_leg - the paths before and after this path
      84             :     // wp_radius - max distance from the waypoint at the apex of the turn
      85             :     // accel_corner - max acceleration that the aircraft may use during the corner
      86             :     // fast_waypoint - true if vehicle will not stop at end of this leg
      87             :     // dt - the time increment the vehicle will move along the path
      88             :     // target_pos - set to this segment's origin and it will be updated to the current position target
      89             :     // target_vel and target_accel - updated with new targets
      90             :     // advance_target_along_track returns true if vehicle has passed the apex of the corner
      91             :     bool advance_target_along_track(SCurve &prev_leg, SCurve &next_leg, float wp_radius, float accel_corner, bool fast_waypoint, float dt, Vector3p &target_pos, Vector3f &target_vel, Vector3f &target_accel) WARN_IF_UNUSED;
      92             : 
      93             :     // time has reached the end of the sequence
      94             :     bool finished() const WARN_IF_UNUSED;
      95             : 
      96             :     // return the vector from the origin to the destination
      97             :     // (for an arc segment this is the chord between the endpoints, not the path flown)
      98       53161 :     const Vector3f& get_origin_to_destination() const WARN_IF_UNUSED { return seg_delta; };
      99             : 
     100             :     // return the angle swept around the arc so far, in radians, unsigned and unwrapped so it
     101             :     // grows past 2*pi on a multi-turn arc.  returns zero for a straight segment
     102             :     float get_arc_angle_covered_rad() const WARN_IF_UNUSED;
     103             : 
     104             :     // calculate the segment times for the trigonometric S-Curve path defined by:
     105             :     // Sm - maximum value of the snap profile
     106             :     // Jm - maximum value of the raised cosine jerk profile
     107             :     // V0 - initial velocity magnitude
     108             :     // Am - maximum constant acceleration
     109             :     // Vm - maximum constant velocity
     110             :     // L - Length of the path
     111             :     // tj_out, t2_out, t4_out, t6_out are the segment durations needed to achieve the kinematic path specified by the input variables
     112             :     // this is an internal function, static for test suite
     113             :     static void calculate_path(float Sm, float Jm, float V0, float Am, float Vm, float L, float &Jm_out, float &tj_out, float &t2_out, float &t4_out, float &t6_out);
     114             : 
     115             : private:
     116             : 
     117             :     // increment time and return the position, velocity and acceleration vectors relative to the origin
     118             :     void move_from_pos_vel_accel(float dt, Vector3p &pos, Vector3f &vel, Vector3f &accel);
     119             : 
     120             :     // increment time and return the position, velocity and acceleration vectors relative to the destination
     121             :     void move_to_pos_vel_accel(float dt, Vector3p &pos, Vector3f &vel, Vector3f &accel);
     122             : 
     123             :     // return the position, velocity and acceleration vectors relative to the origin at a specified time along the path
     124             :     void move_from_time_pos_vel_accel(float t, Vector3p &pos, Vector3f &vel, Vector3f &accel);
     125             : 
     126             :     // project the straight-line S-curve motion profile onto the active track segment
     127             :     // converts scalar S-curve kinematics (A1, V1, P1) into 3D position, velocity and acceleration
     128             :     // along either a circular arc or straight segment
     129             :     void project_scurve_onto_track(float scurve_A1, float scurve_V1, float scurve_P1, Vector3p &pos, Vector3f &vel, Vector3f &accel);
     130             : 
     131             :     // get desired maximum speed along track
     132      106314 :     float get_speed_along_track() const WARN_IF_UNUSED { return vel_max; }
     133             : 
     134             :     // get desired maximum acceleration along track
     135             :     float get_accel_along_track() const WARN_IF_UNUSED { return accel_max; }
     136             : 
     137             :     // get desired maximum acceleration along track
     138      106314 :     float get_accel_z_max() const WARN_IF_UNUSED { return accel_z_max; }
     139             : 
     140             :     // return the current time elapsed
     141     6313451 :     float get_time_elapsed() const WARN_IF_UNUSED { return time; }
     142             : 
     143             :     // time at the end of the sequence
     144             :     float time_end() const WARN_IF_UNUSED;
     145             : 
     146             :     // time left before sequence will complete
     147             :     float get_time_remaining() const WARN_IF_UNUSED;
     148             : 
     149             :     // time when acceleration section of the sequence will complete
     150             :     float get_accel_finished_time() const WARN_IF_UNUSED;
     151             : 
     152             :     // return true if the sequence is braking to a stop
     153             :     bool braking() const WARN_IF_UNUSED;
     154             : 
     155             :     // return time offset used to initiate the turn onto leg
     156             :     float time_accel_end() const WARN_IF_UNUSED;
     157             : 
     158             :     // return time offset used to initiate the turn from leg
     159             :     float time_decel_start() const WARN_IF_UNUSED;
     160             : 
     161             :     // increment the internal time
     162             :     void advance_time(float dt);
     163             : 
     164             :     // calculate the jerk, acceleration, velocity and position at time t
     165             :     void get_jerk_accel_vel_pos_at_time(float time_now, float &Jt_out, float &At_out, float &Vt_out, float &Pt_out) const;
     166             : 
     167             :     // calculate the jerk, acceleration, velocity and position at time t when running the constant jerk time segment
     168             :     void calc_javp_for_segment_const_jerk(float time_now, float J0, float A0, float V0, float P0, float &Jt, float &At, float &Vt, float &Pt) const;
     169             : 
     170             :     // Calculate the jerk, acceleration, velocity and position at time t when running the increasing jerk magnitude time segment based on a raised cosine profile
     171             :     void calc_javp_for_segment_incr_jerk(float time_now, float tj, float Jm, float A0, float V0, float P0, float &Jt, float &At, float &Vt, float &Pt) const;
     172             : 
     173             :     // Calculate the jerk, acceleration, velocity and position at time t when running the decreasing jerk magnitude time segment based on a raised cosine profile
     174             :     void calc_javp_for_segment_decr_jerk(float time_now, float tj, float Jm, float A0, float V0, float P0, float &Jt, float &At, float &Vt, float &Pt) const;
     175             : 
     176             :     // generate time segments for straight segment
     177             :     void add_segments(float L);
     178             : 
     179             :     // generate three time segments forming the jerk profile
     180             :     void add_segments_jerk(uint8_t &seg_pnt, float Jm, float tj, float Tcj);
     181             : 
     182             :     // generate constant jerk time segment
     183             :     void add_segment_const_jerk(uint8_t &seg_pnt, float J0, float tin);
     184             : 
     185             :     // generate increasing jerk magnitude time segment based on a raised cosine profile
     186             :     void add_segment_incr_jerk(uint8_t &seg_pnt, float Jm, float tj);
     187             : 
     188             :     // generate decreasing jerk magnitude time segment based on a raised cosine profile
     189             :     void add_segment_decr_jerk(uint8_t &seg_pnt, float Jm, float tj);
     190             : 
     191             :     // extend the constant velocity segment so the deceleration finishes at target_pos
     192             :     void extend_const_vel_to(float target_pos);
     193             : 
     194             :     // fill segment[first..last] with zero-delta constant-jerk segments anchored to segment[src]
     195             :     void fill_empty_segments(uint8_t first, uint8_t last, uint8_t src);
     196             : 
     197             :     // populate the canonical straight-segment geometry from seg_delta
     198             :     void set_straight_geometry();
     199             : 
     200             :     // populate the canonical arc geometry (center relative to origin, radius and signed swept angle)
     201             :     // seg_delta (its vertical component) must already be set
     202             :     void set_arc_geometry(const Vector2f &center_ne_rel, float radius, float angle_rad);
     203             : 
     204             :     // build the jerk-limited profile from the already-configured geometry and the given limits
     205             :     void generate_path(float speed_xy, float speed_up, float speed_down,
     206             :                         float accel_xy, float accel_z, float accel_c,
     207             :                         float snap_maximum, float jerk_maximum);
     208             : 
     209             :     // return true if the curve is valid.  Used to identify and protect against code errors
     210             :     bool valid() const WARN_IF_UNUSED;
     211             : 
     212             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
     213             :     // debugging messages
     214             :     void debug() const;
     215             : #endif
     216             : 
     217             :     // segment types
     218             :     enum class SegmentType {
     219             :         CONSTANT_JERK,
     220             :         POSITIVE_JERK,
     221             :         NEGATIVE_JERK
     222             :     };
     223             : 
     224             :     // add single segment
     225             :     void add_segment(uint8_t &seg_pnt, float end_time, SegmentType seg_type, float jerk_ref, float end_accel, float end_vel, float end_pos);
     226             : 
     227             :     // members
     228             :     float snap_max;     // maximum snap magnitude
     229             :     float jerk_max;     // maximum jerk magnitude
     230             :     float accel_max;    // maximum acceleration magnitude
     231             :     float accel_c_max;  // maximum cornering acceleration magnitude
     232             :     float accel_z_max;  // maximum acceleration magnitude
     233             :     float vel_max;      // maximum velocity magnitude
     234             :     float time;         // time that defines position on the path
     235             : 
     236             :     // segment 0 is the initial segment and holds the vehicle's initial position and velocity
     237             :     // segments 1 to 7 are the acceleration segments
     238             :     // segments 8 to 14 are the speed change segments
     239             :     // segment 15 is the constant velocity segment
     240             :     // segment 16 to 22 is the deceleration segment
     241             :     const static uint8_t segments_max = 23; // maximum number of time segments
     242             : 
     243             :     uint8_t num_segs;       // number of time segments being used
     244             :     struct {
     245             :         float jerk_ref;     // jerk reference value for time segment (the jerk at the beginning, middle or end depending upon the segment type)
     246             :         SegmentType seg_type;   // segment type (jerk is constant, increasing or decreasing)
     247             :         float end_time;     // final time value for segment
     248             :         float end_accel;    // final acceleration value for segment
     249             :         float end_vel;      // final velocity value for segment
     250             :         float end_pos;      // final position value for segment
     251             :     } segment[segments_max];
     252             : 
     253             :     bool is_arc_segment;    // true if this segment is a circular arc, false if straight line
     254             :     Vector3f seg_delta;     // total displacement vector from start to end point (NED frame)
     255             :     float seg_length;       // 3D scalar length of the path (arc length + vertical component)
     256             : 
     257             :     struct {
     258             :         float angle_rad;    // signed central angle of the arc [rad] (+CCW, -CW), 0 = straight
     259             :         float length_ne;    // horizontal arc length along the circle (R * |theta|)
     260             :         float radius_ne;    // arc radius
     261             :         Vector2f center_ne; // center of the circle in local NE plane, relative to start point
     262             :     } arc;
     263             : };

Generated by: LCOV version 1.14