$darkmode
esmini_ego_control.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2023 Robert Bosch GmbH
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * SPDX-License-Identifier: Apache-2.0
17  */
25 #pragma once
26 
27 #include <algorithm> // for min, max
28 #include <cmath> // for fabs, remainder, atan2, M_PI_2
29 #include <memory> // for shared_ptr<>
30 
31 #include <esminiLib.hpp> // for SE_SimpleVehicleControlAnalog, ..
32 
33 #include <cloe/component/latlong_actuator.hpp> // for LatLongActuator
34 
35 #include "esmini_logger.hpp" // for esmini_logger
36 
37 // Refer to comment on Controller::Type in esminiLib.hpp.
38 #define ESMINI_CONTROLLER_TYPE_EXTERNAL 1
39 
40 namespace cloe_esmini {
41 
48  public:
49  ESMiniSimpleEgoModel(uint64_t ego_id) { init_model(ego_id); }
50  virtual ~ESMiniSimpleEgoModel() = default;
51 
52  void step(double dt_sec, double trg_throttle, double trg_front_wheel_angle) {
53  // Different from the documentation, `steerAngle` seems to be the target wheel angle in radians:
54  // https://github.com/esmini/esmini/blob/master/EnvironmentSimulator/Modules/Controllers/vehicle.cpp#L172
55  SE_SimpleVehicleControlAnalog(model_, dt_sec, trg_throttle, trg_front_wheel_angle);
56  }
57 
58  const SE_SimpleVehicleState& get_ego_state() {
59  SE_SimpleVehicleGetState(model_, &ego_state_);
60  return ego_state_;
61  }
62 
63  [[nodiscard]] double get_throttle_from_acceleration(double trg_accel) const {
64  return std::max(-1.0, std::min(1.0, trg_accel / max_acceleration_abs_));
65  }
66 
67  private:
68  void init_model(uint64_t ego_id) {
69  // Retrieve the ego state from the scenario.
70  SE_ScenarioObjectState sc_ego_state;
71  SE_GetObjectState(ego_id, &sc_ego_state);
72  if (sc_ego_state.ctrl_type != ESMINI_CONTROLLER_TYPE_EXTERNAL) {
73  throw cloe::ModelError(
74  "ESMiniSimpleEgoModel: esminiController must be set to ExternalController in .xosc "
75  "file.");
76  }
77  // Instantiate the vehicle model.
78  if (sc_ego_state.length <= 0.0) {
79  throw cloe::ModelError("ESMiniSimpleEgoModel: Unphysical ego length received.");
80  }
81  model_ = SE_SimpleVehicleCreate(sc_ego_state.x, sc_ego_state.y, sc_ego_state.h,
82  sc_ego_state.length, sc_ego_state.speed);
83  // Configure the vehicle model.
84  SE_SimpleVehicleSetMaxAcceleration(model_, max_acceleration_abs_);
85  SE_SimpleVehicleSetMaxDeceleration(model_, max_acceleration_abs_);
86  SE_SimpleVehicleSetEngineBrakeFactor(model_, engine_brake_factor_);
87  SE_SimpleVehicleSteeringRate(model_, steering_rate_);
88  SE_SimpleVehicleSteeringReturnFactor(model_, steering_return_factor_);
89  // Set thresholds used for clipping the model results.
90  SE_SimpleVehicleSetMaxSpeed(model_, max_speed_kph_);
91  SE_SimpleVehicleSteeringScale(model_, steering_scale_);
92  }
93 
94  private:
96  const double max_acceleration_abs_{19};
98  const double steering_rate_{8};
100  const double engine_brake_factor_{0.001};
102  const double steering_return_factor_{4.0};
104  const double max_speed_kph_{300.0};
106  const double steering_scale_{0.02};
108  void* model_{nullptr};
110  SE_SimpleVehicleState ego_state_{0, 0, 0, 0, 0, 0, 0, 0};
111 };
112 
113 enum class DriverModelType {
114  Simple,
117 };
118 
119 // clang-format off
121  {DriverModelType::Simple, "simple"},
122  {DriverModelType::GhostLookAheadTime, "ghost_time"},
123  {DriverModelType::GhostLookAheadDist, "ghost_distance"},
124 }))
125 // clang-format on
126 
127 class ESMiniDriverModel {
128  public:
129  ESMiniDriverModel(uint64_t ego_id, DriverModelType type) : ego_id_(ego_id), model_type_(type) {}
130  virtual ~ESMiniDriverModel() = default;
131 
132  void step(double ego_vel, double& throttle, double& steering_angle) {
133  throttle = 0.0;
134  steering_angle = 0.0;
135  SE_RoadInfo road_info;
136  float target_vel;
137  esmini_logger()->info("ESMiniDriverModel at {}s", SE_GetSimulationTime());
138  // Check if ghost vehicle is driving ahead of ego.
139  if (SE_ObjectHasGhost(ego_id_) == 1) {
140  SE_ScenarioObjectState ego_state, ghost_state;
141  SE_GetObjectState(ego_id_, &ego_state);
142  SE_GetObjectGhostState(ego_id_, &ghost_state);
143  double ghost_dir_angle = atan2(ghost_state.y - ego_state.y, ghost_state.x - ego_state.x);
144  double delta_ego_dir = remainder(ghost_dir_angle - ego_state.h, 2 * M_PI);
145  if (std::abs(delta_ego_dir) > M_PI_2) {
146  throw cloe::ModelError(
147  "ESMiniDriverModel: Ego vehicle has passed driver model ghost object. Fix scenario.");
148  }
149  } else {
150  throw cloe::ModelError(
151  "ESMiniDriverModel: Ghost vehicle missing. Refer to test-driver.xosc for an example how "
152  "to set property \"useGhost\".");
153  }
154  // Determine the target velocity and steering angle at the look-ahead point.
155  if (model_type_ == DriverModelType::GhostLookAheadDist) {
156  SE_GetRoadInfoAlongGhostTrail(ego_id_, 5 + 0.75f * ego_vel, &road_info, &target_vel);
157  } else if (model_type_ == DriverModelType::GhostLookAheadTime) {
158  SE_GetRoadInfoGhostTrailTime(ego_id_, SE_GetSimulationTime() + 0.25f, &road_info,
159  &target_vel);
160  } else {
161  // Use simple model (refer to the ESMini test-driver.cpp example).
162  // Look ahead along lane center. Scenario actions are ignored.
163  uint16_t lookahead_strategy = 0;
164  SE_GetRoadInfoAtDistance(ego_id_, 5 + 0.75F * ego_vel, &road_info, lookahead_strategy, true);
165  if (road_info.speed_limit <= 0.0) {
166  throw cloe::ModelError("ESMiniDriverModel::Simple: OpenDrive speed limit missing.");
167  }
168  // Slow down in curves using tuning parameter.
169  double curve_weight = 30.0;
170  target_vel = road_info.speed_limit / (1.0 + curve_weight * std::fabs(road_info.angle));
171  }
172  throttle = throttle_weight_ * (target_vel - ego_vel);
173  throttle = std::max(-1.0, std::min(1.0, throttle));
174  steering_angle = road_info.angle;
175  }
176 
177  private:
179  uint64_t ego_id_;
181  DriverModelType model_type_{DriverModelType::GhostLookAheadDist};
183  const double throttle_weight_{0.1};
184 };
185 
192  public:
193  ESMiniEgoControl(uint64_t id) : LatLongActuator("esmini/lat_long_actuator"), ego_id_(id) {
194  vehicle_model_ = std::make_shared<ESMiniSimpleEgoModel>(ego_id_);
195  driver_model_ =
196  std::make_shared<ESMiniDriverModel>(ego_id_, DriverModelType::GhostLookAheadDist);
197  }
198  ~ESMiniEgoControl() override = default;
199 
204  bool has_level_change() { return old_level_ != this->level_; }
205 
210  void save_level_state() { this->old_level_ = this->level_; }
211 
215  void step(const cloe::Sync& s) {
216  double trg_throttle = 0;
217  double trg_steering_angle = 0;
218  get_target_actuation(trg_throttle, trg_steering_angle);
219  // Step the vehicle model forward in time.
220  vehicle_model_->step(std::chrono::duration<double>(s.step_width()).count(), trg_throttle,
221  trg_steering_angle);
222  // Get the updated vehicle state.
223  auto ego_state = vehicle_model_->get_ego_state();
224  // Update new ego position, heading and velocity in the scenario (z, pitch, roll will be aligned to the road).
225  SE_ReportObjectPosXYH(0, 0.0F, ego_state.x, ego_state.y, ego_state.h);
226  SE_ReportObjectSpeed(0, ego_state.speed);
227  // Detect driver or controller takeover for lateral and/or longitudinal control.
228  if (this->has_level_change()) {
229  esmini_logger()->info("ESMiniEgoControl: vehicle {} new controller state: {}", id(),
230  level_.to_human_cstr());
231  }
233  }
234 
235  cloe::Duration process(const cloe::Sync& sync) override { return LatLongActuator::process(sync); }
236 
237  void reset() override {
238  old_level_.set_none();
239  LatLongActuator::reset();
240  }
241 
242  private:
243  void get_target_actuation(double& trg_throttle, double& trg_angle) const {
244  if (!target_acceleration_ || !target_steering_angle_) {
245  // Use driver model to obtain target actuation values.
246  auto ego_state = vehicle_model_->get_ego_state();
247  driver_model_->step(ego_state.speed, trg_throttle, trg_angle);
248  }
249  // Use actuation values provided by a controller, if available.
250  if (target_acceleration_) {
251  trg_throttle = vehicle_model_->get_throttle_from_acceleration(*target_acceleration_);
252  }
253  if (target_steering_angle_) {
254  trg_angle = *target_steering_angle_;
255  }
256  }
257 
258  private:
259  uint64_t ego_id_;
261  std::shared_ptr<ESMiniSimpleEgoModel> vehicle_model_{nullptr};
262  std::shared_ptr<ESMiniDriverModel> driver_model_{nullptr};
263 };
264 
265 } // namespace esmini
Definition: latlong_actuator.hpp:37
Definition: model.hpp:62
Definition: sync.hpp:34
virtual Duration step_width() const =0
Definition: actuation_level.hpp:48
Definition: esmini_ego_control.hpp:191
bool has_level_change()
Definition: esmini_ego_control.hpp:204
void reset() override
Definition: esmini_ego_control.hpp:237
void save_level_state()
Definition: esmini_ego_control.hpp:210
cloe::Duration process(const cloe::Sync &sync) override
Definition: esmini_ego_control.hpp:235
void step(const cloe::Sync &s)
Definition: esmini_ego_control.hpp:215
Definition: esmini_ego_control.hpp:47
std::chrono::nanoseconds Duration
Definition: cloe_fwd.hpp:36
#define ENUM_SERIALIZATION(xType, xMap)
Definition: enum.hpp:51
DriverModelType
Definition: esmini_ego_control.hpp:113
@ Simple
Look ahead to a point on the current lane and steer towards it.
@ GhostLookAheadTime
Use ghost vehicle state in some time ahead to obtain target acceleration and steering.
@ GhostLookAheadDist
Use ghost vehicle state in some distance ahead to obtain target acceleration and steering.