Close

Move it like a ballet dancer

A project log for Building the Thor robot

Building a 6-axis robot based on the Thor robot. When size DOES matter!

olaf-baeyensOlaf Baeyens 01/05/2017 at 20:122 Comments

I have been decades in software development and I have always learned that software has a primary function that you must develop towards to. All the rest is secondary.

So what is the main purpose of a robot?
What gives the robot purpose in life?

Only one answer: Moving like a ballet dancer!


The reason why I started with the Thor project is because it has 6 freedoms of movement (7 stepper motors). And getting all these motors work elegantly is very challenging. And you can't cheat with badly developed software.

The focus this weekend will be on the very core functionality is moving elegantly.


Thor has 7 stepper motors, the used Ultrasonics v1.0 board will have 7 independent Pololu DVR8825 drivers.

The only way where you get a robot moving like a ballet dancer is when you control those those stepper motor as one entity. They must act like one entity in perfect synchronicity.

7 motors can be encoded in 7 bits. So using one byte as one step to move them all simultaneously would be nice.

The elegant design of this all is that a mere loop like this would move them at the fastest rate and perfect predictable.

while(true) {
    OUT[0b0111 1111]  
    Sleep(10)
    OUT[0b0000 0000]  
    Sleep(10)
} 

This one loop has a very cool feature in that we can actually slow it down as we like even to a one step by step by pressing a button. As in this code next example

while(true) {
    OUT[0b0111 1111]  
    Sleep(10)
    OUT[0b0000 0000]  
    Sleep(Delay)
}         

In order to know what the steps should be, should come form a FIFO buffer that gets filled by some other source. It is not the job of this loop to waste time on calculating the next step. Its only purpose is to take the next step and execute it.

while(true) {
    byte NextByte=GetNextByte()
    OUT[NextByte]  
    Sleep(10)
    OUT[0b0000 0000]  
    Sleep(Delay)
}     

Since we deal with real physical situations, and robot can be dangerous we need a way to emergency stop or even decelerate.

while(IsSafeToRun) {
    byte NextByte=GetNextByte()
    OUT[NextByte]  
    Sleep(10)
    OUT[0b0000 0000]  
    var nextDelay=GenNextDelay(IsSafeToRun) 
    if (nextDelay<=0) break;
    Sleep(nextDelay)
}     

We did not take into account the direction the motors have to move, but between fixed 2 positions the motors don't change direction only the steps to move to the next step is important.

If you wonder how we control the speed of each motor independently the trick is in the FIFO queue that defines the steps for every bit: 0001 11100 0011 11001 1001 10010 1010 1111 111 110 101 01110 0011 1000 11000.... per motor not the change of delay.

How we build this FIFO queue and how to define the correct stepper motor bits that is a whole different topic.

The usage of this FIFO actually has a cool side effect, we have a perfect record how the motors moved and record and replay it.

To speed up calculations we even could use already per-calculated fragment that gets repeated.

In order to have agility response of the robot we could create a mechanism where the FIFO buffers can be cleared and filled with new bit series. Maybe even use a double FIFO that can be swapped to keep the data to the motors constant.

To be continued....


Discussions

Olaf Baeyens wrote 01/06/2017 at 03:27 point

I have other ideas, like a way to recalibrate itself during normal operation. Instead of first moving to a home position, I would have a calibration target in the field of operation instead (photo cell maybe). Because with this mechanism here it perfectly knows every step, it also should know when it should hit that calibration target during movement. 

If the target detector gets hit too soon or too later then it also knows how much the stepper motor already shifted. And can now correct during movement.

In order to have a fluid movement and no stalls because it needs time to process, we could optimize the FIFO feed generation easily with pre-calculate bit patterns.

We only need the initial acceleration pattern, the continuous movement pattern and the 

deceleration pattern. Between the acceleration and the deceleration you basically have a repetitive occurring pattern because that stepper motor works at a continuous speed. 

Everything collapses to noting more than bit-wise AND and OR operations that may be accompanied with lookup tables.

Less calculation means more processing power left:-)

  Are you sure? yes | no

dannyvandenheuvel wrote 01/06/2017 at 00:21 point

It makes sense, I am going to modify my 'moveMotor' routine to 'moveMotors', I'll prepare every motor positioning function with the needed data and do just one function afterwards 'moveMotors' where I set a byte of info from every positioning function. Then I have one timebase who generates pulses for all motors togetter at the same time. It will be a factor 7 faster.

It's not that big change because I wrote the rest of the positioning and that works just fine. It will be a speedup.

Thanks Olaf, you made me thinking!

  Are you sure? yes | no