Skip to content

Add speed/direction motor#1147

Open
pigrammer3 wants to merge 8 commits into
gpiozero:masterfrom
pigrammer3:master
Open

Add speed/direction motor#1147
pigrammer3 wants to merge 8 commits into
gpiozero:masterfrom
pigrammer3:master

Conversation

@pigrammer3
Copy link
Copy Markdown

See #300 and #330. Add support for a motor driven by three pins: one that's speed and the other two like the two forward and backward pins on the regular Motor class. I couldn't think of a better name than SpeedDirectionMotor.

Comment thread gpiozero/output_devices.py Outdated
Comment on lines +1383 to +1385
self.backward_device.off()
self.forward_device.on()
self.speed_device.value = speed
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As a safety-measure, I wonder if this should first set self.speed_device.value to 0 before turning the forward_device on? I.e. if you do

my_speed_motor.backward(1)
time.sleep(1)
my_speed_motor.forward(0.2)

you probably don't want a momentary blip where the motor is running forwards at full speed?

@lurch
Copy link
Copy Markdown
Contributor

lurch commented May 30, 2024

Hrmmm, looking at my comments in #366 (specifically this one ), I wonder if this could be shoe-horned into the existing Motor() class by e.g. adding a pwm_the_enable_pin parameter (which defaults to False) to Motor.__init__ ?
And then if you use Motor(forward=2, backward=3, enable=4, pwm=True) you'd get the current behaviour of forward_device & backward_device being PWMOutDevices and enable_device being a DigitalOutputDevice; but if you instead did Motor(forward=2, backward=3, enable=4, pwm_the_enable_pin=True) then you'd get forward_device & backward_device being DigitalOutputDevices and enable_device being a PWMOutputDevice (the behaviour in this PR) ? 🤔
Would probably make sense to make passing both pwmn=True and pwm_the_enable_pin=True be treated as an error. Passing enable=None and pwm_the_enable_pin=True should also be treated as an error.

And I'm too tired right now to think of a better name than pwm_the_enable_pin 😆

@bennuttall
Copy link
Copy Markdown
Member

I thought this felt familiar. Does it not work the same if you just use the enable pin on the Motor class?

@pigrammer3
Copy link
Copy Markdown
Author

Hrmmm, looking at my comments in #366 (specifically this one ), I wonder if this could be shoe-horned into the existing Motor() class by e.g. adding a pwm_the_enable_pin parameter (which defaults to False) to Motor.__init__ ?
And then if you use Motor(forward=2, backward=3, enable=4, pwm=True) you'd get the current behaviour of forward_device & backward_device being PWMOutDevices and enable_device being a DigitalOutputDevice; but if you instead did Motor(forward=2, backward=3, enable=4, pwm_the_enable_pin=True) then you'd get forward_device & backward_device being DigitalOutputDevices and enable_device being a PWMOutputDevice (the behaviour in this PR) ? 🤔
Would probably make sense to make passing both pwmn=True and pwm_the_enable_pin=True be treated as an error. Passing enable=None and pwm_the_enable_pin=True should also be treated as an error.

And I'm too tired right now to think of a better name than pwm_the_enable_pin 😆

Yeah, that makes sense. How about pwm_enable?

@pigrammer3
Copy link
Copy Markdown
Author

pigrammer3 commented May 30, 2024

I thought this felt familiar. Does it not work the same if you just use the enable pin on the Motor class?

That wouldn't work because the enable pin is a DigitalOutputDevice in the Motor class. @lurch's suggestion would make the Motor class optionally make enable a PWMOutputDevice, which would make it work. (Unless I misunderstood and that was also your suggestion).

@lurch
Copy link
Copy Markdown
Contributor

lurch commented May 30, 2024

And I'm too tired right now to think of a better name than pwm_the_enable_pin 😆

Yeah, that makes sense. How about pwm_enable?

I did consider that, but thought that people might confuse pwm=True and pwm_enable=True 😕
I guess enable_is_pwm might work? 🤔

I thought this felt familiar. Does it not work the same if you just use the enable pin on the Motor class?

That wouldn't work... (Unless I misunderstood...)

