Autonomous Racing  1
f1tenth Project Group of Technical University Dortmund, Germany
keyboard_controller.cpp
Go to the documentation of this file.
1 #include "keyboard_controller.h"
2 #include <boost/algorithm/clamp.hpp>
3 #include <cmath>
4 #include <std_msgs/Time.h>
5 
6 #include <teleoperation/keyboard_controllerConfig.h>
7 
8 double map(double value, double in_lower, double in_upper, double out_lower, double out_upper)
9 {
10  return out_lower + (out_upper - out_lower) * (value - in_lower) / (in_upper - in_lower);
11 }
12 
18 {
19  ROS_ASSERT_MSG(m_key_codes.size() == KEY_COUNT, "KEY_CODES needs to have KEY_COUNT many elements.");
20  ROS_ASSERT_MSG(this->m_key_pressed_state.size() == KEY_COUNT,
21  "m_key_pressed_state needs to have KEY_COUNT many elements.");
22 
23  this->m_drive_parameters_publisher =
24  this->m_node_handle.advertise<drive_msgs::drive_param>(TOPIC_DRIVE_PARAMETERS, 1);
25  this->m_enable_manual_publisher = this->m_node_handle.advertise<std_msgs::Time>(TOPIC_HEARTBEAT_MANUAL, 1);
26  this->m_enable_autonomous_publisher = this->m_node_handle.advertise<std_msgs::Time>(TOPIC_HEARTBEAT_AUTONOMOUS, 1);
27  this->m_drive_mode_subscriber =
28  this->m_node_handle.subscribe<std_msgs::Int32>(TOPIC_DRIVE_MODE, 1, &KeyboardController::driveModeCallback,
29  this);
30 
31  this->loadImages();
32  this->createWindow();
33 
34  auto tick_duration = ros::Duration(1.0 / PARAMETER_UPDATE_FREQUENCY);
35  this->m_timer = this->m_node_handle.createTimer(tick_duration, &KeyboardController::timerCallback, this);
36 
37  this->updateDynamicConfig();
38  m_dyn_cfg_server.setCallback([&](teleoperation::keyboard_controllerConfig& cfg, uint32_t) {
39  m_steering_speed = cfg.steering_speed;
40  m_acceleration = cfg.acceleration;
41  m_braking = cfg.braking;
42 
43  m_fast_steer_limit = cfg.fast_steer_limit;
44  m_steering_gravity = cfg.steering_gravity;
45  m_throttle_gravity = cfg.throttle_gravity;
46 
47  m_max_throttle = cfg.max_throttle;
48  });
49 }
50 
52 {
53  SDL_DestroyWindow(this->m_window);
54  SDL_FreeSurface(this->m_image_locked);
55  SDL_FreeSurface(this->m_image_manual);
56  SDL_FreeSurface(this->m_image_autonomous);
57  SDL_Quit();
58 }
59 
60 void KeyboardController::createWindow()
61 {
62  std::string icon_filename = ros::package::getPath("teleoperation") + std::string("/img/wasd.bmp");
63 
64  if (SDL_Init(SDL_INIT_VIDEO) < 0)
65  {
66  throw std::runtime_error("Could not initialize SDL: " + std::string(SDL_GetError()));
67  }
68  this->m_window = SDL_CreateWindow("Keyboard teleoperation", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 580,
69  128, SDL_WINDOW_RESIZABLE);
70 
71  auto window_surface = SDL_GetWindowSurface(this->m_window);
72  SDL_DisplayMode display_mode;
73  SDL_GetCurrentDisplayMode(0, &display_mode);
74  // clang-format off
75  SDL_SetWindowPosition(this->m_window,
76  display_mode.w - window_surface->w - SCREEN_EDGE_MARGIN,
77  display_mode.h - window_surface->h - SCREEN_EDGE_MARGIN);
78  // clang-format on
79 
80  SDL_Surface* icon = SDL_LoadBMP(icon_filename.c_str());
81  if (icon != NULL)
82  {
83  SDL_SetWindowIcon(this->m_window, icon);
84  SDL_FreeSurface(icon);
85  }
86 }
87 
88 void KeyboardController::pollWindowEvents()
89 {
90  SDL_Event event;
91  while (SDL_PollEvent(&event))
92  {
93  if (event.type == SDL_KEYUP || event.type == SDL_KEYDOWN)
94  {
95  for (size_t i = 0; i < KEY_COUNT; i++)
96  {
97  if ((int)event.key.keysym.sym == (int)m_key_codes[i])
98  {
99  this->m_key_pressed_state[i] = event.type == SDL_KEYDOWN;
100  }
101  }
102  }
103  else if (event.type == SDL_QUIT)
104  {
105  ros::shutdown();
106  }
107  else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_EXPOSED)
108  {
109  this->updateWindow();
110  }
111  }
112 }
113 
114 void KeyboardController::updateWindow()
115 {
116  SDL_Surface* surface = SDL_GetWindowSurface(this->m_window);
117  SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, 0, 0, 0));
118  switch (this->m_drive_mode)
119  {
120  case DriveMode::LOCKED:
121  SDL_BlitSurface(this->m_image_locked, NULL, surface, NULL);
122  break;
123  case DriveMode::MANUAL:
124  SDL_BlitSurface(this->m_image_manual, NULL, surface, NULL);
125  break;
127  SDL_BlitSurface(this->m_image_autonomous, NULL, surface, NULL);
128  break;
129  }
130  SDL_UpdateWindowSurface(this->m_window);
131 }
132 
133 void KeyboardController::loadImages()
134 {
135  std::string locked_filename = ros::package::getPath("teleoperation") + std::string("/img/locked.bmp");
136  this->m_image_locked = SDL_LoadBMP(locked_filename.c_str());
137  std::string manual_filename = ros::package::getPath("teleoperation") + std::string("/img/manual.bmp");
138  this->m_image_manual = SDL_LoadBMP(manual_filename.c_str());
139  std::string autonomous_filename = ros::package::getPath("teleoperation") + std::string("/img/autonomous.bmp");
140  this->m_image_autonomous = SDL_LoadBMP(autonomous_filename.c_str());
141 }
142 
147 void KeyboardController::timerCallback(const ros::TimerEvent& event)
148 {
149  double delta_time = (event.current_real - event.last_real).toSec();
150 
151  this->pollWindowEvents();
152  this->updateDriveParameters(delta_time);
153  this->publishDriveParameters();
154  this->updateDeadMansSwitch();
155 }
156 
157 std_msgs::Time createHearbeatMessage()
158 {
159  std_msgs::Time message;
160  message.data = ros::Time::now();
161  return message;
162 }
163 
167 void KeyboardController::updateDeadMansSwitch()
168 {
169  if (this->m_key_pressed_state[(size_t)KeyIndex::ENABLE_MANUAL])
170  {
171  this->m_enable_manual_publisher.publish(createHearbeatMessage());
172  }
173  if (this->m_key_pressed_state[(size_t)KeyIndex::ENABLE_AUTONOMOUS])
174  {
175  this->m_enable_autonomous_publisher.publish(createHearbeatMessage());
176  }
177 }
178 
184 void KeyboardController::updateDriveParameters(double delta_time)
185 {
186 // Disable warnings about equality comparisons for floats.
187 // Equality comparisons are ok here because the variables are assigned the exact values that we compare them against.
188 #pragma GCC diagnostic push
189 #pragma GCC diagnostic ignored "-Wfloat-equal"
190  double steer = this->m_key_pressed_state[(size_t)KeyIndex::STEER_LEFT]
191  ? -1
192  : (this->m_key_pressed_state[(size_t)KeyIndex::STEER_RIGHT] ? +1 : 0);
193  double throttle = this->m_key_pressed_state[(size_t)KeyIndex::ACCELERATE]
194  ? +1
195  : (this->m_key_pressed_state[(size_t)KeyIndex::DECELERATE] ? -1 : 0);
196 
197  double steer_limit = map(std::abs(this->m_velocity), 0, m_max_throttle, 1, m_fast_steer_limit);
198  double angle_update = steer * delta_time * m_steering_speed;
199  this->m_angle = boost::algorithm::clamp(this->m_angle + angle_update, -steer_limit, +steer_limit);
200  double velocity_update = throttle * delta_time * (this->m_velocity * throttle > 0 ? m_acceleration : m_braking);
201  this->m_velocity = boost::algorithm::clamp(this->m_velocity + velocity_update, -m_max_throttle, +m_max_throttle);
202 
203  if (steer == 0 && this->m_angle != 0)
204  {
205  double sign = std::copysign(1.0, this->m_angle);
206  this->m_angle -= m_steering_gravity * delta_time * sign;
207  if (std::abs(this->m_angle) < m_steering_gravity * delta_time)
208  {
209  this->m_angle = 0;
210  }
211  }
212 
213  if (throttle == 0 && this->m_velocity != 0)
214  {
215  double sign = std::copysign(1.0, this->m_velocity);
216  this->m_velocity -= m_throttle_gravity * delta_time * sign;
217  if (std::abs(this->m_velocity) < m_throttle_gravity * delta_time)
218  {
219  this->m_velocity = 0;
220  }
221  }
222 #pragma GCC diagnostic pop
223 }
224 
225 void KeyboardController::publishDriveParameters()
226 {
227  drive_msgs::drive_param drive_parameters;
228  drive_parameters.velocity = this->m_velocity;
229  drive_parameters.angle = this->m_angle;
230  this->m_drive_parameters_publisher.publish(drive_parameters);
231 }
232 
233 void KeyboardController::driveModeCallback(const std_msgs::Int32::ConstPtr& drive_mode_message)
234 {
235  auto mode = (DriveMode)drive_mode_message->data;
236  ROS_ASSERT_MSG(mode == DriveMode::LOCKED || mode == DriveMode::MANUAL || mode == DriveMode::AUTONOMOUS,
237  "Unknown drive mode.");
238  if (this->m_drive_mode != mode)
239  {
240  this->m_drive_mode = mode;
241  this->updateWindow();
242  }
243 }
244 
245 void KeyboardController::updateDynamicConfig()
246 {
247  teleoperation::keyboard_controllerConfig cfg;
248  {
249  cfg.steering_speed = m_steering_speed;
250  cfg.acceleration = m_acceleration;
251  cfg.braking = m_braking;
252 
253  cfg.fast_steer_limit = m_fast_steer_limit;
254  cfg.steering_gravity = m_steering_gravity;
255  cfg.throttle_gravity = m_throttle_gravity;
256 
257  cfg.max_throttle = m_max_throttle;
258  }
259  m_dyn_cfg_server.updateConfig(cfg);
260 }
261 
262 int main(int argc, char** argv)
263 {
264  ros::init(argc, argv, "keyboard_controller");
265  KeyboardController keyboard_controller;
266  ros::spin();
267  return EXIT_SUCCESS;
268 }
constexpr const char * TOPIC_DRIVE_MODE
constexpr const char * TOPIC_HEARTBEAT_MANUAL
std_msgs::Time createHearbeatMessage()
constexpr const char * TOPIC_HEARTBEAT_AUTONOMOUS
constexpr double PARAMETER_UPDATE_FREQUENCY
DriveMode
Definition: drive_mode.h:3
constexpr int SCREEN_EDGE_MARGIN
int main(int argc, char **argv)
double map(double value, double in_lower, double in_upper, double out_lower, double out_upper)