一、浅谈标准DH(SDH)和改进DH(MDH)
1.机器人连杆与关节的标号
先标号,再建系。
连杆编号:基座为杆0,从基座往后依次定义为杆1,杆2,…,杆i_;_
关节编号:杆_i_离基座近的一端(近端)的关节为关节_i_,远的一端(远端)为关节i+1。
为便于理解,这里我把连杆的近端用绿色表示,远端用橙色表示,且远端驱动近端转动。大家只要记住一句话,连杆近端关节的标号和连杆标号是一致的。
图1连杆和关节标号
2.两种建系方法的区别
区别一: 连杆坐标系建立的位置不同。SDH方法将连杆_i_的坐标系固定在连杆的远端,MDH方法把连杆_i_的坐标系固定在连杆的近端。
(a)SDH (b)MDH
图2 建系方法的不同
区别二: 执行变换的的顺序不同。按照SDH方法变换时四个参数相乘的顺序依次为_d_—>θ—>a—>α,而MDH方法则按照_α_—>a—>θ—>d(正好与SDH相反)。
3.影响
对于树形结构或者闭链机构的机器人来说,按照SDH方法建立的连杆坐标系会产生歧义,因为SDH的建系原则是把连杆i的坐标系建立在连杆的远端,如图3(a)所示,这就导致连杆0上同时出现了两个坐标系。而MDH把连杆坐标系建立在每个连杆的近端,则不会坐标系重合的情况,如图3(b)所示,这就克服了SDH方法建系的缺点。
(a)SDH (b)MDH
图3 坐标系分配的不同
4.总结
1.SDH适合应用于开链结构的机器人**;**
2.当使用SDH表示树状或闭链结构的机器人时,会产生歧义;
3.MDH法对开链、树状、闭链结构的机器人都适用,推荐使用。
参考文献
[1] https://en.wikipedia.org/wiki/Denavit%E2%80%93Hartenberg_parameters.
[2] 霍伟.机器人动力学与控制[M].北京:高等教育出版社,2005:16-22.
二、标准DH建模与改进DH建模
standard_DH
根据DH表示法确定一个一般步骤为每个关节指定参考坐标系,然后确定如何实现任意两个相邻坐标系之间的变换,最后写出机器人的总变换矩阵。如图所示表示了三个顺序关节和两个连杆,每个关节都是可以转动和平移的。第一个关节指定为关节i-1,第二个关节指定为关节i,第三个关节指定为关节i+1。在这些关节前后可能还有其他关节,连杆也是如此表示,连杆i位于关节i与关节i+1之间。
1、standard_DH建立每个关节参考坐标系步骤:
(1)所有关节,无一例外用z轴表示。如果是关节是旋转的,z轴位于按右手定则选装的方向,如果关节是滑动的,z轴为沿实现运动的方向。在每一种情况下,关节i处的z轴(以及该关节的本地参考坐标系)的下标为i-1。
(2) 通常关节不一定平行或相交。因此,通常z轴是斜线,但是总有一条距离最短的公垂线,它正交于任意两条斜线。通常在公垂线方向上定义x轴。所以如果 ai 表示 zi−1 与 zi 之间的公垂线,则 xi的方向沿 ai。
(3)如果两个关节的z轴平行,选取与前一关节的公垂线共线的一条公垂线,可简化模型;如果两个相邻关节的z轴是相交的,那么它们之间没有公垂线,可选取两条z轴的叉积2方向作为x轴,可简化模型。
θ 角表示绕z轴旋转角,d 表示在z轴上两条相邻的公垂线之间的距离,a 表示每一条公垂线的长度,角 α 表示两个相邻的z轴之间的角度。
2、standard_DH建模步骤:
(1)绕 zi−1轴旋转θi,它使得 xi−1和 xi互相平行;
(2)沿 zi−1轴平移di距离,使得 xi−1和 xi共线;
(3)沿 xi轴平移 ai距离,使得 xi−1和 xi的原点重合;
(4)将 zi−1轴绕 xi旋转ai,使得 zi−1轴与 zi轴对准。
通过右乘四个运动的四个矩阵就可以得到变换矩阵 i − 1 T i ^{i-1}T_i i−1Ti:
i
−
1
T
i
=
R
o
t
(
z
i
−
1
,
θ
i
)
×
T
r
a
n
s
(
(
z
i
−
1
,
d
i
)
×
T
r
a
n
s
(
(
x
i
,
a
i
)
×
R
o
t
(
x
i
,
α
i
)
\begin{aligned}^{i-1}T_i=Rot(z_{i-1},\theta_i)\times Trans((z_{i-1},d_i)\times Trans((x_{i},a_i)\times Rot(x_{i},\alpha_i)\end{aligned}
i−1Ti=Rot(zi−1,θi)×Trans((zi−1,di)×Trans((xi,ai)×Rot(xi,αi)
modified_DH
1、modified_DH建立每个关节参考坐标系步骤:
(1)找出关节轴i和i+1之间的公垂线或关节轴i和i+1的交点,以关节轴i和i+1的交点或公垂线与关节轴i的交点作为连杆坐标系{i}的原点;
(2)规定 zi轴沿关节轴i的指向;
(3)规定 xi轴沿公垂线的指向,如果关节轴i和i+1相交,则规定 xi垂直于关节轴i和i+1所在平面。
(4)按照右手定则确定 yi轴
2、modified_DH建模步骤:
(1) ai = 沿 xi轴,从 zi移动到 zi+1的距离;
(2) αi = 绕 xi轴,从 zi旋转到 zi+1的角度;
(3) di = 沿 zi轴,从 xi−1移动到 xi的距离;
(4) θi = 沿 zi轴,从 xi−1旋转到 xi的角度;
变换矩阵可以写成:
i
−
1
T
i
=
R
X
(
α
i
−
1
)
×
D
X
(
a
i
−
1
)
×
R
Z
(
θ
i
)
×
D
Z
(
d
i
)
\begin{aligned}^{i-1}T_i=R_X(\alpha_{i-1})\times D_X(a_{i-1})\times R_Z(\theta_{i})\times D_Z(d_{i})\end{aligned}
i−1Ti=RX(αi−1)×DX(ai−1)×RZ(θi)×DZ(di)
SDH与MDH比较
(1)固连坐标系不同
SDH方法关节i上固连的是i-1坐标系,即坐标系建在连杆的输出端;MDH关节i上固连的是i坐标系,即坐标系建在连杆的输入端。
(2)坐标系变换顺序不同
SDH方法是ZX类变换:先绕着i-1坐标系的的Zi-1轴旋转和平移,再绕着坐标系i的Xi轴进行旋转和平移;MDH方法是XZ类变换:先绕着i坐标系的的Xi轴旋转和平移,再绕着坐标系i的Zi轴进行旋转和平移;
三、MATLAB-Robot(2):标准DH和改进DH的区别
1. 区别
2. WIKI百科对比图
3.0 MATLAB仿真对比
1. 标准DH
%标准DH
%Link(DH,option):
%DH = [THETA D A ALPHA SIGMA]//我的代码中此处定义theta的初始值无效
L1 = Link([0 0.2 1 pi/4 0],'standard');
L2 = Link([0 0.2 1 pi/4 0],'standard');
L3 = Link([0 0 0.5 pi/4 0],'standard');
robot = SerialLink([L1 L2 L3]); %建立连杆机器人
robot.plot([0 0 0]) %显示并赋三个关节变量theta值都为0//此处才可以初始theta
对比代码中的DH参数和图中画的坐标系,再次确定标准DH四个参数含义:
theta:绕Zi轴,从Xi旋转到Xi+1的角度
D:沿Zi轴,从Xi移动到Xi+1的距离
A:沿Xi轴,从Zi移动到Zi+1的距离
alpha:绕Xi+1轴,从Zi旋转到Zi+1的角度
2. 改进DH
%改进DH
%Link(DH,option):DH = [THETAi Di Ai-1 ALPHAi-1 SIGMA]
L1 = Link([0 0.2 1 pi/4 0],'modified');
L2 = Link([0 0.2 1 pi/4 0],'modified');
L3 = Link([0 0 0.5 pi/4 0],'modified');
robot = SerialLink([L1 L2 L3]); %建立连杆机器人
robot.plot([0 0 0]) %显示并赋三个关节变量theta的初始值都为0
MATLAB中定义改进DH连杆时一定要注意DH[theta d a alpha]中前两个参数下表为i,
即当前关节的DH值,后两个参数下表为i-1,及前一个关节的DH值参数值!
此时对比代码中的DH参数和图中画的坐标系,得到DH定义为:
theta:绕Zi轴,从Xi-1旋转到Xi的角度
D:沿Zi轴,从Xi-1移动到Xi的距离
A:沿Xi轴,从Zi移动到Zi+1的距离
alpha:绕Xi轴,从Zi旋转到Zi+1的角度
4.0 总结
标准型和改进型中A和alpha定义相同,都是相对于下一关节而言,不同的是theta和D在标准型中都是相对于下一关节,在改进型中是相对于上一关节。
定义DH表格时:
标准型的列标题为:
θ
i
d
i
a
i
α
i
\theta_i \, d_i \, a_i \, \alpha_i
θidiaiαi
改进型的列标题为:
θ
i
d
i
a
i
−
1
α
i
−
1
\theta_i \, d_i \, a_{i-1} \, \alpha_{i-1}
θidiai−1αi−1
所以一定要注意MATLAB机器人工具箱中连杆定义中DH的对应。
四、三自由度双足机器人腿建模
%% 改进DH
clear ; clc; close all;
% 机器人各连杆DH参数
d1 = 0.35;
d2 = 0;
d3 = 0;
d4 = 0;
a1 = 0.25;
a2 = 0;
a3 = 0.35;
a4 = 0.30;
alpha1 = pi/2;
alpha2 = -pi/2;
alpha3 = 0;
alpha4 = 0 ;
% 定义各个连杆,默认为转动关节
% theta d a alpha
L(1)=Link([ 0 d1 a1 alpha1] ,'modified'); L(1).qlim=[-pi,pi]; L(1).offset=-pi/2;
L(2)=Link([ 0 d2 a2 alpha2] ,'modified'); L(2).qlim=[-pi,pi]; L(2).offset= pi/6;
L(3)=Link([ 0 d3 a3 alpha3] ,'modified'); L(3).qlim=[-pi,pi]; L(3).offset=-pi/2;
% 把上述连杆整合
OLRob=SerialLink(L,'name','改进');
view(3);
% 定义机器人基坐标和工具坐标的变换
%OLRob.base = transl(0 ,-0.7 ,0);
OLRob.tool = transl(0.3 ,0 ,0);
view(77,22);
title('改进-有工具坐标');
OLRob.plot([0 0 0]);
OLRob.teach(); %机器人示教界面
%% 标准DH方法
figure(2)
view(3);
% 定义各个连杆,默认为转动关节
% theta d a alpha
f(1)=Link([ 0 0 0 pi/2] ); f(1).qlim=[-pi,pi]; f(1).offset=pi/2;
f(2)=Link([ 0 0 0.35 0] ); f(2).qlim=[-pi,pi]; f(2).offset=-5*pi/6;
f(3)=Link([ 0 0 0.3 0] ); f(3).qlim=[-pi,pi]; f(3).offset= -pi/2;
sta=SerialLink(f,'name','标准');
sta.tool = transl(0 ,0 ,0);
sta.plot([0 0 0]);
sta.teach(); %机器人示教界面
五、Matlab人形机器人建模与仿真
%% ------------------------------------------------------------------------
% Kinematic Model of Humanoid Robot
%
% Mike
%
% Date: 15/7/2022
%
%% ------------------------------- Init Env -------------------------------
clear;
close all;
clc;
pi = 3.14;
%% Link [THETA D A ALPHA SIGMA OFFSET]
%% OFFSET is a constant displacement between the user joint angle vector
% and the true kinematic solution.
%% SIGMA SIGMA=0 for a revolute and 1 for a prismatic joint.
%% ------------------------------ platform -------------------------------
platform=SerialLink([0 0 0 0],'name','platform','modified');
platform.base=[1 0 0 0;
0 1 0 0;
0 0 1 0;
0 0 0 1;];
platform.qlim = [0,0]; %Platform
%% ------------------------------ Right Arm -------------------------------
RA(1)=Link([0 0.49077 0 0 0 0],'modified');
RA(2)=Link([0 0.283 0 pi/2 0 -pi/2],'modified');
RA(3)=Link([0 0 0 pi/2 0 0],'modified');
RA(4)=Link([0 0 0.287 pi/2 0 0],'modified');
RA(5)=Link([0 0 0.287 pi/2 0 0],'modified');
Humanoid_Robot_RA=SerialLink(RA,'name','RIGHTARM');
RA(1).qlim = [0,0]; % Base for Upper body
RA(2).qlim = [deg2rad(-50),deg2rad(50)]; % Shoulder Pitch
RA(3).qlim = [0,deg2rad(60)]; % Shoulder Roll
RA(4).qlim = [0,deg2rad(140)]; % Elbow Pitch
RA(5).qlim = [0,0];%end-effector
%% -------------------------- Assembly Right Arm --------------------------
pRA=SerialLink([platform,Humanoid_Robot_RA],'name','RA');
view(3)
hold on
grid on
len_RA = length(RA);
init_RA = [];
for i=1:len_RA+1
init_RA(i) = 0;
end
pRA.plot([init_RA],'scale',.5)
% pRA.teach
hold on
%% ------------------------------ Left Arm --------------------------------
LA(1)=Link([0 0.49077 0 0 0 0],'modified');
LA(2)=Link([0 -0.283 0 pi/2 0 -pi/2],'modified');
LA(3)=Link([0 0 0 pi/2 0 0],'modified');
LA(4)=Link([0 0 0.287 pi/2 0 0],'modified');
LA(5)=Link([0 0 0.287 pi/2 0 0],'modified');
Humanoid_Robot_LA=SerialLink(LA,'name','LEFTARM');
LA(1).qlim = [0,0]; % Base for Upper body
LA(2).qlim = [deg2rad(-50),deg2rad(50)]; % Shoulder Pitch
LA(3).qlim = [0,deg2rad(60)]; % Shoulder Roll
LA(4).qlim = [0,deg2rad(140)]; % Elbow Pitch
LA(5).qlim = [0,0]; %end-effector
%% -------------------------- Assembly Left Arm ---------------------------
pLA=SerialLink([platform,Humanoid_Robot_LA],'name','LA');
view(3)
hold on
grid on
axis([-1, 1, -1, 1, -1, 1]*0.9)
len_LA = length(LA);
init_LA = [];
for i=1:len_LA+1
init_LA(i) = 0;
end
pLA.plot([init_LA],'scale',.5)
% pLA.teach
hold on
%% ------------------------------ Left Leg --------------------------------
LL(1)=Link([0 -0.120 0 pi/2 0 0 ],'modified');
LL(2)=Link([0 0 0 pi/2 0 -pi/2 ],'modified');
LL(3)=Link([0 0 0 -pi/2 0 -pi/2 ],'modified');
LL(4)=Link([0 0 0.42 pi/2 0 0 ],'modified');
LL(5)=Link([0 0 0.42 0 0 0],'modified');
LL(6)=Link([0 0.0772 0 -pi/2 0 0],'modified');
LL(7)=Link([0 0 0 0 0 0],'modified');
Humanoid_Robot_LL=SerialLink(LL,'name','LEFTLEG');
LL(1).qlim = [deg2rad(-50),deg2rad(50)]; %Hip Pitch -50 to 50
LL(2).qlim = [deg2rad(-50.15923567),deg2rad(50.15923567)]; %Hip yaw -50.15923567 to 50.15923567
LL(3).qlim = [deg2rad(-5),deg2rad(5)]; %Hip roll -5 to 5
LL(4).qlim = [0,deg2rad(90)]; %knee 0 to 90
LL(5).qlim = [deg2rad(-45),deg2rad(45)]; %Ankle Pitch -45 to 45
LL(6).qlim = [deg2rad(-20),deg2rad(20)]; %Ankle Roll -20 to 20
LL(7).qlim = [0,0]; %end-effector
%% -------------------------- Assembly Left Arm ---------------------------
pLL=SerialLink([platform,Humanoid_Robot_LL],'name','LL');
view(3)
hold on
grid on
axis([-1, 1, -1, 1, -1, 1]*0.9)
len_LL = length(LL);
init_LL = [];
for i=1:len_LL+1
init_LL(i) = 0;
end
pLL.plot([init_LL],'scale',.5)
pLL.teach
hold on
%% ------------------------------ Right Leg --------------------------------
RL(1)=Link([0 0.120 0 pi/2 0 0 ],'modified');
RL(2)=Link([0 0 0 pi/2 0 -pi/2 ],'modified');
RL(3)=Link([0 0 0 -pi/2 0 -pi/2 ],'modified');
RL(4)=Link([0 0 0.42 pi/2 0 0 ],'modified');
RL(5)=Link([0 0 0.42 0 0 0],'modified');
RL(6)=Link([0 0.0772 0 -pi/2 0 0],'modified');
RL(7)=Link([0 0 0 0 0 0],'modified');
Humanoid_Robot_RL=SerialLink(RL,'name','RightLEG');
RL(1).qlim = [deg2rad(-50),deg2rad(50)]; %Hip Pitch -50 to 50
RL(2).qlim = [deg2rad(-50.15923567),deg2rad(50.15923567)]; %Hip yaw -50.15923567 to 50.15923567
RL(3).qlim = [deg2rad(-5),deg2rad(5)]; %Hip roll -5 to 5
RL(4).qlim = [0,deg2rad(90)]; %knee 0 to 90
RL(5).qlim = [deg2rad(-45),deg2rad(45)]; %Ankle Pitch -45 to 45
RL(6).qlim = [deg2rad(-20),deg2rad(20)]; %Ankle Roll -20 to 20
RL(7).qlim = [0,0]; %end-effector
%% -------------------------- Assembly Right Arm --------------------------
pRL=SerialLink([platform,Humanoid_Robot_RL],'name','RL');
view(3)
hold on
grid on
axis([-1, 1, -1, 1, -1, 1]*0.9)
len_RL = length(RL);
init_RL = [];
for i=1:len_RL+1
init_RL(i) = 0;
end
pRL.plot([init_RL],'scale',.5)
pRL.teach
hold on
%% -------------------------------- Jacobian ------------------------------
Q = zeros(7,len_LL);
Jacob_LL = pLL.jacobe(Q);
Q = zeros(7,len_RL);
Jacob_RL = pRL.jacobe(Q);
Q = zeros(7,len_LA);
Jacob_LA = pLA.jacobe(Q);
Q = zeros(7,len_RA);
Jacob_RA = pRA.jacobe(Q);
%% -------------------------------------------------------------------------