|
1 | 1 | #!/usr/bin/env python
|
2 |
| -# -*- coding: utf-8 -*- |
| 2 | + |
| 3 | +from __future__ import print_function |
| 4 | + |
| 5 | +import threading |
| 6 | + |
| 7 | +import roslib; roslib.load_manifest('teleop_twist_keyboard') |
3 | 8 | import rospy
|
| 9 | + |
4 | 10 | from geometry_msgs.msg import Twist
|
5 |
| -import sys, select, termios, tty |
| 11 | +from geometry_msgs.msg import TwistStamped |
| 12 | + |
| 13 | +import sys |
| 14 | +from select import select |
| 15 | + |
| 16 | +if sys.platform == 'win32': |
| 17 | + import msvcrt |
| 18 | +else: |
| 19 | + import termios |
| 20 | + import tty |
| 21 | + |
| 22 | + |
| 23 | +TwistMsg = Twist |
6 | 24 |
|
7 | 25 | msg = """
|
8 |
| -Control myagv! |
| 26 | +Reading from the keyboard and Publishing to Twist! |
9 | 27 | ---------------------------
|
10 | 28 | Moving around:
|
11 | 29 | u i o
|
12 | 30 | j k l
|
13 |
| - , |
| 31 | + m , . |
| 32 | +
|
| 33 | +t : up (+z) |
| 34 | +b : down (-z) |
| 35 | +
|
| 36 | +anything else : stop |
14 | 37 |
|
15 |
| -space key, k : stop |
16 |
| -i : foward |
17 |
| -, : backward |
18 |
| -j : turn left |
19 |
| -l : turn right |
20 |
| -u : left revolve |
21 |
| -o : right revolve |
| 38 | +q/z : increase/decrease max speeds by 10% |
| 39 | +w/x : increase/decrease only linear speed by 10% |
| 40 | +e/c : increase/decrease only angular speed by 10% |
22 | 41 |
|
23 | 42 | CTRL-C to quit
|
24 | 43 | """
|
25 | 44 |
|
26 |
| -def getKey(): |
27 |
| - tty.setraw(sys.stdin.fileno()) |
28 |
| - rlist, _, _ = select.select([sys.stdin], [], [], 0.1) |
29 |
| - if rlist: |
30 |
| - key = sys.stdin.read(1) |
| 45 | +moveBindings = { |
| 46 | + 'i':(1,0,0,0), |
| 47 | + 'o':(0,0,0,-1), |
| 48 | + 'j':(0,1,0,0), |
| 49 | + 'l':(0,-1,0,0), |
| 50 | + 'u':(0,0,0,1), |
| 51 | + ',':(-1,0,0,0), |
| 52 | + '.':(-1,0,0,-1), |
| 53 | + 'm':(-1,0,0,1), |
| 54 | + 'O':(0,0,0,-1), |
| 55 | + 'I':(1,0,0,0), |
| 56 | + 'J':(0,1,0,0), |
| 57 | + 'L':(0,-1,0,0), |
| 58 | + 'U':(0,0,0,1), |
| 59 | + '<':(-1,0,0,0), |
| 60 | + '>':(-1,0,0,-1), |
| 61 | + 'M':(-1,0,0,1), |
| 62 | + } |
| 63 | + |
| 64 | +speedBindings={ |
| 65 | + 'q':(1.1,1.1), |
| 66 | + 'z':(.9,.9), |
| 67 | + 'w':(1.1,1), |
| 68 | + 'x':(.9,1), |
| 69 | + 'e':(1,1.1), |
| 70 | + 'c':(1,.9), |
| 71 | + } |
| 72 | + |
| 73 | +class PublishThread(threading.Thread): |
| 74 | + def __init__(self, rate): |
| 75 | + super(PublishThread, self).__init__() |
| 76 | + self.publisher = rospy.Publisher('cmd_vel', TwistMsg, queue_size = 1) |
| 77 | + self.x = 0.0 |
| 78 | + self.y = 0.0 |
| 79 | + self.z = 0.0 |
| 80 | + self.th = 0.0 |
| 81 | + self.speed = 0.0 |
| 82 | + self.turn = 0.0 |
| 83 | + self.condition = threading.Condition() |
| 84 | + self.done = False |
| 85 | + |
| 86 | + # Set timeout to None if rate is 0 (causes new_message to wait forever |
| 87 | + # for new data to publish) |
| 88 | + if rate != 0.0: |
| 89 | + self.timeout = 1.0 / rate |
| 90 | + else: |
| 91 | + self.timeout = None |
| 92 | + |
| 93 | + self.start() |
| 94 | + |
| 95 | + def wait_for_subscribers(self): |
| 96 | + i = 0 |
| 97 | + while not rospy.is_shutdown() and self.publisher.get_num_connections() == 0: |
| 98 | + if i == 4: |
| 99 | + print("Waiting for subscriber to connect to {}".format(self.publisher.name)) |
| 100 | + rospy.sleep(0.5) |
| 101 | + i += 1 |
| 102 | + i = i % 5 |
| 103 | + if rospy.is_shutdown(): |
| 104 | + raise Exception("Got shutdown request before subscribers connected") |
| 105 | + |
| 106 | + def update(self, x, y, z, th, speed, turn): |
| 107 | + self.condition.acquire() |
| 108 | + self.x = x |
| 109 | + self.y = y |
| 110 | + self.z = z |
| 111 | + self.th = th |
| 112 | + self.speed = speed |
| 113 | + self.turn = turn |
| 114 | + # Notify publish thread that we have a new message. |
| 115 | + self.condition.notify() |
| 116 | + self.condition.release() |
| 117 | + |
| 118 | + def stop(self): |
| 119 | + self.done = True |
| 120 | + self.update(0, 0, 0, 0, 0, 0) |
| 121 | + self.join() |
| 122 | + |
| 123 | + def run(self): |
| 124 | + twist_msg = TwistMsg() |
| 125 | + |
| 126 | + if stamped: |
| 127 | + twist = twist_msg.twist |
| 128 | + twist_msg.header.stamp = rospy.Time.now() |
| 129 | + twist_msg.header.frame_id = twist_frame |
| 130 | + else: |
| 131 | + twist = twist_msg |
| 132 | + while not self.done: |
| 133 | + if stamped: |
| 134 | + twist_msg.header.stamp = rospy.Time.now() |
| 135 | + self.condition.acquire() |
| 136 | + # Wait for a new message or timeout. |
| 137 | + self.condition.wait(self.timeout) |
| 138 | + |
| 139 | + # Copy state into twist message. |
| 140 | + twist.linear.x = self.x * self.speed |
| 141 | + twist.linear.y = self.y * self.speed |
| 142 | + twist.linear.z = self.z * self.speed |
| 143 | + twist.angular.x = 0 |
| 144 | + twist.angular.y = 0 |
| 145 | + twist.angular.z = self.th * self.turn |
| 146 | + |
| 147 | + self.condition.release() |
| 148 | + |
| 149 | + # Publish. |
| 150 | + self.publisher.publish(twist_msg) |
| 151 | + |
| 152 | + # Publish stop message when thread exits. |
| 153 | + twist.linear.x = 0 |
| 154 | + twist.linear.y = 0 |
| 155 | + twist.linear.z = 0 |
| 156 | + twist.angular.x = 0 |
| 157 | + twist.angular.y = 0 |
| 158 | + twist.angular.z = 0 |
| 159 | + self.publisher.publish(twist_msg) |
| 160 | + |
| 161 | + |
| 162 | +def getKey(settings, timeout): |
| 163 | + if sys.platform == 'win32': |
| 164 | + # getwch() returns a string on Windows |
| 165 | + key = msvcrt.getwch() |
31 | 166 | else:
|
32 |
| - key = '' |
33 |
| - |
34 |
| - termios.tcsetattr(sys.stdin, termios.TCSADRAIN, settings) |
| 167 | + tty.setraw(sys.stdin.fileno()) |
| 168 | + # sys.stdin.read() returns a string on Linux |
| 169 | + rlist, _, _ = select([sys.stdin], [], [], timeout) |
| 170 | + if rlist: |
| 171 | + key = sys.stdin.read(1) |
| 172 | + else: |
| 173 | + key = '' |
| 174 | + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, settings) |
35 | 175 | return key
|
36 | 176 |
|
| 177 | +def saveTerminalSettings(): |
| 178 | + if sys.platform == 'win32': |
| 179 | + return None |
| 180 | + return termios.tcgetattr(sys.stdin) |
| 181 | + |
| 182 | +def restoreTerminalSettings(old_settings): |
| 183 | + if sys.platform == 'win32': |
| 184 | + return |
| 185 | + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings) |
| 186 | + |
| 187 | +def vels(speed, turn): |
| 188 | + return "currently:\tspeed %s\tturn %s " % (speed,turn) |
37 | 189 |
|
38 | 190 | if __name__=="__main__":
|
39 |
| - settings = termios.tcgetattr(sys.stdin) |
40 |
| - |
41 |
| - rospy.init_node('myagv_teleop') |
42 |
| - pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10) |
| 191 | + settings = saveTerminalSettings() |
| 192 | + |
| 193 | + rospy.init_node('teleop_twist_keyboard') |
| 194 | + |
| 195 | + speed = rospy.get_param("~speed", 0.5) |
| 196 | + turn = rospy.get_param("~turn", 0.5) |
| 197 | + speed_limit = rospy.get_param("~speed_limit", 1.0) |
| 198 | + turn_limit = rospy.get_param("~turn_limit", 1.0) |
| 199 | + repeat = rospy.get_param("~repeat_rate", 0.0) |
| 200 | + key_timeout = rospy.get_param("~key_timeout", 0.52) |
| 201 | + stamped = rospy.get_param("~stamped", False) |
| 202 | + twist_frame = rospy.get_param("~frame_id", '') |
| 203 | + if stamped: |
| 204 | + TwistMsg = TwistStamped |
| 205 | + |
| 206 | + pub_thread = PublishThread(repeat) |
43 | 207 |
|
44 | 208 | x = 0
|
45 | 209 | y = 0
|
46 |
| - theta = 0 |
| 210 | + z = 0 |
| 211 | + th = 0 |
| 212 | + status = 0 |
47 | 213 |
|
48 | 214 | try:
|
| 215 | + pub_thread.wait_for_subscribers() |
| 216 | + pub_thread.update(x, y, z, th, speed, turn) |
| 217 | + |
49 | 218 | print(msg)
|
| 219 | + print(vels(speed,turn)) |
50 | 220 | while(1):
|
51 |
| - key = getKey() |
52 |
| - |
53 |
| - if key == ' ' or key == 'k' : |
| 221 | + key = getKey(settings, key_timeout) |
| 222 | + if key in moveBindings.keys(): |
| 223 | + x = moveBindings[key][0] |
| 224 | + y = moveBindings[key][1] |
| 225 | + z = moveBindings[key][2] |
| 226 | + th = moveBindings[key][3] |
| 227 | + elif key in speedBindings.keys(): |
| 228 | + speed = min(speed_limit, speed * speedBindings[key][0]) |
| 229 | + turn = min(turn_limit, turn * speedBindings[key][1]) |
| 230 | + if speed == speed_limit: |
| 231 | + print("Linear speed limit reached!") |
| 232 | + if turn == turn_limit: |
| 233 | + print("Angular speed limit reached!") |
| 234 | + print(vels(speed,turn)) |
| 235 | + if (status == 14): |
| 236 | + print(msg) |
| 237 | + status = (status + 1) % 15 |
| 238 | + else: |
| 239 | + # Skip updating cmd_vel if key timeout and robot already |
| 240 | + # stopped. |
| 241 | + if key == '' and x == 0 and y == 0 and z == 0 and th == 0: |
| 242 | + continue |
54 | 243 | x = 0
|
55 | 244 | y = 0
|
56 |
| - theta = 0 |
57 |
| - |
58 |
| - elif key == 'i': |
59 |
| - x = 0.5 |
60 |
| - y = 0 |
61 |
| - theta = 0 |
62 |
| - elif key == ',': |
63 |
| - x = -0.5 |
64 |
| - y = 0 |
65 |
| - theta = 0 |
66 |
| - elif key == 'j': |
67 |
| - x = 0 |
68 |
| - y = 0.5 |
69 |
| - theta = 0 |
70 |
| - elif key == 'l': |
71 |
| - x = 0 |
72 |
| - y = -0.5 |
73 |
| - theta = 0 |
74 |
| - elif key == 'u': |
75 |
| - x = 0 |
76 |
| - y = 0 |
77 |
| - theta = 0.5 |
78 |
| - elif key == 'o': |
79 |
| - x = 0 |
80 |
| - y = 0 |
81 |
| - theta = -0.5 |
82 |
| - elif key == '\x03': |
83 |
| - break |
84 |
| - |
85 |
| - twist = Twist() |
86 |
| - twist.linear.x = x; |
87 |
| - twist.linear.y = y; |
88 |
| - twist.linear.z = 0 |
89 |
| - twist.angular.x = 0; |
90 |
| - twist.angular.y = 0; |
91 |
| - twist.angular.z = theta |
92 |
| - pub.publish(twist) |
93 |
| - |
94 |
| - except: |
| 245 | + z = 0 |
| 246 | + th = 0 |
| 247 | + if (key == '\x03'): |
| 248 | + break |
| 249 | + |
| 250 | + pub_thread.update(x, y, z, th, speed, turn) |
| 251 | + |
| 252 | + except Exception as e: |
95 | 253 | print(e)
|
96 | 254 |
|
97 | 255 | finally:
|
98 |
| - twist = Twist() |
99 |
| - twist.linear.x = 0; twist.linear.y = 0; twist.linear.z = 0 |
100 |
| - twist.angular.x = 0; twist.angular.y = 0; twist.angular.z = 0 |
101 |
| - pub.publish(twist) |
102 |
| - |
103 |
| - termios.tcsetattr(sys.stdin, termios.TCSADRAIN, settings) |
| 256 | + pub_thread.stop() |
| 257 | + restoreTerminalSettings(settings) |
0 commit comments