John Park | 33858a3 | 2018-09-28 23:05:48 -0700 | [diff] [blame] | 1 | #include "aos/util/trapezoid_profile.h" |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 2 | |
Stephan Pleines | b117767 | 2024-05-27 17:48:32 -0700 | [diff] [blame] | 3 | #include <math.h> |
| 4 | |
| 5 | #include <algorithm> |
| 6 | #include <cstdlib> |
| 7 | #include <ostream> |
| 8 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 9 | #include "glog/logging.h" |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 10 | |
Stephan Pleines | b117767 | 2024-05-27 17:48:32 -0700 | [diff] [blame] | 11 | #include "aos/time/time.h" |
| 12 | |
Stephan Pleines | f63bde8 | 2024-01-13 15:59:33 -0800 | [diff] [blame] | 13 | namespace aos::util { |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 14 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 15 | AsymmetricTrapezoidProfile::AsymmetricTrapezoidProfile( |
| 16 | ::std::chrono::nanoseconds delta_time) |
| 17 | : timestep_(delta_time) { |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 18 | output_.setZero(); |
| 19 | } |
| 20 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 21 | void AsymmetricTrapezoidProfile::UpdateVals(double acceleration, |
| 22 | double delta_time) { |
James Kuszmaul | 651fc3f | 2019-05-15 21:14:25 -0700 | [diff] [blame] | 23 | output_(0) += |
| 24 | output_(1) * delta_time + 0.5 * acceleration * delta_time * delta_time; |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 25 | output_(1) += acceleration * delta_time; |
| 26 | } |
| 27 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 28 | const Eigen::Matrix<double, 2, 1> &AsymmetricTrapezoidProfile::Update( |
| 29 | double goal_position, double goal_velocity) { |
| 30 | CalculateTimes(goal_position - output_(0), goal_velocity, output_); |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 31 | |
James Kuszmaul | 651fc3f | 2019-05-15 21:14:25 -0700 | [diff] [blame] | 32 | double next_timestep = ::aos::time::DurationInSeconds(timestep_); |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 33 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 34 | if (deceleration_reversal_time_ > next_timestep) { |
| 35 | UpdateVals(deceleration_reversal_, next_timestep); |
| 36 | return output_; |
| 37 | } |
| 38 | |
| 39 | UpdateVals(deceleration_reversal_, deceleration_reversal_time_); |
| 40 | next_timestep -= deceleration_reversal_time_; |
| 41 | |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 42 | if (acceleration_time_ > next_timestep) { |
| 43 | UpdateVals(acceleration_, next_timestep); |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 44 | return output_; |
| 45 | } |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 46 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 47 | UpdateVals(acceleration_, acceleration_time_); |
| 48 | next_timestep -= acceleration_time_; |
| 49 | |
| 50 | if (constant_time_ > next_timestep) { |
| 51 | UpdateVals(0, next_timestep); |
| 52 | return output_; |
| 53 | } |
| 54 | |
| 55 | UpdateVals(0, constant_time_); |
| 56 | next_timestep -= constant_time_; |
| 57 | if (deceleration_time_ > next_timestep) { |
| 58 | UpdateVals(deceleration_, next_timestep); |
| 59 | } else { |
| 60 | UpdateVals(deceleration_, deceleration_time_); |
| 61 | next_timestep -= deceleration_time_; |
| 62 | UpdateVals(0, next_timestep); |
| 63 | |
| 64 | if (next_timestep >= 0 && goal_velocity == 0) { |
| 65 | output_(0) = goal_position; |
| 66 | output_(1) = goal_velocity; |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 67 | } |
| 68 | } |
| 69 | |
| 70 | return output_; |
| 71 | } |
| 72 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 73 | void AsymmetricTrapezoidProfile::CalculateTimes( |
| 74 | double distance_to_target, double goal_velocity, |
| 75 | Eigen::Matrix<double, 2, 1> current) { |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 76 | if (distance_to_target == 0) { |
| 77 | // We're there. Stop everything. |
| 78 | // TODO(aschuh): Deal with velocity not right. |
| 79 | acceleration_time_ = 0; |
| 80 | acceleration_ = 0; |
| 81 | constant_time_ = 0; |
| 82 | deceleration_time_ = 0; |
| 83 | deceleration_ = 0; |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 84 | deceleration_reversal_time_ = 0; |
| 85 | deceleration_reversal_ = 0; |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 86 | return; |
| 87 | } else if (distance_to_target < 0) { |
| 88 | // Recurse with everything inverted. |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 89 | current(1) *= -1; |
| 90 | CalculateTimes(-distance_to_target, -goal_velocity, current); |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 91 | acceleration_ *= -1; |
| 92 | deceleration_ *= -1; |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 93 | deceleration_reversal_ *= -1; |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 94 | return; |
| 95 | } |
| 96 | |
| 97 | constant_time_ = 0; |
| 98 | acceleration_ = maximum_acceleration_; |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 99 | |
| 100 | // Calculate the fastest speed we could get going to by the distance to |
| 101 | // target. We will have normalized everything out to be a positive distance |
| 102 | // by now so we never have to deal with going "backwards". |
Diana Vandenberg | 19bb9e2 | 2016-02-03 21:24:31 -0800 | [diff] [blame] | 103 | double maximum_acceleration_velocity = |
| 104 | distance_to_target * 2 * std::abs(acceleration_) + |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 105 | current(1) * current(1); |
| 106 | CHECK_GE(maximum_acceleration_velocity, 0); |
| 107 | maximum_acceleration_velocity = sqrt(maximum_acceleration_velocity); |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 108 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 109 | // If we could get going faster than the target, we will need to decelerate |
| 110 | // after accelerating. |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 111 | if (maximum_acceleration_velocity > goal_velocity) { |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 112 | deceleration_ = -maximum_deceleration_; |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 113 | } else { |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 114 | // We couldn't get up to speed by the destination. Set our decel to |
| 115 | // accelerate to keep accelerating to get up to speed. |
| 116 | // |
| 117 | // Note: goal_velocity != 0 isn't well tested, use at your own risk. |
| 118 | LOG(FATAL) << "Untested"; |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 119 | deceleration_ = maximum_acceleration_; |
| 120 | } |
| 121 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 122 | // If we are going away from the goal, we will need to change directions. |
| 123 | if (current(1) < 0) { |
| 124 | deceleration_reversal_time_ = current(1) / deceleration_; |
| 125 | deceleration_reversal_ = -deceleration_; |
| 126 | } else if ((goal_velocity - current(1)) * (goal_velocity + current(1)) < |
| 127 | 2.0 * deceleration_ * distance_to_target) { |
| 128 | // Then, can we stop in time if we get after it? If so, we don't need to |
| 129 | // decel first before running the profile. |
| 130 | deceleration_reversal_time_ = -current(1) / deceleration_; |
| 131 | deceleration_reversal_ = deceleration_; |
| 132 | } else { |
| 133 | // Otherwise, we are all good and don't need to handle reversing. |
| 134 | deceleration_reversal_time_ = 0.0; |
| 135 | deceleration_reversal_ = 0.0; |
| 136 | } |
| 137 | |
| 138 | current(0) += current(1) * deceleration_reversal_time_ + |
| 139 | 0.5 * deceleration_reversal_ * deceleration_reversal_time_ * |
| 140 | deceleration_reversal_time_; |
| 141 | current(1) += deceleration_reversal_ * deceleration_reversal_time_; |
| 142 | // OK, now we've compensated for slowing down. |
| 143 | |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 144 | // We now know the top velocity we can get to. |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 145 | const double top_velocity = sqrt( |
| 146 | (distance_to_target + (current(1) * current(1)) / (2.0 * acceleration_) + |
James Kuszmaul | 651fc3f | 2019-05-15 21:14:25 -0700 | [diff] [blame] | 147 | (goal_velocity * goal_velocity) / (2.0 * deceleration_)) / |
| 148 | (-1.0 / (2.0 * deceleration_) + 1.0 / (2.0 * acceleration_))); |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 149 | |
| 150 | // If it can go too fast, we now know how long we get to accelerate for and |
| 151 | // how long to go at constant velocity. |
| 152 | if (top_velocity > maximum_velocity_) { |
James Kuszmaul | 651fc3f | 2019-05-15 21:14:25 -0700 | [diff] [blame] | 153 | acceleration_time_ = |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 154 | (maximum_velocity_ - current(1)) / maximum_acceleration_; |
Austin Schuh | b3b799e | 2024-02-20 14:20:12 -0800 | [diff] [blame] | 155 | constant_time_ = ((-0.5 * maximum_acceleration_ * acceleration_time_ * |
| 156 | acceleration_time_ - |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 157 | current(1) * acceleration_time_) + |
Austin Schuh | b3b799e | 2024-02-20 14:20:12 -0800 | [diff] [blame] | 158 | distance_to_target + |
| 159 | (goal_velocity * goal_velocity - |
| 160 | maximum_velocity_ * maximum_velocity_) / |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 161 | (2.0 * maximum_deceleration_)) / |
Austin Schuh | b3b799e | 2024-02-20 14:20:12 -0800 | [diff] [blame] | 162 | maximum_velocity_; |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 163 | } else { |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 164 | acceleration_time_ = (top_velocity - current(1)) / acceleration_; |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 165 | } |
| 166 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 167 | CHECK_GT(top_velocity, -maximum_velocity_); |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 168 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 169 | if (current(1) > maximum_velocity_) { |
Ben Fredrickson | f33d653 | 2015-03-15 00:29:29 -0700 | [diff] [blame] | 170 | constant_time_ = 0; |
| 171 | acceleration_time_ = 0; |
| 172 | } |
| 173 | |
Austin Schuh | b3b799e | 2024-02-20 14:20:12 -0800 | [diff] [blame] | 174 | deceleration_time_ = |
| 175 | (goal_velocity - ::std::min(top_velocity, maximum_velocity_)) / |
| 176 | deceleration_; |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame] | 177 | if (acceleration_time_ <= 0) acceleration_time_ = 0; |
Austin Schuh | b3b799e | 2024-02-20 14:20:12 -0800 | [diff] [blame] | 178 | if (constant_time_ <= 0) constant_time_ = 0; |
| 179 | if (deceleration_time_ <= 0) deceleration_time_ = 0; |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 180 | } |
| 181 | |
Stephan Pleines | f63bde8 | 2024-01-13 15:59:33 -0800 | [diff] [blame] | 182 | } // namespace aos::util |