Problem with Inverse Kinematics

Recently I built a 4-axis robot for palletization applications with its 4th axis being mechanically controlled, the rest of the 3 axis are controlled by nema17 motors using a drv8825 driver, a cnc shield v3 and a arduino mega, the problem I am encountering is that despite de correct calculations of the inverse kinematics, I am struggling with the precision of its movements, mainly translating on its y-axis and z-axis, I attached below some images, videos and the code

Robot Video


Forward Kinematics

Inverse Kinematics

#include <MobaTools.h>
#include <math.h>
#include <FastTrig.h>
#include <string.h>

#define pin 38
#define step1 2
#define step2 3
#define step3 4
#define dir1 5
#define dir2 6
#define dir3 7
#define l1 69.5
#define l2 100
#define l3 100

MoToStepper motor1(800, STEPDIR);
MoToStepper motor2(800, STEPDIR);
MoToStepper motor3(800, STEPDIR);
MoToServo servo1;

int step = 0, pos1 = 0, pos2 = 0, pos3 = 0, servo_pos = 0;
double xp, yp, zp, alpha1, beta1, gamma1;
bool Joint_Mode = true, First_Message = true, Repeat = false, Play = false;

double degreesToRadians(double degrees) {
  return degrees * M_PI / 180.0;
}
double radiansToDegrees(double radians) {
  return radians * 180.0 / M_PI;
}

void setup() {
  Serial.begin(9600);
  delay(1000);

  servo1.attach(pin);
  motor1.attach(step1, dir1);
  motor2.attach(step2, dir2);
  motor3.attach(step3, dir3);
  motor1.setMaxSpeed(200);
  motor2.setMaxSpeed(200);
  motor3.setMaxSpeed(200);
  motor1.setRampLen(10);
  motor2.setRampLen(10);
  motor3.setRampLen(10);

  Serial.println("Set the Step size using - and + keys.");
  Serial.println("Joint mode can be toggled on and off with the q ans w keys.");
  Serial.println("Effector is controlled using i, o and p keys");
  Serial.println("The robot is controlled with the numpad using all the numbers");

  alpha1 = motor1.currentPosition() * 0.1125;
  beta1 = motor2.currentPosition() * 0.1125;
  gamma1 = motor3.currentPosition() * 0.1125;

  xp = (l1 * sin(degreesToRadians(beta1)) + l3 * sin(degreesToRadians(90 - beta1 + gamma1))) * sin(degreesToRadians(alpha1));
  yp = (l1 * sin(degreesToRadians(beta1)) + l3 * sin(degreesToRadians(90 - beta1 + gamma1))) * cos(degreesToRadians(alpha1));
  zp = l1 + l2 * cos(degreesToRadians(beta1)) - l3 * cos(degreesToRadians(90 - beta1 + gamma1));

  zp = zp - l1;
}

void loop() {
  double alpha, beta, gamma;

  if (Serial.available() > 0) {

    char input = Serial.read();

      switch (input) {
      case '+':
        if (step >= 100) {
          Serial.println("Step can't be set higher than 100!");
          break;
        }
        Serial.print("Step set to: ");
        if (step < 5)
          ++step;
        else if (step == 5)
          step += 5;
        else
          step += 10;
        Serial.print(step);
        Serial.print('\n');
        break;
      case '-':
        if (step <= 0) {
          Serial.println("Step can't be set as a negative value!");
          break;
        }
        Serial.print("Step set to: ");
        if (step <= 5)
          --step;
        else if (step == 10)
          step -= 5;
        else
          step -= 10;
        Serial.print(step);
        Serial.print('\n');
        break;
      case '7':
        pos1 += step;
        xp += step;
        break;
      case '8':
        pos1 = 0;
        xp = 0;
        break;
      case '9':
        pos1 -= step;
        xp -= step;
        break;
      case '4':
        pos2 += step;
        yp += step;
        break;
      case '5':
        pos2 = 0;
        yp = 100;
        break;
      case '6':
        pos2 -= step;
        yp -= step;
        break;
      case '1':
        pos3 += step;
        zp += step;
        break;
      case '2':
        pos3 = 0;
        zp = 100;
        break;
      case '3':
        pos3 -= step;
        zp -= step;
        break;
      case '0':
        pos1 = 0;
        xp = 0;
        pos2 = 0;
        yp = 100;
        pos3 = 0;
        zp = 100;
        break;
      case 'i':
        if (servo_pos == 180) {
          Serial.println("Servo cant go above 180");
          break;
        }
        servo_pos += 10;
        break;
      case 'o':
        servo_pos = 0;
        break;
      case 'p':
        if (servo_pos == 0) {
          Serial.println("Servo cant go below 0");
          break;
        }
        servo_pos -= 10;
        break;
      case 'q':
        Joint_Mode = true;
        Serial.println("Joint mode on");
        break;
      case 'w':
        Joint_Mode = false;
        Serial.println("Joint mode off");
        break;
      }

    if (Joint_Mode == false) {

      alpha = xp;
      double l = sqrt((xp * xp) + (yp * yp));
      double a = sqrt((zp * zp) + (l * l));
      beta = degreesToRadians(90) - acos(((l2 * l2) + (a * a) - (l3 * l3)) / (2.0 * l2 * a)) - atan(zp / l);
      gamma = acos(((l2 * l2) + (l3 * l3) - (a * a)) / (2.0 * l2 * l3)) - degreesToRadians(90);    

      //alpha = radiansToDegrees(alpha);
      gamma = radiansToDegrees(gamma);
      beta = radiansToDegrees(beta);

      motor1.moveTo(alpha / 0.1125); 
      motor2.moveTo(beta / 0.1125);
      motor3.moveTo(gamma / 0.1125);

      Serial.print("alpha = ");
      Serial.println(alpha , 10);
      Serial.print("beta = ");
      Serial.println(beta , 10);
      Serial.print("gamma = ");
      Serial.println(gamma , 10);
      Serial.println();


    } else {

      motor1.moveTo(pos1 * 16);
      motor2.moveTo(pos2 * 16);
      motor3.moveTo(pos3 * 16);
    }

    servo1.write(servo_pos);
  }
}

I modified some bits of the code, taking into account the x and y axis not the z and y, but the problem still persists, the robot translates at a 45 degree angle on the z axis, I tried all the combinations with adding, substracting 45 and 90 degree angles, I dont know what to do, any suggestions?

Not sure this is your issue but on the MEGA double have only simple precision (ie they behave as float on 32 bits). This might create precision issues when you have lots of maths

So switching to a esp32 should be better?

you'll get double on 64 bits, so a better precision when doing maths.

You should get the MEGA to calculate a few elements for a path with your formula and check in excel on your PC how different that is

May be the error comes from the quality of your motors (Nema17 is just information about the size, does not say anything about the motor itself)

The motor model is: 17HS8401S

OK - how much power do you have for the cnc shield v3 and did you tune the max current for your drivers ?

Doesn't matter apparently atan2 takes the x value first witch is the cos and the y latter, which is sin that was the problem all along. Thank you for the suggestion!

atan2 takes (y, x) as arguments.
https://en.cppreference.com/w/cpp/numeric/math/atan2

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.