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"
|
side: "sample-config"
|
||||||
num_joints: 7
|
num_joints: 7
|
||||||
joint_signs: [1, 1, 1, 1, 1, -1, 1]
|
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."""
|
"""Whether or not the gripper is attached."""
|
||||||
|
|
||||||
joint_ids = list(range(1, self.num_joints + 1))
|
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."""
|
"""The driver for the Dynamixel motors."""
|
||||||
|
|
||||||
self.best_offsets = np.array(config[self.port]["best_offsets"])
|
self.best_offsets = np.array(config[self.port]["best_offsets"])
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue