00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 import curses
00011 import math
00012
00013 import rospy
00014 from geometry_msgs.msg import Twist
00015 from geometry_msgs.msg import TwistStamped
00016
00017
00018 class Velocity(object):
00019
00020 def __init__(self, min_velocity, max_velocity, num_steps):
00021 assert min_velocity > 0 and max_velocity > 0 and num_steps > 0
00022 self._min = min_velocity
00023 self._max = max_velocity
00024 self._num_steps = num_steps
00025 if self._num_steps > 1:
00026 self._step_incr = (max_velocity - min_velocity) / (self._num_steps - 1)
00027 else:
00028
00029 self._step_incr = 0
00030
00031 def __call__(self, value, step):
00032 """
00033 Takes a value in the range [0, 1] and the step and returns the
00034 velocity (usually m/s or rad/s).
00035 """
00036 if step == 0:
00037 return 0
00038
00039 assert step > 0 and step <= self._num_steps
00040 max_value = self._min + self._step_incr * (step - 1)
00041 return value * max_value
00042
00043 class TextWindow():
00044
00045 _screen = None
00046 _window = None
00047 _num_lines = None
00048
00049 def __init__(self, stdscr, lines=10):
00050 self._screen = stdscr
00051 self._screen.nodelay(True)
00052 curses.curs_set(0)
00053
00054 self._num_lines = lines
00055
00056 def read_key(self):
00057 keycode = self._screen.getch()
00058 return keycode if keycode != -1 else None
00059
00060 def clear(self):
00061 self._screen.clear()
00062
00063 def write_line(self, lineno, message):
00064 if lineno < 0 or lineno >= self._num_lines:
00065 raise ValueError, 'lineno out of bounds'
00066 height, width = self._screen.getmaxyx()
00067 y = (height / self._num_lines) * lineno
00068 x = 10
00069 for text in message.split('\n'):
00070 text = text.ljust(width)
00071 self._screen.addstr(y, x, text)
00072 y += 1
00073
00074 def refresh(self):
00075 self._screen.refresh()
00076
00077 def beep(self):
00078 curses.flash()
00079
00080 class KeyTeleop():
00081
00082 _interface = None
00083
00084 _linear = None
00085 _angular = None
00086
00087 def __init__(self, interface):
00088 self._interface = interface
00089 self._pub_cmd = rospy.Publisher('/mavros/setpoint_velocity/cmd_vel', TwistStamped)
00090
00091 self._hz = rospy.get_param('~hz', 10)
00092
00093 self._num_steps = rospy.get_param('~turbo/steps', 4)
00094
00095 forward_min = rospy.get_param('~turbo/linear_forward_min', 0.5)
00096 forward_max = rospy.get_param('~turbo/linear_forward_max', 1.0)
00097 self._forward = Velocity(forward_min, forward_max, self._num_steps)
00098
00099 backward_min = rospy.get_param('~turbo/linear_backward_min', 0.25)
00100 backward_max = rospy.get_param('~turbo/linear_backward_max', 0.5)
00101 self._backward = Velocity(backward_min, backward_max, self._num_steps)
00102
00103 angular_min = rospy.get_param('~turbo/angular_min', 0.7)
00104 angular_max = rospy.get_param('~turbo/angular_max', 1.2)
00105 self._rotation = Velocity(angular_min, angular_max, self._num_steps)
00106
00107 def run(self):
00108 self._linearX = 0
00109 self._linearY = 0
00110
00111
00112 rate = rospy.Rate(self._hz)
00113 while True:
00114 keycode = self._interface.read_key()
00115 if keycode:
00116 if self._key_pressed(keycode):
00117 self._publish()
00118 else:
00119 self._publish()
00120 rate.sleep()
00121
00122 def _get_twist(self, linear, angular):
00123 twist = TwistStamped()
00124 if linearX >= 0:
00125 twist.twist.linear.x = self._forward(1.0, linearX)
00126 else:
00127 twist.twist.linear.x = self._backward(-1.0, -linearX)
00128 if linearY >= 0:
00129 twist.twist.linear.y = self._left(1.0, linearY)
00130 else:
00131 twist.twist.linear.yx = self._right(-1.0, -linearY)
00132
00133
00134 return twist
00135
00136 def _key_pressed(self, keycode):
00137 movement_bindings = {
00138 curses.KEY_UP: ( 1, 0),
00139 curses.KEY_DOWN: (-1, 0),
00140 curses.KEY_LEFT: ( 0, 1),
00141 curses.KEY_RIGHT: ( 0, -1),
00142 }
00143 speed_bindings = {
00144 ord(' '): (0, 0),
00145 }
00146 if keycode in movement_bindings:
00147 acc = movement_bindings[keycode]
00148 ok = False
00149 if acc[0]:
00150 linear = self._linear + acc[0]
00151 if abs(linear) <= self._num_steps:
00152 self._linear = linear
00153 ok = True
00154 if acc[1]:
00155 angular = self._angular + acc[1]
00156 if abs(angular) <= self._num_steps:
00157 self._angular = angular
00158 ok = True
00159 if not ok:
00160 self._interface.beep()
00161 elif keycode in speed_bindings:
00162 acc = speed_bindings[keycode]
00163
00164 if acc[0] is not None:
00165 self._linear = acc[0]
00166 if acc[1] is not None:
00167 self._angular = acc[1]
00168
00169 elif keycode == ord('q'):
00170 rospy.signal_shutdown('Bye')
00171 else:
00172 return False
00173
00174 return True
00175
00176 def _publish(self):
00177 self._interface.clear()
00178 self._interface.write_line(2, 'Linear: %d, Angular: %d' % (self._linear, self._angular))
00179 self._interface.write_line(5, 'Use arrow keys to move, space to stop, q to exit.')
00180 self._interface.refresh()
00181
00182 twist = self._get_twist(self._linear, self._angular)
00183 self._pub_cmd.publish(twist)
00184
00185
00186 class SimpleKeyTeleop():
00187 def __init__(self, interface):
00188 self._interface = interface
00189 self._pub_cmd = rospy.Publisher('/mavros/setpoint_velocity/cmd_vel', TwistStamped)
00190
00191 self._hz = rospy.get_param('~hz', 10)
00192
00193 self._forward_rate = rospy.get_param('~forward_rate', 0.8)
00194 self._backward_rate = rospy.get_param('~backward_rate', 0.5)
00195 self._rigth_rate = rospy.get_param('~rigth_rate', 0.5)
00196 self._left_rate = rospy.get_param('~left_rate', 0.5)
00197
00198 self._last_pressed = {}
00199
00200 self._linearX = 0
00201 self._linearY = 0
00202
00203 movement_bindings = {
00204 curses.KEY_LEFT: ( 1, 0),
00205 curses.KEY_RIGHT:(-1, 0),
00206 curses.KEY_DOWN: ( 0, 1),
00207 curses.KEY_UP: ( 0, -1),
00208 }
00209
00210 def run(self):
00211 rate = rospy.Rate(self._hz)
00212 self._running = True
00213 while self._running:
00214 while True:
00215 keycode = self._interface.read_key()
00216 if keycode is None:
00217 break
00218 self._key_pressed(keycode)
00219 self._set_velocity()
00220 self._publish()
00221 rate.sleep()
00222
00223 def _get_twist(self, linearX, linearY):
00224 twist = TwistStamped()
00225 twist.twist.linear.x = linearX
00226 twist.twist.linear.y = linearY
00227 return twist
00228
00229 def _set_velocity(self):
00230 now = rospy.get_time()
00231 keys = []
00232 for a in self._last_pressed:
00233 if now - self._last_pressed[a] < 0.4:
00234 keys.append(a)
00235
00236
00237 linearX= 0.0
00238 linearY= 0.0
00239 for k in keys:
00240 lx, ly = self.movement_bindings[k]
00241 linearX += lx
00242 linearY += ly
00243 if linearX > 0:
00244 linearX = linearX * self._forward_rate
00245 else:
00246 linearX = linearX * self._backward_rate
00247
00248 if linearY > 0:
00249 linearY = linearY * self._left_rate
00250 else:
00251 linearY = linearY * self._rigth_rate
00252
00253 self._linearX = linearX
00254 self._linearY = linearY
00255
00256 def _key_pressed(self, keycode):
00257 if keycode == ord('q'):
00258 self._running = False
00259 rospy.signal_shutdown('Bye')
00260 elif keycode in self.movement_bindings:
00261 self._last_pressed[keycode] = rospy.get_time()
00262
00263 def _publish(self):
00264 self._interface.clear()
00265 self._interface.write_line(2, 'Linear x: %f, Linear y: %f' % (self._linearX, self._linearY))
00266 self._interface.write_line(5, 'Use arrow keys to move, q to exit.')
00267 self._interface.refresh()
00268
00269 twist = self._get_twist(self._linearX, self._linearY)
00270 self._pub_cmd.publish(twist)
00271
00272
00273 def main(stdscr):
00274 rospy.init_node('tele')
00275 app = SimpleKeyTeleop(TextWindow(stdscr))
00276 app.run()
00277
00278 if __name__ == '__main__':
00279 try:
00280 curses.wrapper(main)
00281 except rospy.ROSInterruptException:
00282 pass