I think @bennuttall was suggesting that rather than having enable be PWM and forward & backward be Digital (which is what you're doing in this PR), have you tried having enable be Digital and forward and backward be PWM? (which is something the Motor() class already supports)
Or does the particular motor-driver chip you're using not support that mode of operation?

@bennuttall
Copy link
Copy Markdown
Member

Yes I'm pretty sure that's where we ended up last time

@pigrammer3
Copy link
Copy Markdown
Author

pigrammer3 commented May 30, 2024

I think @bennuttall was suggesting that rather than having enable be PWM and forward & backward be Digital (which is what you're doing in this PR), have you tried having enable be Digital and forward and backward be PWM? (which is something the Motor() class already supports)
Or does the particular motor-driver chip you're using not support that mode of operation?

I'm using the L298N motor driver, which while it does work with PWM on the forward & backward pins, is better with PWM on the enable pin (see #366 (comment)):

For 'Fast Stop' to be engaged, enable pin must be high and and the drive pins must be equal. Therefore if the drive pins are being PWMed, each cycle of the PWM the 'Fast Stop" feature will kick in. If PWM is on the enable pin the motors will 'free spin' between cycles.

In critical applications this may be important, but in terms of hobby electronics, I'm not sure how important it is. It might reduce the life cycle of the motor driver or the motor. L298 data sheet attached, refer to page 6 for 'Fast Stop' feature.
en.CD00000240.pdf

Also see page 4 of https://www.st.com/resource/en/application_note/CD00003776-.pdf:

The situation is different for bidirectional motors driven by a bridge. In this case the two alternatives have different effects. If the channel inputs are driven by the PWM signal, with suitable logic, the recirculation path is through a diode, the motor and a transistor (figure 7a), givind [sic] a slow decay. On the other hand, if the enable input is controlled the recirculation path is from ground to supply through two diodes and the winding. This path gives a faster decay (figure 7b).

Comment thread gpiozero/output_devices.py Outdated
Comment thread gpiozero/output_devices.py Outdated
stopped.
"""
if hasattr(self, "enable_device"):
return (self.forward_device.value - self.backward_device.value) * self.enable_device.value
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hah, clever 😉

Comment thread tests/test_outputs.py Outdated
Comment thread gpiozero/output_devices.py Outdated
Comment thread gpiozero/output_devices.py Outdated
@lurch
Copy link
Copy Markdown
Contributor

lurch commented May 31, 2024

Should the stop() method also do

    if isinstance(self.enable_device, PWMOutputDevice):
        self.enable_device.value = 1

after turning off the forward and backward pins, in order to activate the 'Fast Stop' feature you mentioned?

Thanks for adding such comprehensive tests 👍

@pigrammer3
Copy link
Copy Markdown
Author

Should the stop() method also do

    if isinstance(self.enable_device, PWMOutputDevice):
        self.enable_device.value = 1

after turning off the forward and backward pins, in order to activate the 'Fast Stop' feature you mentioned?

Good point, I did that.

Comment thread gpiozero/output_devices.py Outdated
'backward speed must be 0 or 1 with non-PWM Motors')
self.forward_device.off()
if isinstance(self.enable_device, PWMOutputDevice):
if hasattr(self, "enable_device") and isinstance(self.enable_device, PWMOutputDevice):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wonder if it'd be worth having a self.enable_is_pwm boolean attribute and checking that, rather than all of these hasattr(...) and isinstance(...) tests? 🤷‍♂️

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

how about self.control_type, an enum that can be NO_PWM, PWM, NO_PWM_WITH_ENABLE, PWM_WITH_ENABLE, or ENABLE_IS_PWM? that way no isinstance checks have to be done at all?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sounds good. But I wonder if DIGITAL, PWM, DIGITAL_WITH_DIGITAL_ENABLE, PWM_WITH_DIGITAL_ENABLE, DIGITAL_WITH_PWM_ENABLE is a slightly clearer set of names?

Copy link
Copy Markdown
Author

@pigrammer3 pigrammer3 Jun 2, 2024

Choose a reason for hiding this comment

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

I've done this and run unit tests. Thanks for all the feedback.

Comment thread gpiozero/output_devices.py Outdated
@pigrammer3 pigrammer3 requested a review from lurch June 5, 2024 22:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants