After a month of work on the project, the Turnantenna’s driver is evolving to the definitive version.
During this month I worked hard in order to write a good driver for the stepper motors. If you’re looking for more details and want to understand the basic functioning of the Turnantenna system look at my first blog. My work could be find on GitHub.
Overview
From wikipedia:
a unipolar stepper motor has one winding with center tap per phase. Each section of windings is switched on for each direction of magnetic field.
To control the position of the rotor, we have to play with the stator’s windings, following a proper pattern in order to make the first move smoothly.
The driver does exactly this, it turns ON and OFF the pins of the logical board (the orange pi) following the correct pattern; doing so the coils of the engine are powered properly, and the rotor turns.
The older version of the driver was a good prototype, and I based my work on it. As a good starting point, I didn’t change the overall scheme, but I found some problems with my mentor and I worked to solve them. The most important issues were the following.
Problem #1 : A poor control of inputs
We used python 3 to write the code. The driver is a class, called Stepper, that has some methods. Calling those methods, the object controls the real engine. To do so we needed to use the python wiringpi library.
The old code was something like this:
import wiringpi as w class stepper(): def __init__(self, pin1, pin2, pin3, pin4): """Set up the GPIO pins and all the attributes""" w.some_method_to_initialize_the_board() w.some_method_to_configure_the_pins(pin#) self.initial_attributes = XYZ ... def stop(self): """make the engine stops""" w.some_method_to_turn_off_the_pin(pin1) w.some_method_to_turn_off_the_pin(pin2) ... def move(self,speed,rel=1,dir=1): """make the engine run somehow""" ... engine = Stepper(0,1,5,12) # Random pin numbers engine.move(250, 100)
To make sure that everything works, inputs should be controlled properly:
- Pins choice should be controlled, different coils can’t use the same pin.
- Values should be integers, not chars, lists, tuples or others
- At this time, I don’t know what happen if I run the engine with negative speed. Will it go back?
This kind of control was lacking in the older version. This is our first problem.
Problem #2 : Too many input variable
Look at the “move” method, could it be simplified? The answer is yes.
dir (direction) parameter is not so useful. It was intended to switch between “go ahead” and “go backward”. But if I want to go in the opposite direction, I could say I want to do a movement with a negative speed. In the newer version I adopted this scheme allowing negative values for the speed, and removing the dir parameter.
rel (relative) could be removed too. Its function is to keep in memory the last position of the engine, but it could be better done using an object’s attribute.
Problem #3 : Wrong speed management
Thanks to Leonardo (my mentor) who discovered this problem, I had to plot the following graph that demonstrates the presence of an error in the algorithm used to accelerate the engine. To understand the problem, you need to see that the driver was intended to make the engine accelerate with a constant rate. In other words, acceleration has to be constant [1], and speed has to increase linearly [2].
Note: in the real code I added an acceleration factor which allows to manage the magnitude of this acceleration. That doesn’t impact on the constant acceleration hypothesis.
The simplified version of the algorithm is something like this:
# inputs: num_step, final_speed, speed = 0 step_count = 0 while step_count <= num_step speed += 1 t = 1 / speed self.do_one_step() time.sleep(t) step_count += 1
Where:
- num_step stands for the total number of steps that should be done in the acceleration phase;
- final_speed is the goal, the speed wanted by the user;
- speed is the actual speed value. Here we are in acceleration phase, and that’s why we’re starting from speed=0
It’s clear that the algorithm increase the speed constantly (and linearly). So the condition [2] seems to be satisfied.
However, speed is not directly used. To manage the step frequency, the time delay is used instead: this simplify the algorithm. But there is a problem, and this graph demonstrate that hypothesis [1] is not verified:
The graph shows a first interval where speed increases (acceleration), another one where speed is constant, and the last one where the engines slows down (deceleration). We can say that deceleration is a negative acceleration, in fact the two external intervals are specular.
In the graph we can note an hyperbolic shape of the acceleration phases. That’s a proof of the non-constant acceleration. The problem is that we expected a different graph, and wanted to see a linear shape of the speed function. The driver works, but not as we like.
The bug here is in time managing, since it don’t decrease linearly. Time is defined as t=1/speed, and that’s an equilateral hyperbola’s equation. Right now I’m working on a solution to obtain a linear, simple equation.
Solving problems : Make it clear
As this code will be published, I added a lot of documentation like comments, docstrings, and more verbose and specific error handlers. I rebuilt the entire move method to make it more clear and readable, and to solve the problems listed before. I wrote it down simultaneously with the tests, and it’s still work in progress just because it has to be bullet proof.
Tests
I can say that tests was the core activity of this month. I wrote tests, done tests, tests the tests, delete some tests, write new tests, correct other tests, added new tests, and still testing.
All the improvements done to the original code, was born from tests. I’m a newbie with python, and testing opened my mind to a tons of things about coding. Now I’m still testing and deepen the code, and how to improve it.
Conclusion
Now this first period is almost finished, and it’s time to conclude the last things. After that I’ll starting develop the web interface to control the driver from remote.
I make no secret of the fact that this project is a very challenge for me. This is my first serious coding experience. Almost everything is new, but the Ninux community is really supporting me. A special thanks goes to Leonardo and Edoardo, and their patient.
See you at the next post!