机器狗移动原理

控制舵机转动实现机器狗足端的移动

重点:根据足端坐标获取两个舵机的转动角度

狗腿反解

image.png

在当前的狗腿子中,我们其实是知道每个关节的长度的,现在我们其实要做的事情就是已知足端左表(x,y) ,
求解机械狗上面的舵机需要转动的角度. 这个该如何来进行求解呢?
为了便于大家学习, 我们先将机械狗模型简化为平面几何,如下图所示:
image.png
我们需要求出上图左表的夹角alpha, 右边的夹角为beta

反解代码

  1. def __init__(self):
  2. super().__init__()
  3. # 设置标题
  4. self.setWindowTitle('机器狗绘制')
  5. # 设置窗口大小
  6. self.setFixedSize(640, 480)
  7. # -------------- 中心点坐标 -------------- #
  8. self.center_x = 320
  9. self.center_y = 100
  10. # 大小的缩放系数
  11. self.ratio = 4
  12. # -------------- 已知狗腿数据 -------------- #
  13. self.AF = 12.5 * self.ratio
  14. self.AB = 21.5 * self.ratio
  15. self.EF = 21.5 * self.ratio
  16. self.CE = 21.5 * self.ratio
  17. self.BC = 34.19 * self.ratio
  18. self.CD = 34.95 * self.ratio
  19. # BC和CD的夹角
  20. self.bcd_angle = math.radians(120)
  21. self.half_AF = self.AF / 2
  22. # -------------- 各个端点坐标 -------------- #
  23. self.AX = -self.half_AF
  24. self.AY = 0
  25. self.FX = self.half_AF
  26. self.FY = 0
  27. self.BX = 0
  28. self.BY = 0
  29. self.CX = 0
  30. self.CY = 0
  31. self.EX = 0
  32. self.EY = 0
  33. # -------------- 足端坐标 -------------- #
  34. self.DX = 0
  35. self.DY = 60 * self.ratio
  36. # -------------- 按下的坐标 -------------- #
  37. self.press_x = self.DX
  38. self.press_y = self.DY
  39. # -------------- alpha和beta -------------- #
  40. self.alpha = 0
  41. self.beta = 0
  42. # -------------- 开始逆解 -------------- #
  43. self.results = self.inverse(self.DX, self.DY)
  44. def inverse(self, fx, fy):
  45. # AD长度
  46. AD2 = (self.half_AF + fx) ** 2 + fy ** 2
  47. AD = math.sqrt(AD2)
  48. # DAF夹角
  49. angle_DAF = math.asin(fy / AD)
  50. # BD长度
  51. BD2 = self.BC ** 2 + self.CD ** 2 - 2 * self.BC * self.CD * math.cos(self.bcd_angle)
  52. BD = math.sqrt(BD2)
  53. # BAD夹角
  54. angle_BAD = math.acos((self.AB ** 2 + AD2 - BD2) / (2 * self.AB * AD))
  55. # AB对应的alpha夹角
  56. alpha = math.pi - angle_BAD - angle_DAF
  57. angle_DBA = math.acos((self.AB ** 2 + BD2 - AD2) / (2 * self.AB * BD))
  58. angle_DBC = math.acos((self.BC ** 2 + BD2 - self.CD ** 2) / (2 * self.BC * BD))
  59. angle_ABC = angle_DBA - angle_DBC
  60. # AC长度
  61. AC2 = self.AB ** 2 + self.BC ** 2 - 2 * self.AB * self.BC * math.cos(angle_ABC)
  62. AC = math.sqrt(AC2)
  63. angle_BAC = math.acos((self.AB ** 2 + AC2 - self.BC ** 2) / (2 * self.AB * AC))
  64. angle_CAF = math.pi - alpha - angle_BAC
  65. CF2 = self.AF ** 2 + AC2 - 2 * self.AF * AC * math.cos(angle_CAF)
  66. CF = math.sqrt(CF2)
  67. angle_AFC = math.acos((self.AF ** 2 + CF2 - AC2) / (2 * self.AF * CF))
  68. angle_CFE = math.acos((CF2 + self.EF ** 2 - self.CE ** 2) / (2 * CF * self.EF))
  69. beta = math.pi - angle_AFC - angle_CFE
  70. # 计算各个点的坐标
  71. self.BX = -(self.AB * math.cos(alpha) + self.half_AF)
  72. self.BY = self.AB * math.sin(alpha)
  73. self.CX = math.cos(angle_CAF) * AC - self.half_AF
  74. self.CY = AC * math.sin(angle_CAF)
  75. self.DX = fx
  76. self.DY = fy
  77. self.EX = self.half_AF + self.EF * math.cos(beta)
  78. self.EY = self.EF * math.sin(beta)
  79. self.alpha = alpha
  80. self.beta = beta
  81. return alpha, beta

