Skip to content

Commit d505bb8

Browse files
authored
Add on_to_position(), on_for_XYZ accept additional speed inputs (#407)
* return early for 'motor will not move' scenarios * Add on_to_position(), on_for_XYZ accept additional speed inputs * Add SpeedInteger classes * Calc speed percentages in SpeedInteger classes * Update fake-sys * assert SpeedInteger conversions are valid
1 parent 96f77b4 commit d505bb8

File tree

2 files changed

+165
-14
lines changed

2 files changed

+165
-14
lines changed

ev3dev/motor.py

Lines changed: 164 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,74 @@
6767
raise Exception("Unsupported platform '%s'" % platform)
6868

6969

70+
class SpeedInteger(int):
71+
pass
72+
73+
74+
class SpeedRPS(SpeedInteger):
75+
"""
76+
Speed in rotations-per-second
77+
"""
78+
79+
def __str__(self):
80+
return ("%d rps" % self)
81+
82+
def get_speed_pct(self, motor):
83+
"""
84+
Return the motor speed percentage to achieve desired rotations-per-second
85+
"""
86+
assert self <= motor.max_rps, "%s max RPS is %s, %s was requested" % (motor, motor.max_rps, self)
87+
return (self/motor.max_rps) * 100
88+
89+
90+
class SpeedRPM(SpeedInteger):
91+
"""
92+
Speed in rotations-per-minute
93+
"""
94+
95+
def __str__(self):
96+
return ("%d rpm" % self)
97+
98+
def get_speed_pct(self, motor):
99+
"""
100+
Return the motor speed percentage to achieve desired rotations-per-minute
101+
"""
102+
assert self <= motor.max_rpm, "%s max RPM is %s, %s was requested" % (motor, motor.max_rpm, self)
103+
return (self/motor.max_rpm) * 100
104+
105+
106+
class SpeedDPS(SpeedInteger):
107+
"""
108+
Speed in degrees-per-second
109+
"""
110+
111+
def __str__(self):
112+
return ("%d dps" % self)
113+
114+
def get_speed_pct(self, motor):
115+
"""
116+
Return the motor speed percentage to achieve desired degrees-per-second
117+
"""
118+
assert self <= motor.max_dps, "%s max DPS is %s, %s was requested" % (motor, motor.max_dps, self)
119+
return (self/motor.max_dps) * 100
120+
121+
122+
class SpeedDPM(SpeedInteger):
123+
"""
124+
Speed in degrees-per-minute
125+
"""
126+
127+
def __str__(self):
128+
return ("%d dpm" % self)
129+
130+
def get_speed_pct(self, motor):
131+
"""
132+
Return the motor speed percentage to achieve desired degrees-per-minute
133+
"""
134+
assert self <= motor.max_dps, "%s max DPM is %s, %s was requested" % (motor, motor.max_dpm, self)
135+
return (self/motor.max_dpm) * 100
136+
137+
70138
class Motor(Device):
71139

72140
"""
@@ -113,6 +181,10 @@ class Motor(Device):
113181
'_stop_actions',
114182
'_time_sp',
115183
'_poll',
184+
'max_rps',
185+
'max_rpm',
186+
'max_dps',
187+
'max_dpm',
116188
]
117189

118190
#: Run the motor until another command is sent.
@@ -222,6 +294,10 @@ def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, nam
222294
self._stop_actions = None
223295
self._time_sp = None
224296
self._poll = None
297+
self.max_rps = float(self.max_speed/self.count_per_rot)
298+
self.max_rpm = self.max_rps * 60
299+
self.max_dps = self.max_rps * 360
300+
self.max_dpm = self.max_rpm * 360
225301

226302
@property
227303
def address(self):
@@ -732,6 +808,18 @@ def wait_while(self, s, timeout=None):
732808
"""
733809
return self.wait(lambda state: s not in state, timeout)
734810

811+
def _speed_pct(self, speed_pct):
812+
813+
# If speed_pct is SpeedInteger object we must convert
814+
# SpeedRPS, etc to an actual speed percentage
815+
if isinstance(speed_pct, SpeedInteger):
816+
speed_pct = speed_pct.get_speed_pct(self)
817+
818+
assert -100 <= speed_pct <= 100,\
819+
"%s is an invalid speed_pct, must be between -100 and 100 (inclusive)" % speed_pct
820+
821+
return speed_pct
822+
735823
def _set_position_rotations(self, speed_pct, rotations):
736824

737825
# +/- speed is used to control direction, rotations must be positive
@@ -760,10 +848,18 @@ def _set_brake(self, brake):
760848

761849
def on_for_rotations(self, speed_pct, rotations, brake=True, block=True):
762850
"""
763-
Rotate the motor at 'speed' for 'rotations'
851+
Rotate the motor at 'speed_pct' for 'rotations'
852+
853+
'speed_pct' can be an integer or a SpeedInteger object which will be
854+
converted to an actual speed percentage in _speed_pct()
764855
"""
765-
assert speed_pct >= -100 and speed_pct <= 100,\
766-
"%s is an invalid speed_pct, must be between -100 and 100 (inclusive)" % speed_pct
856+
speed_pct = self._speed_pct(speed_pct)
857+
858+
if not speed_pct or not rotations:
859+
log.warning("%s speed_pct is %s but rotations is %s, motor will not move" % (self, speed_pct, rotations))
860+
self._set_brake(brake)
861+
return
862+
767863
self.speed_sp = int((speed_pct * self.max_speed) / 100)
768864
self._set_position_rotations(speed_pct, rotations)
769865
self._set_brake(brake)
@@ -775,10 +871,18 @@ def on_for_rotations(self, speed_pct, rotations, brake=True, block=True):
775871

776872
def on_for_degrees(self, speed_pct, degrees, brake=True, block=True):
777873
"""
778-
Rotate the motor at 'speed' for 'degrees'
874+
Rotate the motor at 'speed_pct' for 'degrees'
875+
876+
'speed_pct' can be an integer or a SpeedInteger object which will be
877+
converted to an actual speed percentage in _speed_pct()
779878
"""
780-
assert speed_pct >= -100 and speed_pct <= 100,\
781-
"%s is an invalid speed_pct, must be between -100 and 100 (inclusive)" % speed_pct
879+
speed_pct = self._speed_pct(speed_pct)
880+
881+
if not speed_pct or not degrees:
882+
log.warning("%s speed_pct is %s but degrees is %s, motor will not move" % (self, speed_pct, degrees))
883+
self._set_brake(brake)
884+
return
885+
782886
self.speed_sp = int((speed_pct * self.max_speed) / 100)
783887
self._set_position_degrees(speed_pct, degrees)
784888
self._set_brake(brake)
@@ -788,12 +892,43 @@ def on_for_degrees(self, speed_pct, degrees, brake=True, block=True):
788892
self.wait_until('running', timeout=WAIT_RUNNING_TIMEOUT)
789893
self.wait_until_not_moving()
790894

895+
def on_to_position(self, speed_pct, position, brake=True, block=True):
896+
"""
897+
Rotate the motor at 'speed_pct' to 'position'
898+
899+
'speed_pct' can be an integer or a SpeedInteger object which will be
900+
converted to an actual speed percentage in _speed_pct()
901+
"""
902+
speed_pct = self._speed_pct(speed_pct)
903+
904+
if not speed_pct:
905+
log.warning("%s speed_pct is %s, motor will not move" % (self, speed_pct))
906+
self._set_brake(brake)
907+
return
908+
909+
self.speed_sp = int((speed_pct * self.max_speed) / 100)
910+
self.position_sp = position
911+
self._set_brake(brake)
912+
self.run_to_abs_pos()
913+
914+
if block:
915+
self.wait_until('running', timeout=WAIT_RUNNING_TIMEOUT)
916+
self.wait_until_not_moving()
917+
791918
def on_for_seconds(self, speed_pct, seconds, brake=True, block=True):
792919
"""
793-
Rotate the motor at 'speed' for 'seconds'
920+
Rotate the motor at 'speed_pct' for 'seconds'
921+
922+
'speed_pct' can be an integer or a SpeedInteger object which will be
923+
converted to an actual speed percentage in _speed_pct()
794924
"""
795-
assert speed_pct >= -100 and speed_pct <= 100,\
796-
"%s is an invalid speed_pct, must be between -100 and 100 (inclusive)" % speed_pct
925+
speed_pct = self._speed_pct(speed_pct)
926+
927+
if not speed_pct or not seconds:
928+
log.warning("%s speed_pct is %s but seconds is %s, motor will not move" % (self, speed_pct, seconds))
929+
self._set_brake(brake)
930+
return
931+
797932
self.speed_sp = int((speed_pct * self.max_speed) / 100)
798933
self.time_sp = int(seconds * 1000)
799934
self._set_brake(brake)
@@ -803,15 +938,31 @@ def on_for_seconds(self, speed_pct, seconds, brake=True, block=True):
803938
self.wait_until('running', timeout=WAIT_RUNNING_TIMEOUT)
804939
self.wait_until_not_moving()
805940

806-
def on(self, speed_pct):
941+
def on(self, speed_pct, brake=True, block=False):
807942
"""
808-
Rotate the motor at 'speed' for forever
943+
Rotate the motor at 'speed_pct' for forever
944+
945+
'speed_pct' can be an integer or a SpeedInteger object which will be
946+
converted to an actual speed percentage in _speed_pct()
947+
948+
Note that `block` is False by default, this is different from the
949+
other `on_for_XYZ` methods
809950
"""
810-
assert speed_pct >= -100 and speed_pct <= 100,\
811-
"%s is an invalid speed_pct, must be between -100 and 100 (inclusive)" % speed_pct
951+
speed_pct = self._speed_pct(speed_pct)
952+
953+
if not speed_pct:
954+
log.warning("%s speed_pct is %s, motor will not move" % (self, speed_pct))
955+
self._set_brake(brake)
956+
return
957+
812958
self.speed_sp = int((speed_pct * self.max_speed) / 100)
959+
self._set_brake(brake)
813960
self.run_forever()
814961

962+
if block:
963+
self.wait_until('running', timeout=WAIT_RUNNING_TIMEOUT)
964+
self.wait_until_not_moving()
965+
815966
def off(self, brake=True):
816967
self._set_brake(brake)
817968
self.stop()

tests/fake-sys

0 commit comments

Comments
 (0)