Speed Calculation of 6-DOF Stewart-Platform

Hey there! So I am working on a project to build a 6-DOF Motion Platform. I went for the Stewart-Platform "method". I've build it and have all the parts together as far as possible in the testing phase. I am using linear actuators https://shop.boxdrive.ch/p/DLA-24-5-A-300-POT-IP65.aspx Datasheets and more can be found on their website. I am using an Arduino Mega 2560 with 6 IBT2. The calculation of coordinates works and is correct, and the actuators can be reliably controlled and they work fine. However, I am stuck at the speed calculation. Obviously I want all the actuators to reach their target positions simultaneously, so the platform does not break, because of invalid positions during the "driving-phase". I would really appreciate it, if you guys could give me some tips! Thanks in advance! My code:

#include <math.h>
float RbMatrix[3][6];

float sMatrix[3][6];
float maxlist[6];
//float ActuatorBaseLength = 450;

float bMatrix[3][6] = {
  {-70,-330,-285,285,330,70},
  {-350,125,230,230,125,-350},
  {-88,-88,-88,-88,-88,-88}
};

float aMatrix[3][6] = {
  {-285,-330,-70,70,330,285},
  {-230,-125,350,350,-125,-230},
  {88,88,88,88,88,88}
};



float newspeed[6];
float speed[6];
float s[6];
float news[6];
float mf[6];
float turnmode[6];
int rpwm[6] = { 2,13,4,7,9,11 };
int lpwm[6] = { 3,12,5,6,8,10 };
int m[6] = { A5,A4,A3,A2,A1,A0 };
int abweichungunten[6] = { 25,18,25,21,21,23 };
int abweichungoben[6] = { 938,940,946,937,941,938 };

float current;
int maxI;
float speedmax;
float max = 0;
float longestway;
float actfullin = 0;
float actfullout = 305;
float adeg = 0; //z
float bdeg = 0; //y
float cdeg = 0; //x
float a;
float b;
float c;
float RMatrix[3][3];
float time[6];

float pMatrix[3][1] = {
  {0},
  {0},
  {600} //11.3 sek für rein mit voll geschwindigkeit
};



void setup()
{
    for (int i = 0; i < 6; i++) {
        pinMode(rpwm[i], OUTPUT);
        pinMode(lpwm[i], OUTPUT);
    }

    Serial.begin(9600);
}

// Add the main program code into the continuous loop() function
void loop()
{
    RMatrixCalc();
    ReadFeedback();
    LengthCalc();
    anpassung();
    RequestedPosMapping();
    SpeedCalc();
    Execution();
    Serial.print("Feedbacks: ");
    for (int i = 0; i < 6; i++) {
        Serial.print(mf[i]);
        Serial.print(" , ");
    }
    Serial.println();

    //Serial.print(s1 - 450);
    //Serial.print(" , m1f:");
    //Serial.print(m1f);
    //Serial.print(" , ");
    //Serial.println(news1);

}

void RMatrixCalc() {
    a = adeg * 1000 / 57296;
    b = bdeg * 1000 / 57296;
    c = cdeg * 1000 / 57296;

    RMatrix[0][0] = (cos(a) * cos(b));
    RMatrix[0][1] = (cos(a) * cos(b) * sin(c) - sin(a) * cos(c));
    RMatrix[0][2] = (cos(a) * sin(b) * cos(c) + sin(a) * sin(c));
    RMatrix[1][0] = (sin(a) * cos(b));
    RMatrix[1][1] = (sin(a) * sin(b) * sin(c) + cos(a) * cos(c));
    RMatrix[1][2] = (sin(a) * sin(b) * cos(c) - cos(a) * sin(c));
    RMatrix[2][0] = (-sin(b));
    RMatrix[2][1] = (cos(b) * sin(c));
    RMatrix[2][2] = (cos(b) * cos(c));

}


void LengthCalc() {

    for (int j = 0; j < 6; j++) { //amount of motors (1 column is 1 motor)

        for (int i = 0; i < 3; i++)//rows
        {
            RbMatrix[i][j] = (RMatrix[i][0] * bMatrix[0][j]) + (RMatrix[i][1] * bMatrix[1][j]) + (RMatrix[i][2] * bMatrix[2][j]);
        }

        //p+Rbi-ai
        for (int i = 0; i < 3; i++) //rows, columns not need because always 1, resp. 0
        {
            sMatrix[i][j] = pMatrix[i][0] + RbMatrix[i][j] - aMatrix[i][j];
        }

        //Calculation of absolute value of vector
        s[j] = sqrt(pow(sMatrix[0][j], 2) + pow(sMatrix[1][j], 2) + pow(sMatrix[2][j], 2));
    }
}

