java - 如何正确计算 Delta 机器人的直接运动学?

标签 java math computational-geometry robotics kinematics

我正在尝试为 Delta 机器人进行简单的模拟,我想使​​用正向运动学(直接运动学)通过传递 3 个角度来计算末端执行器在空间中的位置。

我从 Trossen Robotics Forum Delta Robot Tutorial 开始我能理解大部分数学,但不是全部。当我试图计算 3 个球体的相交点时,我迷失在正向运动学的最后一部分。我看过一般的球坐标,但无法计算出用于找到旋转方向(到 E(x,y,z))的两个角度。我看到他们正在求解球体方程,但这正是我迷路的地方。

delta robot direct kinematics

delta robot direct kinematics

delta robot direct kinematics

Delta 机器人是并联机器人(意味着底座和末端执行器(头部)始终保持平行)。底座和末端执行器是等边三角形,腿(通常)放置在三角形边的中间。

Delta 机器人底座的一侧标有f。 Delta 机器人执行器的一侧标记为 e。 腿的上半部分标记为 rf,下半部分标记为 re

原点(O) 位于底三角形的中心。 伺服电机位于底部三角形边(F1、F2、F3)的中间。 接头标记为 J1、J2、J3。小腿在 E1、E2、E3 点连接末端执行器 E为末端执行器三角形的中心。

我可以轻松计算点 F1、F2、F3 和 J1、J2、J3。 我遇到问题的是 E1、E2、E3。从解释来看, 我知道 J1 点向内平移了一点(末端执行器中位数的一半) 到 J1',它成为半径为 re(小腿长度)的球体的中心。 对所有关节执行此操作将导致 3 个球体在同一位置相交:E(x,y,z)。通过求解球体方程,我们得到 E(x,y,z)。

还有一个公式解释:

dk equation 1

dk equation 2 但这是我迷路的地方。我的数学能力不是很好。 有人可以用更简单的方式解释一下吗, 对于我们这些不太懂数学的人?

我还使用了提供的示例代码(如果您启用了 WebGL 浏览器)你可以运行here .单击并拖动以旋转场景。要控制三个角度,请使用 q/Q、w/W、e/E 来减小/增大角度。

完整代码 list :

//Rhino measurements in cm
final float e = 21;//end effector side
final float f = 60.33;//base side
final float rf = 67.5;//upper leg length - radius of upper sphere
final float re = 95;//lower leg length - redius of lower sphere (with offset will join in E(x,y,z))

final float sqrt3 = sqrt(3.0);
final float sin120 = sqrt3/2.0;   
final float cos120 = -0.5;        
final float tan60 = sqrt3;
final float sin30 = 0.5;
final float tan30 = 1/sqrt3;
final float a120 = TWO_PI/3;
final float a60 = TWO_PI/6;

//bounds
final float minX = -200;
final float maxX = 200;
final float minY = -200;
final float maxY = 200;
final float minZ = -200;
final float maxZ = -10;
final float maxT = 54;
final float minT = -21;

float xp = 0;
float yp = 0;
float zp =-45;
float t1 = 0;//theta
float t2 = 0;
float t3 = 0;

float prevX;
float prevY;
float prevZ;
float prevT1;
float prevT2;
float prevT3;

boolean validPosition;
//cheap arcball
PVector offset,cameraRotation = new PVector(),cameraTargetRotation = new PVector();

void setup() {
  size(900,600,P3D);
}

