fix driver & baudrate & dxl_ids
This commit is contained in:
parent
e6ba5325ac
commit
f55181d0f7
3 changed files with 276 additions and 2 deletions
|
@ -1,4 +1,4 @@
|
|||
usb-FTDI_USB__-__Serial_Converter_FT94EVW4-if00-port0:
|
||||
usb-FTDI_USB__-__Serial_Converter_FTA7NPA1-if00-port0:
|
||||
side: "sample-config"
|
||||
num_joints: 7
|
||||
joint_signs: [1, 1, 1, 1, 1, -1, 1]
|
||||
|
|
|
@ -0,0 +1,274 @@
|
|||
import time
|
||||
from threading import Event, Lock, Thread
|
||||
from typing import Protocol, Sequence
|
||||
|
||||
import numpy as np
|
||||
from dynamixel_sdk.group_sync_read import GroupSyncRead
|
||||
from dynamixel_sdk.group_sync_write import GroupSyncWrite
|
||||
from dynamixel_sdk.packet_handler import PacketHandler
|
||||
from dynamixel_sdk.port_handler import PortHandler
|
||||
from dynamixel_sdk.robotis_def import (
|
||||
COMM_SUCCESS,
|
||||
DXL_HIBYTE,
|
||||
DXL_HIWORD,
|
||||
DXL_LOBYTE,
|
||||
DXL_LOWORD,
|
||||
)
|
||||
|
||||
# Constants
|
||||
ADDR_TORQUE_ENABLE = 64
|
||||
ADDR_GOAL_POSITION = 116
|
||||
LEN_GOAL_POSITION = 4
|
||||
ADDR_PRESENT_POSITION = 132
|
||||
LEN_PRESENT_POSITION = 4
|
||||
TORQUE_ENABLE = 1
|
||||
TORQUE_DISABLE = 0
|
||||
|
||||
|
||||
class DynamixelDriverProtocol(Protocol):
|
||||
def set_joints(self, joint_angles: Sequence[float]):
|
||||
"""Set the joint angles for the Dynamixel servos.
|
||||
|
||||
Args:
|
||||
joint_angles (Sequence[float]): A list of joint angles.
|
||||
"""
|
||||
...
|
||||
|
||||
def torque_enabled(self) -> bool:
|
||||
"""Check if torque is enabled for the Dynamixel servos.
|
||||
|
||||
Returns:
|
||||
bool: True if torque is enabled, False if it is disabled.
|
||||
"""
|
||||
...
|
||||
|
||||
def set_torque_mode(self, enable: bool):
|
||||
"""Set the torque mode for the Dynamixel servos.
|
||||
|
||||
Args:
|
||||
enable (bool): True to enable torque, False to disable.
|
||||
"""
|
||||
...
|
||||
|
||||
def get_joints(self) -> np.ndarray:
|
||||
"""Get the current joint angles in radians.
|
||||
|
||||
Returns:
|
||||
np.ndarray: An array of joint angles.
|
||||
"""
|
||||
...
|
||||
|
||||
def close(self):
|
||||
"""Close the driver."""
|
||||
|
||||
|
||||
class FakeDynamixelDriver(DynamixelDriverProtocol):
|
||||
def __init__(self, ids: Sequence[int]):
|
||||
self._ids = ids
|
||||
self._joint_angles = np.zeros(len(ids), dtype=int)
|
||||
self._torque_enabled = False
|
||||
|
||||
def set_joints(self, joint_angles: Sequence[float]):
|
||||
if len(joint_angles) != len(self._ids):
|
||||
raise ValueError(
|
||||
"The length of joint_angles must match the number of servos"
|
||||
)
|
||||
if not self._torque_enabled:
|
||||
raise RuntimeError("Torque must be enabled to set joint angles")
|
||||
self._joint_angles = np.array(joint_angles)
|
||||
|
||||
def torque_enabled(self) -> bool:
|
||||
return self._torque_enabled
|
||||
|
||||
def set_torque_mode(self, enable: bool):
|
||||
self._torque_enabled = enable
|
||||
|
||||
def get_joints(self) -> np.ndarray:
|
||||
return self._joint_angles.copy()
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
|
||||
class DynamixelDriver(DynamixelDriverProtocol):
|
||||
def __init__(
|
||||
self, ids: Sequence[int], port: str = "/dev/ttyUSB0", baudrate: int = 57600
|
||||
):
|
||||
"""Initialize the DynamixelDriver class.
|
||||
|
||||
Args:
|
||||
ids (Sequence[int]): A list of IDs for the Dynamixel servos.
|
||||
port (str): The USB port to connect to the arm.
|
||||
baudrate (int): The baudrate for communication.
|
||||
"""
|
||||
self._ids = ids
|
||||
self._joint_angles = None
|
||||
self._lock = Lock()
|
||||
|
||||
# Initialize the port handler, packet handler, and group sync read/write
|
||||
self._portHandler = PortHandler(port)
|
||||
self._packetHandler = PacketHandler(2.0)
|
||||
self._groupSyncRead = GroupSyncRead(
|
||||
self._portHandler,
|
||||
self._packetHandler,
|
||||
ADDR_PRESENT_POSITION,
|
||||
LEN_PRESENT_POSITION,
|
||||
)
|
||||
self._groupSyncWrite = GroupSyncWrite(
|
||||
self._portHandler,
|
||||
self._packetHandler,
|
||||
ADDR_GOAL_POSITION,
|
||||
LEN_GOAL_POSITION,
|
||||
)
|
||||
|
||||
# Open the port and set the baudrate
|
||||
if not self._portHandler.openPort():
|
||||
raise RuntimeError("Failed to open the port")
|
||||
|
||||
if not self._portHandler.setBaudRate(baudrate):
|
||||
raise RuntimeError(f"Failed to change the baudrate, {baudrate}")
|
||||
|
||||
# Add parameters for each Dynamixel servo to the group sync read
|
||||
for dxl_id in self._ids:
|
||||
if not self._groupSyncRead.addParam(dxl_id):
|
||||
raise RuntimeError(
|
||||
f"Failed to add parameter for Dynamixel with ID {dxl_id}"
|
||||
)
|
||||
|
||||
# Disable torque for each Dynamixel servo
|
||||
self._torque_enabled = False
|
||||
try:
|
||||
self.set_torque_mode(self._torque_enabled)
|
||||
except Exception as e:
|
||||
print(f"port: {port}, {e}")
|
||||
|
||||
self._stop_thread = Event()
|
||||
self._start_reading_thread()
|
||||
|
||||
def set_joints(self, joint_angles: Sequence[float]):
|
||||
if len(joint_angles) != len(self._ids):
|
||||
raise ValueError(
|
||||
"The length of joint_angles must match the number of servos"
|
||||
)
|
||||
if not self._torque_enabled:
|
||||
raise RuntimeError("Torque must be enabled to set joint angles")
|
||||
|
||||
for dxl_id, angle in zip(self._ids, joint_angles):
|
||||
# Convert the angle to the appropriate value for the servo
|
||||
position_value = int(angle * 2048 / np.pi)
|
||||
|
||||
# Allocate goal position value into byte array
|
||||
param_goal_position = [
|
||||
DXL_LOBYTE(DXL_LOWORD(position_value)),
|
||||
DXL_HIBYTE(DXL_LOWORD(position_value)),
|
||||
DXL_LOBYTE(DXL_HIWORD(position_value)),
|
||||
DXL_HIBYTE(DXL_HIWORD(position_value)),
|
||||
]
|
||||
|
||||
# Add goal position value to the Syncwrite parameter storage
|
||||
dxl_addparam_result = self._groupSyncWrite.addParam(
|
||||
dxl_id, param_goal_position
|
||||
)
|
||||
if not dxl_addparam_result:
|
||||
raise RuntimeError(
|
||||
f"Failed to set joint angle for Dynamixel with ID {dxl_id}"
|
||||
)
|
||||
|
||||
# Syncwrite goal position
|
||||
dxl_comm_result = self._groupSyncWrite.txPacket()
|
||||
if dxl_comm_result != COMM_SUCCESS:
|
||||
raise RuntimeError("Failed to syncwrite goal position")
|
||||
|
||||
# Clear syncwrite parameter storage
|
||||
self._groupSyncWrite.clearParam()
|
||||
|
||||
def torque_enabled(self) -> bool:
|
||||
return self._torque_enabled
|
||||
|
||||
def set_torque_mode(self, enable: bool):
|
||||
torque_value = TORQUE_ENABLE if enable else TORQUE_DISABLE
|
||||
with self._lock:
|
||||
for dxl_id in self._ids:
|
||||
dxl_comm_result, dxl_error = self._packetHandler.write1ByteTxRx(
|
||||
self._portHandler, dxl_id, ADDR_TORQUE_ENABLE, torque_value
|
||||
)
|
||||
if dxl_comm_result != COMM_SUCCESS or dxl_error != 0:
|
||||
print(dxl_comm_result)
|
||||
print(dxl_error)
|
||||
raise RuntimeError(
|
||||
f"Failed to set torque mode for Dynamixel with ID {dxl_id}"
|
||||
)
|
||||
|
||||
self._torque_enabled = enable
|
||||
|
||||
def _start_reading_thread(self):
|
||||
self._reading_thread = Thread(target=self._read_joint_angles)
|
||||
self._reading_thread.daemon = True
|
||||
self._reading_thread.start()
|
||||
|
||||
def _read_joint_angles(self):
|
||||
# Continuously read joint angles and update the joint_angles array
|
||||
while not self._stop_thread.is_set():
|
||||
time.sleep(0.001)
|
||||
with self._lock:
|
||||
_joint_angles = np.zeros(len(self._ids), dtype=int)
|
||||
dxl_comm_result = self._groupSyncRead.txRxPacket()
|
||||
if dxl_comm_result != COMM_SUCCESS:
|
||||
print(f"warning, comm failed: {dxl_comm_result}")
|
||||
continue
|
||||
for i, dxl_id in enumerate(self._ids):
|
||||
if self._groupSyncRead.isAvailable(
|
||||
dxl_id, ADDR_PRESENT_POSITION, LEN_PRESENT_POSITION
|
||||
):
|
||||
angle = self._groupSyncRead.getData(
|
||||
dxl_id, ADDR_PRESENT_POSITION, LEN_PRESENT_POSITION
|
||||
)
|
||||
angle = np.int32(np.uint32(angle))
|
||||
_joint_angles[i] = angle
|
||||
else:
|
||||
raise RuntimeError(
|
||||
f"Failed to get joint angles for Dynamixel with ID {dxl_id}"
|
||||
)
|
||||
self._joint_angles = _joint_angles
|
||||
# self._groupSyncRead.clearParam() # TODO what does this do? should i add it
|
||||
|
||||
def get_joints(self) -> np.ndarray:
|
||||
# Return a copy of the joint_angles array to avoid race conditions
|
||||
while self._joint_angles is None:
|
||||
time.sleep(0.1)
|
||||
# with self._lock:
|
||||
_j = self._joint_angles.copy()
|
||||
return _j / 2048.0 * np.pi
|
||||
|
||||
def close(self):
|
||||
self._stop_thread.set()
|
||||
self._reading_thread.join()
|
||||
self._portHandler.closePort()
|
||||
|
||||
|
||||
def main():
|
||||
# Set the port, baudrate, and servo IDs
|
||||
ids = [1]
|
||||
|
||||
# Create a DynamixelDriver instance
|
||||
try:
|
||||
driver = DynamixelDriver(ids)
|
||||
except FileNotFoundError:
|
||||
driver = DynamixelDriver(ids, port="/dev/cu.usbserial-FT7WBMUB")
|
||||
|
||||
# Test setting torque mode
|
||||
driver.set_torque_mode(True)
|
||||
driver.set_torque_mode(False)
|
||||
|
||||
# Test reading the joint angles
|
||||
try:
|
||||
while True:
|
||||
joint_angles = driver.get_joints()
|
||||
print(f"Joint angles for IDs {ids}: {joint_angles}")
|
||||
# print(f"Joint angles for IDs {ids[1]}: {joint_angles[1]}")
|
||||
except KeyboardInterrupt:
|
||||
driver.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main() # Test the driver
|
|
@ -72,7 +72,7 @@ class GelloPublisher(Node):
|
|||
"""Whether or not the gripper is attached."""
|
||||
|
||||
joint_ids = list(range(1, self.num_joints + 1))
|
||||
self.driver = DynamixelDriver(joint_ids, port=self.com_port, baudrate=57600)
|
||||
self.driver = DynamixelDriver(joint_ids, port=self.com_port, baudrate=2000000)
|
||||
"""The driver for the Dynamixel motors."""
|
||||
|
||||
self.best_offsets = np.array(config[self.port]["best_offsets"])
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue