Jump to content

SkySurveyBanner.jpg.21855908fce40597655603b6c9af720d.jpg

DIY Stepper RA Drive Using Raspberrypi - Stepping Issue


Juju

Recommended Posts

As this is my first post I'm not sure how exactly to word this.

I started a project where i am trying to create a DIY RA Drive instead of buying a commercial one, I have successfully created a bracket and my wiring is all completed and works fine, however this may seem ridicule but I don't know how to match the rotation of the stepper to the earths rotation, in other words I am not sure on how to calculate the delay in which I need to send a signal for pulses.

Below is the code i am using.

i have up to 32 micro steps and am using python on a raspberry pi 3+.

I have seen many people complete this project but all guides have been using Arduinos.

Below is my code, could someone be so kind as to explain how i would go about the above problem? many thanks

As said above the code and hardware works fine it is just the steps and how to match earths rotation i do not understand.

 

from time import sleep
import RPi.GPIO as GPIO
from tkinter import *
from tkinter import messagebox
#
PUL = 17  # Stepper Drive Pulses
DIR = 27  # Controller Direction Bit (High for Controller default / LOW to Force a Direction Change).
ENA = 22  # Controller Enable Bit (High to Enable / LOW to Disable).
channel = 2 #Relay GPIO is set as channel
GPIO.setmode(GPIO.BCM)
#
GPIO.setup(PUL, GPIO.OUT)
GPIO.setup(DIR, GPIO.OUT)
GPIO.setup(ENA, GPIO.OUT)
GPIO.setup(channel, GPIO.OUT) #Relay GPIO Set up

durationFwd = 1000
delay = 0.05 # This is actualy a delay between PUL pulses - effectively sets the mtor rotation speed.
print('Speed set to ' + str(delay))

def Relay_on():
    GPIO.output(2, GPIO.LOW)  # Turn motor on

def Relay_off():
    GPIO.output(2, GPIO.HIGH)  # Turn motor off

def Exit():
    GPIO.cleanup()
    exit()
   
def forward():
    GPIO.output(ENA, GPIO.HIGH)
   
    print('ENA set to HIGH - Controller Enabled')
    #
    sleep(.1) # pause due to a possible change direction
    GPIO.output(DIR, GPIO.LOW)
   
    print('DIR set to LOW - Moving Forward at ' + str(delay))
    print('Controller PUL being driven.')
    for x in range(durationFwd):
        GPIO.output(PUL, GPIO.HIGH)
        sleep(delay)
        GPIO.output(PUL, GPIO.LOW)
        sleep(delay)
    GPIO.output(ENA, GPIO.LOW)
    return
   
tk = Tk()
tk.geometry("400x300")
b1 = Button(tk, text="Relay On", command=Relay_on).place(x=100, y=50)
b2 = Button(tk, text="Relay Off", command=Relay_off).place(x=100, y=100)
b3 = Button(tk, text="Exit", command=Exit).place(x=100, y=200)
b4 = Button(tk, text="Forward", command=forward).place(x=100, y=150)
tk.mainloop()

GPIO.cleanup()
#

Link to comment
Share on other sites

Hello, nice project.

Just like a motorized barn door tracker, you need to count steps of each part of your mechanism, and join them (multiply / divide depending on input/output situation) to write an analytic equation. Parts include gears (number of teeth per full revolution) of mount and intermediate transmission, stepper motor (steps per revolution), and sky (360° / ~23h56mn).

The base equation is mainly : 1 main mount gear revolution = 1 day. Develop the left part with gear ratios, down to stepper total steps and period, then you will be able to extract the period = seconds or milliseconds in 1 day / some number of steps.

HTH

Link to comment
Share on other sites

1 hour ago, Juju said:

As said above the code and hardware works fine it is just the steps and how to match earths rotation i do not understand.

That code style is not the best for what you are trying to achieve.

Don't use pauses that you don't precisely control - use active loop for good precision (this will drain more power but you want as "real time" as possible).

Here is pseudo code for something like this:

while not done:
  if current_time>=time_of_next_event:
    perform_next_event // like make step forward or step backward
    time_of_next_event = calculate_time_of_next_event(current_time, time_of_next_event)
  else:
    // here you can do microsleep if you want to save power or just do nothing and resume next step in iteration

As for timing - it is simple.

Sidereal rate is 15.043"/s

You need to calculate how many micro steps you have per revolution.

For example - let's say that your worm gear is 180:1 and that you are using standard stepper with 1.8 degrees step (200 steps per revolution) and you have 32 micro steps.

One full revolution will have in that case 180 * 200 * 32 = 1152000 steps

One full revolution, on the other hand has 360 * 60 * 60 = 1296000 arc seconds.

If you divide these two numbers - you will get arc seconds per step 1296000/1152000 = 1.125 arc seconds per step

Now we have 15.043"/s and 1.125"/step we can then divide those two to give us timing information

15.043 / 1.125 = 13.371555.... steps per second or

1.125 / 15.043 = ~0.0747856s per step or 74.7856 milliseconds per step or 74785.6 microseconds per step

In above pseudo code you would then put

time_of_next_event = time_of_this_event + 0.0747856s

  • Like 1
Link to comment
Share on other sites

As rotatux says, there is some arithmetic involved.

One specification of a stepper motor is the steps per revolution number. For example, it might be 200. So with your 32 microsteps you would have to send 32*200 pulses to turn the motor's axis 1 revolution. There will almost certainly be further gear reductions after the stepper motor. You will need to find out what they are and therefore how many revolutions os the stepper axis it takes to turn your RA drive one complete revolution.

 

Link to comment
Share on other sites

9 minutes ago, vlaiv said:

That code style is not the best for what you are trying to achieve.

Don't use pauses that you don't precisely control - use active loop for good precision (this will drain more power but you want as "real time" as possible).

Here is pseudo code for something like this:

while not done:
  if current_time>=time_of_next_event:
    perform_next_event // like make step forward or step backward
    time_of_next_event = calculate_time_of_next_event(current_time, time_of_next_event)
  else:
    // here you can do microsleep if you want to save power or just do nothing and resume next step in iteration

As for timing - it is simple.

Sidereal rate is 15.043"/s

You need to calculate how many micro steps you have per revolution.

For example - let's say that your worm gear is 180:1 and that you are using standard stepper with 1.8 degrees step (200 steps per revolution) and you have 32 micro steps.

One full revolution will have in that case 180 * 200 * 32 = 1152000 steps

One full revolution, on the other hand has 360 * 60 * 60 = 1296000 arc seconds.

If you divide these two numbers - you will get arc seconds per step 1296000/1152000 = 1.125 arc seconds per step

Now we have 15.043"/s and 1.125"/step we can then divide those two to give us timing information

15.043 / 1.125 = 13.371555.... steps per second or

1.125 / 15.043 = ~0.0747856s per step or 74.7856 milliseconds per step or 74785.6 microseconds per step

In above pseudo code you would then put

time_of_next_event = time_of_this_event + 0.0747856s

I am a complete beginner in python, to further learn on the style of coding you mentioned i would just need to research active loops? and thank you for explaining the calculation

Link to comment
Share on other sites

2 minutes ago, Juju said:

I am a complete beginner in python, to further learn on the style of coding you mentioned i would just need to research active loops? and thank you for explaining the calculation

I just named it like that, it's not official name for it.

If you want to learn more about that - look into real time systems. RPI is not real time system (linux in general), but can be made sort of real time with RT patch.

Another good source is algorithms for games and physics simulations - where you also need to keep accurate time.

With linux (or other non real time operating systems) there is no guarantee that next step will be performed at wanted time. There is always some sort of interrupt that can kick in and take away processing time needed for timing.

For this reason it is best to just iterate in a loop and observe timer and as soon as current time is equal or larger (we are on time, or we missed by a bit) then time for action - we need to perform action.

If you have series of delays - any sort of error will accumulate over time if say delay is not precise but instead finishes a bit late due to some interrupt.

With above loop - precision will only depend on your arithmetic to calculate time of next event and precision of system timer (you can get accumulated error only if your next step calculation is nor precise - like using 0.075 seconds instead of 0.0747856.... seconds)

It also has handy property - say unwanted delay happens (some IO related interrupt or something like that) - above algorithm will "fast forward" steps - it won't miss steps and start trailing behind. If there is need - it will perform 2 or 3 steps in fast succession to "catch up" with where it is supposed to be.

Link to comment
Share on other sites

I'm no expert coder, but as mentioned above using a pause as a delay is not recommended as it's not accurate.  Most board computers have interrupts which are timers based on the processors clock frequency and thus provide a means of precision timing.  The advantage is that the timing "loops" can continue to run whilst your code jumps to other routines, such as displaying menus or updating a display with the mounts position.

The EQMOD pre-requisites web page  has some useful information on the gear ratios and steps per complete revolution for most Skywatcher / Orion mounts that may help with the calculations used in your code.

Good luck with your project, and feel free to post up some pictures as it progresses

Link to comment
Share on other sites

23 hours ago, vlaiv said:

That code style is not the best for what you are trying to achieve.

+1 Vlaiv is mainly right and here is why : on embedded devices the duration-based sleep is approximate, in nominal situation it's usually a minimum but in case of hardware interrupts some devices can exit the sleep prematurely and return control to your program. So usually this kind of loop will drift from its target period, so much that it will be useless for sky tracking.

Rather than turning to power consuming "active" loops, you can get the best of both worlds by using time monitoring in the loop, which is called a fixed-rate passive loop (vs your fixed-delay passive loop).

The code to use is basically the one shown by Vlaiv, completed with :

  • as calculate_time_of_next_event function : time_of_next_event = time_of_next_event + target_rate_average_period (can even be a float)

  • use as microsleep : sleep(time_of_next_event - current_time())

This kind of loop will automatically compensate for both early and late sleep exits on the scale of several steps, to achieve a very precise average rate (depending on the precision of your embedded device's clock).

EDIT : crossed with other answers :) but nothing contradictory

Edited by rotatux
  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. By using this site, you agree to our Terms of Use.