一.模型
二.MDH模型
使用修改DH模型怠益、改進DH模型與標(biāo)準(zhǔn)DH模型主要區(qū)別在于末端仪搔,改進DH并沒有建到工具坐標(biāo)系,這里將4蜻牢、5僻造、6軸的坐標(biāo)系都建在同一點,因此機器人連桿的參數(shù)只需要4個值a1孩饼、a2髓削、a3、d4
镀娶。a2立膛、d4
分別代表大臂與小臂的長度。要完全得到末端或者工具尖點的位姿還需要再右成一個工具坐標(biāo)系6Te
-
DH圖
DH參數(shù)表
θ | d | a | α |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 0 | 109 | -90 |
0 | 0 | 250.5 | 0 |
0 | 239.5 | 115.5 | -90 |
0 | 0 | 0 | 90 |
0 | 0 | 0 | -90 |
-
MATLAB仿真效果
MATLAB代碼
clear;
clc;
%建立機器人模型
% theta d a alpha offset
ML1=Link([0 0 0 0 0 ],'modified');
ML2=Link([0 0 109 -pi/2 0 ],'modified');
ML3=Link([0 0 250.5 0 0 ],'modified');
ML4=Link([0 239.5 115.5 -pi/2 0 ],'modified');
ML5=Link([0 0 0 pi/2 0 ],'modified');
ML6=Link([0 0 0 -pi/2 0 ],'modified');
modrobot=SerialLink([ML1 ML2 ML3 ML4 ML5 ML6],'name','6axis');
teach(modrobot)
- 一般6軸機器人初始狀態(tài)為大臂垂直與地面梯码,主要是通過給2軸一個初始角度
-90°
來實現(xiàn)宝泵,下圖第一個模型為各軸0°時的姿態(tài),第二圖模型為一般回零后機器人保持的姿態(tài)轩娶。
三.6軸機器人運動學(xué)正解
- 代碼
#include <Eigen/Dense>
/* 正解函數(shù)
* @joint 6軸各關(guān)節(jié)值結(jié)構(gòu)體儿奶,單位弧度
* @return 返回一個4*4的位姿矩陣
*/
Eigen::Matrix4d IFKinematics::fKine(const Joint & joint)
{
double j1 = joint.j1,
j2 = joint.j2,
j3 = joint.j3,
j4 = joint.j4,
j5 = joint.j5,
j6 = joint.j6;
double s1 = sin(j1), c1 = cos(j1),
s2 = sin(j2), c2 = cos(j2),
s3 = sin(j3), c3 = cos(j3),
s4 = sin(j4), c4 = cos(j4),
s5 = sin(j5), c5 = cos(j5),
s6 = sin(j6), c6 = cos(j6);
Eigen::Matrix4d T01, T12, T23, T34, T45, T56, T0_6;
T01 << c1, 0, -s1, a1 * c1,
s1, 0, c1, a1 * s1,
0, -1, 0, 0,
0, 0, 0, 1;
T12 << c2, -s2, 0, a2 * c2,
s2, c2, 0, a2 * s2,
0, 0, 1, 0,
0, 0, 0, 1;
T23 << c3, 0, -s3, a3 * c3,
s3, 0, c3, a3 * s3,
0, -1, 0, 0,
0, 0, 0, 1;
T34 << c4, 0, s4, 0,
s4, 0, -c4, 0,
0, 1, 0, d4,
0, 0, 0, 1;
T45 << c5, 0, -s5, 0,
s5, 0, c5, 0,
0, -1, 0, 0,
0, 0, 0, 1;
T56 << c6, -s6, 0, 0,
s6, c6, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1;
T0_6 = T01 * T12 * T23 * T34 * T45 * T56;
return T0_6;
}
四.6軸機器人運動學(xué)逆解
- 代碼
#include <Eigen/Dense>
#include <math.h>
#define EPSINON 1e-4 // 精度,小于它就認(rèn)為是0
#define ALLOW_MAX_DIFF (1 / 180 * pi) /* 距離上一軸角度的最大允許偏差 */
/* 各軸運動角度范圍 */
const double MAX_JOINTS[6] = { 90, 30, 90, 135, 135, 180};
const double MIN_JOINTS[6] = { -90, -135, -135, -135, -135, -180};
const double pi = 3.1415926
/* 角度值轉(zhuǎn)弧度值 */
inline double toRadian(double degree)
{
return degree * pi / 180;
}
/* 逆解函數(shù)
* @Joint 6軸各關(guān)節(jié)值結(jié)構(gòu)體鳄抒,單位弧度
* @lastJoints 上一次計算的關(guān)節(jié)值結(jié)構(gòu)體
* @pose 要解逆解的位姿矩陣
*/
Joint IFKinematics::sixJointsIK(Eigen::Matrix4d pose, Joint lastJoints)
{
double nx = pose(0, 0), ox = pose(0, 1), ax = pose(0, 2),
ny = pose(1, 0), oy = pose(1, 1), ay = pose(1, 2),
nz = pose(2, 0), oz = pose(2, 1), az = pose(2, 2),
px = pose(0 ,3), py = pose(1, 3), pz = pose(2, 3);
/** joint 1 */
double j1[2] = {0, 0};
j1[0] = -atan2(-py, px);
j1[1] = -atan2(-py, px) + pi;
/** joint 3 */
double j3[4] = {0, 0, 0, 0};
/* temp, x, y, z, A are temp valiables */
double temp[2], A[2];
double x = a2 * d4,
y = a2 * a3,
z = a2 * a2 + a3 * a3 + d4 * d4;
for (int i = 0; i < 4; i+=2) {
temp[i/2] = a1 - px * cos(j1[i/2]) - py * sin(j1[i/2]);
A[i/2] = z - pz * pz - temp[i/2] * temp[i/2];
j3[i] = -atan2(-y, x) + atan2(A[i/2],sqrt( (4 * x * x + 4 * y * y - A[i/2] * A[i/2])));
j3[i+1] = -atan2(-y, x) + atan2(A[i/2],-sqrt((4 * x * x + 4 * y * y - A[i/2] * A[i/2])));
}
/** joint 2 */
double j2[4] = {0, 0, 0, 0};
double m[4] = {0, 0, 0, 0};
double n[4] = {0, 0, 0, 0};
for (int i = 0; i < 4; ++i) {
m[i] = a2 + a3 * cos(j3[i]) - d4 * sin(j3[i]);
n[i] = a3 * sin(j3[i]) + d4 * cos(j3[i]);
j2[i] = atan2(-pz * m[i] + n[i] * temp[i/2],-pz * n[i] - m[i] * temp[i/2]);
}
/** joint 5 */
double j5[8] = {0, 0, 0, 0, 0, 0, 0, 0};
for (int i = 0; i < 4; ++i) {
m[i] = (ax * cos(j1[i / 2]) * cos(j2[i]) + ay * sin(j1[i / 2]) * cos(j2[i]) + az * -1 * sin(j2[i]));
n[i] = -(ax * cos(j1[i / 2]) * sin(j2[i]) + ay * sin(j1[i / 2]) * sin(j2[i]) - az * -1 * cos(j2[i]));
}
for (int i = 0; i < 8; ) {
x = n[i/2] * cos(j3[i/2]) - m[i/2] * sin(j3[i/2]);
y = sqrt(pow((ay * cos(j1[i / 4]) - ax * sin(j1[i / 4])),2) + pow((m[i/2] * cos(j3[i/2]) + n[i/2] * sin(j3[i/2])),2));
j5[i++] = atan2( y, x);
j5[i++] = atan2(-y, x);
}
/** joint 4 */
double j4[8] = {0, 0, 0, 0, 0, 0, 0, 0};
for (int i = 0; i < 8; ++i) {
if (fabs(sin(j5[i])) >= EPSINON) {
y = ((ay*cos(j1[i/4])-ax*sin(j1[i/4]))) / sin(j5[i]);
x= (-m[i/2]*cos(j3[i/2])-n[i/2]*sin(j3[i/2]))/(sin(j5[i]));
j4[i] = atan2(y , x);
} else {
j4[i] = 0;
}
}
/** joint 6 */
double j6[8] = {0, 0, 0, 0, 0, 0, 0 ,0};
for (int i = 0; i < 8; ++i) {
double u = nx * sin(j1[i/4]) - ny * cos(j1[i/4]);
double v = ox * sin(j1[i/4]) - oy * cos(j1[i/4]);
j6[i] = atan2(cos(j4[i]) * u - cos(j5[i]) * sin(j4[i]) * v, cos(j4[i]) * v + cos(j5[i]) * sin(j4[i]) * u);
}
/** 8組逆解結(jié)果 */
double ans[8][6];
for (int i = 0; i < 8; ++i) {
ans[i][0] = j1[i / 4];
ans[i][1] = j2[i / 2];
ans[i][2] = j3[i / 2];
ans[i][3] = j4[i];
ans[i][4] = j5[i];
ans[i][5] = j6[i];
}
/** 在選擇8組解中的最優(yōu)解闯捎,主要根據(jù)三個原則:連續(xù)原則、限位原則许溅、最短路徑原則 瓤鼻,連續(xù)原則暫時不使用 */
double last[6] = {lastJoints.j1, lastJoints.j2, lastJoints.j3, lastJoints.j4, lastJoints.j5, lastJoints.j5};
bool isOutLimit[8] = {false, false, false, false, false, false, false, false};
//bool isContinuity[8] = {true, true, true, true, true, true, true, true};
/** 排除掉超過運動范圍的解 */
for (int i = 0; i < 8; ++i) {
for (int j = 0; j < 6; ++j) {
if (ans[i][j] < toRadian(MIN_JOINTS[j]) || ans[i][j] > toRadian(MAX_JOINTS[j])) {
isOutLimit[i] = true;
break;
}
}
}
/** 運動連續(xù):排除掉離上一軸角度過大的解,需要設(shè)定最大允許偏差值 ALLOW_MAX_DIFF */
// for (int i = 0; i < 8; ++i) {
// for (int j = 0; j < 6; ++j) {
// if (abs(ans[i][j] - last[j]) > ALLOW_MAX_DIFF) {
// isContinuity[i] = false;
// break;
// }
// }
// }
/** 最短路徑原則:選出距離上一次姿態(tài)各軸所需最小的運動量贤重,給各軸分配一個權(quán)重茬祷,使盡量動小軸或姿態(tài)軸 */
double weight[6] = {0.2, 0.25, 0.2, 0.15, 0.12, 0.08};
//double weight[6] = {1, 1, 1, 1, 1, 1}; // 各軸相同權(quán)重
double sum[8] = {0, 0, 0, 0, 0, 0, 0, 0};
double minSum = INT_MAX;
int opt = -1;
for (int i = 0; i < 8; ++i) {
if (isOutLimit[i]) {
//printf("第%d解超限\n", i + 1);
continue;
}
for (int j = 0; j < 6; ++j) {
sum[i] += fabs(ans[i][j] - last[j]) * weight[j];
}
if (sum[i] < minSum) {
minSum = sum[i];
opt = i;
}
}
/** 最優(yōu)解 */
Joint res;
if (opt >= 0) {
/// 因為4軸和6軸平行,因此4軸轉(zhuǎn)-x°和6軸轉(zhuǎn)x°效果相同,因此如果遇到這種情況就將他們矯正
if (-ans[opt][3] - ans[opt][5] <= 1e-2) {
ans[opt][3] = 0;
ans[opt][5] = 0;
}
res = Joint(ans[opt][0], ans[opt][1], ans[opt][2],
ans[opt][3], ans[opt][4], ans[opt][5]);
} else {
printf("無合適逆解\n");
res = lastJoints;
}
return res;
}