keyop.cpp
Go to the documentation of this file.
1 
6 #include <ecl/exceptions.hpp>
7 #include <ecl/time.hpp>
8 #include <ros/ros.h>
9 #include <std_msgs/String.h>
10 #include "yocs_keyop/keyop.hpp"
11 
12 #define KEYCODE_RIGHT 67 // 0x43
13 #define KEYCODE_LEFT 68 // 0x44
14 #define KEYCODE_UP 65 // 0x41
15 #define KEYCODE_DOWN 66 // 0x42
16 #define KEYCODE_SPACE 32 // 0x20
17 
18 
19 namespace yocs_keyop
20 {
21 
22 KeyOp::KeyOp() : last_zero_vel_sent_(true), // avoid zero-vel messages from the beginning
23  accept_incoming_(true),
24  power_status_(false),
25  wait_for_connection_(true),
26  cmd_(new geometry_msgs::Twist()),
27  cmd_stamped_(new geometry_msgs::TwistStamped()),
28  linear_vel_step_(0.1),
29  linear_vel_max_(3.4),
30  angular_vel_step_(0.02),
31  angular_vel_max_(1.2),
32  quit_requested_(false),
33  key_file_descriptor_(0)
34 {
35  tcgetattr(key_file_descriptor_, &original_terminal_state_); // get terminal properties
36 }
37 
39 {
40  tcsetattr(key_file_descriptor_, TCSANOW, &original_terminal_state_);
41 }
42 
47 {
48  ros::NodeHandle nh("~");
49 
51 
52  /*********************
53  ** Parameters
54  **********************/
55  nh.getParam("linear_vel_step", linear_vel_step_);
56  nh.getParam("linear_vel_max", linear_vel_max_);
57  nh.getParam("angular_vel_step", angular_vel_step_);
58  nh.getParam("angular_vel_max", angular_vel_max_);
59  nh.getParam("wait_for_connection", wait_for_connection_);
60 
61  ROS_INFO_STREAM("KeyOp : using linear vel step [" << linear_vel_step_ << "].");
62  ROS_INFO_STREAM("KeyOp : using linear vel max [" << linear_vel_max_ << "].");
63  ROS_INFO_STREAM("KeyOp : using angular vel step [" << angular_vel_step_ << "].");
64  ROS_INFO_STREAM("KeyOp : using angular vel max [" << angular_vel_max_ << "].");
65 
66  /*********************
67  ** Publishers
68  **********************/
69  velocity_publisher_ = nh.advertise<geometry_msgs::Twist>("cmd_vel", 1);
70  enable_motors_publisher_ = nh.advertise<std_msgs::String>("enable_motors", 1);
71  disable_motors_publisher_ = nh.advertise<std_msgs::String>("disable_motors", 1);
72 
73  /*********************
74  ** Velocities
75  **********************/
76  cmd_->linear.x = 0.0;
77  cmd_->linear.y = 0.0;
78  cmd_->linear.z = 0.0;
79  cmd_->angular.x = 0.0;
80  cmd_->angular.y = 0.0;
81  cmd_->angular.z = 0.0;
82 
83  /*********************
84  ** Wait for connection
85  **********************/
86  bool connected = false;
88  {
89  connected = true;
90  }
91  ecl::MilliSleep millisleep;
92  int count = 0;
93  while (!connected)
94  {
95  if ((enable_motors_publisher_.getNumSubscribers() > 0) &&
96  (disable_motors_publisher_.getNumSubscribers() > 0))
97  {
98  connected = true;
99  break;
100  }
101  if (count == 6)
102  {
103  connected = false;
104  break;
105  }
106  else
107  {
108  ROS_WARN_STREAM("KeyOp: Could not connect, trying again after 500ms...");
109  try
110  {
111  millisleep(500);
112  }
113  catch (ecl::StandardException& e)
114  {
115  ROS_ERROR_STREAM("Waiting has been interrupted.");
116  ROS_DEBUG_STREAM(e.what());
117  return false;
118  }
119  ++count;
120  }
121  }
122  if (!connected)
123  {
124  ROS_ERROR("KeyOp: Could not connect.");
125  ROS_ERROR("KeyOp: Check remappings for enable/disable topics.");
126  }
127  else
128  {
129  std_msgs::String msg;
130  msg.data = "all";
131  enable_motors_publisher_.publish(msg);
132  ROS_INFO("KeyOp: connected.");
133  power_status_ = true;
134  }
135 
136  // start keyboard input thread
137  thread_.start(&KeyOp::keyboardInputLoop, *this);
138  return true;
139 }
140 
141 /*****************************************************************************
142  ** Implementation [Spin]
143  *****************************************************************************/
144 
151 {
152  ros::Rate loop_rate(10);
153 
154  while (!quit_requested_ && ros::ok())
155  {
156  // Avoid spamming robot with continuous zero-velocity messages
157  if ((cmd_->linear.x != 0.0) || (cmd_->linear.y != 0.0) || (cmd_->linear.z != 0.0) ||
158  (cmd_->angular.x != 0.0) || (cmd_->angular.y != 0.0) || (cmd_->angular.z != 0.0))
159  {
161  last_zero_vel_sent_ = false;
162  }
163  else if (last_zero_vel_sent_ == false)
164  {
166  last_zero_vel_sent_ = true;
167  }
168  accept_incoming_ = true;
169  ros::spinOnce();
170  loop_rate.sleep();
171  }
172  if (quit_requested_)
173  { // ros node is still ok, send a disable command
174  disable();
175  }
176  else
177  {
178  // just in case we got here not via a keyboard quit request
179  quit_requested_ = true;
180  thread_.cancel();
181  }
182  thread_.join();
183 }
184 
185 /*****************************************************************************
186  ** Implementation [Keyboard]
187  *****************************************************************************/
188 
196 {
197  struct termios raw;
198  memcpy(&raw, &original_terminal_state_, sizeof(struct termios));
199 
200  raw.c_lflag &= ~(ICANON | ECHO);
201  // Setting a new line, then end of file
202  raw.c_cc[VEOL] = 1;
203  raw.c_cc[VEOF] = 2;
204  tcsetattr(key_file_descriptor_, TCSANOW, &raw);
205 
206  puts("Reading from keyboard");
207  puts("---------------------------");
208  puts("Forward/back arrows : linear velocity incr/decr.");
209  puts("Right/left arrows : angular velocity incr/decr.");
210  puts("Spacebar : reset linear/angular velocities.");
211  puts("d : disable motors.");
212  puts("e : enable motors.");
213  puts("q : quit.");
214  char c;
215  while (!quit_requested_)
216  {
217  if (read(key_file_descriptor_, &c, 1) < 0)
218  {
219  perror("read char failed():");
220  exit(-1);
221  }
223  }
224 }
225 
232 {
233  /*
234  * Arrow keys are a bit special, they are escape characters - meaning they
235  * trigger a sequence of keycodes. In this case, 'esc-[-Keycode_xxx'. We
236  * ignore the esc-[ and just parse the last one. So long as we avoid using
237  * the last one for its actual purpose (e.g. left arrow corresponds to
238  * esc-[-D) we can keep the parsing simple.
239  */
240  switch (c)
241  {
242  case KEYCODE_LEFT:
243  {
245  break;
246  }
247  case KEYCODE_RIGHT:
248  {
250  break;
251  }
252  case KEYCODE_UP:
253  {
255  break;
256  }
257  case KEYCODE_DOWN:
258  {
260  break;
261  }
262  case KEYCODE_SPACE:
263  {
264  resetVelocity();
265  break;
266  }
267  case 'q':
268  {
269  quit_requested_ = true;
270  break;
271  }
272  case 'd':
273  {
274  disable();
275  break;
276  }
277  case 'e':
278  {
279  enable();
280  break;
281  }
282  default:
283  {
284  break;
285  }
286  }
287 }
288 
289 /*****************************************************************************
290  ** Implementation [Commands]
291  *****************************************************************************/
301 {
302  accept_incoming_ = false;
303  cmd_->linear.x = 0.0;
304  cmd_->angular.z = 0.0;
306 
307  if (power_status_)
308  {
309  ROS_INFO_STREAM("KeyOp: die, die, die (disabling power to the device's motor system).");
310  std_msgs::String msg;
311  msg.data = "all";
313  power_status_ = false;
314  }
315  else
316  {
317  ROS_WARN_STREAM("KeyOp: Motor system has already been powered down.");
318  }
319 }
320 
328 {
329  accept_incoming_ = false;
330  cmd_->linear.x = 0.0;
331  cmd_->angular.z = 0.0;
333 
334  if (!power_status_)
335  {
336  ROS_INFO_STREAM("KeyOp: Enabling power to the device subsystem.");
337  std_msgs::String msg;
338  msg.data = "all";
340  power_status_ = true;
341  }
342  else
343  {
344  ROS_WARN_STREAM("KeyOp: Device has already been powered up.");
345  }
346 }
347 
352 {
353  if (power_status_)
354  {
355  if (cmd_->linear.x <= linear_vel_max_)
356  {
357  cmd_->linear.x += linear_vel_step_;
358  }
359  ROS_INFO_STREAM("KeyOp: linear velocity incremented [" << cmd_->linear.x << "|" << cmd_->angular.z << "]");
360  }
361  else
362  {
363  ROS_WARN_STREAM("KeyOp: motors are not yet powered up.");
364  }
365 }
366 
371 {
372  if (power_status_)
373  {
374  if (cmd_->linear.x >= -linear_vel_max_)
375  {
376  cmd_->linear.x -= linear_vel_step_;
377  }
378  ROS_INFO_STREAM("KeyOp: linear velocity decremented [" << cmd_->linear.x << "|" << cmd_->angular.z << "]");
379  }
380  else
381  {
382  ROS_WARN_STREAM("KeyOp: motors are not yet powered up.");
383  }
384 }
385 
390 {
391  if (power_status_)
392  {
393  if (cmd_->angular.z <= angular_vel_max_)
394  {
395  cmd_->angular.z += angular_vel_step_;
396  }
397  ROS_INFO_STREAM("KeyOp: angular velocity incremented [" << cmd_->linear.x << "|" << cmd_->angular.z << "]");
398  }
399  else
400  {
401  ROS_WARN_STREAM("KeyOp: motors are not yet powered up.");
402  }
403 }
404 
409 {
410  if (power_status_)
411  {
412  if (cmd_->angular.z >= -angular_vel_max_)
413  {
414  cmd_->angular.z -= angular_vel_step_;
415  }
416  ROS_INFO_STREAM("KeyOp: angular velocity decremented [" << cmd_->linear.x << "|" << cmd_->angular.z << "]");
417  }
418  else
419  {
420  ROS_WARN_STREAM("KeyOp: motors are not yet powered up.");
421  }
422 }
423 
425 {
426  if (power_status_)
427  {
428  cmd_->angular.z = 0.0;
429  cmd_->linear.x = 0.0;
430  ROS_INFO_STREAM("KeyOp: reset linear/angular velocities.");
431  }
432  else
433  {
434  ROS_WARN_STREAM("KeyOp: motors are not yet powered up.");
435  }
436 }
437 
438 } // namespace yocs_keyop
const std::string & getUnresolvedNamespace() const
bool quit_requested_
Definition: keyop.hpp:67
double linear_vel_max_
Definition: keyop.hpp:45
void publish(const boost::shared_ptr< M > &message) const
const char * what() const
ros::Publisher disable_motors_publisher_
Definition: keyop.hpp:38
#define KEYCODE_UP
Definition: keyop.cpp:14
ros::Publisher velocity_publisher_
Definition: keyop.hpp:38
void keyboardInputLoop()
The worker thread function that accepts input keyboard commands.
Definition: keyop.cpp:195
void enable()
Reset/re-enable the navigation system.
Definition: keyop.cpp:327
ros::Publisher enable_motors_publisher_
Definition: keyop.hpp:38
void decrementLinearVelocity()
If not already minned, decrement the linear velocities..
Definition: keyop.cpp:370
bool power_status_
Definition: keyop.hpp:41
void spin()
Worker thread loop; sends current velocity command at a fixed rate.
Definition: keyop.cpp:150
int key_file_descriptor_
Definition: keyop.hpp:68
void incrementAngularVelocity()
If not already maxxed, increment the angular velocities..
Definition: keyop.cpp:389
#define KEYCODE_SPACE
Definition: keyop.cpp:16
#define KEYCODE_RIGHT
Definition: keyop.cpp:12
bool accept_incoming_
Definition: keyop.hpp:40
#define KEYCODE_LEFT
Definition: keyop.cpp:13
double linear_vel_step_
Definition: keyop.hpp:45
void processKeyboardInput(char c)
Process individual keyboard inputs.
Definition: keyop.cpp:231
std::string name_
Definition: keyop.hpp:47
#define ROS_INFO(...)
double angular_vel_max_
Definition: keyop.hpp:46
ROSCPP_DECL bool ok()
ecl::Thread thread_
Definition: keyop.hpp:70
void incrementLinearVelocity()
If not already maxxed, increment the command velocities..
Definition: keyop.cpp:351
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
#define ROS_WARN_STREAM(args)
#define ROS_DEBUG_STREAM(args)
void resetVelocity()
Definition: keyop.cpp:424
bool sleep()
struct termios original_terminal_state_
Definition: keyop.hpp:69
#define ROS_INFO_STREAM(args)
double angular_vel_step_
Definition: keyop.hpp:46
bool last_zero_vel_sent_
Definition: keyop.hpp:39
bool getParam(const std::string &key, std::string &s) const
bool wait_for_connection_
Definition: keyop.hpp:42
#define KEYCODE_DOWN
Definition: keyop.cpp:15
geometry_msgs::TwistPtr cmd_
Definition: keyop.hpp:43
bool init()
Initialises the node.
Definition: keyop.cpp:46
#define ROS_ERROR_STREAM(args)
void disable()
Disables commands to the navigation system.
Definition: keyop.cpp:300
ROSCPP_DECL void spinOnce()
#define ROS_ERROR(...)
void decrementAngularVelocity()
If not already mined, decrement the angular velocities..
Definition: keyop.cpp:408


yocs_keyop
Author(s): Daniel Stonier, Marcus Liebhardt
autogenerated on Mon Jun 10 2019 15:53:34