Stepper motor only moving 1 step at a time with 'while true' loop

CircuitPython on hardware including Adafruit's boards, and CircuitPython libraries using Blinka on host computers.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
fongj525
 
Posts: 6
Joined: Sun Mar 26, 2023 10:20 pm

Stepper motor only moving 1 step at a time with 'while true' loop

Post by fongj525 »

Need help with a school project.

I'm programming a stepper motor with a SparkFun Thing Plus rp2040 along with a LIS3DH accelerometer and the goal of the project is the move two stepper motor independently in CW/CCW direction when the accelerometer is reading a tilt value within a positive range and the other direction when it is reading a value in the negative range. One stepper motor is responsible for the x axis and the other is responsible for the y axis.
In order for me to remove some of the noise produced by the LIS3DH, I implemented a moving average calculation within the loop and converted average to degrees, but as the sample window gets bigger (ex: 75 and above) the code also runs slower, which causes my stepper motors to move only one step per loop iteration.

Is there a way to speed up the loop? or make the stepper motor move without regards to the loop iteration?
I'm I doing something wrong? Kinda new to code as well btw.

Code: Select all

import time
import math
import board
import digitalio
import adafruit_lis3dh

# Initialize the I2C bus
i2c = board.STEMMA_I2C()
# Initialize the LSM6DSOX accelerometer
lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c)

# Initialize the stepper motor pins
x_step = digitalio.DigitalInOut(board.D26)
x_step.direction = digitalio.Direction.OUTPUT
x_dir = digitalio.DigitalInOut(board.D27)
x_dir.direction = digitalio.Direction.OUTPUT
x_ms1 = digitalio.DigitalInOut(board.D18)
x_ms1.direction = digitalio.Direction.OUTPUT
x_ms2 = digitalio.DigitalInOut(board.D17)
x_ms2.direction = digitalio.Direction.OUTPUT
x_ms3 = digitalio.DigitalInOut(board.D16)
x_ms3.direction = digitalio.Direction.OUTPUT

y_step = digitalio.DigitalInOut(board.D28)
y_step.direction = digitalio.Direction.OUTPUT
y_dir = digitalio.DigitalInOut(board.D29)
y_dir.direction = digitalio.Direction.OUTPUT
y_ms1 = digitalio.DigitalInOut(board.D22)
y_ms1.direction = digitalio.Direction.OUTPUT
y_ms2 = digitalio.DigitalInOut(board.D21)
y_ms2.direction = digitalio.Direction.OUTPUT
y_ms3 = digitalio.DigitalInOut(board.D20)
y_ms3.direction = digitalio.Direction.OUTPUT

#Function setup for x and y axis
def x_spin_function():
    x_step.value = True
    time.sleep(0.001)
    x_step.value = False
    time.sleep(0.001)
    
def y_spin_function():
    y_step.value = True
    time.sleep(0.001)
    y_step.value = False
    time.sleep(0.001)
    
# Set up a few global variables & list
G_CONST = 9.81
sum_x_value = 0
sum_y_value = 0
avg_x_value_offset = 0
avg_y_value_offset = 0
degrees_list_x = [0]
degrees_list_y = [0]
sample_counter = 0
sample_window = 100

while True:
    raw_x_value = lis3dh.acceleration[0] # "0" represents the x axis
    raw_y_value = lis3dh.acceleration[1] # "1" represents the y axis
    sum_x_value += raw_x_value
    sum_y_value += raw_y_value
    sample_counter += 1
    
    if sample_counter == sample_window:
        avg_x_value = ((sum_x_value)/(sample_window)) + (avg_x_value_offset)
        avg_y_value = ((sum_y_value)/(sample_window)) + (avg_y_value_offset)
        degrees_x = math.asin((avg_x_value)/(G_CONST)) * (180/math.pi)
        degrees_y = math.asin((avg_y_value)/(G_CONST)) * (180/math.pi)
        degrees_list_x[0] = degrees_x
        degrees_list_y[0] = degrees_y
        
        sum_x_value = 0 # x Reset
        sum_y_value = 0 # y Reset
        sample_counter = 0 
    
        print(degrees_list_x, degrees_list_y)
        
        if 1 < degrees_list_x[0] and degrees_list_y[0] < 90:
            x_dir.value = True
            y_dir.value = True
            x_spin_function()
            y_spin_function()
            continue
    
        if -90 < degrees_list_x[0] and degrees_list_y[0] < -1:
            x_dir.value = False
            y_dir.value = False
            x_spin_function()
            y_spin_function()
            continue

User avatar
edgjr
 