绘制狗腿

  1. def paintEvent(self, event):
  2. # 创建画家
  3. painter = QPainter(self)
  4. # 设置画笔中心
  5. painter.translate(self.center_x, self.center_y)
  6. painter.setRenderHint(QPainter.Antialiasing, True)
  7. # 创建画笔
  8. pen = QPen()
  9. # 设置画笔颜色
  10. pen.setColor(QColor(255, 0, 0))
  11. pen.setWidth(3)
  12. # 设置画笔
  13. painter.setPen(pen)
  14. # 绘制
  15. painter.drawLine(self.AX, self.AY, self.FX, self.FY)
  16. pen.setColor(QColor(0, 255, 0))
  17. painter.setPen(pen)
  18. painter.drawLine(self.AX, self.AY, self.BX, self.BY)
  19. pen.setColor(QColor(0, 0, 255))
  20. painter.setPen(pen)
  21. painter.drawLine(self.BX, self.BY, self.CX, self.CY)
  22. pen.setColor(QColor(0, 0, 255))
  23. painter.setPen(pen)
  24. painter.drawLine(self.CX, self.CY, self.DX, self.DY)
  25. pen.setColor(QColor(255, 0, 255))
  26. painter.setPen(pen)
  27. painter.drawLine(self.CX, self.CY, self.EX, self.EY)
  28. pen.setColor(QColor(255, 255, 0))
  29. painter.setPen(pen)
  30. painter.drawLine(self.EX, self.EY, self.FX, self.FY)
  31. # -------------- 绘制点的label -------------- #
  32. # 设置刷子
  33. painter.setBrush(QColor(255, 0, 0))
  34. painter.drawEllipse(self.press_x - 2, self.press_y - 2, 4, 4)
  35. radius = 4
  36. pen.setColor(QColor(255, 0, 0))
  37. pen.setWidth(5)
  38. font = QFont()
  39. font.setBold(True)
  40. font.setPixelSize(30)
  41. painter.setFont(font)
  42. # 设置画笔
  43. painter.setPen(pen)
  44. self.draw_point_label(painter, self.AX, self.AY, "A")
  45. self.draw_point_label(painter, self.BX, self.BY, "B")
  46. self.draw_point_label(painter, self.CX, self.CY, "C")
  47. self.draw_point_label(painter, self.DX, self.DY, "D")
  48. self.draw_point_label(painter, self.EX, self.EY, "E")
  49. self.draw_point_label(painter, self.FX, self.FY, "F")
  50. font.setPixelSize(20)
  51. painter.setFont(font)
  52. painter.drawText(QPoint(self.DX + 10, self.DY + 10), f"x={self.DX:.2f}.y={self.DY:.2f}")
  53. # painter.translate(-self.center_x,-self.center_y)
  54. painter.drawText(QPoint(self.AX - 200, self.AY - 30), f"alpha:{math.degrees(self.alpha):.2f}")
  55. painter.drawText(QPoint(self.FX + 100, self.FY - 30), f"beta:{math.degrees(self.beta):.2f}")
  56. def draw_point_label(self, painter, x, y, label):
  57. '''绘制点的标题'''
  58. x = int(x)
  59. y = int(y)
  60. point = QPoint(x, y)
  61. painter.drawEllipse(point, 5, 5)
  62. painter.drawText(QPoint(x + 10, y), label)
  63. def mousePressEvent(self, event):
  64. '''
  65. 鼠标按下
  66. :param event:
  67. :return:
  68. '''
  69. x = event.x()
  70. y = event.y()
  71. self.press_x = x - self.center_x
  72. self.press_y = y - self.center_y
  73. # 主动绘制
  74. # update 自动调用paintEvent
  75. self.update()
  76. def mouseReleaseEvent(self, event):
  77. '''
  78. 鼠标松开
  79. :param event:
  80. :return:
  81. '''
  82. x = event.x()
  83. y = event.y()
  84. # 修改腿的坐标
  85. self.foot_x = x - self.center_x
  86. self.foot_y = y - self.center_y
  87. try:
  88. self.results = self.inverse(self.foot_x, self.foot_y)
  89. except:
  90. return
  91. # 主动绘制
  92. self.update()

