当前位置:   article > 正文

Python 机器人学 —— 机械臂工作空间分析_机械臂求作业空间代码是自己编的吗

机械臂求作业空间代码是自己编的吗

设关节1、关节5的高 (圆柱高) 分别为 h_{1}, h_{5},5个关节的转动角分别为 \theta_{1}, \theta_{2}, \theta_{3}, \theta_{4}, \theta_{5}

可得出DH参数表:

#\thetada\alpha
1 - 2\theta_{1}L_1-\frac{h_1}{2}090

2 - 3

90+\theta_{2}0L_{2}0

3 - 4

\theta_{3}0L_{3}0

4 - *

-90+\theta_{4}00-90

*- 5

\theta_{5}L_4-\frac{h_5}{2}00

5 - H

0\frac{h_5}{2}00

因为把各个关节的坐标系位置都定在了圆柱的中心,所以需要考虑关节1、关节5的高度。其次,关节1在全局坐标系中的位置应为:[0, 0, \frac{h_1}{2}]

根据5个关节的旋转角 + DH参数表,即可得到各个关节的坐标系,利用坐标系信息可完成图像的绘制

拟定义一个类,使其在输入5个关节的旋转角时,可完成绘制工作

环境设置

这次实验主要用到了 numpy、matplotlib,另外还用到了我自己编写的一个模块 coord.py

coord.py 中的 CoordSys_3d 用于描述齐次坐标系,并为图形的仿射变换提供了接口,源代码位于:https://blog.csdn.net/qq_55745968/article/details/129912954

  1. from functools import partial
  2. import matplotlib.pyplot as plt
  3. import numpy as np
  4. # coord.py 详见: https://blog.csdn.net/qq_55745968/article/details/129912954
  5. from coord import CoordSys_3d
  6. rot = CoordSys_3d.rot
  7. trans = CoordSys_3d.trans
  8. red = 'orangered'
  9. orange = 'orange'
  10. yellow = 'yellow'
  11. green = 'greenyellow'
  12. cyan = 'aqua'
  13. blue = 'deepskyblue'
  14. purple = 'mediumpurple'
  15. pink = 'violet'
  16. ROUND_EDGE = 30 # 圆等效多边形边数
  17. DTYPE = np.float16 # 矩阵使用的数据类型
  18. # 三个相同关节的尺寸参数
  19. COMMON_HEIGHT = 20
  20. COMMON_RADIUS_OUT = 10
  21. COMMON_RADIUS_IN = 3
  22. # 连杆外径
  23. CONNECT_ROD_RADIUS = COMMON_RADIUS_OUT / 3
  24. # 各个关节的尺寸参数
  25. JOINTS_SHAPE = [[COMMON_HEIGHT * 0.8, COMMON_RADIUS_IN * 2, COMMON_RADIUS_OUT, 'Wistia_r', 10],
  26. [COMMON_RADIUS_OUT, COMMON_RADIUS_IN, COMMON_HEIGHT, 'cool', 2],
  27. [COMMON_RADIUS_OUT, COMMON_RADIUS_IN, COMMON_HEIGHT, 'cool', 2],
  28. [COMMON_RADIUS_OUT, COMMON_RADIUS_IN, COMMON_HEIGHT, 'cool', 2],
  29. [None for _ in range(5)],
  30. [COMMON_RADIUS_OUT * 0.8, COMMON_RADIUS_IN, COMMON_HEIGHT * 0.8, 'cool', 10]]
  31. PACE_NUM = 20 # 转动范围分解步数
  32. THETA = np.linspace(-90, 90, PACE_NUM)
  33. def DH_PARAM(theta):
  34. ''' DH 参数表'''
  35. return [[theta[0], 56 - JOINTS_SHAPE[0][2] / 2, 0, 90], # 关节 1 -> 关节 2
  36. [90 + theta[1], 0, 43, 0], # 关节 2 -> 关节 3
  37. [theta[2], 0, 43, 0], # 关节 3 -> 关节 4
  38. [-90 + theta[3], 0, 0, -90], # 关节 4 -> 关节 5
  39. [theta[4], 45.5 - JOINTS_SHAPE[-1][2] / 2, 0, 0]]

