teleop_wiimote.cpp
Go to the documentation of this file.
1 /*
2  * ROS Node for using a wiimote control unit to direct a robot.
3  * Copyright (c) 2016, Intel Corporation.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU General Public License,
7  * version 2, as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  * more details.
13  */
14 
15 /*
16  * Initial C++ implementation by
17  * Mark Horn <mark.d.horn@intel.com>
18  *
19  * Revisions:
20  *
21  */
22 
23 #include "wiimote/teleop_wiimote.h"
24 #include "geometry_msgs/Twist.h"
25 #include "sensor_msgs/JoyFeedbackArray.h"
26 
27 #include <string>
28 
30 {
31  ros::NodeHandle nh_private("~");
32  ros::NodeHandle nh;
33  std::string base_name;
34 
35  if (nh_private.hasParam("base"))
36  {
37  if (nh_private.getParam("base", base_name))
38  {
39  ROS_INFO("User namespace '%s' for robot limits.", base_name.c_str());
40  }
41  }
42  base_name += "/";
43 
44  if (!nh.getParam(base_name + "linear/x/max_velocity", linear_x_max_velocity_))
45  {
47  ROS_INFO("Defaulting to maximum linear x: %f", linear_x_max_velocity_);
48  }
49  else
50  {
51  ROS_INFO("Maximum linear x: %f", linear_x_max_velocity_);
52  }
53  if (!nh.getParam(base_name + "linear/x/min_velocity", linear_x_min_velocity_))
54  {
56  ROS_INFO("Defaulting to minimum linear x: %f", linear_x_min_velocity_);
57  }
58  else
59  {
60  ROS_INFO("Minimum linear x: %f", linear_x_min_velocity_);
61  }
62 
63  if (!nh.getParam(base_name + "angular/z/max_velocity", angular_z_max_velocity_))
64  {
66  ROS_INFO("Defaulting to maximum angular z: %f", angular_z_max_velocity_);
67  }
68  else
69  {
70  ROS_INFO("Maximum angular z: %f", angular_z_max_velocity_);
71  }
72  if (!nh.getParam(base_name + "angular/z/min_velocity", angular_z_min_velocity_))
73  {
75  ROS_INFO("Defaulting to minimum angular z: %f", angular_z_min_velocity_);
76  }
77  else
78  {
79  ROS_INFO("Minimum angular z: %f", angular_z_min_velocity_);
80  }
81 
82  // Percent throttle limiter
83  if (nh_private.hasParam("linear/x/throttle_percent"))
84  {
85  if (!nh_private.getParam("linear/x/throttle_percent", percent_linear_throttle_))
86  {
87  ROS_WARN("Failed to get linear x throttle percent; using %3.0f", DEFAULT_PERCENT_LINEAR_THROTTLE * 100.0);
89  }
90  }
91  else
92  {
94  }
95  nh_private.setParam("linear/x/throttle_percent", percent_linear_throttle_);
96  ROS_INFO("Linear X Throttle Percent: %3.0f", percent_linear_throttle_ * 100.0);
97 
98  if (nh_private.hasParam("angular/z/throttle_percent"))
99  {
100  if (!nh_private.getParam("angular/z/throttle_percent", percent_angular_throttle_))
101  {
102  ROS_WARN("Failed to get angular z throttle percent; using %3.0f", DEFAULT_PERCENT_ANGULAR_THROTTLE * 100.0);
104  }
105  }
106  else
107  {
109  }
110  nh_private.setParam("angular/x/throttle_percent", percent_angular_throttle_);
111  ROS_INFO("Angular Z Throttle Percent: %3.0f", percent_angular_throttle_ * 100.0);
112 
113  vel_pub_ = nh.advertise<geometry_msgs::Twist>("/cmd_vel", 1);
114  joy_pub_ = nh.advertise<sensor_msgs::JoyFeedbackArray>("joy/set_feedback", 1);
115 
116  joy_sub_ = nh.subscribe<sensor_msgs::Joy>("wiimote/nunchuk", 10, &TeleopWiimote::joyCallback, this);
117  wiimote_sub_ = nh.subscribe<wiimote::State>("wiimote/state", 10, &TeleopWiimote::wiimoteStateCallback, this);
118 
119  dpad_in_use_ = false;
120  njoy_in_use_ = false;
121 }
122 
124 {
125  sensor_msgs::JoyFeedbackArray joy_feedback_array;
126  sensor_msgs::JoyFeedback fb_led0;
127  sensor_msgs::JoyFeedback fb_led1;
128  sensor_msgs::JoyFeedback fb_led2;
129  sensor_msgs::JoyFeedback fb_led3;
130 
131  fb_led0.type = sensor_msgs::JoyFeedback::TYPE_LED;
132  fb_led0.id = 0;
133  fb_led0.intensity = 0.0;
134  fb_led1.type = sensor_msgs::JoyFeedback::TYPE_LED;
135  fb_led1.id = 1;
136  fb_led1.intensity = 0.0;
137  fb_led2.type = sensor_msgs::JoyFeedback::TYPE_LED;
138  fb_led2.id = 2;
139  fb_led2.intensity = 0.0;
140  fb_led3.type = sensor_msgs::JoyFeedback::TYPE_LED;
141  fb_led3.id = 3;
142  fb_led3.intensity = 0.0;
143 
144  if (value > 10.0)
145  {
146  fb_led0.intensity = 1.0;
147  }
148  if (value > 35.0)
149  {
150  fb_led1.intensity = 1.0;
151  }
152  if (value > 60.0)
153  {
154  fb_led2.intensity = 1.0;
155  }
156  if (value > 85.0)
157  {
158  fb_led3.intensity = 1.0;
159  }
160 
161  joy_feedback_array.array.push_back(fb_led0);
162  joy_feedback_array.array.push_back(fb_led1);
163  joy_feedback_array.array.push_back(fb_led2);
164  joy_feedback_array.array.push_back(fb_led3);
165 
166  joy_pub_.publish(joy_feedback_array);
167 }
168 
170 {
171  sensor_msgs::JoyFeedbackArray joy_feedback_array;
172  sensor_msgs::JoyFeedback fb_rumble;
173 
174  fb_rumble.type = sensor_msgs::JoyFeedback::TYPE_RUMBLE;
175  fb_rumble.id = 0;
176 
177  fb_rumble.intensity = 1.0;
178 
179  joy_feedback_array.array.push_back(fb_rumble);
180 
181  joy_pub_.publish(joy_feedback_array);
182  usleep(useconds);
183  fb_rumble.intensity = 0.0;
184 
185  joy_feedback_array.array.push_back(fb_rumble);
186 
187  joy_pub_.publish(joy_feedback_array);
188 }
189 
190 void TeleopWiimote::joyCallback(const sensor_msgs::Joy::ConstPtr& joy)
191 {
192  geometry_msgs::Twist vel;
193 
194  static const int MSG_BTN_Z = 0;
195  static const int MSG_BTN_C = 1;
196 
197  if (dpad_in_use_)
198  {
199  return;
200  }
201 
202  float x = joy->axes[0];
203  float y = joy->axes[1];
204  float const abs_error = 0.000001;
205 
206  if (fabs(x) > abs_error || fabs(y) > abs_error)
207  {
208  njoy_in_use_ = true;
209 
210  float boost = 1.0;
211 
212  ROS_INFO("nunchuk: x: %f, y: %f", x, y);
213 
214  if (joy->buttons[MSG_BTN_Z] ||
215  joy->buttons[MSG_BTN_C])
216  {
217  ROS_INFO("buttons[]: Z: %d, C: %d",
218  joy->buttons[MSG_BTN_Z],
219  joy->buttons[MSG_BTN_C]);
220 
221  // Z-Button is thrusters on!
222  if (joy->buttons[MSG_BTN_Z])
223  {
224  boost = 2.0;
225  }
226 
227  // C-Button is easy does it.
228  if (joy->buttons[MSG_BTN_C])
229  {
230  boost = 0.25;
231  }
232  }
233 
234  if (y >= 0.0)
235  {
236  vel.linear.x = fmin((y * boost * (linear_x_max_velocity_ * percent_linear_throttle_)),
238 
239  if (x >= 0.0)
240  {
241  vel.angular.z = fmin((x * boost * (angular_z_max_velocity_ * percent_angular_throttle_)),
243  }
244  else
245  {
246  vel.angular.z = fmax((fabs(x) * boost * (angular_z_min_velocity_ * percent_angular_throttle_)),
248  }
249  }
250  else
251  {
252  vel.linear.x = fmax((fabs(y) * boost * (linear_x_min_velocity_ * percent_linear_throttle_)),
254 
255  if (x > 0.0)
256  {
257  vel.angular.z = fmax((x * boost * (angular_z_min_velocity_ * percent_angular_throttle_)),
259  }
260  else
261  {
262  vel.angular.z = fmin((fabs(x) * boost * (angular_z_max_velocity_ * percent_angular_throttle_)),
264  }
265  }
266 
267  // In order to spin-in-place left or right, we need full angular with NO linear component.
268  // To enable this, we will publish no linear motion if nunchuk joystick Y value is
269  // "really small" as the joy stick isn't 100 accurate.
270  if (fabs(y) < 0.01)
271  {
272  vel.linear.x = 0;
273  }
274 
275  vel_pub_.publish(vel);
276  }
277  else
278  {
279  if (njoy_in_use_)
280  {
281  vel_pub_.publish(vel);
282 
283  njoy_in_use_ = false;
284  }
285  }
286 }
287 void TeleopWiimote::wiimoteStateCallback(const wiimote::State::ConstPtr& wiistate)
288 {
289  ros::NodeHandle nh_private("~");
290  geometry_msgs::Twist vel;
291 
292  static const int MSG_BTN_1 = 0;
293  static const int MSG_BTN_2 = 1;
294  static const int MSG_BTN_PLUS = 2;
295  static const int MSG_BTN_MINUS = 3;
296  static const int MSG_BTN_A = 4;
297  static const int MSG_BTN_B = 5;
298  static const int MSG_BTN_UP = 6;
299  static const int MSG_BTN_DOWN = 7;
300  static const int MSG_BTN_LEFT = 8;
301  static const int MSG_BTN_RIGHT = 9;
302  static const int MSG_BTN_HOME = 10;
303 
304  static bool one_depressed = false;
305  static bool two_depressed = false;
306  static bool plus_depressed = false;
307  static bool minus_depressed = false;
308  static bool home_depressed = false;
309 
310 
311  // 1-Button used to set the amount of Linear Throttle
312  // Pressing the button show approx setting level on the
313  // Wiimote LEDs (see setLEDFeedback for levels).
314  // Wiimote uses a short Rumble when the minimum or
315  // maximum is reached.
316  // +-Button increases; --Button decreases while hold 1-Button
317  if (wiistate->buttons[MSG_BTN_1])
318  {
319  if (wiistate->buttons[MSG_BTN_PLUS])
320  {
321  if (!plus_depressed)
322  {
323  percent_linear_throttle_ += 0.05;
324  if (percent_linear_throttle_ >= 1.0)
325  {
326  rumbleFeedback(100000);
327  }
329  plus_depressed = true;
330 
332 
333  nh_private.setParam("linear/x/throttle_percent", percent_linear_throttle_);
334  ROS_INFO("Linear X Throttle Percent: %3.0f", percent_linear_throttle_ * 100.0);
335  }
336  }
337  else
338  {
339  plus_depressed = false;
340 
341  if (wiistate->buttons[MSG_BTN_MINUS])
342  {
343  if (!minus_depressed)
344  {
345  percent_linear_throttle_ -= 0.05;
346  if (percent_linear_throttle_ <= 0.1)
347  {
348  rumbleFeedback(100000);
349  }
351  minus_depressed = true;
352 
354 
355  nh_private.setParam("linear/x/throttle_percent", percent_linear_throttle_);
356  ROS_INFO("Linear X Throttle Percent: %3.0f", percent_linear_throttle_ * 100.0);
357  }
358  }
359  else
360  {
361  minus_depressed = false;
362  }
363  }
364 
365  if (!one_depressed)
366  {
368 
369  nh_private.setParam("linear/x/throttle_percent", percent_linear_throttle_);
370  ROS_INFO("Linear X Throttle Percent: %3.0f", percent_linear_throttle_ * 100.0);
371 
372  one_depressed = true;
373  }
374  }
375  // 2-Button used to set the amount of Angular Throttle
376  // Same function and feedbacks as 1-Button (see above)
377  else if (wiistate->buttons[MSG_BTN_2])
378  {
379  if (wiistate->buttons[MSG_BTN_PLUS])
380  {
381  if (!plus_depressed)
382  {
384  if (percent_angular_throttle_ >= 1.0)
385  {
386  rumbleFeedback(100000);
387  }
389  plus_depressed = true;
390 
392 
393  nh_private.setParam("angular/x/throttle_percent", percent_angular_throttle_);
394  ROS_INFO("Angular Z Throttle Percent: %3.0f", percent_angular_throttle_ * 100.0);
395  }
396  }
397  else
398  {
399  plus_depressed = false;
400 
401  if (wiistate->buttons[MSG_BTN_MINUS])
402  {
403  if (!minus_depressed)
404  {
406  if (percent_angular_throttle_ <= 0.1)
407  {
408  rumbleFeedback(100000);
409  }
411  minus_depressed = true;
412 
414 
415  nh_private.setParam("angular/x/throttle_percent", percent_angular_throttle_);
416  ROS_INFO("Angular Z Throttle Percent: %3.0f", percent_angular_throttle_ * 100.0);
417  }
418  }
419  else
420  {
421  minus_depressed = false;
422  }
423  }
424 
425  if (!two_depressed)
426  {
428 
429  nh_private.setParam("angular/x/throttle_percent", percent_angular_throttle_);
430  ROS_INFO("Angular Z Throttle Percent: %3.0f", percent_angular_throttle_ * 100.0);
431 
432  two_depressed = true;
433  }
434  }
435  else
436  {
437  if (one_depressed || two_depressed)
438  {
439  setLEDFeedback(0.0);
440  }
441 
442  one_depressed = false;
443  two_depressed = false;
444 
445  // Home-Button used the Wiimote LEDs (see setLEDFeedback for levels).
446  // to show the approx battery leve of the Wiimote.
447  // Only works if the 1-Button or 2-Button are not in use.
448  if (wiistate->buttons[MSG_BTN_HOME])
449  {
450  if (!home_depressed)
451  {
452  ROS_INFO("Battery[]: raw: %f, percent: %f", wiistate->raw_battery, wiistate->percent_battery);
453  setLEDFeedback(wiistate->percent_battery);
454  home_depressed = true;
455  }
456  }
457  else
458  {
459  if (home_depressed)
460  {
461  setLEDFeedback(0.0);
462  }
463 
464  home_depressed = false;
465  }
466  }
467 
468  if (!njoy_in_use_ &&
469  (wiistate->buttons[MSG_BTN_RIGHT] ||
470  wiistate->buttons[MSG_BTN_LEFT] ||
471  wiistate->buttons[MSG_BTN_UP] ||
472  wiistate->buttons[MSG_BTN_DOWN]))
473  {
474  dpad_in_use_ = true;
475 
476  ROS_INFO("buttons[]: Right: %d, Left: %d, Up: %d, Down: %d, A: %d, B: %d",
477  wiistate->buttons[MSG_BTN_RIGHT],
478  wiistate->buttons[MSG_BTN_LEFT],
479  wiistate->buttons[MSG_BTN_UP],
480  wiistate->buttons[MSG_BTN_DOWN],
481  wiistate->buttons[MSG_BTN_A],
482  wiistate->buttons[MSG_BTN_B]);
483 
484  float boost = 1.0;
485 
486  // B-Button is thrusters on!
487  if (wiistate->buttons[MSG_BTN_B])
488  {
489  boost = 2.0;
490  }
491 
492  // A-Button is easy does it.
493  if (wiistate->buttons[MSG_BTN_A])
494  {
495  boost = 0.25;
496  }
497 
498  if (wiistate->buttons[MSG_BTN_UP])
499  {
500  vel.linear.x = fmin((boost * (linear_x_max_velocity_ * percent_linear_throttle_)),
502  }
503  else if (wiistate->buttons[MSG_BTN_DOWN])
504  {
505  vel.linear.x = fmax((boost * (linear_x_min_velocity_ * percent_linear_throttle_)),
507  }
508 
509  if (wiistate->buttons[MSG_BTN_LEFT])
510  {
511  vel.angular.z = fmin((boost * (angular_z_max_velocity_ * percent_angular_throttle_)),
513  }
514  else if (wiistate->buttons[MSG_BTN_RIGHT])
515  {
516  vel.angular.z = fmax((boost * (angular_z_min_velocity_ * percent_angular_throttle_)),
518  }
519 
520  vel_pub_.publish(vel);
521  }
522  else
523  {
524  if (dpad_in_use_)
525  {
526  vel_pub_.publish(vel);
527  dpad_in_use_ = false;
528  }
529  }
530 }
531 
532 int main(int argc, char** argv)
533 {
534  ros::init(argc, argv, "teleop_wiimote");
535  TeleopWiimote teleop_wiimote;
536 
537  ros::spin();
538 }
void joyCallback(const sensor_msgs::Joy::ConstPtr &joy)
#define DEFAULT_MAX_LINEAR_X
void publish(const boost::shared_ptr< M > &message) const
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
#define ROS_WARN(...)
double linear_x_min_velocity_
void wiimoteStateCallback(const wiimote::State::ConstPtr &wiistate)
ros::Publisher joy_pub_
#define ROS_INFO(...)
double linear_x_max_velocity_
void rumbleFeedback(int useconds)
void setLEDFeedback(double value)
ros::Subscriber wiimote_sub_
ros::Publisher vel_pub_
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
ROSCPP_DECL void spin()
double angular_z_max_velocity_
double percent_angular_throttle_
ros::Subscriber joy_sub_
int main(int argc, char **argv)
bool getParam(const std::string &key, std::string &s) const
#define DEFAULT_PERCENT_LINEAR_THROTTLE
double angular_z_min_velocity_
bool hasParam(const std::string &key) const
#define DEFAULT_MAX_ANGULAR_Z
double percent_linear_throttle_
void setParam(const std::string &key, const XmlRpc::XmlRpcValue &v) const
#define DEFAULT_PERCENT_ANGULAR_THROTTLE


wiimote
Author(s): Andreas Paepcke, Melonee Wise, Mark Horn
autogenerated on Mon Jun 10 2019 13:42:43