|
1 | 1 | #!/usr/bin/env python3 |
2 | 2 |
|
3 | 3 | import logging |
4 | | -import math |
5 | 4 | import os |
6 | 5 | import re |
7 | | -import sys |
8 | | -import time |
9 | | -import ev3dev.auto |
10 | | -from ev3dev.helper import LargeMotorPair, list_motors |
| 6 | +from ev3dev.motor import MoveJoystick, list_motors, LargeMotor |
11 | 7 | from http.server import BaseHTTPRequestHandler, HTTPServer |
12 | | -from time import sleep |
13 | 8 |
|
14 | 9 | log = logging.getLogger(__name__) |
15 | 10 |
|
16 | 11 |
|
17 | | -# =============================================== |
18 | | -# "Joystick" code for the web interface for tanks |
19 | | -# =============================================== |
20 | | -def angle_to_speed_percentage(angle): |
21 | | - """ |
22 | | - (1, 1) |
23 | | - . . . . . . . |
24 | | - . | . |
25 | | - . | . |
26 | | - (0, 1) . | . (1, 0) |
27 | | - . | . |
28 | | - . | . |
29 | | - . | . |
30 | | - . | . |
31 | | - . | . |
32 | | - . | x-axis . |
33 | | - (-1, 1) .---------------------------------------. (1, -1) |
34 | | - . | . |
35 | | - . | . |
36 | | - . | . |
37 | | - . | y-axis . |
38 | | - . | . |
39 | | - (0, -1) . | . (-1, 0) |
40 | | - . | . |
41 | | - . | . |
42 | | - . . . . . . . |
43 | | - (-1, -1) |
44 | | -
|
45 | | -
|
46 | | - The joystick is a circle within a circle where the (x, y) coordinates |
47 | | - of the joystick form an angle with the x-axis. Our job is to translate |
48 | | - this angle into the percentage of power that should be sent to each motor. |
49 | | - For instance if the joystick is moved all the way to the top of the circle |
50 | | - we want both motors to move forward with 100% power...that is represented |
51 | | - above by (1, 1). If the joystick is moved all the way to the right side of |
52 | | - the circle we want to rotate clockwise so we move the left motor forward 100% |
53 | | - and the right motor backwards 100%...so (1, -1). If the joystick is at |
54 | | - 45 degrees then we move apply (1, 0) to move the left motor forward 100% and |
55 | | - the right motor stays still. |
56 | | -
|
57 | | - The 8 points shown above are pretty easy. For the points in between those 8 |
58 | | - we do some math to figure out what the percentages should be. Take 11.25 degrees |
59 | | - for example. We look at how the motors transition from 0 degrees to 45 degrees: |
60 | | - - the left motor is 1 so that is easy |
61 | | - - the right motor moves from -1 to 0 |
62 | | -
|
63 | | - We determine how far we are between 0 and 45 degrees (11.25 is 25% of 45) so we |
64 | | - know that the right motor should be 25% of the way from -1 to 0...so -0.75 is the |
65 | | - percentage for the right motor at 11.25 degrees. |
66 | | - """ |
67 | | - |
68 | | - if angle >= 0 and angle <= 45: |
69 | | - |
70 | | - # left motor stays at 1 |
71 | | - left_speed_percentage = 1 |
72 | | - |
73 | | - # right motor transitions from -1 to 0 |
74 | | - right_speed_percentage = -1 + (angle/45.0) |
75 | | - |
76 | | - elif angle > 45 and angle <= 90: |
77 | | - |
78 | | - # left motor stays at 1 |
79 | | - left_speed_percentage = 1 |
80 | | - |
81 | | - # right motor transitions from 0 to 1 |
82 | | - percentage_from_45_to_90 = (angle - 45) / 45.0 |
83 | | - right_speed_percentage = percentage_from_45_to_90 |
84 | | - |
85 | | - elif angle > 90 and angle <= 135: |
86 | | - |
87 | | - # left motor transitions from 1 to 0 |
88 | | - percentage_from_90_to_135 = (angle - 90) / 45.0 |
89 | | - left_speed_percentage = 1 - percentage_from_90_to_135 |
90 | | - |
91 | | - # right motor stays at 1 |
92 | | - right_speed_percentage = 1 |
93 | | - |
94 | | - elif angle > 135 and angle <= 180: |
95 | | - |
96 | | - # left motor transitions from 0 to -1 |
97 | | - percentage_from_135_to_180 = (angle - 135) / 45.0 |
98 | | - left_speed_percentage = -1 * percentage_from_135_to_180 |
99 | | - |
100 | | - # right motor stays at 1 |
101 | | - right_speed_percentage = 1 |
102 | | - |
103 | | - elif angle > 180 and angle <= 225: |
104 | | - |
105 | | - # left motor transitions from -1 to 0 |
106 | | - percentage_from_180_to_225 = (angle - 180) / 45.0 |
107 | | - left_speed_percentage = -1 + percentage_from_180_to_225 |
108 | | - |
109 | | - # right motor transitions from 1 to -1 |
110 | | - # right motor transitions from 1 to 0 between 180 and 202.5 |
111 | | - if angle < 202.5: |
112 | | - percentage_from_180_to_202 = (angle - 180) / 22.5 |
113 | | - right_speed_percentage = 1 - percentage_from_180_to_202 |
114 | | - |
115 | | - # right motor is 0 at 202.5 |
116 | | - elif angle == 202.5: |
117 | | - right_speed_percentage = 0 |
118 | | - |
119 | | - # right motor transitions from 0 to -1 between 202.5 and 225 |
120 | | - else: |
121 | | - percentage_from_202_to_225 = (angle - 202.5) / 22.5 |
122 | | - right_speed_percentage = -1 * percentage_from_202_to_225 |
123 | | - |
124 | | - elif angle > 225 and angle <= 270: |
125 | | - |
126 | | - # left motor transitions from 0 to -1 |
127 | | - percentage_from_225_to_270 = (angle - 225) / 45.0 |
128 | | - left_speed_percentage = -1 * percentage_from_225_to_270 |
129 | | - |
130 | | - # right motor stays at -1 |
131 | | - right_speed_percentage = -1 |
132 | | - |
133 | | - elif angle > 270 and angle <= 315: |
134 | | - |
135 | | - # left motor stays at -1 |
136 | | - left_speed_percentage = -1 |
137 | | - |
138 | | - # right motor transitions from -1 to 0 |
139 | | - percentage_from_270_to_315 = (angle - 270) / 45.0 |
140 | | - right_speed_percentage = -1 + percentage_from_270_to_315 |
141 | | - |
142 | | - elif angle > 315 and angle <= 360: |
143 | | - |
144 | | - # left motor transitions from -1 to 1 |
145 | | - # left motor transitions from -1 to 0 between 315 and 337.5 |
146 | | - if angle < 337.5: |
147 | | - percentage_from_315_to_337 = (angle - 315) / 22.5 |
148 | | - left_speed_percentage = (1 - percentage_from_315_to_337) * -1 |
149 | | - |
150 | | - # left motor is 0 at 337.5 |
151 | | - elif angle == 337.5: |
152 | | - left_speed_percentage = 0 |
153 | | - |
154 | | - # left motor transitions from 0 to 1 between 337.5 and 360 |
155 | | - elif angle > 337.5: |
156 | | - percentage_from_337_to_360 = (angle - 337.5) / 22.5 |
157 | | - left_speed_percentage = percentage_from_337_to_360 |
158 | | - |
159 | | - # right motor transitions from 0 to -1 |
160 | | - percentage_from_315_to_360 = (angle - 315) / 45.0 |
161 | | - right_speed_percentage = -1 * percentage_from_315_to_360 |
162 | | - |
163 | | - else: |
164 | | - raise Exception('You created a circle with more than 360 degrees (%s)...that is quite the trick' % angle) |
165 | | - |
166 | | - return (left_speed_percentage, right_speed_percentage) |
167 | | - |
168 | | - |
169 | | -def xy_to_speed(x, y, max_speed, radius=100.0): |
170 | | - """ |
171 | | - Convert x,y joystick coordinates to left/right motor speed |
172 | | - """ |
173 | | - |
174 | | - vector_length = math.hypot(x, y) |
175 | | - angle = math.degrees(math.atan2(y, x)) |
176 | | - |
177 | | - if angle < 0: |
178 | | - angle += 360 |
179 | | - |
180 | | - # Should not happen but can happen (just by a hair) due to floating point math |
181 | | - if vector_length > radius: |
182 | | - vector_length = radius |
183 | | - |
184 | | - # print "radius : %s" % radius |
185 | | - # print "angle : %s" % angle |
186 | | - # print "vector length : %s" % vector_length |
187 | | - |
188 | | - (left_speed_percentage, right_speed_percentage) = angle_to_speed_percentage(angle) |
189 | | - # print "init left_speed_percentage: %s" % left_speed_percentage |
190 | | - # print "init right_speed_percentage: %s" % right_speed_percentage |
191 | | - |
192 | | - # scale the speed percentages based on vector_length vs. radius |
193 | | - left_speed_percentage = (left_speed_percentage * vector_length) / radius |
194 | | - right_speed_percentage = (right_speed_percentage * vector_length) / radius |
195 | | - # print "final left_speed_percentage: %s" % left_speed_percentage |
196 | | - # print "final right_speed_percentage: %s" % right_speed_percentage |
197 | | - |
198 | | - # calculate the motor speeds based on speed percentages and max_speed of the motors |
199 | | - left_speed = round(left_speed_percentage * max_speed) |
200 | | - right_speed = round(right_speed_percentage * max_speed) |
201 | | - |
202 | | - # safety net |
203 | | - if left_speed > max_speed: |
204 | | - left_speed = max_speed |
205 | | - |
206 | | - if right_speed > max_speed: |
207 | | - right_speed = max_speed |
208 | | - |
209 | | - return (left_speed, right_speed) |
210 | | - |
211 | | - |
212 | | -def test_xy_to_speed(): |
213 | | - """ |
214 | | - Used to test changes to xy_to_speed() and angle_to_speed_percentage() |
215 | | - """ |
216 | | - |
217 | | - # Move straight forward |
218 | | - assert xy_to_speed(0, 100, 400) == (400, 400), "FAILED" |
219 | | - |
220 | | - # Spin clockwise |
221 | | - assert xy_to_speed(100, 0, 400) == (400, -400), "FAILED" |
222 | | - |
223 | | - # Spin counter clockwise |
224 | | - assert xy_to_speed(-100, 0, 400) == (-400, 400), "FAILED" |
225 | | - |
226 | | - # Move straight back |
227 | | - assert xy_to_speed(0, -100, 400) == (-400, -400), "FAILED" |
228 | | - |
229 | | - # Test vector length to power percentages |
230 | | - # Move straight forward, 1/2 power |
231 | | - assert xy_to_speed(0, 50, 400) == (200, 200), "FAILED" |
232 | | - |
233 | | - # Test motor max_speed |
234 | | - # Move straight forward, 1/2 power with lower max_speed |
235 | | - assert xy_to_speed(0, 50, 200) == (100, 100), "FAILED" |
236 | | - |
237 | | - # http://www.pagetutor.com/trigcalc/trig.html |
238 | | - |
239 | | - # top right quadrant |
240 | | - # ================== |
241 | | - # 0 -> 45 degrees |
242 | | - assert xy_to_speed(98.07852804032305, 19.509032201612825, 400) == (400, -300), "FAILED" # 11.25 degrees |
243 | | - assert xy_to_speed(92.38795325112868, 38.26834323650898, 400) == (400, -200), "FAILED" # 22.5 degrees |
244 | | - assert xy_to_speed(83.14696123025452, 55.557023301960214, 400) == (400, -100), "FAILED" # 33.75 degrees |
245 | | - |
246 | | - # 45 degrees, only left motor should turn |
247 | | - assert xy_to_speed(70.71068, 70.71068, 400) == (400, 0), "FAILED" |
248 | | - |
249 | | - # 45 -> 90 degrees |
250 | | - assert xy_to_speed(55.55702330196023, 83.14696123025452, 400) == (400, 100), "FAILED" # 56.25 degrees |
251 | | - assert xy_to_speed(38.26834323650898, 92.38795325112868, 400) == (400, 200), "FAILED" # 67.5 degrees |
252 | | - assert xy_to_speed(19.509032201612833, 98.07852804032305, 400) == (400, 300), "FAILED" # 78.75 degrees |
253 | | - |
254 | | - |
255 | | - # top left quadrant |
256 | | - # ================= |
257 | | - # 90 -> 135 degrees |
258 | | - assert xy_to_speed(-19.509032201612833, 98.07852804032305, 400) == (300, 400), "FAILED" |
259 | | - assert xy_to_speed(-38.26834323650898, 92.38795325112868, 400) == (200, 400), "FAILED" |
260 | | - assert xy_to_speed(-55.55702330196023, 83.14696123025452, 400) == (100, 400), "FAILED" |
261 | | - |
262 | | - # 135 degrees, only right motor should turn |
263 | | - assert xy_to_speed(-70.71068, 70.71068, 400) == (0, 400), "FAILED" |
264 | | - |
265 | | - # 135 -> 180 degrees |
266 | | - assert xy_to_speed(-83.14696123025452, 55.55702330196023, 400) == (-100, 400), "FAILED" |
267 | | - assert xy_to_speed(-92.38795325112868, 38.26834323650898, 400) == (-200, 400), "FAILED" |
268 | | - assert xy_to_speed(-98.07852804032305, 19.509032201612833, 400) == (-300, 400), "FAILED" |
269 | | - |
270 | | - |
271 | | - # bottom left quadrant |
272 | | - # ==================== |
273 | | - # 180 -> 225 degrees |
274 | | - assert xy_to_speed(-98.07852804032305, -19.509032201612833, 400) == (-300, 200), "FAILED" |
275 | | - assert xy_to_speed(-92.38795325112868, -38.26834323650898, 400) == (-200, 0), "FAILED" |
276 | | - assert xy_to_speed(-83.14696123025452, -55.55702330196023, 400) == (-100, -200), "FAILED" |
277 | | - |
278 | | - # 225 degrees, only right motor should turn (backwards) |
279 | | - assert xy_to_speed(-70.71068, -70.71068, 400) == (0, -400), "FAILED" |
280 | | - |
281 | | - # 225 -> 270 degrees |
282 | | - assert xy_to_speed(-55.55702330196023, -83.14696123025452, 400) == (-100, -400), "FAILED" |
283 | | - assert xy_to_speed(-38.26834323650898, -92.38795325112868, 400) == (-200, -400), "FAILED" |
284 | | - assert xy_to_speed(-19.509032201612833, -98.07852804032305, 400) == (-300, -400), "FAILED" |
285 | | - |
286 | | - |
287 | | - # bottom right quadrant |
288 | | - # ===================== |
289 | | - # 270 -> 315 degrees |
290 | | - assert xy_to_speed(19.509032201612833, -98.07852804032305, 400) == (-400, -300), "FAILED" |
291 | | - assert xy_to_speed(38.26834323650898, -92.38795325112868, 400) == (-400, -200), "FAILED" |
292 | | - assert xy_to_speed(55.55702330196023, -83.14696123025452, 400) == (-400, -100), "FAILED" |
293 | | - |
294 | | - # 315 degrees, only left motor should turn (backwards) |
295 | | - assert xy_to_speed(70.71068, -70.71068, 400) == (-400, 0), "FAILED" |
296 | | - |
297 | | - # 315 -> 360 degrees |
298 | | - assert xy_to_speed(83.14696123025452, -55.557023301960214, 400) == (-200, -100), "FAILED" |
299 | | - assert xy_to_speed(92.38795325112868, -38.26834323650898, 400) == (0, -200), "FAILED" |
300 | | - assert xy_to_speed(98.07852804032305, -19.509032201612825, 400) == (200, -300), "FAILED" |
301 | | - |
302 | | - |
303 | 12 | # ================== |
304 | 13 | # Web Server classes |
305 | 14 | # ================== |
@@ -372,7 +81,7 @@ def log_message(self, format, *args): |
372 | 81 | max_move_xy_seq = 0 |
373 | 82 | motor_max_speed = None |
374 | 83 | medium_motor_max_speed = None |
375 | | -joystick_enaged = False |
| 84 | +joystick_engaged = False |
376 | 85 |
|
377 | 86 | class TankWebHandler(RobotWebHandler): |
378 | 87 |
|
@@ -426,8 +135,6 @@ def do_GET(self): |
426 | 135 | We can also ignore any move-xy requests that show up late by tracking the |
427 | 136 | max seq for any move-xy we service. |
428 | 137 | ''' |
429 | | - # dwalton - fix this |
430 | | - |
431 | 138 | path = self.path.split('/') |
432 | 139 | seq = int(path[1]) |
433 | 140 | action = path[2] |
@@ -502,19 +209,9 @@ def do_GET(self): |
502 | 209 |
|
503 | 210 | if joystick_engaged: |
504 | 211 | if seq > max_move_xy_seq: |
505 | | - (left_speed, right_speed) = xy_to_speed(x, y, motor_max_speed) |
506 | | - log.debug("seq %d: (x, y) %4d, %4d -> speed %d %d" % (seq, x, y, left_speed, right_speed)) |
| 212 | + self.robot.on(x, y, motor_max_speed) |
507 | 213 | max_move_xy_seq = seq |
508 | | - |
509 | | - if left_speed == 0: |
510 | | - self.robot.left_motor.stop() |
511 | | - else: |
512 | | - self.robot.left_motor.run_forever(speed_sp=left_speed) |
513 | | - |
514 | | - if right_speed == 0: |
515 | | - self.robot.right_motor.stop() |
516 | | - else: |
517 | | - self.robot.right_motor.run_forever(speed_sp=right_speed) |
| 214 | + log.debug("seq %d: (x, y) (%4d, %4d)" % (seq, x, y)) |
518 | 215 | else: |
519 | 216 | log.debug("seq %d: (x, y) %4d, %4d (ignore, max seq %d)" % |
520 | 217 | (seq, x, y, max_move_xy_seq)) |
@@ -576,13 +273,13 @@ def run(self): |
576 | 273 | motor.stop() |
577 | 274 |
|
578 | 275 |
|
579 | | -class WebControlledTank(LargeMotorPair): |
| 276 | +class WebControlledTank(MoveJoystick): |
580 | 277 | """ |
581 | 278 | A tank that is controlled via a web browser |
582 | 279 | """ |
583 | 280 |
|
584 | | - def __init__(self, left_motor, right_motor, polarity='normal', port_number=8000): |
585 | | - LargeMotorPair.__init__(self, left_motor, right_motor, polarity) |
| 281 | + def __init__(self, left_motor, right_motor, port_number=8000, desc=None, motor_class=LargeMotor): |
| 282 | + MoveJoystick.__init__(self, left_motor, right_motor, desc, motor_class) |
586 | 283 | self.www = RobotWebServer(self, TankWebHandler, port_number) |
587 | 284 |
|
588 | 285 | def main(self): |
|
0 commit comments