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