scenario_simulator_v2 C++ API
follow_waypoint_controller.hpp
Go to the documentation of this file.
1 // Copyright 2023 TIER IV, Inc. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef TRAFFIC_SIMULATOR__BEHAVIOR__FOLLOW_WAYPOINT_CONTROLLER_HPP_
16 #define TRAFFIC_SIMULATOR__BEHAVIOR__FOLLOW_WAYPOINT_CONTROLLER_HPP_
17 
18 #include <algorithm>
19 #include <cmath>
20 #include <iomanip>
21 #include <iostream>
22 #include <limits>
23 #include <optional>
25 #include <traffic_simulator_msgs/msg/behavior_parameter.hpp>
26 #include <traffic_simulator_msgs/msg/entity_status.hpp>
27 #include <traffic_simulator_msgs/msg/polyline_trajectory.hpp>
28 
29 namespace traffic_simulator
30 {
31 namespace follow_trajectory
32 {
34 {
35  template <typename... Ts>
36  explicit ControllerError(Ts &&... xs)
37  : common::Error(common::concatenate(
38  "An error occurred in the internal controller operation in the FollowTrajectoryAction. ",
39  "Please report the following information to the developer: ",
40  std::forward<decltype(xs)>(xs)...))
41  {
42  }
43 };
44 
46 {
47  double acceleration;
48  double speed;
50  double travel_time;
51 
52  auto moveStraight(const double step_acceleration, const double step_time) -> void
53  {
54  acceleration = step_acceleration;
55  speed += acceleration * step_time;
56  traveled_distance += speed * step_time;
57  travel_time += step_time;
58  }
59 
60  auto isImmobile(const double tolerance) const
61  {
62  return std::abs(speed) < tolerance && std::abs(acceleration) < tolerance;
63  }
64 
65  template <typename StreamType>
66  friend auto operator<<(StreamType & stream, const PredictedState & state) -> StreamType &
67  {
68  stream << std::setprecision(16) << std::fixed;
69  stream << "PredictedState: acceleration: " << state.acceleration << ", speed: " << state.speed
70  << ", distance: " << state.traveled_distance << ", time: " << state.travel_time << ". ";
71  return stream;
72  }
73 };
74 
76 {
77  const double step_time;
78  const bool with_breaking;
79 
80  const double max_speed;
81  const double max_acceleration;
82  const double max_acceleration_rate;
83  const double max_deceleration;
84  const double max_deceleration_rate;
85 
86  const double target_speed;
87 
88  /*
89  Achieving official epsilon (1e-16) accuracy when using doubles is
90  difficult for this reason the controller uses less accuracy.
91 
92  There is no technical basis for this value, it was determined based on
93  Dawid Moszynski experiments.
94  */
95  static constexpr double local_epsilon = 1e-12;
96 
97  /*
98  Acceptable time step inaccuracy, allowing the time to be rounded up to the
99  full number of steps.
100 
101  There is no technical basis for this value, it was determined based on
102  Dawid Moszynski experiments.
103  */
104  static constexpr double step_time_tolerance = 1e-6;
105 
106  /*
107  Accuracy of the final arrival distance at a waypoint with a specified
108  time.
109 
110  There is no technical basis for this value, it was determined based on
111  Dawid Moszynski experiments.
112  */
113  static constexpr double finish_distance_tolerance = 1e-2;
114 
115  /*
116  Accuracy of the predicted arrival distance at the waypoint with the
117  specified time it is only used to detect in advance that it is most likely
118  impossible to arrive at a sufficient final accuracy.
119 
120  There is no technical basis for this value, it was determined based on
121  Dawid Moszynski experiments.
122  */
123  static constexpr double predicted_distance_tolerance = 1.0;
124 
125  /*
126  Number of considered acceleration candidates. This is a discretization of
127  the current range of [min_acceleration, max_acceleration].
128 
129  There is no technical basis for this value, it was determined based on
130  Dawid Moszynski experiments.
131  */
132  static constexpr std::size_t number_of_acceleration_candidates = 30;
133 
134  /*
135  This is a debugging method, it is not worth giving it much attention.
136  */
137  template <typename StreamType>
138  friend auto operator<<(StreamType & stream, const FollowWaypointController & c) -> StreamType &
139  {
140  stream << std::setprecision(16) << std::fixed;
141  stream << "FollowWaypointController: step_time: " << c.step_time
142  << ", with_breaking: " << c.with_breaking << ", max_speed: " << c.max_speed
143  << ", max_acceleration: " << c.max_acceleration
144  << ", max_deceleration: " << c.max_deceleration
145  << ", max_acceleration_rate: " << c.max_acceleration_rate
146  << ", max_deceleration_rate: " << c.max_deceleration_rate
147  << ", target_speed: " << c.target_speed << ". ";
148  return stream;
149  }
150 
151  /*
152  This provides an analytical method for calculating the acceleration that
153  will allow the best possible accuracy in arriving at the waypoint during
154  the last few frames. It provides the calculation of acceleration for
155  arriving at waypoint without and with braking.
156 
157  The method allows to calculate only the last 3 steps without braking and 4
158  with braking, because analytical calculation of a greater number of steps
159  is difficult - a greater number of steps is processed through the
160  prediction of the controller.
161 
162  Details:
163 
164  - Acceleration at the last step equal to 0.0, therefore in the penultimate
165  step the acceleration is set to 0.0
166 
167  - Only with braking: speed at the last and penultimate step equal to 0.0,
168  therefore when there are 3 steps remain the acceleration is calculated
169  in such a way as to set the speed to 0.0 (a=-v/t)
170 
171  - Only with braking: because the speed is set to 0.0 when there are 3
172  steps remain, in the last 3 steps no distance will be traveled,
173  therefore, when there are 4 steps remain, it is necessary to choose the
174  acceleration in such a way as to travel exactly the remaining distance
175  in this step (s = at^2/2+vt -> a = 2(s-vt)/t^2).
176 
177  - Only without braking: in the penultimate step, the acceleration is set
178  to 0, therefore, when 3 steps remain it is necessary to choose the
179  acceleration in such a way as to ensure that in the penultimate and last
180  step the distance traveled is equal to the remaining distance (s = last
181  + penultimate = (v+at)t + at^2/2+vt -> a = 2/3(s-2vt)/t^2).
182  */
183  auto getAnalyticalAccelerationForLastSteps(
184  const double remaining_time, const double remaining_distance, const double acceleration,
185  const double speed) const -> double;
186 
187  /*
188  This allows the correct counting of the remaining number of steps.
189  Without this, inaccuracy sometimes results in a decrease of more than 1
190  step which can cause major difficulties if we want to arrive at the
191  waypoint at the precise time.
192  */
193  auto roundTimeToFullStepsWithTolerance(
194  const double remaining_time_source, const double time_tolerance) const -> double;
195 
196  /*
197  This allows to calculate how much time (rounded up to whole steps) is
198  needed to reach acceleration equal to zero - which is necessary in case of
199  achieving the maximum speed (to not exceed it) and achieving the speed
200  equal to 0.0 (to not start moving backwards).
201  */
202  auto getTimeRequiredForNonAcceleration(const double acceleration) const -> double;
203 
204  /*
205  This allows the calculation of acceleration limits that meet the
206  constraints. The limits depend on the current acceleration, the current
207  speed and the limits on the change in acceleration (rate).
208  */
209  auto getAccelerationLimits(const double acceleration, const double speed) const
210  -> std::pair<double, double>;
211 
212  auto clampAcceleration(
213  const double candidate_acceleration, const double acceleration, const double speed) const
214  -> double;
215 
216  auto moveStraight(PredictedState & state, const double candidate_acceleration) const -> void;
217 
218  auto getPredictedStopStateWithoutConsideringTime(
219  const double step_acceleration, const double remaining_distance, const double acceleration,
220  const double speed) const -> std::optional<PredictedState>;
221 
222 public:
223  explicit constexpr FollowWaypointController(
224  const traffic_simulator_msgs::msg::BehaviorParameter & behavior_parameter,
225  const double step_time, const bool with_breaking,
226  const std::optional<double> & target_speed = std::nullopt)
227  : step_time{step_time},
228  with_breaking{with_breaking},
229  max_speed{behavior_parameter.dynamic_constraints.max_speed},
230  max_acceleration{behavior_parameter.dynamic_constraints.max_acceleration},
231  max_acceleration_rate{behavior_parameter.dynamic_constraints.max_acceleration_rate},
232  max_deceleration{behavior_parameter.dynamic_constraints.max_deceleration},
233  max_deceleration_rate{behavior_parameter.dynamic_constraints.max_deceleration_rate},
234  target_speed{(target_speed) ? target_speed.value() : max_speed}
235  {
236  }
237 
238  /*
239  This is a debugging method, it is not worth giving it much attention.
240  */
242  const traffic_simulator_msgs::msg::PolylineTrajectory & polyline_trajectory) const
243  -> std::string
244  {
245  if (const auto & vertices = polyline_trajectory.shape.vertices; !vertices.empty()) {
246  std::stringstream waypoint_details;
247  waypoint_details << "Currently followed waypoint: ";
248  if (const auto first_waypoint_with_arrival_time_specified = std::find_if(
249  vertices.begin(), vertices.end(),
250  [](auto && vertex) { return not std::isnan(vertex.time); });
251  first_waypoint_with_arrival_time_specified !=
252  std::end(polyline_trajectory.shape.vertices)) {
253  waypoint_details << "[" << first_waypoint_with_arrival_time_specified->position.position.x
254  << ", " << first_waypoint_with_arrival_time_specified->position.position.y
255  << "] with specified time equal to "
256  << first_waypoint_with_arrival_time_specified->time << "s. ";
257  } else {
258  waypoint_details << "[" << vertices.back().position.position.x << ", "
259  << vertices.back().position.position.y << "] without specified time. ";
260  }
261  return waypoint_details.str();
262  } else {
263  return "No followed waypoint - trajectory is empty.";
264  }
265  }
266 
267  /*
268  This allows to predict the state that will be reached if the current
269  acceleration is changed from acceleration to step_acceleration.
270  */
272  const double step_acceleration, const double remaining_time, const double remaining_distance,
273  const double acceleration, const double speed) const -> std::optional<PredictedState>;
274  /*
275  This allows the best acceleration to be found for the current conditions,
276  without taking into account the arrival time - this is the case when every
277  next point of the trajectory has no specified time.
278  */
279  auto getAcceleration(
280  const double remaining_distance, const double acceleration, const double speed) const -> double;
281 
282  /*
283  This allows the best acceleration to be found for the current conditions,
284  taking into account the arrival time.
285  */
286  auto getAcceleration(
287  const double remaining_time_source, const double remaining_distance, const double acceleration,
288  const double speed) const -> double;
289 
291  const double acceleration, const double speed, const double distance) const -> double
292  {
293  return (!with_breaking || std::abs(speed) < local_epsilon) &&
294  std::abs(acceleration) < local_epsilon && distance < finish_distance_tolerance;
295  }
296 };
297 } // namespace follow_trajectory
298 } // namespace traffic_simulator
299 
300 #endif // TRAFFIC_SIMULATOR__BEHAVIOR__FOLLOW_WAYPOINT_CONTROLLER_HPP_
Definition: follow_waypoint_controller.hpp:76
auto getAcceleration(const double remaining_distance, const double acceleration, const double speed) const -> double
Definition: follow_waypoint_controller.cpp:294
friend auto operator<<(StreamType &stream, const FollowWaypointController &c) -> StreamType &
Definition: follow_waypoint_controller.hpp:138
constexpr FollowWaypointController(const traffic_simulator_msgs::msg::BehaviorParameter &behavior_parameter, const double step_time, const bool with_breaking, const std::optional< double > &target_speed=std::nullopt)
Definition: follow_waypoint_controller.hpp:223
auto getPredictedWaypointArrivalState(const double step_acceleration, const double remaining_time, const double remaining_distance, const double acceleration, const double speed) const -> std::optional< PredictedState >
Definition: follow_waypoint_controller.cpp:205
auto getFollowedWaypointDetails(const traffic_simulator_msgs::msg::PolylineTrajectory &polyline_trajectory) const -> std::string
Definition: follow_waypoint_controller.hpp:241
auto areConditionsOfArrivalMet(const double acceleration, const double speed, const double distance) const -> double
Definition: follow_waypoint_controller.hpp:290
auto concatenate
Definition: concatenate.hpp:27
Definition: concatenate.hpp:24
auto distance(const geometry_msgs::Pose &pose1, const geometry_msgs::Pose &pose2)
Definition: detection_sensor.cpp:34
Definition: cache.hpp:27
Definition: api.hpp:48
Definition: junit5.hpp:25
std::string string
Definition: junit5.hpp:26
Definition: exception.hpp:27
Definition: follow_waypoint_controller.hpp:34
ControllerError(Ts &&... xs)
Definition: follow_waypoint_controller.hpp:36
Definition: follow_waypoint_controller.hpp:46
friend auto operator<<(StreamType &stream, const PredictedState &state) -> StreamType &
Definition: follow_waypoint_controller.hpp:66
double traveled_distance
Definition: follow_waypoint_controller.hpp:49
double acceleration
Definition: follow_waypoint_controller.hpp:47
double speed
Definition: follow_waypoint_controller.hpp:48
double travel_time
Definition: follow_waypoint_controller.hpp:50
auto moveStraight(const double step_acceleration, const double step_time) -> void
Definition: follow_waypoint_controller.hpp:52
auto isImmobile(const double tolerance) const
Definition: follow_waypoint_controller.hpp:60