void draw() {
  background(192);
  pushMatrix();
  translate(width * .5,height * .5,300);
  //rotateY(map(mouseX,0,width,-PI,PI));

  if (mousePressed && (mouseX > 300)){
    cameraTargetRotation.x += -float(mouseY-pmouseY);
    cameraTargetRotation.y +=  float(mouseX-pmouseX);
  }
  rotateX(radians(cameraRotation.x -= (cameraRotation.x - cameraTargetRotation.x) * .35));
  rotateY(radians(cameraRotation.y -= (cameraRotation.y - cameraTargetRotation.y) * .35));

  stroke(0);
  et(f,color(255));
  drawPoint(new PVector(),2,color(255,0,255));
  float[] t = new float[]{t1,t2,t3};
  for(int i = 0 ; i < 3; i++){
    float a = HALF_PI+(radians(120)*i);
    float r1 = f / 1.25 * tan(radians(30));
    float r2 = e / 1.25 * tan(radians(30));
    PVector F = new PVector(cos(a) * r1,sin(a) * r1,0);
    PVector E = new PVector(cos(a) * r2,sin(a) * r2,0);
    E.add(xp,yp,zp);
    //J = F * rxMat
    PMatrix3D m = new PMatrix3D();
    m.translate(F.x,F.y,F.z);
    m.rotateZ(a);
    m.rotateY(radians(t[i]));
    m.translate(rf,0,0);

    PVector J = new PVector();
    m.mult(new PVector(),J);
    line(F.x,F.y,F.z,J.x,J.y,J.z);
    line(E.x,E.y,E.z,J.x,J.y,J.z);
    drawPoint(F,2,color(255,0,0));
    drawPoint(J,2,color(255,255,0));
    drawPoint(E,2,color(0,255,0));
    //println(dist(F.x,F.y,F.z,J.x,J.y,J.z)+"\t"+rf);
    println(dist(E.x,E.y,E.z,J.x,J.y,J.z)+"\t"+re);//length should not change
  }
  pushMatrix();
    translate(xp,yp,zp);
    drawPoint(new PVector(),2,color(0,255,255));
    et(e,color(255));
    popMatrix();
  popMatrix(); 
}
void drawPoint(PVector p,float s,color c){
  pushMatrix();
    translate(p.x,p.y,p.z);
    fill(c);
    box(s);
  popMatrix();
}
void et(float r,color c){//draw equilateral triangle, r is radius ( median), c is colour
  pushMatrix();
  rotateZ(-HALF_PI);
  fill(c);
  beginShape();
  for(int i = 0 ; i < 3; i++)
    vertex(cos(a120*i) * r,sin(a120*i) * r,0);
  endShape(CLOSE);
  popMatrix();
}
void keyPressed(){
  float amt = 3;
  if(key == 'q') t1 -= amt;
  if(key == 'Q') t1 += amt;
  if(key == 'w') t2 -= amt;
  if(key == 'W') t2 += amt;
  if(key == 'e') t3 -= amt;
  if(key == 'E') t3 += amt;
  t1 = constrain(t1,minT,maxT);
  t2 = constrain(t2,minT,maxT);
  t3 = constrain(t3,minT,maxT);
  dk();
}

void ik() {
  if (xp < minX) { xp = minX; }
  if (xp > maxX) { xp = maxX; }
  if (yp < minX) { yp = minX; }
  if (yp > maxX) { yp = maxX; }
  if (zp < minZ) { zp = minZ; }
  if (zp > maxZ) { zp = maxZ; }

  validPosition = true;
  //set the first angle
  float theta1 = rotateYZ(xp, yp, zp);
  if (theta1 != 999) {
    float theta2 = rotateYZ(xp*cos120 + yp*sin120, yp*cos120-xp*sin120, zp);  // rotate coords to +120 deg
    if (theta2 != 999) {
      float theta3 = rotateYZ(xp*cos120 - yp*sin120, yp*cos120+xp*sin120, zp);  // rotate coords to -120 deg
      if (theta3 != 999) {
        //we succeeded - point exists
        if (theta1 <= maxT && theta2 <= maxT && theta3 <= maxT && theta1 >= minT && theta2 >= minT && theta3 >= minT ) { //bounds check
          t1 = theta1;
          t2 = theta2;
          t3 = theta3;
        } else {
          validPosition = false;
        }

      } else {
        validPosition = false;
      }
    } else {
      validPosition = false;
    }
  } else {
    validPosition = false;
  }

  //uh oh, we failed, revert to our last known good positions
  if ( !validPosition ) {
    xp = prevX;
    yp = prevY;
    zp = prevZ;
  }

}

void dk() {
  validPosition = true;

  float t = (f-e)*tan30/2;
  float dtr = PI/(float)180.0;

  float theta1 = dtr*t1;
  float theta2 = dtr*t2;
  float theta3 = dtr*t3;

  float y1 = -(t + rf*cos(theta1));
  float z1 = -rf*sin(theta1);

  float y2 = (t + rf*cos(theta2))*sin30;
  float x2 = y2*tan60;
  float z2 = -rf*sin(theta2);

  float y3 = (t + rf*cos(theta3))*sin30;
  float x3 = -y3*tan60;
  float z3 = -rf*sin(theta3);

  float dnm = (y2-y1)*x3-(y3-y1)*x2;

  float w1 = y1*y1 + z1*z1;
  float w2 = x2*x2 + y2*y2 + z2*z2;
  float w3 = x3*x3 + y3*y3 + z3*z3;

  // x = (a1*z + b1)/dnm
  float a1 = (z2-z1)*(y3-y1)-(z3-z1)*(y2-y1);
  float b1 = -((w2-w1)*(y3-y1)-(w3-w1)*(y2-y1))/2.0;

  // y = (a2*z + b2)/dnm;
  float a2 = -(z2-z1)*x3+(z3-z1)*x2;
  float b2 = ((w2-w1)*x3 - (w3-w1)*x2)/2.0;

  // a*z^2 + b*z + c = 0
  float a = a1*a1 + a2*a2 + dnm*dnm;
  float b = 2*(a1*b1 + a2*(b2-y1*dnm) - z1*dnm*dnm);
  float c = (b2-y1*dnm)*(b2-y1*dnm) + b1*b1 + dnm*dnm*(z1*z1 - re*re);

  // discriminant
  float d = b*b - (float)4.0*a*c;
  if (d < 0) { validPosition = false; }

  zp = -(float)0.5*(b+sqrt(d))/a;
  xp = (a1*zp + b1)/dnm;
  yp = (a2*zp + b2)/dnm;

  if (xp >= minX && xp <= maxX&& yp >= minX && yp <= maxX && zp >= minZ & zp <= maxZ) {  //bounds check
  } else {
    validPosition = false;
  }

  if ( !validPosition ) {    
    xp = prevX;
    yp = prevY;
    zp = prevZ;
    t1 = prevT1;
    t2 = prevT2;
    t3 = prevT3;  
  }

}