绘图函数

  1. def figure3d():
  2. ''' 创建3d工作站'''
  3. figure = plt.subplot(projection='3d')
  4. tuple(getattr(figure, f'set_{i}label')(i) for i in 'xyz')
  5. return figure
  6. def cylinder(figure, state: CoordSys_3d,
  7. R: float, h: float, r: float = 0,
  8. axis: int = 2, smooth: int = 2):
  9. ''' 以 state 的 z 轴为主轴绘制圆柱
  10. figure: 3D 工作站对象
  11. state: CoordSys_3d 齐次坐标系
  12. R: 圆柱底面外径
  13. r: 圆柱底面内径
  14. h: 圆柱高度
  15. axis: 圆柱两底面圆心连线所在的轴索引
  16. smooth: 图像细致程度 (至少 2)'''
  17. # 当主轴为 x,y 时, 对坐标系进行变换
  18. if axis < 2:
  19. rotate = CoordSys_3d.rot(90, 'y' if axis == 0 else 'x')
  20. state = state.rela_tf(rotate)
  21. func = []
  22. theta = np.linspace(0, 2 * np.pi, ROUND_EDGE, dtype=DTYPE)
  23. z = np.linspace(-h / 2, h / 2, smooth, dtype=DTYPE)
  24. theta, z = np.meshgrid(theta, z)
  25. # 绘制圆柱内外曲面: 以 z 轴为主轴, 原点为中心
  26. x, y = np.cos(theta), np.sin(theta)
  27. func.append(partial(figure.plot_surface, *state.apply(x * R, y * R, z)))
  28. func.append(partial(figure.plot_surface, *state.apply(x * r, y * r, z)))
  29. phi = np.linspace(0, 2 * np.pi, ROUND_EDGE, dtype=DTYPE)
  30. radius = np.linspace(r, R, 2, dtype=DTYPE)
  31. phi, radius = np.meshgrid(phi, radius)
  32. # 绘制上下两底面: 法向量为 z 轴, 原点为中心, 在 z 轴上偏移得到两底面
  33. x, y = np.cos(phi) * radius, np.sin(phi) * radius
  34. z = np.zeros_like(x)
  35. for dz in (-h / 2, h / 2):
  36. s = state.rela_tf(CoordSys_3d.trans(dz=dz))
  37. func.append(partial(figure.plot_surface, *s.apply(x, y, z)))
  38. # 返回函数流的执行函数
  39. return lambda cmap: tuple(f(cmap=cmap) for f in func)

机械臂对象

每次工作状态改变时 (即机械臂运动),保存机械臂末端的点坐标,绘制成工作空间。在这个过程中,通过 numpy 判断新的工作点是否与旧的工作点重叠,并只保存不重叠的工作点

  1. class Robot_Arm:
  2. ''' 机械臂对象'''
  3. fig = figure3d()
  4. state = CoordSys_3d().rela_tf(trans(0, 0, JOINTS_SHAPE[0][2] / 2))
  5. def __init__(self):
  6. self.restart()
  7. def restart(self):
  8. ''' 重启工作空间记录器'''
  9. self.work_space = np.ones([0, 3])
  10. def refresh(self, theta=[0] * 5, cla=True, draw_body=True, keep_all=False):
  11. ''' 根据旋转角度刷新画面
  12. theta: 机械臂各个关节的旋转角
  13. cla: 刷新时清空画布
  14. draw_body: 绘制机械臂
  15. keep_all: 不筛除重复工作点'''
  16. joints = self.search(theta)
  17. if cla:
  18. plt.cla()
  19. self.fig.view_init(elev=5, azim=-90)
  20. # 设置 3D 工作站边界
  21. for set_ in self.fig.set_xlim3d, self.fig.set_ylim3d:
  22. set_(-150, 150)
  23. self.fig.set_zlim3d(-50, 200)
  24. plt.tight_layout()
  25. if draw_body:
  26. # 绘制关节及其坐标系
  27. for (R, r, h, cmap, smooth), joint in zip(JOINTS_SHAPE, joints):
  28. if R:
  29. cylinder(self.fig, joint, R=R, r=r, h=h, axis=2, smooth=smooth)(cmap)
  30. joint.plot_coord_sys(linewidth=2, length=30)
  31. # 绘制连杆
  32. for axis, length, rear in zip([1, 0, 0, None, 2], [56, 43, 43, 0, 45.5], joints[1:]):
  33. if length:
  34. move = -length / 2
  35. connect_rod = rear.rela_tf(trans(*map(lambda i: (axis == i) * move, range(3))))
  36. cylinder(self.fig, connect_rod, CONNECT_ROD_RADIUS, length, axis=axis)('winter_r')
  37. tail = joints[-1].rela_tf(trans(0, 0, JOINTS_SHAPE[-1][2] / 2)).position.reshape(1, -1)
  38. # 检测新工作点是否与旧工作点重叠
  39. if keep_all or np.all(((tail - self.work_space) ** 2).sum(axis=1) > 18):
  40. self.work_space = np.append(self.work_space, tail, axis=0)
  41. if cla:
  42. self.fig.scatter(*map(lambda i: self.work_space[:, i], range(3)), c=red, alpha=0.4, s=10)
  43. plt.pause(0.001)
  44. def search(self, theta):
  45. ''' 搜索关节的位置'''
  46. joints = [self.state]
  47. for rot_z, trans_z, trans_x, rot_x in DH_PARAM(theta):
  48. joints.append(joints[-1].rela_tf(rot(rot_z, 'z')
  49. ).rela_tf(trans(trans_x, 0, trans_z)
  50. ).rela_tf(rot(rot_x, 'x')))
  51. return joints

