You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

robot.py 7.4 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. """Module for controlling robot hardware and movements.
  2. This module provides functionality for controlling robot hardware components,
  3. including servo motors and various control modes.
  4. """
  5. import time
  6. from enum import Enum, auto
  7. from typing import Union
  8. import numpy as np
  9. from dynamixel import OperatingMode, ReadAttribute
  10. from dynamixel_sdk import (
  11. DXL_HIBYTE,
  12. DXL_HIWORD,
  13. DXL_LOBYTE,
  14. DXL_LOWORD,
  15. GroupSyncRead,
  16. GroupSyncWrite,
  17. )
  18. class MotorControlType(Enum):
  19. """Enumeration of different motor control modes.
  20. Defines the various control modes available for the robot's motors,
  21. including PWM control, position control, and disabled states.
  22. """
  23. PWM = auto()
  24. POSITION_CONTROL = auto()
  25. DISABLED = auto()
  26. UNKNOWN = auto()
  27. class Robot:
  28. """A class for controlling robot hardware components.
  29. This class provides methods for controlling robot servos, managing different
  30. control modes, and reading sensor data.
  31. """
  32. # def __init__(self, device_name: str, baudrate=1_000_000, servo_ids=[1, 2, 3, 4, 5]):
  33. def __init__(self, dynamixel, baudrate=1_000_000, servo_ids=[1, 2, 3, 4, 5]):
  34. """Initialize the robot controller.
  35. Args:
  36. dynamixel: Dynamixel controller instance
  37. baudrate: Communication baudrate for servo control
  38. servo_ids: List of servo IDs to control
  39. """
  40. self.servo_ids = servo_ids
  41. self.dynamixel = dynamixel
  42. # self.dynamixel = Dynamixel.Config(baudrate=baudrate, device_name=device_name).instantiate()
  43. self.position_reader = GroupSyncRead(
  44. self.dynamixel.portHandler,
  45. self.dynamixel.packetHandler,
  46. ReadAttribute.POSITION.value,
  47. 4,
  48. )
  49. for id in self.servo_ids:
  50. self.position_reader.addParam(id)
  51. self.velocity_reader = GroupSyncRead(
  52. self.dynamixel.portHandler,
  53. self.dynamixel.packetHandler,
  54. ReadAttribute.VELOCITY.value,
  55. 4,
  56. )
  57. for id in self.servo_ids:
  58. self.velocity_reader.addParam(id)
  59. self.pos_writer = GroupSyncWrite(
  60. self.dynamixel.portHandler,
  61. self.dynamixel.packetHandler,
  62. self.dynamixel.ADDR_GOAL_POSITION,
  63. 4,
  64. )
  65. for id in self.servo_ids:
  66. self.pos_writer.addParam(id, [2048])
  67. self.pwm_writer = GroupSyncWrite(
  68. self.dynamixel.portHandler,
  69. self.dynamixel.packetHandler,
  70. self.dynamixel.ADDR_GOAL_PWM,
  71. 2,
  72. )
  73. for id in self.servo_ids:
  74. self.pwm_writer.addParam(id, [2048])
  75. self._disable_torque()
  76. self.motor_control_state = MotorControlType.DISABLED
  77. def read_position(self, tries=2):
  78. """Read the current joint positions of the robot.
  79. Args:
  80. tries: Maximum number of attempts to read positions
  81. Returns:
  82. list: Joint positions in range [0, 4096]
  83. """
  84. result = self.position_reader.txRxPacket()
  85. if result != 0:
  86. if tries > 0:
  87. return self.read_position(tries=tries - 1)
  88. print("failed to read position!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
  89. positions = []
  90. for id in self.servo_ids:
  91. position = self.position_reader.getData(id, ReadAttribute.POSITION.value, 4)
  92. if position > 2**31:
  93. position -= 2**32
  94. positions.append(position)
  95. return positions
  96. def read_velocity(self):
  97. """Read the current joint velocities of the robot.
  98. Returns:
  99. list: Current joint velocities
  100. """
  101. self.velocity_reader.txRxPacket()
  102. velocties = []
  103. for id in self.servo_ids:
  104. velocity = self.velocity_reader.getData(id, ReadAttribute.VELOCITY.value, 4)
  105. if velocity > 2**31:
  106. velocity -= 2**32
  107. velocties.append(velocity)
  108. return velocties
  109. def set_goal_pos(self, action):
  110. """Set goal positions for the robot joints.
  111. Args:
  112. action: List or numpy array of target joint positions in range [0, 4096]
  113. """
  114. if self.motor_control_state is not MotorControlType.POSITION_CONTROL:
  115. self._set_position_control()
  116. for i, motor_id in enumerate(self.servo_ids):
  117. data_write = [
  118. DXL_LOBYTE(DXL_LOWORD(action[i])),
  119. DXL_HIBYTE(DXL_LOWORD(action[i])),
  120. DXL_LOBYTE(DXL_HIWORD(action[i])),
  121. DXL_HIBYTE(DXL_HIWORD(action[i])),
  122. ]
  123. self.pos_writer.changeParam(motor_id, data_write)
  124. self.pos_writer.txPacket()
  125. def set_pwm(self, action):
  126. """Set PWM values for the servos.
  127. Args:
  128. action: List or numpy array of PWM values in range [0, 885]
  129. """
  130. if self.motor_control_state is not MotorControlType.PWM:
  131. self._set_pwm_control()
  132. for i, motor_id in enumerate(self.servo_ids):
  133. data_write = [
  134. DXL_LOBYTE(DXL_LOWORD(action[i])),
  135. DXL_HIBYTE(DXL_LOWORD(action[i])),
  136. ]
  137. self.pwm_writer.changeParam(motor_id, data_write)
  138. self.pwm_writer.txPacket()
  139. def set_trigger_torque(self):
  140. """Set a constant torque for the last servo in the chain.
  141. This is useful for the trigger of the leader arm.
  142. """
  143. self.dynamixel._enable_torque(self.servo_ids[-1])
  144. self.dynamixel.set_pwm_value(self.servo_ids[-1], 200)
  145. def limit_pwm(self, limit: Union[int, list, np.ndarray]):
  146. """Limit the PWM values for the servos in position control.
  147. Args:
  148. limit: PWM limit value in range 0-885
  149. """
  150. if isinstance(limit, int):
  151. limits = [
  152. limit,
  153. ] * 5
  154. else:
  155. limits = limit
  156. self._disable_torque()
  157. for motor_id, limit in zip(self.servo_ids, limits):
  158. self.dynamixel.set_pwm_limit(motor_id, limit)
  159. self._enable_torque()
  160. def _disable_torque(self):
  161. print(f"disabling torque for servos {self.servo_ids}")
  162. for motor_id in self.servo_ids:
  163. self.dynamixel._disable_torque(motor_id)
  164. def _enable_torque(self):
  165. print(f"enabling torque for servos {self.servo_ids}")
  166. for motor_id in self.servo_ids:
  167. self.dynamixel._enable_torque(motor_id)
  168. def _set_pwm_control(self):
  169. self._disable_torque()
  170. for motor_id in self.servo_ids:
  171. self.dynamixel.set_operating_mode(motor_id, OperatingMode.PWM)
  172. self._enable_torque()
  173. self.motor_control_state = MotorControlType.PWM
  174. def _set_position_control(self):
  175. self._disable_torque()
  176. for motor_id in self.servo_ids:
  177. self.dynamixel.set_operating_mode(motor_id, OperatingMode.POSITION)
  178. self._enable_torque()
  179. self.motor_control_state = MotorControlType.POSITION_CONTROL
  180. if __name__ == "__main__":
  181. robot = Robot(device_name="/dev/tty.usbmodem57380045631")
  182. robot._disable_torque()
  183. for _ in range(10000):
  184. s = time.time()
  185. pos = robot.read_position()
  186. elapsed = time.time() - s
  187. print(f"read took {elapsed} pos {pos}")