void  storePrev() {
  prevX = xp;
  prevY = yp;
  prevZ = zp;
  prevT1 = t1;
  prevT2 = t2;
  prevT3 = t3;
}

float rotateYZ(float x0, float y0, float z0) {
  float y1 = -0.5 * 0.57735 * f; // f/2 * tg 30
  y0 -= 0.5 * 0.57735    * e;    // shift center to edge
  // z = a + b*y
  float a = (x0*x0 + y0*y0 + z0*z0 +rf*rf - re*re - y1*y1)/(2*z0);
  float b = (y1-y0)/z0;
  // discriminant
  float d = -(a+b*y1)*(a+b*y1)+rf*(b*b*rf+rf); 
  if (d < 0) return 999; // non-existing point
  float yj = (y1 - a*b - sqrt(d))/(b*b + 1); // choosing outer point
  float zj = a + b*yj;
  return 180.0*atan(-zj/(y1 - yj))/PI + ((yj>y1)?180.0:0.0);
} 

问题是,在可视化时,下部会改变长度(正如您在打印的 message0 中看到的那样,但它不应该这样,这进一步增加了我的困惑。

我在 Java/Processing 中使用了提供的 C 代码,但编程语言是最不重要的。

[由 spektre 编辑]

我只需要添加这张图片(出于教学原因)。

  • 一字排开的废话并不是掌握运动学能力的最好方法
  • 据我所知,带电机的底座位于上三角平面上的这张图片上
  • 工具在底部三角平面上

delta robot

最佳答案

我会这样做(图形解决方案的代数表示):

  1. 计算 F1、F2、F3;
  2. 求解系统

    // spheres from Ji to Ei ... parallelograms (use lower Z half sphere)
    (x1-J1.x)^2 + (y1-J1.y)^2 +(z1-J1.z)^2 = re^2 
    (x2-J2.x)^2 + (y2-J2.y)^2 +(z2-J2.z)^2 = re^2
    (x3-J3.x)^2 + (y3-J3.y)^2 +(z3-J3.z)^2 = re^2
    // Ei lies on the sphere
    E1=(x1,y1,z1)
    E2=(x2,y2,z2)
    E3=(x3,y3,z3)
    // Ei is parallel to Fi ... coordinate system must be adjusted 
    // so base triangles are parallel with XY-plane
    z1=z2
    z1=z3
    z2=z3
    // distance between any Ei Ej must be always q
    // else it is invalid position (kinematics get stuck or even damage)
    |E1-E2|=q
    |E1-E3|=q
    |E2-E3|=q
    // midpoint is just average of Ei
    E=(E1+E2+E3)/3
    
    • 其中 q 是关节距离 |Ei-E|这是不变的

[注释]

不要手动解决

  • 使用派生或其他东西来获得代数解
  • 并且只使用有效的解决方案
  • 它的二次系统,所以很可能会有更多的解决方案,所以你必须检查正确的一个

只是一个愚蠢的问题,你为什么不解决逆运动学问题

  • 这很可能是您需要的(如果您不只是进行可视化的话)
  • 在这种情况下也更简单一些

当你只使用直接运动学时也是如此

  • 我不完全相信你应该驱动所有 3 个关节
  • 最有可能只驾驶其中的 2 个
  • 并计算第 3 次,使运动学保持在有效位置

[编辑1]

在我看来有一种简化:

  1. Ti = 将 Ji 向 Z 轴平移 q(平行于 XY 平面)
  2. 现在如果您只需要从 Ti 中找到 3 个球体的交集

    • 这个点是E
  3. 所以 Ei 现在是 E 的简单翻译(与 Ji 翻译相反)

附言。我希望你知道当你拥有所有点时如何计算角度......

关于java - 如何正确计算 Delta 机器人的直接运动学?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18162880/

相关文章:

java - Spring Boot 错误 Controller 检索原始请求

algorithm - 两个整数之间加减的最短路径

r - 在R中计算封闭物体的曲率

algorithm - 快速隐藏相交矩形

字符串数组中的 JavaScript 算术运算符

python - 更高维度的凸包,找到多面体的顶点

java - SQL - 根据不同的属性获取任何行,不重复

java - 增量或序列代替表生成 JPA

java - java中构建失败

javascript - 使 SVG 变换矩阵围绕其中心旋转