juanmf / StepperMotors

Python stepper motor library with customizable acceleration strategies and responsive, interruptible motor operations.
https://github.com/juanmf/StepperMotors
MIT License
19 stars 0 forks source link

PONG #17

Open ProbablyNotAaronFrater opened 6 months ago

ProbablyNotAaronFrater commented 6 months ago

Hello, My senior design team and I (we are seniors in an EE program) are working on a display for the game PONG using two stepper motors and the CoreXY methodology. We are using your code to run the motors simultaneously to decrease the jerkiness of the motion. We are having a hard time figuring out where the "Move Complete" flag is stored. We'd like to use it as a flag to start the next movement. Can you help?

juanmf commented 6 months ago

Hi, Interesting application, I'd use the getMpCustomTorqueCharacteristicsDRV8825With factory method if you managed to benchmark your motors (it's hard to do on a finite linear actuator, because it can't continually rotate) otherwise Exponential is very good.

Stepping jobs act like Future proxies (in fact they contain 3 Futures but the result() of a Jobs is a proxy of the result() call to the "job done" future.), Following snippet goes back and forth 1/8th of rotation for ever.


from stepper_motors_juanmf1.Controller import MotorDriver, BipolarStepperMotorDriver
from stepper_motors_juanmf1.ControllerFactory import MultiProcessingControllerFactory
from stepper_motors_juanmf1.StepperMotor import GenericStepper, PG35S_D48_HHC2

def main():
    factory = MultiProcessingControllerFactory()
    motor = GenericStepper(minPps=50, maxPps=300, maxSleepTime=1/50, minSleepTime=1/300, spr=200)
    # motor = PG35S_D48_HHC2(loaded=True)

    driver: BipolarStepperMotorDriver = (factory.setUpProcess()
            .withDriver(multiprocessObserver=None,
                        factoryFnReference=factory.getMpExponentialTMC2209With,
                        # factoryFnReference=factory.getMpCustomTorqueCharacteristicsDRV8825With,
                        stepperMotor=motor, directionGpioPin=16, stepGpioPin=20, enableGpioPin=21)
            .spawn())[0]

    while True:
        driver.signedSteps(int(motor.spr * 1/8)).result()
        driver.signedSteps(-int(motor.spr * 1/8)).result()

You could also use events. If you need not to block on a stepping job, say your app needs to keep vigilant of balls coming, then you could be open to change course on the fly or wait for 2 built in events:

So, if you are confident you can wait until a job is done to decide on next move, use the snippet as example.

ProbablyNotAaronFrater commented 6 months ago

I'm running two motors synchronously and need to know when the move I have given them is complete so I can tell them to move again. Currently we are using the sleep command as a poor substitute to ensure the motors don't get overlapping commands. I am an EE and not a CompSci major so I apologize if I am asking an ignorant question.

juanmf commented 6 months ago

No worries,

Your (basic) answer is already there.

No Sleep

Above, these lines complete 1/8th turn in one direction, no time.sleep used, then goes back to original position. then repeat.

    # .result() blocks until stepping job is done.
    while True:
        driver.signedSteps(int(motor.spr * 1/8)).result(). 
        driver.signedSteps(-int(motor.spr * 1/8)).result()

2 motors in sync

Make sure you use the MultiProcessingControllerFactory factory to minimize pulse timing issues (also start your app with nice -15 or something high to help here) .

Then you have several scenarios. 1) your main app can have the luxury of blocking while motors work (your time.sleep case is a poor approach to this one).

xJob = driverX.signedSteps(300)
yJob = driverY.signedSteps(200)

# We get here immediately, jobs probably didn't start yet.
xJob.result(). # blocks until done
yJob.result()  # blocks until done, continues if already done

print("both drivers just completed their job, but this thread was blocked all along")

2) You can't afford to block because your app need to be responsive to other events. I'd assume a PONG game won't work well with blocking. Maybe you are going to an initial position and the opponent already shot, so you need to change course before running stepping job is done.

Simplest approach is same as before but instead of blocking right away you test for some input.

If there are other parts of your system that need to get notified of a stepping job done you could use the EventDispatecher to register listeners. But that's a more sophisticated topic.

Hope this helps.

BTW BasicSynchronizedNavigation has a latch feature to start motors after the last expected one sent it's job for stepping. If you want full sync, including starts time. If motors/drivers are of the same kind, this can be more efficient as both motors' initial and subsequent pulses would require one go through the logic, emitting signals perfectly at the same time.