完整代码

  1. import sys
  2. from PyQt5.QtWidgets import QApplication, QWidget
  3. import math
  4. from PyQt5.QtCore import QPoint
  5. from PyQt5.QtGui import QPainter, QColor, QPen, QFont
  6. class MainWindow(QWidget):
  7. def __init__(self):
  8. super().__init__()
  9. # 设置标题
  10. self.setWindowTitle('机器狗绘制')
  11. # 设置窗口大小
  12. self.setFixedSize(640, 480)
  13. # -------------- 中心点坐标 -------------- #
  14. self.center_x = 320
  15. self.center_y = 100
  16. # 大小的缩放系数
  17. self.ratio = 4
  18. # -------------- 已知狗腿数据 -------------- #
  19. self.AF = 12.5 * self.ratio
  20. self.AB = 21.5 * self.ratio
  21. self.EF = 21.5 * self.ratio
  22. self.CE = 21.5 * self.ratio
  23. self.BC = 34.19 * self.ratio
  24. self.CD = 34.95 * self.ratio
  25. # BC和CD的夹角
  26. self.bcd_angle = math.radians(120)
  27. self.half_AF = self.AF / 2
  28. # -------------- 各个端点坐标 -------------- #
  29. self.AX = -self.half_AF
  30. self.AY = 0
  31. self.FX = self.half_AF
  32. self.FY = 0
  33. self.BX = 0
  34. self.BY = 0
  35. self.CX = 0
  36. self.CY = 0
  37. self.EX = 0
  38. self.EY = 0
  39. # -------------- 足端坐标 -------------- #
  40. self.DX = 0
  41. self.DY = 60 * self.ratio
  42. # -------------- 按下的坐标 -------------- #
  43. self.press_x = self.DX
  44. self.press_y = self.DY
  45. # -------------- alpha和beta -------------- #
  46. self.alpha = 0
  47. self.beta = 0
  48. # -------------- 开始逆解 -------------- #
  49. self.results = self.inverse(self.DX, self.DY)
  50. def inverse(self, fx, fy):
  51. # AD长度
  52. AD2 = (self.half_AF + fx) ** 2 + fy ** 2
  53. AD = math.sqrt(AD2)
  54. # DAF夹角
  55. angle_DAF = math.asin(fy / AD)
  56. # BD长度
  57. BD2 = self.BC ** 2 + self.CD ** 2 - 2 * self.BC * self.CD * math.cos(self.bcd_angle)
  58. BD = math.sqrt(BD2)
  59. # BAD夹角
  60. angle_BAD = math.acos((self.AB ** 2 + AD2 - BD2) / (2 * self.AB * AD))
  61. # AB对应的alpha夹角
  62. alpha = math.pi - angle_BAD - angle_DAF
  63. angle_DBA = math.acos((self.AB ** 2 + BD2 - AD2) / (2 * self.AB * BD))
  64. angle_DBC = math.acos((self.BC ** 2 + BD2 - self.CD ** 2) / (2 * self.BC * BD))
  65. angle_ABC = angle_DBA - angle_DBC
  66. # AC长度
  67. AC2 = self.AB ** 2 + self.BC ** 2 - 2 * self.AB * self.BC * math.cos(angle_ABC)
  68. AC = math.sqrt(AC2)
  69. angle_BAC = math.acos((self.AB ** 2 + AC2 - self.BC ** 2) / (2 * self.AB * AC))
  70. angle_CAF = math.pi - alpha - angle_BAC
  71. CF2 = self.AF ** 2 + AC2 - 2 * self.AF * AC * math.cos(angle_CAF)
  72. CF = math.sqrt(CF2)
  73. angle_AFC = math.acos((self.AF ** 2 + CF2 - AC2) / (2 * self.AF * CF))
  74. angle_CFE = math.acos((CF2 + self.EF ** 2 - self.CE ** 2) / (2 * CF * self.EF))
  75. beta = math.pi - angle_AFC - angle_CFE
  76. # 计算各个点的坐标
  77. self.BX = -(self.AB * math.cos(alpha) + self.half_AF)
  78. self.BY = self.AB * math.sin(alpha)
  79. self.CX = math.cos(angle_CAF) * AC - self.half_AF
  80. self.CY = AC * math.sin(angle_CAF)
  81. self.DX = fx
  82. self.DY = fy
  83. self.EX = self.half_AF + self.EF * math.cos(beta)
  84. self.EY = self.EF * math.sin(beta)
  85. self.alpha = alpha
  86. self.beta = beta
  87. return alpha, beta
  88. def paintEvent(self, event):
  89. # 创建画家
  90. painter = QPainter(self)
  91. # 设置画笔中心
  92. painter.translate(self.center_x, self.center_y)
  93. painter.setRenderHint(QPainter.Antialiasing, True)
  94. # 创建画笔
  95. pen = QPen()
  96. # 设置画笔颜色
  97. pen.setColor(QColor(255, 0, 0))
  98. pen.setWidth(3)
  99. # 设置画笔
  100. painter.setPen(pen)
  101. # 绘制
  102. painter.drawLine(self.AX, self.AY, self.FX, self.FY)
  103. pen.setColor(QColor(0, 255, 0))
  104. painter.setPen(pen)
  105. painter.drawLine(self.AX, self.AY, self.BX, self.BY)
  106. pen.setColor(QColor(0, 0, 255))
  107. painter.setPen(pen)
  108. painter.drawLine(self.BX, self.BY, self.CX, self.CY)
  109. pen.setColor(QColor(0, 0, 255))
  110. painter.setPen(pen)
  111. painter.drawLine(self.CX, self.CY, self.DX, self.DY)
  112. pen.setColor(QColor(255, 0, 255))
  113. painter.setPen(pen)
  114. painter.drawLine(self.CX, self.CY, self.EX, self.EY)
  115. pen.setColor(QColor(255, 255, 0))
  116. painter.setPen(pen)
  117. painter.drawLine(self.EX, self.EY, self.FX, self.FY)
  118. # -------------- 绘制点的label -------------- #
  119. # 设置刷子
  120. painter.setBrush(QColor(255, 0, 0))
  121. painter.drawEllipse(self.press_x - 2, self.press_y - 2, 4, 4)
  122. radius = 4
  123. pen.setColor(QColor(255, 0, 0))
  124. pen.setWidth(5)
  125. font = QFont()
  126. font.setBold(True)
  127. font.setPixelSize(30)
  128. painter.setFont(font)
  129. # 设置画笔
  130. painter.setPen(pen)
  131. self.draw_point_label(painter, self.AX, self.AY, "A")
  132. self.draw_point_label(painter, self.BX, self.BY, "B")
  133. self.draw_point_label(painter, self.CX, self.CY, "C")
  134. self.draw_point_label(painter, self.DX, self.DY, "D")
  135. self.draw_point_label(painter, self.EX, self.EY, "E")
  136. self.draw_point_label(painter, self.FX, self.FY, "F")
  137. font.setPixelSize(20)
  138. painter.setFont(font)
  139. painter.drawText(QPoint(self.DX + 10, self.DY + 10), f"x={self.DX:.2f}.y={self.DY:.2f}")
  140. # painter.translate(-self.center_x,-self.center_y)
  141. painter.drawText(QPoint(self.AX - 200, self.AY - 30), f"alpha:{math.degrees(self.alpha):.2f}")
  142. painter.drawText(QPoint(self.FX + 100, self.FY - 30), f"beta:{math.degrees(self.beta):.2f}")
  143. def draw_point_label(self, painter, x, y, label):
  144. '''绘制点的标题'''
  145. x = int(x)
  146. y = int(y)
  147. point = QPoint(x, y)
  148. painter.drawEllipse(point, 5, 5)
  149. painter.drawText(QPoint(x + 10, y), label)
  150. def mousePressEvent(self, event):
  151. '''
  152. 鼠标按下
  153. :param event:
  154. :return:
  155. '''
  156. x = event.x()
  157. y = event.y()
  158. self.press_x = x - self.center_x
  159. self.press_y = y - self.center_y
  160. # 主动绘制
  161. # update 自动调用paintEvent
  162. self.update()
  163. def mouseReleaseEvent(self, event):
  164. '''
  165. 鼠标松开
  166. :param event:
  167. :return:
  168. '''
  169. x = event.x()
  170. y = event.y()
  171. # 修改腿的坐标
  172. self.foot_x = x - self.center_x
  173. self.foot_y = y - self.center_y
  174. try:
  175. self.results = self.inverse(self.foot_x, self.foot_y)
  176. except:
  177. return
  178. # 主动绘制
  179. self.update()
  180. if __name__ == '__main__':
  181. app = QApplication(sys.argv)
  182. window = MainWindow()
  183. window.show()
  184. # window.autoAnimation2()
  185. sys.exit(app.exec_())