双连杆机械臂运动学正向和逆向 Python 仿真
DH(Denavit-Hartenberg)方法
DH 方法描述了相邻坐标系之间的旋转与移动的关系,只需要找到 4 个参数,以基座坐标 $(x_0, y_0, z_0)$ 为参考,就可以依次求出相连接的连杆坐标系 $(x_i, y_i, z_i)$ 和末端执行器坐标系 $(x_n, y_n, z_n)$ 的远点与方向。

DH 方法要求相邻坐标系之间满足下面约定
- $x_i$ 和 $z_{i-1}$ 轴垂直;
- $x_i$ 和 $z_{i-1}$ 轴相交;
4 个 DH 参数分别为:
- 关节转角(joint angle) $\theta_{i}$
- 连杆扭角(link twist) $\alpha_{i}$
- 连杆距离(link offset) $d_{i}$
- 连杆长度(link length) $a_{i}$
正运动学公式

逆运动学公式
$$ \theta _ { 2 } = \tan ^ { - 1 } \left( \frac { \sin \theta _ { 2 } } { \cos \theta _ { 2 } } \right) = \tan ^ { - 1 } \left( \frac { \frac { - S _ { 1 } ^ { \theta } p _ { x } ^ { 0 } + C _ { 1 } ^ { \theta } p _ { y } ^ { 0 } } { l _ { 2 } } } { \frac { C _ { 1 } ^ { \theta } p _ { x } ^ { 0 } + S _ { 1 } ^ { \theta } p _ { y } ^ { 0 } - l _ { 1 } } { l _ { 2 } } } \right) = \tan ^ { - 1 } \left( \frac { - S _ { 1 } ^ { \theta } p _ { x } ^ { 0 } + C _ { 1 } ^ { \theta } p _ { y } ^ { 0 } } { C _ { 1 } ^ { \theta } p _ { x } ^ { 0 } + S _ { 1 } ^ { \theta } p _ { y } ^ { 0 } - l _ { 1 } } \right) $$
平面连杆正运动学仿真

import numpy as np
from numpy import sin, cos
import matplotlib.pyplot as plt
def dhmat(delta, d, a, alpha):
return np.array([[cos(delta), -sin(delta)*cos(alpha), sin(delta)*sin(alpha), a*cos(delta)],
[sin(delta), cos(delta)*cos(alpha), -cos(delta)*sin(alpha), a*sin(delta)],
[0, sin(alpha), cos(alpha), d],
[0, 0, 0, 1]])
def main():
theta_1 = np.radians(np.arange(20, 50, 1))
theta_2 = np.radians(np.arange(0, 30, 1))
d_1 = 0
d_2 = 0
l_1 = 0.1
l_2 = 0.2
alpha_1 = 0
alpha_2 = 0
P_0 = np.array([0, 0, 0, 1]).T
P_1 = np.array([0, 0, 0, 1]).T
P_2 = np.array([0, 0, 0, 1]).T
fig, ax = plt.subplots()
for i in range(len(theta_1)):
P1_0 = dhmat(theta_1[i], d_1, l_1, alpha_1).dot(P_1)
P2_0 = dhmat(theta_1[i], d_1, l_1, alpha_1).dot(dhmat(theta_2[i], d_2, l_2, alpha_2)).dot(P_2)
ax.plot([P_0[0], P1_0[0], P2_0[0]], [P_0[1], P1_0[1], P2_0[1]], '-bo')
ax.set_xlabel('x (m)')
ax.set_ylabel('y (m)')
fig.savefig('./twp_link_planar_robot.png')
plt.show()
if __name__ == '__main__':
main()
平面连杆逆运动学仿真

import numpy as np
from numpy import linalg as LA
from numpy import sin, cos, arctan
import matplotlib.pyplot as plt
def dhmat(delta, d, a, alpha):
return np.array([[cos(delta), -sin(delta)*cos(alpha), sin(delta)*sin(alpha), a*cos(delta)],
[sin(delta), cos(delta)*cos(alpha), -cos(delta)*sin(alpha), a*sin(delta)],
[0, sin(alpha), cos(alpha), d],
[0, 0, 0, 1]])
def main():
d_1 = 0
d_2 = 0
l_1 = 0.1
l_2 = 0.2
alpha_1 = 0
alpha_2 = 0
theta_1 = np.radians(np.arange(20, 50, 1))
theta_2 = np.zeros(len(theta_1))
st = 1
des_P2_0 = np.array([0.22, 0.2, 0, 1]).T
err_dist = np.zeros(len(theta_1))
P_0 = np.array([0, 0, 0, 1]).T
P_1 = np.array([0, 0, 0, 1]).T
P_2 = np.array([0, 0, 0, 1]).T
fig, ax = plt.subplots()
for i in range(len(theta_1)):
P1_0 = dhmat(theta_1[i], d_1, l_1, alpha_1).dot(P_1)
theta_2[i] = arctan((-sin(theta_1[i]) * des_P2_0[0] + cos(theta_1[i]) * des_P2_0[1])
/ (cos(theta_1[i]) * des_P2_0[0] + sin(theta_1[i]) * des_P2_0[1] - l_1))
P2_0 = dhmat(theta_1[i], d_1, l_1, alpha_1).dot(dhmat(theta_2[i], d_2, l_2, alpha_2)).dot(P_2)
err_dist[i] = LA.norm(des_P2_0 - P2_0)
ax.plot([P_0[0], P1_0[0], P2_0[0]], [P_0[1], P1_0[1], P2_0[1]], '-bo')
# 双连杆平面机械运动逆向运动学仿真
ax.plot(des_P2_0[0], des_P2_0[1], '-rx')
ax.set_xlabel('x (m)')
ax.set_ylabel('y (m)')
fig.savefig('./ik_two_link_planar_robot_0.png')
plt.show()
# theta_1, theta_2 角度变化轨迹
fig, ax = plt.subplots(nrows=2)
ax[0].plot(theta_1, 'k')
ax[0].set_ylabel('theta_1')
ax[1].plot(theta_2, 'k')
ax[1].set_ylabel('theta_2')
ax[1].set_xlabel('time (s)')
fig.savefig('./ik_two_link_planar_robot_1.png')
plt.show()
# 目标点与实际点之间的距离误差
fig, ax = plt.subplots()
ax.plot(err_dist, 'k')
ax.set_ylabel('distance error (m)')
ax.set_xlabel('time (s)')
fig.savefig('./ik_two_link_planar_robot_2.png')
plt.show()
if __name__ == '__main__':
main()