blob: 34dc35c0ab26c18037ab4fb50710b00e4603317c [file] [log] [blame]
Austin Schuh60e77942022-05-16 17:48:24 -07001#include "aos/util/trapezoid_profile.h"
briansf0165ca2013-03-02 06:17:47 +00002
3#include "Eigen/Dense"
Austin Schuh60e77942022-05-16 17:48:24 -07004#include "gtest/gtest.h"
briansf0165ca2013-03-02 06:17:47 +00005
Stephan Pleinesf63bde82024-01-13 15:59:33 -08006namespace aos::util::testing {
briansf0165ca2013-03-02 06:17:47 +00007
8class TrapezoidProfileTest : public ::testing::Test {
9 public:
10 EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
11
12 protected:
Austin Schuhb3b799e2024-02-20 14:20:12 -080013 TrapezoidProfileTest() : profile_(kDeltaTime) {
briansf0165ca2013-03-02 06:17:47 +000014 position_.setZero();
15 profile_.set_maximum_acceleration(0.75);
16 profile_.set_maximum_velocity(1.75);
17 }
18
19 // Runs an iteration.
Austin Schuh60e77942022-05-16 17:48:24 -070020 void RunIteration(double goal_position, double goal_velocity) {
21 position_ = profile_.Update(goal_position, goal_velocity);
briansf0165ca2013-03-02 06:17:47 +000022 }
23
Austin Schuhb3b799e2024-02-20 14:20:12 -080024 void RunFor(double goal_position, double goal_velocity,
25 std::chrono::nanoseconds duration) {
26 while (duration > std::chrono::nanoseconds(0)) {
27 position_ = profile_.Update(goal_position, goal_velocity);
28 duration -= kDeltaTime;
29 }
30
31 ASSERT_EQ(duration.count(), 0);
32 }
33
briansf0165ca2013-03-02 06:17:47 +000034 const Eigen::Matrix<double, 2, 1> &position() { return position_; }
35
36 TrapezoidProfile profile_;
37
38 ::testing::AssertionResult At(double position, double velocity) {
briansd412b3f2013-03-03 21:13:44 +000039 static const double kDoubleNear = 0.00001;
40 if (::std::abs(velocity - position_(1)) > kDoubleNear) {
Austin Schuh60e77942022-05-16 17:48:24 -070041 return ::testing::AssertionFailure()
42 << "velocity is " << position_(1) << " not " << velocity;
briansf0165ca2013-03-02 06:17:47 +000043 }
briansd412b3f2013-03-03 21:13:44 +000044 if (::std::abs(position - position_(0)) > kDoubleNear) {
Austin Schuh60e77942022-05-16 17:48:24 -070045 return ::testing::AssertionFailure()
46 << "position is " << position_(0) << " not " << position;
briansf0165ca2013-03-02 06:17:47 +000047 }
Austin Schuh60e77942022-05-16 17:48:24 -070048 return ::testing::AssertionSuccess()
49 << "at " << position << " moving at " << velocity;
briansf0165ca2013-03-02 06:17:47 +000050 }
51
52 private:
Austin Schuhb3b799e2024-02-20 14:20:12 -080053 static constexpr ::std::chrono::nanoseconds kDeltaTime =
Austin Schuh214e9c12016-11-25 17:26:20 -080054 ::std::chrono::milliseconds(10);
briansf0165ca2013-03-02 06:17:47 +000055
56 Eigen::Matrix<double, 2, 1> position_;
57};
Austin Schuh214e9c12016-11-25 17:26:20 -080058
Austin Schuhb3b799e2024-02-20 14:20:12 -080059constexpr ::std::chrono::nanoseconds TrapezoidProfileTest::kDeltaTime;
briansf0165ca2013-03-02 06:17:47 +000060
61TEST_F(TrapezoidProfileTest, ReachesGoal) {
62 for (int i = 0; i < 450; ++i) {
63 RunIteration(3, 0);
64 }
65 EXPECT_TRUE(At(3, 0));
66}
67
Austin Schuhb3b799e2024-02-20 14:20:12 -080068// Tests that decreasing the maximum velocity in the middle when it is already
Ben Fredricksonf33d6532015-03-15 00:29:29 -070069// moving faster than the new max is handled correctly.
70TEST_F(TrapezoidProfileTest, ContinousUnderVelChange) {
71 profile_.set_maximum_velocity(1.75);
72 RunIteration(12.0, 0);
73 double last_pos = position()(0);
74 double last_vel = 1.75;
75 for (int i = 0; i < 1600; ++i) {
76 if (i == 400) {
77 profile_.set_maximum_velocity(0.75);
78 }
79 RunIteration(12.0, 0);
80 if (i >= 400) {
81 EXPECT_TRUE(::std::abs(last_pos - position()(0)) <= 1.75 * 0.01);
82 EXPECT_NEAR(last_vel, ::std::abs(last_pos - position()(0)), 0.0001);
83 }
84 last_vel = ::std::abs(last_pos - position()(0));
85 last_pos = position()(0);
86 }
87 EXPECT_TRUE(At(12.0, 0));
88}
89
briansf0165ca2013-03-02 06:17:47 +000090// There is some somewhat tricky code for dealing with going backwards.
91TEST_F(TrapezoidProfileTest, Backwards) {
92 for (int i = 0; i < 400; ++i) {
93 RunIteration(-2, 0);
94 }
95 EXPECT_TRUE(At(-2, 0));
96}
97
98TEST_F(TrapezoidProfileTest, SwitchGoalInMiddle) {
99 for (int i = 0; i < 200; ++i) {
100 RunIteration(-2, 0);
101 }
102 EXPECT_FALSE(At(-2, 0));
103 for (int i = 0; i < 550; ++i) {
104 RunIteration(0, 0);
105 }
106 EXPECT_TRUE(At(0, 0));
107}
108
109// Checks to make sure that it hits top speed.
110TEST_F(TrapezoidProfileTest, TopSpeed) {
111 for (int i = 0; i < 200; ++i) {
112 RunIteration(4, 0);
113 }
114 EXPECT_NEAR(1.5, position()(1), 10e-5);
115 for (int i = 0; i < 2000; ++i) {
116 RunIteration(4, 0);
117 }
118 EXPECT_TRUE(At(4, 0));
119}
120
Austin Schuh5d571d02017-02-11 12:28:17 -0800121// Tests that the position and velocity exactly match at the end. Some code we
122// have assumes this to be true as a simplification.
123TEST_F(TrapezoidProfileTest, ExactlyReachesGoal) {
124 for (int i = 0; i < 450; ++i) {
125 RunIteration(1, 0);
126 }
127 EXPECT_EQ(position()(1), 0.0);
128 EXPECT_EQ(position()(0), 1.0);
129}
130
Austin Schuhb3b799e2024-02-20 14:20:12 -0800131// Tests that we can move a goal without the trajectory teleporting.
132TEST_F(TrapezoidProfileTest, MoveGoal) {
133 profile_.set_maximum_acceleration(2.0);
134 profile_.set_maximum_velocity(2.0);
135
136 RunFor(5.0, 0, std::chrono::seconds(1));
137 EXPECT_TRUE(At(1.0, 2.0));
138 RunFor(5.0, 0, std::chrono::seconds(1));
139 EXPECT_TRUE(At(3.0, 2.0));
140 RunFor(3.5, 0, std::chrono::seconds(1));
141 EXPECT_TRUE(At(4.0, 0.0));
142 RunFor(3.5, 0, std::chrono::seconds(1));
143 EXPECT_TRUE(At(3.5, 0.0));
144}
145
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800146} // namespace aos::util::testing