工作空间绘制

  1. def draw_work_space():
  2. ''' 绘制工作空间剖面轮廓'''
  3. out_points = []
  4. in_points = []
  5. # 绘制外圆上半部分
  6. for t in THETA:
  7. theta = [0 for _ in range(5)]
  8. theta[1] = t
  9. arm.refresh(theta)
  10. out_points.append(arm.work_space)
  11. # 绘制外圆下半部分
  12. for c in 90, -90:
  13. arm.restart()
  14. for t in THETA:
  15. arm.refresh([0, c, t, 0, 0])
  16. out_points.append(arm.work_space)
  17. # 绘制外圆中下部分
  18. for c in 90, -90:
  19. arm.restart()
  20. for t in THETA:
  21. arm.refresh([0, c, c, t, 0])
  22. out_points.append(arm.work_space)
  23. # 绘制内圆
  24. for c in 90, -90:
  25. arm.restart()
  26. for t in THETA:
  27. arm.refresh([0, t, c, c, 0])
  28. in_points.append(arm.work_space)
  29. # 绘制轨迹
  30. arm.restart()
  31. arm.refresh()
  32. for line in out_points + in_points:
  33. loc = list(map(lambda i: line[:, i], range(3)))
  34. arm.fig.plot(*loc, c=red, linewidth=2, alpha=0.7)
  35. arm.fig.scatter(*loc, c=red, alpha=0.4, s=10)
  36. def draw_point_clound():
  37. ''' 绘制工作空间点云'''
  38. for a in THETA:
  39. for b in THETA:
  40. for c in THETA:
  41. for d in THETA:
  42. arm.refresh([a, b, c, d, 0], draw_body=False, cla=False, keep_all=True)
  43. arm.refresh()
  44. arm = Robot_Arm()
  45. draw_work_space()
  46. plt.show()

最终结果

当5个关节的转动角均为0时,机械臂处在工作原点 (如下图所示)。对于这5个旋转关节而言,其z轴 (粉色轴) 均处在其右手规则旋转的方向上;其x轴 (橙色轴) 均处在其z轴与上一个关节的z轴的公垂线方向上,满足DH表示法的要求

当机械臂的5个转角分别为 [0, \theta_{2}, 0, 0, 0] 时,其工作点分布在  r=L_{2}+L_{3}+L_{4}=131.5 的圆内

 当机械臂的5个转角分别为 [0, \theta_{2}, -90, -90, 0] 时,可绘制出如下工作点。取 \theta_{2}=90 进行计算,半径 r=\sqrt{(L_{2}-L_{4})^{2}+L_{3}^{2}}=43.073

当机械臂的5个转角分别为 [0, -90, \theta_{3}, 0, 0] 时,其工作点分布在  r=L_{3}+L_{4}=88.5 的圆内

当机械臂的5个转角分别为 [0, -90, -90, \theta_{4}, 0] 时,其工作点分布在  r=L_{4}=45.5 的圆内

 将所有范围叠加,得到工作空间的边界如下:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/96825
推荐阅读
相关标签
  

闽ICP备14008679号