void RequestedPosMapping() {
    //map the required actuator lengths whcih are needed in order to reach the requested position onto a compatible scale to compare with current locations
    for (int i = 0; i < 6; i++) {
        news[i] = map(s[i], 450 + actfullin, 450 + actfullout, abweichungunten[i], abweichungoben[i]); //25 and 938 are the real limits of motors when they stop/hit the stop switches
    }
}

void ReadFeedback() {
    for (int i = 0; i < 6; i++) {
        mf[i] = analogRead(m[i]);
    }
}

void anpassung() {
    s[0] = s[0] - 2;
    //s[1] = s[1] + ;
    s[2] = s[2] - 5;
    s[3] = s[3] - 3;
    s[4] = s[4] - 2;
    //s[5] = s[5] + ;
}

void Execution() {
    for (int i = 0; i < 6; i++) {

        if (news[i] - mf[i] > 0)
        {
            //if requested position is further back than actual position, set turnmode to backwards
            turnmode[i] = 0;
        }
        else if ((news[i] - mf[i] < 30) && (news[i] - mf[i] > -30)) {
            turnmode[i] = 2;
        }
        else
        {
            //if requested position is further out than actual position, set turnmode to forwards
            turnmode[i] = 1;
        }

        if (turnmode[i] == 1) {
            analogWrite(rpwm[i], 0);
            analogWrite(lpwm[i], newspeed[i]);
        }
        else if (turnmode[i] == 2) {
            analogWrite(rpwm[i], 0);
            analogWrite(lpwm[i], 0);
        }

        else {
            analogWrite(rpwm[i], newspeed[i]);
            analogWrite(lpwm[i], 0);
        }
    }
}

void SpeedCalc() {
    //find out which one is the biggest distance to drive

    max = 0;

    for (int i = 0; i < 6; i++) {
        maxlist[i] = abs(news[i] - mf[i]); //maxlist = absoluti distanz
    }

    for (int i = 0; i < 6; i++) {
        if (maxlist[i] > max) {
            max = maxlist[i];
            maxI = i;
        }
    }
    Serial.print(" MaxI: ");
    Serial.print(maxI);
    Serial.print(" Speeds: ");
    for (int i = 0; i < 6; i++) {
        newspeed[i] = map(maxlist[i], 0, maxlist[maxI], 55, 225);
        Serial.print(newspeed[i]);
        Serial.print(" , ");
    }

    Serial.println();
}

Welcome to the forum, and thanks for using code tags on your first post! A rare event.

What have you tried, and what is the actual problem?

Hey, I really appreciate your answer!

Well, I want all of the 6 actuators to reach the target position simultaneously. So i tried to map the distance (0-1023) onto the 8-bit "speed scale" (0-255) respectively 30-255 (55-225 in the program above, but that was testing => didnt work either) as speeds below 30 are too low for the actuator to move. Because of my observations of the actuators not reaching the target position simultaneously i thought, that the speed scale might not be linear. If that is the case, I am not really sure how to continue. in void SpeedCalc() you can see my code, how I calculated the speed. news is the target actuator stroke in motor units (0-1023), mf is the motorfeedback of the current stroke (0-1023), maxlist is the absolute distance/difference of the requested stroke and the current stroke. Then it finds the highest absolute distance and sets its speed to max speed (255 (in this case 225)). According to that, it maps all the other distances from 0 to the highest absolute distance onto - in this case - 55 to 225. Additionally, I can observe that there are fluctuations/vibrations especially at the moment when multiple motors have the same distance or when the highest distance is rather small. Hope that helps you to understand my problem, if you need anything more, let me know. And sorry for the non-english parts in the code, I am not native :slight_smile:

That sounds like a power supply issue, where the power supply can not deliver the required current.

However your basic method of setting the speed for each motor, presumably by applying a PWM signal to them is unlikely to work because it is not a very accurate way of controlling the speed. When a motor is given a PWM signal the motor speed changes but this speed change is not the same, it depends on the load on each motor.

This problem is like having an X-Y plotter reach the same end point at the same time. You can do this by making each motor into a servo system (not a hobby servo) but that requires positional feedback from each motor. This is not a simple thing to do and I suspect this is way too complex for you to implement and for six motors is probably more than an Arduino can do.

Hey there, thank you so much for the answer!

Could you elaborate on the servo system? My linear Actuators actually have positional feedback. The way I understood it is, that I would need a servo motor, because they have the implemented "error-correction-system"/PID-control, is this, what you mean? I know, that it is rather difficult to implement a "good" PID-control-system in actuators like mine and I was trying to somehow get around that, is there no other way of fixing it? Like, it doesnt have to be perfect at all, it would just need to be just about at the same time. but actually priority #1 is to get rid of the vibration, I am gonna look into the power supply.

