keyop_core.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012, Yujin Robot.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of Yujin Robot nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
37 /*****************************************************************************
38  ** Includes
39  *****************************************************************************/
40 
41 #include <ros/ros.h>
42 #include <ecl/time.hpp>
43 #include <ecl/exceptions.hpp>
44 #include <std_srvs/Empty.h>
45 #include <kobuki_msgs/MotorPower.h>
46 #include "../include/keyop_core/keyop_core.hpp"
47 
48 /*****************************************************************************
49  ** Namespaces
50  *****************************************************************************/
51 
52 namespace keyop_core
53 {
54 
55 /*****************************************************************************
56  ** Implementation
57  *****************************************************************************/
58 
62 KeyOpCore::KeyOpCore() : last_zero_vel_sent(true), // avoid zero-vel messages from the beginning
63  accept_incoming(true),
64  power_status(false),
65  wait_for_connection_(true),
66  cmd(new geometry_msgs::Twist()),
67  cmd_stamped(new geometry_msgs::TwistStamped()),
68  linear_vel_step(0.1),
69  linear_vel_max(3.4),
70  angular_vel_step(0.02),
71  angular_vel_max(1.2),
72  quit_requested(false),
73  key_file_descriptor(0)
74 {
75  tcgetattr(key_file_descriptor, &original_terminal_state); // get terminal properties
76 }
77 
79 {
80  tcsetattr(key_file_descriptor, TCSANOW, &original_terminal_state);
81 }
82 
87 {
88  ros::NodeHandle nh("~");
89 
91 
92  /*********************
93  ** Parameters
94  **********************/
95  nh.getParam("linear_vel_step", linear_vel_step);
96  nh.getParam("linear_vel_max", linear_vel_max);
97  nh.getParam("angular_vel_step", angular_vel_step);
98  nh.getParam("angular_vel_max", angular_vel_max);
99  nh.getParam("wait_for_connection", wait_for_connection_);
100 
101  ROS_INFO_STREAM("KeyOpCore : using linear vel step [" << linear_vel_step << "].");
102  ROS_INFO_STREAM("KeyOpCore : using linear vel max [" << linear_vel_max << "].");
103  ROS_INFO_STREAM("KeyOpCore : using angular vel step [" << angular_vel_step << "].");
104  ROS_INFO_STREAM("KeyOpCore : using angular vel max [" << angular_vel_max << "].");
105 
106  /*********************
107  ** Subscribers
108  **********************/
110 
111  /*********************
112  ** Publishers
113  **********************/
114  velocity_publisher_ = nh.advertise<geometry_msgs::Twist>("cmd_vel", 1);
115  motor_power_publisher_ = nh.advertise<kobuki_msgs::MotorPower>("motor_power", 1);
116 
117  /*********************
118  ** Velocities
119  **********************/
120  cmd->linear.x = 0.0;
121  cmd->linear.y = 0.0;
122  cmd->linear.z = 0.0;
123  cmd->angular.x = 0.0;
124  cmd->angular.y = 0.0;
125  cmd->angular.z = 0.0;
126 
127  /*********************
128  ** Wait for connection
129  **********************/
131  {
132  return true;
133  }
134  ecl::MilliSleep millisleep;
135  int count = 0;
136  bool connected = false;
137  while (!connected)
138  {
139  if (motor_power_publisher_.getNumSubscribers() > 0)
140  {
141  connected = true;
142  break;
143  }
144  if (count == 6)
145  {
146  connected = false;
147  break;
148  }
149  else
150  {
151  ROS_WARN_STREAM("KeyOp: could not connect, trying again after 500ms...");
152  try
153  {
154  millisleep(500);
155  }
156  catch (ecl::StandardException e)
157  {
158  ROS_ERROR_STREAM("Waiting has been interrupted.");
159  ROS_DEBUG_STREAM(e.what());
160  return false;
161  }
162  ++count;
163  }
164  }
165  if (!connected)
166  {
167  ROS_ERROR("KeyOp: could not connect.");
168  ROS_ERROR("KeyOp: check remappings for enable/disable topics).");
169  }
170  else
171  {
172  kobuki_msgs::MotorPower power_cmd;
173  power_cmd.state = kobuki_msgs::MotorPower::ON;
174  motor_power_publisher_.publish(power_cmd);
175  ROS_INFO("KeyOp: connected.");
176  power_status = true;
177  }
178 
179  // start keyboard input thread
180  thread.start(&KeyOpCore::keyboardInputLoop, *this);
181  return true;
182 }
183 
184 /*****************************************************************************
185  ** Implementation [Spin]
186  *****************************************************************************/
187 
194 {
195  ros::Rate loop_rate(10);
196 
197  while (!quit_requested && ros::ok())
198  {
199  // Avoid spamming robot with continuous zero-velocity messages
200  if ((cmd->linear.x != 0.0) || (cmd->linear.y != 0.0) || (cmd->linear.z != 0.0) ||
201  (cmd->angular.x != 0.0) || (cmd->angular.y != 0.0) || (cmd->angular.z != 0.0))
202  {
204  last_zero_vel_sent = false;
205  }
206  else if (last_zero_vel_sent == false)
207  {
209  last_zero_vel_sent = true;
210  }
211  accept_incoming = true;
212  ros::spinOnce();
213  loop_rate.sleep();
214  }
215  if (quit_requested)
216  { // ros node is still ok, send a disable command
217  disable();
218  }
219  else
220  {
221  // just in case we got here not via a keyboard quit request
222  quit_requested = true;
223  thread.cancel();
224  }
225  thread.join();
226 }
227 
228 /*****************************************************************************
229  ** Implementation [Keyboard]
230  *****************************************************************************/
231 
239 {
240  struct termios raw;
241  memcpy(&raw, &original_terminal_state, sizeof(struct termios));
242 
243  raw.c_lflag &= ~(ICANON | ECHO);
244  // Setting a new line, then end of file
245  raw.c_cc[VEOL] = 1;
246  raw.c_cc[VEOF] = 2;
247  tcsetattr(key_file_descriptor, TCSANOW, &raw);
248 
249  puts("Reading from keyboard");
250  puts("---------------------------");
251  puts("Forward/back arrows : linear velocity incr/decr.");
252  puts("Right/left arrows : angular velocity incr/decr.");
253  puts("Spacebar : reset linear/angular velocities.");
254  puts("d : disable motors.");
255  puts("e : enable motors.");
256  puts("q : quit.");
257  char c;
258  while (!quit_requested)
259  {
260  if (read(key_file_descriptor, &c, 1) < 0)
261  {
262  perror("read char failed():");
263  exit(-1);
264  }
266  }
267 }
268 
272 void KeyOpCore::remoteKeyInputReceived(const kobuki_msgs::KeyboardInput& key)
273 {
274  processKeyboardInput(key.pressedKey);
275 }
276 
283 {
284  /*
285  * Arrow keys are a bit special, they are escape characters - meaning they
286  * trigger a sequence of keycodes. In this case, 'esc-[-Keycode_xxx'. We
287  * ignore the esc-[ and just parse the last one. So long as we avoid using
288  * the last one for its actual purpose (e.g. left arrow corresponds to
289  * esc-[-D) we can keep the parsing simple.
290  */
291  switch (c)
292  {
293  case kobuki_msgs::KeyboardInput::KeyCode_Left:
294  {
296  break;
297  }
298  case kobuki_msgs::KeyboardInput::KeyCode_Right:
299  {
301  break;
302  }
303  case kobuki_msgs::KeyboardInput::KeyCode_Up:
304  {
306  break;
307  }
308  case kobuki_msgs::KeyboardInput::KeyCode_Down:
309  {
311  break;
312  }
313  case kobuki_msgs::KeyboardInput::KeyCode_Space:
314  {
315  resetVelocity();
316  break;
317  }
318  case 'q':
319  {
320  quit_requested = true;
321  break;
322  }
323  case 'd':
324  {
325  disable();
326  break;
327  }
328  case 'e':
329  {
330  enable();
331  break;
332  }
333  default:
334  {
335  break;
336  }
337  }
338 }
339 
340 /*****************************************************************************
341  ** Implementation [Commands]
342  *****************************************************************************/
352 {
353  cmd->linear.x = 0.0;
354  cmd->angular.z = 0.0;
356  accept_incoming = false;
357 
358  if (power_status)
359  {
360  ROS_INFO("KeyOp: die, die, die (disabling power to the device's motor system).");
361  kobuki_msgs::MotorPower power_cmd;
362  power_cmd.state = kobuki_msgs::MotorPower::OFF;
363  motor_power_publisher_.publish(power_cmd);
364  power_status = false;
365  }
366  else
367  {
368  ROS_WARN("KeyOp: Motor system has already been powered down.");
369  }
370 }
371 
379 {
380  accept_incoming = false;
381 
382  cmd->linear.x = 0.0;
383  cmd->angular.z = 0.0;
385 
386  if (!power_status)
387  {
388  ROS_INFO("KeyOp: Enabling power to the device subsystem.");
389  kobuki_msgs::MotorPower power_cmd;
390  power_cmd.state = kobuki_msgs::MotorPower::ON;
391  motor_power_publisher_.publish(power_cmd);
392  power_status = true;
393  }
394  else
395  {
396  ROS_WARN("KeyOp: Device has already been powered up.");
397  }
398 }
399 
404 {
405  if (power_status)
406  {
407  if (cmd->linear.x <= linear_vel_max)
408  {
409  cmd->linear.x += linear_vel_step;
410  }
411  ROS_INFO_STREAM("KeyOp: linear velocity incremented [" << cmd->linear.x << "|" << cmd->angular.z << "]");
412  }
413  else
414  {
415  ROS_WARN_STREAM("KeyOp: motors are not yet powered up.");
416  }
417 }
418 
423 {
424  if (power_status)
425  {
426  if (cmd->linear.x >= -linear_vel_max)
427  {
428  cmd->linear.x -= linear_vel_step;
429  }
430  ROS_INFO_STREAM("KeyOp: linear velocity decremented [" << cmd->linear.x << "|" << cmd->angular.z << "]");
431  }
432  else
433  {
434  ROS_WARN_STREAM("KeyOp: motors are not yet powered up.");
435  }
436 }
437 
442 {
443  if (power_status)
444  {
445  if (cmd->angular.z <= angular_vel_max)
446  {
447  cmd->angular.z += angular_vel_step;
448  }
449  ROS_INFO_STREAM("KeyOp: angular velocity incremented [" << cmd->linear.x << "|" << cmd->angular.z << "]");
450  }
451  else
452  {
453  ROS_WARN_STREAM("KeyOp: motors are not yet powered up.");
454  }
455 }
456 
461 {
462  if (power_status)
463  {
464  if (cmd->angular.z >= -angular_vel_max)
465  {
466  cmd->angular.z -= angular_vel_step;
467  }
468  ROS_INFO_STREAM("KeyOp: angular velocity decremented [" << cmd->linear.x << "|" << cmd->angular.z << "]");
469  }
470  else
471  {
472  ROS_WARN_STREAM("KeyOp: motors are not yet powered up.");
473  }
474 }
475 
477 {
478  if (power_status)
479  {
480  cmd->angular.z = 0.0;
481  cmd->linear.x = 0.0;
482  ROS_INFO_STREAM("KeyOp: reset linear/angular velocities.");
483  }
484  else
485  {
486  ROS_WARN_STREAM("KeyOp: motors are not yet powered up.");
487  }
488 }
489 
490 } // namespace keyop_core
const std::string & getUnresolvedNamespace() const
void enable()
Reset/re-enable the navigation system.
Definition: keyop_core.cpp:378
void disable()
Disables commands to the navigation system.
Definition: keyop_core.cpp:351
void publish(const boost::shared_ptr< M > &message) const
bool init()
Initialises the node.
Definition: keyop_core.cpp:86
const char * what() const
void decrementLinearVelocity()
If not already minned, decrement the linear velocities..
Definition: keyop_core.cpp:422
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
string cmd
ros::Subscriber keyinput_subscriber
Definition: keyop_core.hpp:85
geometry_msgs::TwistPtr cmd
Definition: keyop_core.hpp:92
#define ROS_WARN(...)
ros::Publisher motor_power_publisher_
Definition: keyop_core.hpp:87
void remoteKeyInputReceived(const kobuki_msgs::KeyboardInput &key)
Callback function for remote keyboard inputs subscriber.
Definition: keyop_core.cpp:272
void keyboardInputLoop()
The worker thread function that accepts input keyboard commands.
Definition: keyop_core.cpp:238
KeyOpCore()
Default constructor, needs initialisation.
Definition: keyop_core.cpp:62
#define ROS_INFO(...)
ros::Publisher velocity_publisher_
Definition: keyop_core.hpp:86
void incrementLinearVelocity()
If not already maxxed, increment the command velocities..
Definition: keyop_core.cpp:403
ROSCPP_DECL bool ok()
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
void spin()
Worker thread loop; sends current velocity command at a fixed rate.
Definition: keyop_core.cpp:193
#define ROS_WARN_STREAM(args)
#define ROS_DEBUG_STREAM(args)
bool sleep()
void processKeyboardInput(char c)
Process individual keyboard inputs.
Definition: keyop_core.cpp:282
void decrementAngularVelocity()
If not already mined, decrement the angular velocities..
Definition: keyop_core.cpp:460
struct termios original_terminal_state
Definition: keyop_core.hpp:119
#define ROS_INFO_STREAM(args)
bool getParam(const std::string &key, std::string &s) const
void incrementAngularVelocity()
If not already maxxed, increment the angular velocities..
Definition: keyop_core.cpp:441
#define ROS_ERROR_STREAM(args)
ROSCPP_DECL void spinOnce()
#define ROS_ERROR(...)


kobuki_keyop
Author(s): Daniel Stonier
autogenerated on Mon Jun 10 2019 13:45:04