xbox_mapper.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 from __future__ import division
3 
4 # Author: Nick Fragale
5 # Description: This script converts Joystick commands into Joint Velocity commands
6 # Monitors A, B, X and Y buttons and toggles their state (False on startup) publishes
7 # a latched Bool() to /joystick/<button> where button is A, B, Y, or X
8 # these can be remapped to different topics to control various things like E-stoping the robot
9 # or starting to record a bagfile, or taking a picture.
10 
11 # Xbox controller mapping:
12 # axes: [l-stick horz,l-stick vert, l-trigger, r-stick horz, r-stick vert, r-trigger]
13 # buttons: [a,b,x,y,lb,rb,back,start,xbox,l-stick,r-stick,l-pad,r-pad,u-pad,d-pad]
14 
15 import time
16 
17 import rospy
18 from actionlib_msgs.msg import GoalID
19 from geometry_msgs.msg import TwistStamped
20 from sensor_msgs.msg import Joy
21 from std_msgs.msg import Bool
22 from std_msgs.msg import Float32, String
23 import rosnode
24 
25 rospy.init_node('xbox_mapper_node', anonymous=True)
26 
27 cmd = TwistStamped()
28 seq = 0
29 last_a_button = time.time()
30 last_b_button = time.time()
31 last_x_button = time.time()
32 last_y_button = time.time()
33 last_joycb_device_check = time.time()
34 button_msg = String()
35 
36 # difference between xboxdrv USB driver and xpad.ko kernel module
37 # 1) xboxdrv has only 11 indices
38 # 2) U/D_PAD_BUTTON is the 7th axis
39 # 3) xpad module uses 2, xboxdrv uses 3
40 driver = rospy.get_param('/xbox_mapper_node/driver', 'xboxdrv')
41 wired_or_wireless = rospy.get_param('/xbox_mapper_node/wired_or_wireless', 'wireless')
42 
43 if driver == 'xpad':
44  rospy.logfatal('[{node_name}] xpad driver not supported.'.format(node_name=rospy.get_name()))
45  rospy.signal_shutdown('xpad driver not supported.')
46  exit(-1)
47 
48 elif wired_or_wireless == 'wired' and driver == 'xboxdrv':
49  rospy.loginfo('XBOX CONFIG: wired & xboxdrv')
50  rospy.logwarn('If the wired controller becomes unplugged during operation '
51  'xboxdrv may continue to publish the last command from the '
52  'controller, causing the vehicle to run away.')
53 
54 elif wired_or_wireless == 'wireless' and driver == 'xboxdrv':
55  rospy.loginfo('XBOX CONFIG: wireless & xboxdrv')
56 
57 else:
58  rospy.logfatal('Unsupported controller configuration: {driver}, {connection}'
59  .format(driver=driver, connection=wired_or_wireless))
60  rospy.signal_shutdown('Unsupported controller configuration.')
61  exit(-1)
62 
63 L_STICK_H_AXES = 0
64 L_STICK_V_AXES = 1
65 L_TRIG_AXES = 5
66 R_STICK_H_AXES = 2
67 R_STICK_V_AXES = 3
68 R_TRIG_AXES = 4
69 DPAD_H_AXES = 6
70 DPAD_V_AXES = 7
71 
72 A_BUTTON = 0
73 B_BUTTON = 1
74 X_BUTTON = 2
75 Y_BUTTON = 3
76 LB_BUTTON = 4
77 RB_BUTTON = 5
78 BACK_BUTTON = 6
79 START_BUTTON = 7
80 POWER_BUTTON = 8
81 L_STICK_BUTTON = 9
82 R_STICK_BUTTON = 10
83 
84 prev_fwd = 0
85 prev_trn = 0
86 
87 PREV_CMD_TIME = 0
88 PREV_SEQ_NUM = 0
89 
90 MAX_VEL_FWD = rospy.get_param('~max_vel_drive', 2.6)
91 MAX_VEL_TURN = rospy.get_param('~max_vel_turn', 9.0)
92 MAX_VEL_FLIPPER = rospy.get_param('~max_vel_flipper', 1.4)
93 DRIVE_THROTTLE = rospy.get_param('~default_drive_throttle', 0.15)
94 FLIPPER_THROTTLE = rospy.get_param('~default_flipper_throttle', 0.6)
95 ADJ_THROTTLE = rospy.get_param('~adjustable_throttle', True)
96 A_BUTTON_TOGGLE = rospy.get_param('~a_button_toggle', False)
97 B_BUTTON_TOGGLE = rospy.get_param('~b_button_toggle', False)
98 X_BUTTON_TOGGLE = rospy.get_param('~x_button_toggle', False)
99 Y_BUTTON_TOGGLE = rospy.get_param('~y_button_toggle', False)
100 MIN_TOGGLE_DUR = 0.5
101 DRIVE_INCREMENTS = rospy.get_param('~drive_increment', 20.0)
102 FLIPPER_INCREMENTS = rospy.get_param('~drive_increment', 20.0)
103 DEADBAND = 0.2
104 FWD_ACC_LIM = 0.2
105 TRN_ACC_LIM = 0.4
106 DPAD_ACTIVE = False
107 
108 a_button_msg = Bool()
109 a_button_msg.data = False
110 b_button_msg = Bool()
111 b_button_msg.data = False
112 x_button_msg = Bool()
113 x_button_msg.data = False
114 y_button_msg = Bool()
115 y_button_msg.data = False
116 
117 # define publishers
118 pub = rospy.Publisher('/cmd_vel/joystick', TwistStamped, queue_size=3)
119 a_button_pub = rospy.Publisher('/joystick/a_button', Bool, queue_size=1, latch=True)
120 b_button_pub = rospy.Publisher('/joystick/b_button', Bool, queue_size=1, latch=True)
121 x_button_pub = rospy.Publisher('/joystick/x_button', Bool, queue_size=1, latch=True)
122 y_button_pub = rospy.Publisher('/joystick/y_button', Bool, queue_size=1, latch=True)
123 pub_delay = rospy.Publisher('/joystick/delay', Float32, queue_size=3)
124 pub_cancel_move_base = rospy.Publisher('/move_base/cancel', GoalID, queue_size=10)
125 
126 
127 def limit_acc(fwd, trn):
128  # TODO calculate delta t and times acc limits by that so that acc_limits are correct units
129  global prev_fwd, prev_trn
130 
131  fwd_acc = fwd - prev_fwd
132  if fwd_acc > FWD_ACC_LIM:
133  fwd = prev_fwd + FWD_ACC_LIM
134  elif fwd_acc < -FWD_ACC_LIM:
135  fwd = prev_fwd - FWD_ACC_LIM
136 
137  trn_acc = trn - prev_trn
138  if trn_acc > TRN_ACC_LIM:
139  trn = prev_trn + TRN_ACC_LIM
140  elif trn_acc < -TRN_ACC_LIM:
141  trn = prev_trn - TRN_ACC_LIM
142 
143  prev_fwd = fwd
144  prev_trn = trn
145 
146  return fwd, trn
147 
148 
149 def joy_cb(Joy):
150  global ADJ_THROTTLE
151  global MAX_VEL_FWD
152  global MAX_VEL_TURN
153  global MAX_VEL_FLIPPER
154  global DRIVE_THROTTLE
155  global FLIPPER_THROTTLE
156  global DRIVE_INCREMENTS
157  global FLIPPER_INCREMENTS
158  global PREV_CMD_TIME
159  global PREV_SEQ_NUM
160  global DPAD_ACTIVE
161 
162  global cmd
163  global seq
164  global last_a_button, last_b_button, last_x_button, last_y_button
165  global last_joycb_device_check
166  global a_button_pub, a_button_msg, b_button_pub, b_button_msg
167  global x_button_pub, x_button_msg, y_button_pub, y_button_msg
168 
169  cmd_time = float(Joy.header.stamp.secs) + (float(Joy.header.stamp.nsecs) / 1000000000)
170  rbt_time = time.time()
171  signal_delay = rbt_time - cmd_time
172 
173  joy_delay = Float32()
174  joy_delay.data = signal_delay
175  pub_delay.publish(joy_delay)
176 
177  # Record timestamp and seq for use in next loop
178  PREV_CMD_TIME = cmd_time
179  PREV_SEQ_NUM = Joy.header.seq
180 
181  # check for other two user-defined buttons. We only debounce them and monitor on/off status on a latched pub
182  # (green/A)
183  if A_BUTTON_TOGGLE:
184  if Joy.buttons[A_BUTTON] == 1:
185  if time.time() - last_a_button > MIN_TOGGLE_DUR:
186  last_a_button = time.time()
187  a_button_state = not a_button_msg.data
188  rospy.loginfo('A button toggled: {state}'.format(state=a_button_state))
189  a_button_msg.data = a_button_state
190  else:
191  if Joy.buttons[A_BUTTON] == 1:
192  a_button_msg.data = True
193  else:
194  a_button_msg.data = False
195  a_button_pub.publish(a_button_msg)
196 
197  # (red/B)
198  if B_BUTTON_TOGGLE:
199  if Joy.buttons[B_BUTTON] == 1:
200  if time.time() - last_b_button > MIN_TOGGLE_DUR:
201  last_b_button = time.time()
202  b_button_state = not b_button_msg.data
203  rospy.loginfo('B button toggled: {state}'.format(state=b_button_state))
204  b_button_msg.data = b_button_state
205  else:
206  if Joy.buttons[B_BUTTON] == 1:
207  b_button_msg.data = True
208  else:
209  b_button_msg.data = False
210  b_button_pub.publish(b_button_msg)
211 
212  # (blue/X)
213  if X_BUTTON_TOGGLE:
214  if Joy.buttons[X_BUTTON] == 1:
215  if time.time() - last_x_button > MIN_TOGGLE_DUR:
216  last_x_button = time.time()
217  x_button_state = not x_button_msg.data
218  rospy.loginfo('X button toggled: {state}'.format(state=x_button_state))
219  x_button_msg.data = x_button_state
220  else:
221  if Joy.buttons[X_BUTTON] == 1:
222  x_button_msg.data = True
223  else:
224  x_button_msg.data = False
225  x_button_pub.publish(x_button_msg)
226 
227  # (yellow/Y)
228  if Y_BUTTON_TOGGLE:
229  if Joy.buttons[Y_BUTTON] == 1:
230  if time.time() - last_y_button > MIN_TOGGLE_DUR:
231  last_y_button = time.time()
232  y_button_state = not y_button_msg.data
233  rospy.loginfo('Y button toggled: {state}'.format(state=y_button_state))
234  y_button_msg.data = y_button_state
235  else:
236  if Joy.buttons[Y_BUTTON] == 1:
237  y_button_msg.data = True
238  else:
239  y_button_msg.data = False
240  y_button_pub.publish(y_button_msg)
241 
242  if ADJ_THROTTLE:
243  # Increase/Decrease Max Speed
244  if (driver == 'xpad'):
245  if int(Joy.axes[DPAD_V_AXES]) == 1 and not DPAD_ACTIVE:
246  DRIVE_THROTTLE += (1 / DRIVE_INCREMENTS)
247  DPAD_ACTIVE = True
248  if int(Joy.axes[DPAD_V_AXES]) == -1 and not DPAD_ACTIVE:
249  DRIVE_THROTTLE -= (1 / DRIVE_INCREMENTS)
250  DPAD_ACTIVE = True
251  elif (driver == 'xboxdrv'):
252  if int(Joy.axes[DPAD_V_AXES]) == 1 and not DPAD_ACTIVE:
253  DRIVE_THROTTLE += (1 / DRIVE_INCREMENTS)
254  DPAD_ACTIVE = True
255  if int(Joy.axes[DPAD_V_AXES]) == -1 and not DPAD_ACTIVE:
256  DRIVE_THROTTLE -= (1 / DRIVE_INCREMENTS)
257  DPAD_ACTIVE = True
258 
259  if Joy.buttons[LB_BUTTON] == 1:
260  FLIPPER_THROTTLE -= (1 / FLIPPER_INCREMENTS)
261  rospy.loginfo(FLIPPER_THROTTLE)
262  if Joy.buttons[RB_BUTTON] == 1:
263  FLIPPER_THROTTLE += (1 / FLIPPER_INCREMENTS)
264  rospy.loginfo(FLIPPER_THROTTLE)
265 
266  # If the user tries to decrease full throttle to 0
267  # Then set it back up to 0.2 m/s
268  if DRIVE_THROTTLE <= 0.001:
269  DRIVE_THROTTLE = (1 / DRIVE_INCREMENTS)
270  if FLIPPER_THROTTLE <= 0.001:
271  FLIPPER_THROTTLE = (1 / FLIPPER_INCREMENTS)
272 
273  # If the user tries to increase the velocity limit when its at max
274  # then set velocity limit to max allowed velocity
275  if DRIVE_THROTTLE >= 1:
276  DRIVE_THROTTLE = 1
277  if FLIPPER_THROTTLE >= 1:
278  FLIPPER_THROTTLE = 1
279 
280  # Update DEADBAND
281  FWD_DEADBAND = 0.2 * DRIVE_THROTTLE * MAX_VEL_FWD
282  TURN_DEADBAND = 0.2 * DRIVE_THROTTLE * MAX_VEL_TURN
283  FLIPPER_DEADBAND = 0.2 * FLIPPER_THROTTLE * MAX_VEL_FLIPPER
284 
285  if DPAD_ACTIVE:
286  rospy.loginfo('Drive Throttle: %f', DRIVE_THROTTLE)
287 
288  if (Joy.axes[DPAD_V_AXES], Joy.axes[DPAD_H_AXES]) == (0, 0):
289  DPAD_ACTIVE = False
290 
291  # Drive Forward/Backward commands
292  drive_cmd = DRIVE_THROTTLE * MAX_VEL_FWD * Joy.axes[L_STICK_V_AXES] # left joystick
293  if drive_cmd < FWD_DEADBAND and -FWD_DEADBAND < drive_cmd:
294  drive_cmd = 0
295 
296  # Turn left/right commands
297  turn_cmd = (1.1 - (drive_cmd / MAX_VEL_FWD)) * DRIVE_THROTTLE * MAX_VEL_TURN * Joy.axes[
298  R_STICK_H_AXES] # right joystick
299  if turn_cmd < TURN_DEADBAND and -TURN_DEADBAND < turn_cmd:
300  turn_cmd = 0
301 
302  # Flipper up/down commands
303  flipper_cmd = (FLIPPER_THROTTLE * MAX_VEL_FLIPPER * Joy.axes[L_TRIG_AXES]) - (
304  FLIPPER_THROTTLE * MAX_VEL_FLIPPER * Joy.axes[R_TRIG_AXES])
305  if flipper_cmd < FLIPPER_DEADBAND and -FLIPPER_DEADBAND < flipper_cmd:
306  flipper_cmd = 0
307 
308  # Limit acceleration
309  # drive_cmd, turn_cmd = limit_acc(drive_cmd, turn_cmd)
310 
311  # update the last time joy_cb was called
312  if (drive_cmd != 0) or (turn_cmd != 0):
313  last_joycb_device_check = time.time()
314 
315  # Publish move commands
316  cmd.header.seq = seq
317  cmd.header.stamp = rospy.Time.now()
318  cmd.twist.linear.x = drive_cmd
319  cmd.twist.angular.y = flipper_cmd
320  cmd.twist.angular.z = turn_cmd
321  pub.publish(cmd)
322  seq += 1
323 
324 
325 # Main Function
327  # Initialize driver node
328  r = rospy.Rate(10) # 10hz
329  # publish the latched button initializations
330  a_button_pub.publish(a_button_msg)
331  b_button_pub.publish(b_button_msg)
332  x_button_pub.publish(x_button_msg)
333  y_button_pub.publish(y_button_msg)
334 
335  while not rospy.is_shutdown():
336  # Subscribe to the joystick topic
337  sub_cmds = rospy.Subscriber('joystick', Joy, joy_cb)
338 
339  # spin() simply keeps python from exiting until this node is stopped
340  rospy.spin()
341  r.sleep()
342 
343 
344 if __name__ == '__main__':
345  try:
346  joystick_main()
347  except rospy.ROSInterruptException:
348  pass
def limit_acc(fwd, trn)
Definition: xbox_mapper.py:127
def joy_cb(Joy)
Definition: xbox_mapper.py:149
def joystick_main()
Definition: xbox_mapper.py:326


rr_control_input_manager
Author(s):
autogenerated on Thu Sep 10 2020 03:38:35