The mapping between PWM and speed will depend on a lot of factors. I don't think you can pre-calculate a PWM value for each actuator that will cause them to all reach the destination at the same time.

What I would do is run a PID controller for each actuator and dynamically calculate the PWM. I would break the move into steps and change the 'setpoint' for each actuator to match where is should be at that point in the move. Each PID would then adjust the PWM to try and follow the setpoints.

Basically yes.
For a good introduction to servo systems see this PDF file servo basics for the layman

So how many pulses per revolution do you get with them?
You can try and implement a PID system with them, try with just one first.

Alright thank you very much, i was thinking about this, I am gonna look into that! Thanks!

Hey, well they are not servos, I am not sure how many pulses per revolution they have/need. but I can check their current position which is given by built-in potentiometers ranging from 0-1023. Which should theoretically make it possible for me to implement a full PID controller. However I am a bit scared to implement that haha, as I've heard and seen that it can be tricky math-wise and I am only 16 years old and not the best in mathematical problems. But I am gonna look into PID a bit more anywasy, thanks!

So that means that they do not have a rotary shaft encoder but a pot. So is that a pot that outputs that same reading every 360 degrees, or does it simply cover the range of the linear actuator.

OK I didn't do servo mechanisms until the first year of my under graduate course and they are maths heavy. Still take a look at the link I added to reply #7.

It covers the range of the linear actuator as a whole.

Aight I will, thanks!

IF you know the distance to travel, then you will be able to compute the actual speed required for each actuator. I don't see anywhere you identify the distance for each axis to travel.
Paul

Hey! So the 's' array in the code contains the length of the whole axis, so also the part of the actuator which cant move. in RequestedPosMapping it maps the movable part onto a scale of 0-1023, which is the "measurement" of the potentiometers in the actuators for positional feedback. That what you said is what I tried to implement in the beginning. However, I do not know the "actual" speeds of the actuators, i only know the range (in this case 0-255), but i dont know the actual speed in m/s. And also, the actual speed differs from actuator to actuator, so not all 6 actuators have the same "speed scale".

You need to build an array of information that contains the position, a speed number for each actuator and the distance to the end point. The actual "speed" is not important as long as all axes are related to that number. The fastest could be 1.00 and the slowest could be 3.05, but that number will tell you how fast each actuator must move to reach a common location at the same time. MAny moving parts!
Paul

Okay, I am still trying to really "deeply" understand what you mean exactly. So can I just assume that the speed scale is linear, or doesnt it matter?

okay so I have the current position in "motor units" and the distance to the target position in motor units, a speed number, which i assign the maximum (255, in the code 225) to the actuator with the longest distance to drive, and all the other actuators get a speed number from 50-225 proportiionally according to the distance of the actuator with the max speed and longest distance to drive. Is that correct? Or like, is that what you mean?

Also: I found the following as PID control: (I know it is not "all", if you need more information let me know)

inline void move(uint8_t motor)
{
    // Compute the error value
    pos_diff = pos[motor] - desired_pos[motor];

    // Compute error-dependent PID variables
    if (abs(pos_diff) <= POS_THRESHOLD[motor])
    {
        prop_diff = 0;
        total_diff[motor] = 0;
    }
    else
    {
        prop_diff = pos_diff;
        total_diff[motor] += pos_diff;
    }
    if (previous_diff[motor] != pos_diff)
    {
        previous_inst[motor] = current_inst[motor];
        current_inst[motor] = 1;
    }

    // Compute PID gain values
    p_corr = P_COEFF[motor] * prop_diff;
    i_corr = I_COEFF[motor] * total_diff[motor];
    d_corr = D_COEFF[motor] * ((float) pos_diff - previous_diff[motor]) / (current_inst[motor] + previous_inst[motor]);

    // Compute the correction value and set the direction and PWM for the actuator
    corr = p_corr + i_corr + d_corr;
    dir[motor] = (corr > 0) ? RETRACT : EXTEND;  // direction based on the sign of the error
    pwm[motor] = constrain(abs(corr), MIN_PWM, MAX_PWM);  // bound the correction by the PWM limits

    // Move the actuator with the new direction and PWM values
    digitalWrite(DIR_PINS[motor], dir[motor]);
    analogWrite(PWM_PINS[motor], pwm[motor]);

is that correct/could that work? Is that what you guys meant?

Yes, that is what I mean. Do you have a function that identifies the starting location for each actuator when your program begins?

What do you mean with "identifies starting location"? During ReadFeedbacks() it reads the Feedback potentiometer inputs... So yes, it always "knows" where the actuator is

If you do not know the starting point, you do not know the current location for each actuator in relation to the distance to move. Are your pots accurate enough and reliable enough to give you this information?
Paul