Posts: 143
Joined: Mon Jan 16, 2012 6:18 pm

Re: Stepper motor only moving 1 step at a time with 'while true' loop

Post by edgjr »

It's pretty natural to expect the stepper to "slow down" if it's taking more time to run through the average calculation - these processors aren't expected to be mathematical power-houses (they run at megahertz clock speeds, at best, whereas desktops are easily 100x times faster). 75 "read" operations in order to get enough samples is a lot when you consider all the timing and processing constraints. You're also going to get a lot of rounding/noise errors no matter what, so you might want to reconsider what kind of precision you want vs. what you can get out of the platform.

User avatar
adafruit_support_bill
 
Posts: 88096
Joined: Sat Feb 07, 2009 10:11 am

Re: Stepper motor only moving 1 step at a time with 'while true' loop

Post by adafruit_support_bill »

You could use a moving average. Instead of taking N new readings each time through the loop, you can have a moving window of the last N readings. Each time through the loop, you take one new reading and then average that with the previous N-1 readings.

User avatar
fongj525
 
Posts: 6
Joined: Sun Mar 26, 2023 10:20 pm

Re: Stepper motor only moving 1 step at a time with 'while true' loop

Post by fongj525 »

adafruit_support_bill,

I just want to get this right. So what you are recommending is that instead of taking a sum of lets say, 100 readings then dividing it by 100 to arrive at the average, and then collect 100 more new readings and dividing and repeating.

I should instead take 100 readings and each time one singular new reading comes in, I should remove the 1st reading from the previous loop and index every previous value to the 'left' and insert the new reading in the 100th place to arrive at the new average?

Or you are saying take the 100 readings and through each loop, take one less reading (N-1) from the previous loops? For example, starting with 100 readings then 99, then 98, then 97....

User avatar
adafruit_support_bill
 
Posts: 88096
Joined: Sat Feb 07, 2009 10:11 am

Re: Stepper motor only moving 1 step at a time with 'while true' loop

Post by adafruit_support_bill »

I should instead take 100 readings and each time one singular new reading comes in, I should remove the 1st reading from the previous loop and index every previous value to the 'left' and insert the new reading in the 100th place to arrive at the new average?
That is the most straightforward implementation of the concept. However, you could reduce both memory and computation overhead substantially as follows:

* Take a reading at startup and multiply it by 100.
* Assign it to a variable called "Accumulator".
* Then enter your loop.

On each iteration of the loop:
* divide the value of Accumulator by 100 and call that "AverageValue".
* Use AverageValue as needed for your computations.
* subtract AverageValue from Accumulator.
* take a new reading and add it to Accumulator.

User avatar
edgjr
 
Posts: 143
Joined: Mon Jan 16, 2012 6:18 pm

Re: Stepper motor only moving 1 step at a time with 'while true' loop

Post by edgjr »

I don't think that's what you want, really - a "moving average" would not indicate actual movement, but a statistical value in the time-frame of the accumulated values - it would not indicate where everything is now.

This is where you start thinking about "how accurate does the value need to be?" Averaging 100 readings across that time will produce a value for the whole time it takes to read them all. For example, you start reading and produce 3 rapid movements in a very short period of time. If those movements occur within the time it takes to read 100 values, your average will not be representative of the actual movement, but rather some garbled summation of the three, depending on how long the platform was in each position.

The actual hardware also makes a huge difference: getting analog data out of an ADC device hooked directly to a GPIO pin is faster than using something like a SeeSaw backback, due to the extra micro-controller in the mix.

What you're looking for is a quick way to determine if (and what general direction) a movement is occurring. Use a much smaller sample (like 3 to 5) and just look for gross differences between loop iterations. And note that anything else you put in the loop will affect how fast the steppers can run, as well.

User avatar
westfw
 
Posts: 2008
Joined: Fri Apr 27, 2007 1:01 pm

Re: Stepper motor only moving 1 step at a time with 'while true' loop

Post by westfw »

Taking 75 reads before you compute an average doesn’t tell you where the thing is “now”, either.

Is the accelerometer really that noisy, that you need so many samples to average?

User avatar
adafruit_support_bill
 
Posts: 88096
Joined: Sat Feb 07, 2009 10:11 am

Re: Stepper motor only moving 1 step at a time with 'while true' loop

Post by adafruit_support_bill »

If this is a tilting platform moved by motors, your accelerometers are also measuring the accelerations due to that movement, as well as any vibrations from the motors. That is not 'noise produced by the LIS3DH'. It is actual accelerations produced by your system.

Locked
Please be positive and constructive with your questions and comments.

Return to “Adafruit CircuitPython”