Skip to content

Commit 074b11c

Browse files
committed
Merge branch 'develop' of https://github.com/rhempel/ev3dev-lang-python into develop
2 parents 8596b55 + a8c65f7 commit 074b11c

File tree

10 files changed

+319
-0
lines changed

10 files changed

+319
-0
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/usr/bin/env python
2+
3+
import logging
4+
import sys
5+
from ev3dev.auto import OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D
6+
from ev3dev.helper import RemoteControlledTank, MediumMotor
7+
8+
log = logging.getLogger(__name__)
9+
10+
11+
class TRACK3R(RemoteControlledTank):
12+
"""
13+
Base class for all TRACK3R variations. The only difference in the child
14+
classes are in how the medium motor is handled.
15+
16+
To enable the medium motor toggle the beacon button on the EV3 remote.
17+
"""
18+
19+
def __init__(self, medium_motor, left_motor, right_motor):
20+
RemoteControlledTank.__init__(self, left_motor, right_motor)
21+
self.medium_motor = MediumMotor(medium_motor)
22+
23+
if not self.medium_motor.connected:
24+
log.error("%s is not connected" % self.medium_motor)
25+
sys.exit(1)
26+
27+
self.medium_motor.reset()
28+
29+
30+
class TRACK3RWithBallShooter(TRACK3R):
31+
32+
def __init__(self, medium_motor=OUTPUT_A, left_motor=OUTPUT_B, right_motor=OUTPUT_C):
33+
TRACK3R.__init__(self, medium_motor, left_motor, right_motor)
34+
self.remote.on_beacon = self.fire_ball
35+
36+
def fire_ball(self, state):
37+
if state:
38+
self.medium_motor.run_to_rel_pos(duty_cycle_sp=100, position_sp=3*360)
39+
else:
40+
self.medium_motor.stop()
41+
42+
43+
class TRACK3RWithSpinner(TRACK3R):
44+
45+
def __init__(self, medium_motor=OUTPUT_A, left_motor=OUTPUT_B, right_motor=OUTPUT_C):
46+
TRACK3R.__init__(self, medium_motor, left_motor, right_motor)
47+
self.remote.on_beacon = self.spinner
48+
49+
def spinner(self, state):
50+
if state:
51+
self.medium_motor.run_forever(duty_cycle_sp=50)
52+
else:
53+
self.medium_motor.stop()
54+
55+
56+
class TRACK3RWithClaw(TRACK3R):
57+
58+
def __init__(self, medium_motor=OUTPUT_A, left_motor=OUTPUT_B, right_motor=OUTPUT_C):
59+
TRACK3R.__init__(self, medium_motor, left_motor, right_motor)
60+
self.remote.on_beacon = self.move_claw
61+
62+
def move_claw(self, state):
63+
if state:
64+
self.medium_motor.run_to_rel_pos(duty_cycle_sp=40, position_sp=-75)
65+
else:
66+
self.medium_motor.run_to_rel_pos(duty_cycle_sp=40, position_sp=75)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env python
2+
3+
import logging
4+
from TRACK3R import TRACK3RWithBallShooter
5+
6+
logging.basicConfig(level=logging.DEBUG,
7+
format='%(asctime)s %(levelname)5s: %(message)s')
8+
log = logging.getLogger(__name__)
9+
10+
log.info("Starting TRACK3RWithBallShooter")
11+
tracker = TRACK3RWithBallShooter()
12+
tracker.main()
13+
log.info("Exiting TRACK3RWithBallShooter")
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env python
2+
3+
import logging
4+
from TRACK3R import TRACK3RWithClaw
5+
6+
logging.basicConfig(level=logging.DEBUG,
7+
format='%(asctime)s %(levelname)5s: %(message)s')
8+
log = logging.getLogger(__name__)
9+
10+
log.info("Starting TRACK3RWithClaw")
11+
tracker = TRACK3RWithClaw()
12+
tracker.main()
13+
log.info("Exiting TRACK3RWithClaw")
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env python
2+
3+
import logging
4+
from TRACK3R import TRACK3RWithSpinner
5+
6+
logging.basicConfig(level=logging.DEBUG,
7+
format='%(asctime)s %(levelname)5s: %(message)s')
8+
log = logging.getLogger(__name__)
9+
10+
log.info("Starting TRACK3RWithSpinner")
11+
tracker = TRACK3RWithSpinner()
12+
tracker.main()
13+
log.info("Exiting TRACK3RWithSpinner")

ev3dev/brickpi.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class Leds(object):
5353
LED1 = ( blue_led1, )
5454
LED2 = ( blue_led2, )
5555

56+
BLACK = ( 0, )
5657
BLUE = ( 1, )
5758

5859
@staticmethod

ev3dev/core.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ def __init__(self, class_name, name_pattern='*', name_exact=False, **kwargs):
163163

164164
classpath = abspath(Device.DEVICE_ROOT_PATH + '/' + class_name)
165165
self._attribute_cache = FileCache()
166+
self.kwargs = kwargs
166167

167168
def get_index(file):
168169
match = Device._DEVICE_INDEX.match(file)
@@ -182,8 +183,16 @@ def get_index(file):
182183
self._device_index = get_index(name)
183184
self.connected = True
184185
except StopIteration:
186+
self._path = None
187+
self._device_index = None
185188
self.connected = False
186189

190+
def __str__(self):
191+
if 'address' in self.kwargs:
192+
return "%s(%s)" % (self.__class__.__name__, self.kwargs.get('address'))
193+
else:
194+
return self.__class__.__name__
195+
187196
def _matches(self, attribute, pattern):
188197
"""Test if attribute value matches pattern (that is, if pattern is a
189198
substring of attribute value). If pattern is a list, then a match with
@@ -1893,6 +1902,9 @@ class ButtonBase(object):
18931902
Abstract button interface.
18941903
"""
18951904

1905+
def __str__(self):
1906+
return self.__class__.__name__
1907+
18961908
@staticmethod
18971909
def on_change(changed_buttons):
18981910
"""

ev3dev/ev3.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class Leds(object):
5555
LEFT = ( red_left, green_left, )
5656
RIGHT = ( red_right, green_right, )
5757

58+
BLACK = ( 0, 0, )
5859
RED = ( 1, 0, )
5960
GREEN = ( 0, 1, )
6061
AMBER = ( 1, 1, )

ev3dev/helper.py

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#!/usr/bin/env python
2+
3+
import logging
4+
import sys
5+
import time
6+
import ev3dev.auto
7+
from ev3dev.auto import (RemoteControl, list_motors,
8+
INPUT_1, INPUT_2, INPUT_3,INPUT_4,
9+
OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D)
10+
from math import pi
11+
from time import sleep
12+
13+
log = logging.getLogger(__name__)
14+
15+
INPUTS = (INPUT_1, INPUT_2, INPUT_3, INPUT_4)
16+
OUTPUTS = (OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D)
17+
18+
def wait_for(condition, timeout=1e5, interval=0.01):
19+
tic = time.time() + timeout
20+
done = condition()
21+
22+
while time.time() < tic and not done:
23+
time.sleep(interval)
24+
done = condition()
25+
26+
return done
27+
28+
29+
# =============
30+
# Motor classes
31+
# =============
32+
class MotorMixin(object):
33+
34+
def wait_for_running(self):
35+
return wait_for(lambda: 'running' in self.state, 1)
36+
37+
def wait_for_stop(self):
38+
return wait_for(lambda: 'running' not in self.state, 1)
39+
40+
41+
class LargeMotor(ev3dev.auto.LargeMotor, MotorMixin):
42+
pass
43+
44+
45+
class MediumMotor(ev3dev.auto.MediumMotor, MotorMixin):
46+
pass
47+
48+
49+
# ============
50+
# Tank classes
51+
# ============
52+
class Tank(object):
53+
54+
def __init__(self, left_motor, right_motor, polarity='normal'):
55+
56+
for motor in (left_motor, right_motor):
57+
if motor not in OUTPUTS:
58+
log.error("%s in an invalid motor, choices are %s" % (motor, ', '.join(OUTPUTS)))
59+
sys.exit(1)
60+
61+
self.left_motor = LargeMotor(left_motor)
62+
self.right_motor = LargeMotor(right_motor)
63+
64+
for x in (self.left_motor, self.right_motor):
65+
if not x.connected:
66+
log.error("%s is not connected" % x)
67+
sys.exit(1)
68+
69+
self.left_motor.reset()
70+
self.right_motor.reset()
71+
self.duty_cycle_sp = 90
72+
self.left_motor.duty_cycle_sp = self.duty_cycle_sp
73+
self.right_motor.duty_cycle_sp = self.duty_cycle_sp
74+
self.set_polarity(polarity)
75+
76+
def set_polarity(self, polarity):
77+
valid_choices = ('normal', 'inversed')
78+
assert polarity in valid_choices,\
79+
"%s is an invalid polarity choice, must be %s" % (polarity, ', '.join(valid_choices))
80+
81+
self.left_motor.polarity = polarity
82+
self.right_motor.polarity = polarity
83+
84+
85+
class RemoteControlledTank(Tank):
86+
87+
def __init__(self, left_motor, right_motor, polarity='inversed'):
88+
Tank.__init__(self, left_motor, right_motor, polarity)
89+
self.remote = RemoteControl(channel=1)
90+
91+
if not self.remote.connected:
92+
log.error("%s is not connected" % self.remote)
93+
sys.exit(1)
94+
95+
self.remote.on_red_up = self.make_move(self.left_motor, self.duty_cycle_sp)
96+
self.remote.on_red_down = self.make_move(self.left_motor, self.duty_cycle_sp * -1)
97+
self.remote.on_blue_up = self.make_move(self.right_motor, self.duty_cycle_sp)
98+
self.remote.on_blue_down = self.make_move(self.right_motor, self.duty_cycle_sp * -1)
99+
100+
def make_move(self, motor, dc_sp):
101+
def move(state):
102+
if state:
103+
motor.run_forever(duty_cycle_sp=dc_sp)
104+
else:
105+
motor.stop()
106+
return move
107+
108+
def main(self):
109+
110+
try:
111+
while True:
112+
self.remote.process()
113+
time.sleep(0.01)
114+
115+
# Exit cleanly so that all motors are stopped
116+
except (KeyboardInterrupt, Exception) as e:
117+
log.exception(e)
118+
119+
for motor in list_motors():
120+
motor.stop()
121+
122+
123+
# =====================
124+
# Wheel and Rim classes
125+
# =====================
126+
class Wheel(object):
127+
"""
128+
A base class for various types of wheels, tires, etc
129+
All units are in mm
130+
"""
131+
132+
def __init__(self, diameter, width):
133+
self.diameter = float(diameter)
134+
self.width = float(width)
135+
self.radius = float(diameter/2)
136+
self.circumference = diameter * pi
137+
138+
139+
# A great reference when adding new wheels is http://wheels.sariel.pl/
140+
class EV3RubberWheel(Wheel):
141+
142+
def __init__(self):
143+
Wheel.__init__(self, 43.2, 21)

utils/move_motor.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env python
2+
3+
"""
4+
Used to adjust the position of a motor in an already assembled robot
5+
where you can"t move the motor by hand.
6+
"""
7+
8+
from ev3dev.auto import OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D
9+
from ev3dev.helper import LargeMotor
10+
import argparse
11+
import logging
12+
import sys
13+
14+
# command line args
15+
parser = argparse.ArgumentParser(description="Used to adjust the position of a motor in an already assembled robot")
16+
parser.add_argument("motor", type=str, help="A, B, C or D")
17+
parser.add_argument("degrees", type=int)
18+
parser.add_argument("-s", "--speed", type=int, default=50)
19+
args = parser.parse_args()
20+
21+
# logging
22+
logging.basicConfig(level=logging.DEBUG,
23+
format="%(asctime)s %(levelname)5s: %(message)s")
24+
log = logging.getLogger(__name__)
25+
26+
# For this it doesn't really matter if it is a LargeMotor or a MediumMotor
27+
if args.motor == "A":
28+
motor = LargeMotor(OUTPUT_A)
29+
elif args.motor == "B":
30+
motor = LargeMotor(OUTPUT_B)
31+
elif args.motor == "C":
32+
motor = LargeMotor(OUTPUT_C)
33+
elif args.motor == "D":
34+
motor = LargeMotor(OUTPUT_D)
35+
else:
36+
raise Exception("%s is invalid, options are A, B, C, D")
37+
38+
if not motor.connected:
39+
log.error("%s is not connected" % motor)
40+
sys.exit(1)
41+
42+
motor.reset()
43+
log.info("Motor %s, move to position %d" % (args.motor, args.degrees))
44+
motor.run_to_rel_pos(duty_cycle_sp=args.speed, position_sp=args.degrees)
45+
motor.wait_for_running()
46+
log.info("Motor %s is running" % args.motor)
47+
motor.wait_for_stop()
48+
log.info("Motor %s stopped, final position %d" % (args.motor, motor.position))

utils/stop_all_motors.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env python
2+
3+
"""
4+
Stop all motors
5+
"""
6+
from ev3dev.auto import list_motors
7+
8+
for motor in list_motors():
9+
motor.stop()

0 commit comments

Comments
 (0)