blob: e58324f5889b8c3889d31340b309c4002e0a58d6 [file] [log] [blame]
#include "aos/util/trapezoid_profile.h"
#include "aos/logging/logging.h"
using ::Eigen::Matrix;
namespace aos {
namespace util {
TrapezoidProfile::TrapezoidProfile(::std::chrono::nanoseconds delta_time)
: maximum_acceleration_(0), maximum_velocity_(0), timestep_(delta_time) {
output_.setZero();
}
void TrapezoidProfile::UpdateVals(double acceleration,
double delta_time) {
output_(0) += output_(1) * delta_time +
0.5 * acceleration * delta_time * delta_time;
output_(1) += acceleration * delta_time;
}
const Matrix<double, 2, 1> &TrapezoidProfile::Update(
double goal_position,
double goal_velocity) {
CalculateTimes(goal_position - output_(0), goal_velocity);
double next_timestep =
::std::chrono::duration_cast<::std::chrono::duration<double>>(timestep_)
.count();
if (acceleration_time_ > next_timestep) {
UpdateVals(acceleration_, next_timestep);
} else {
UpdateVals(acceleration_, acceleration_time_);
next_timestep -= acceleration_time_;
if (constant_time_ > next_timestep) {
UpdateVals(0, next_timestep);
} else {
UpdateVals(0, constant_time_);
next_timestep -= constant_time_;
if (deceleration_time_ > next_timestep) {
UpdateVals(deceleration_, next_timestep);
} else {
UpdateVals(deceleration_, deceleration_time_);
next_timestep -= deceleration_time_;
UpdateVals(0, next_timestep);
}
}
}
return output_;
}
void TrapezoidProfile::CalculateTimes(double distance_to_target,
double goal_velocity) {
if (distance_to_target == 0) {
// We're there. Stop everything.
// TODO(aschuh): Deal with velocity not right.
acceleration_time_ = 0;
acceleration_ = 0;
constant_time_ = 0;
deceleration_time_ = 0;
deceleration_ = 0;
return;
} else if (distance_to_target < 0) {
// Recurse with everything inverted.
output_(1) *= -1;
CalculateTimes(-distance_to_target, -goal_velocity);
output_(1) *= -1;
acceleration_ *= -1;
deceleration_ *= -1;
return;
}
constant_time_ = 0;
acceleration_ = maximum_acceleration_;
double maximum_acceleration_velocity =
distance_to_target * 2 * std::abs(acceleration_) +
output_(1) * output_(1);
if (maximum_acceleration_velocity > 0) {
maximum_acceleration_velocity = sqrt(maximum_acceleration_velocity);
} else {
maximum_acceleration_velocity = -sqrt(-maximum_acceleration_velocity);
}
// Since we know what we'd have to do if we kept after it to decelerate, we
// know the sign of the acceleration.
if (maximum_acceleration_velocity > goal_velocity) {
deceleration_ = -maximum_acceleration_;
} else {
deceleration_ = maximum_acceleration_;
}
// We now know the top velocity we can get to.
double top_velocity = sqrt((distance_to_target +
(output_(1) * output_(1)) /
(2.0 * acceleration_) +
(goal_velocity * goal_velocity) /
(2.0 * deceleration_)) /
(-1.0 / (2.0 * deceleration_) +
1.0 / (2.0 * acceleration_)));
// If it can go too fast, we now know how long we get to accelerate for and
// how long to go at constant velocity.
if (top_velocity > maximum_velocity_) {
acceleration_time_ = (maximum_velocity_ - output_(1)) /
maximum_acceleration_;
constant_time_ = (distance_to_target +
(goal_velocity * goal_velocity -
maximum_velocity_ * maximum_velocity_) /
(2.0 * maximum_acceleration_)) / maximum_velocity_;
} else {
acceleration_time_ = (top_velocity - output_(1)) /
acceleration_;
}
CHECK_GT(top_velocity, -maximum_velocity_);
if (output_(1) > maximum_velocity_) {
constant_time_ = 0;
acceleration_time_ = 0;
}
deceleration_time_ = (goal_velocity - top_velocity) /
deceleration_;
}
} // namespace util
} // namespace aos