Skip to content

Only reset port settings for motor on first start() #125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 18, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion buildhat/motors.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def __init__(self, port):
self._default_speed = 20
self.plimit(0.7)
self.bias(0.3)
self.current_freerun_speed = 0

def set_default_speed(self, default_speed):
"""Sets the default speed of the motor
Expand All @@ -34,18 +35,24 @@ def start(self, speed=None):

:param speed: Speed ranging from -100 to 100
"""
if self.current_freerun_speed == speed:
# Already running at this speed, do nothing
return

if speed is None:
speed = self._default_speed
else:
if not (speed >= -100 and speed <= 100):
raise MotorException("Invalid Speed")
self.current_freerun_speed = speed
cmd = "port {} ; pwm ; set {}\r".format(self.port, speed/100)
self._write(cmd)

def stop(self):
"""Stops motor"""
cmd = "port {} ; off\r".format(self.port)
self._write(cmd)
self.current_freerun_speed = 0

def plimit(self, plimit):
if not (plimit >= 0 and plimit <= 1):
Expand All @@ -57,6 +64,13 @@ def bias(self, bias):
raise MotorException("bias should be 0 to 1")
self._write("port {} ; bias {}\r".format(self.port, bias))


class MotorRunmode(Enum):
NONE = 0
FREE = 1
DEGREES = 2
SECONDS = 3

class Motor(Device):
"""Motor device

Expand All @@ -67,6 +81,7 @@ class Motor(Device):
def __init__(self, port):
super().__init__(port)
self.default_speed = 20
self.current_freerun_speed = 0
self.mode([(1,0),(2,0),(3,0)])
self.plimit(0.7)
self.bias(0.3)
Expand All @@ -75,6 +90,7 @@ def __init__(self, port):
self._cvqueue = Condition()
self.when_rotated = None
self._oldpos = None
self.runmode = MotorRunmode.NONE

def set_default_speed(self, default_speed):
"""Sets the default speed of the motor
Expand All @@ -91,6 +107,7 @@ def run_for_rotations(self, rotations, speed=None, blocking=True):
:param rotations: Number of rotations
:param speed: Speed ranging from -100 to 100
"""
self.runmode = MotorRunmode.DEGREES
if speed is None:
self.run_for_degrees(int(rotations * 360), self.default_speed, blocking)
else:
Expand All @@ -99,6 +116,7 @@ def run_for_rotations(self, rotations, speed=None, blocking=True):
self.run_for_degrees(int(rotations * 360), speed, blocking)

def _run_for_degrees(self, degrees, speed):
self.runmode = MotorRunmode.DEGREES
mul = 1
if speed < 0:
speed = abs(speed)
Expand All @@ -116,8 +134,10 @@ def _run_for_degrees(self, degrees, speed):
if self._release:
time.sleep(0.2)
self.coast()
self.runmode = MotorRunmode.NONE

def _run_to_position(self, degrees, speed, direction):
self.runmode = MotorRunmode.DEGREES
data = self.get()
pos = data[1]
apos = data[2]
Expand Down Expand Up @@ -148,6 +168,7 @@ def _run_to_position(self, degrees, speed, direction):
if self._release:
time.sleep(0.2)
self.coast()
self.runmode = MotorRunmode.NONE

def run_for_degrees(self, degrees, speed=None, blocking=True):
"""Runs motor for N degrees
Expand All @@ -157,6 +178,7 @@ def run_for_degrees(self, degrees, speed=None, blocking=True):
:param degrees: Number of degrees to rotate
:param speed: Speed ranging from -100 to 100
"""
self.runmode = MotorRunmode.DEGREES
if speed is None:
speed = self.default_speed
if not (speed >= -100 and speed <= 100):
Expand All @@ -174,6 +196,7 @@ def run_to_position(self, degrees, speed=None, blocking=True, direction="shortes
:param degrees: Position in degrees
:param speed: Speed ranging from 0 to 100
"""
self.runmode = MotorRunmode.DEGREES
if speed is None:
speed = self.default_speed
if not (speed >= 0 and speed <= 100):
Expand All @@ -188,19 +211,22 @@ def run_to_position(self, degrees, speed=None, blocking=True, direction="shortes
self._run_to_position(degrees, speed, direction)

def _run_for_seconds(self, seconds, speed):
self.runmode = MotorRunmode.SECONDS
cmd = "port {} ; combi 0 1 0 2 0 3 0 ; select 0 ; pid {} 0 0 s1 1 0 0.003 0.01 0 100; set pulse {} 0.0 {} 0\r".format(self.port, self.port, speed, seconds);
self._write(cmd);
with self._hat.pulsecond[self.port]:
self._hat.pulsecond[self.port].wait()
if self._release:
self.coast()
self.runmode = MotorRunmode.NONE

def run_for_seconds(self, seconds, speed=None, blocking=True):
"""Runs motor for N seconds

:param seconds: Time in seconds
:param speed: Speed ranging from -100 to 100
"""
self.runmode = MotorRunmode.SECONDS
if speed is None:
speed = self.default_speed
if not (speed >= -100 and speed <= 100):
Expand All @@ -217,16 +243,31 @@ def start(self, speed=None):

:param speed: Speed ranging from -100 to 100
"""
if self.runmode == MotorRunmode.FREE:
if self.current_freerun_speed == speed:
# Already running at this speed, do nothing

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a first order filter.
In a scenario with a constantly changing speed value, this code will fail to reduce the message rate. Therefore, the interface must sill be able to sustain the full message speed in order to rule out a random failure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but but the Pi is fast compared to the serial port and you're going to need/want all the bandwidth you can get out of the serial port in your scenario. This is seems kind of cheap.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, in this case there might be a minor benefit.

return
elif self.runmode != MotorRunmode.NONE:
# Motor is running some other mode, wait for it to stop or stop() it yourself
return

if speed is None:
speed = self.default_speed
else:
if not (speed >= -100 and speed <= 100):
raise MotorException("Invalid Speed")
cmd = "port {} ; combi 0 1 0 2 0 3 0 ; select 0 ; pid {} 0 0 s1 1 0 0.003 0.01 0 100; set {}\r".format(self.port, self.port, speed)
cmd = "port {} ; set {}\r".format(self.port, speed)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is the solution.

if self.runmode == MotorRunmode.NONE:
cmd = "port {} ; combi 0 1 0 2 0 3 0 ; select 0 ; pid {} 0 0 s1 1 0 0.003 0.01 0 100; set {}\r".format(self.port, self.port, speed)
self.runmode = MotorRunmode.FREE
self.current_freerun_speed = speed
self._write(cmd)


def stop(self):
"""Stops motor"""
self.runmode = MotorRunmode.NONE
self.current_freerun_speed = 0
self.coast()

def get_position(self):
Expand Down