2.增加多臂多控制器模式 3.末端姿态由欧拉角控制切换到四元数控制 4.增加vr手柄控制器 Signed-off-by: 1002142102@qq.com <1002142102@qq.com>
306 lines
9.8 KiB
Python
306 lines
9.8 KiB
Python
import threading
|
|
import time
|
|
from typing import Any, Dict
|
|
from Robotic_Arm.rm_robot_interface import *
|
|
from lerobot.motors import Motor, MotorCalibration, MotorsBus
|
|
from .robot_mock import RealmanMock
|
|
from scipy.spatial.transform import Rotation as R
|
|
|
|
import numpy
|
|
#动作执行成功
|
|
ACTION_SUCCESS = 0
|
|
|
|
|
|
class RealmanMotorsBus(MotorsBus):
|
|
model_number_table={}
|
|
model_ctrl_table={
|
|
"realman":{}
|
|
}
|
|
ip: str
|
|
port: int
|
|
"""
|
|
对Realman SDK的二次封装
|
|
"""
|
|
def __init__(
|
|
self,
|
|
port: str,
|
|
motors:dict[str, Motor],
|
|
gripper_range:list[int],
|
|
joint: list,
|
|
udp_ip: str,
|
|
udp_port: int,
|
|
mock: bool = False,
|
|
calibration: dict[str, MotorCalibration] | None = None,
|
|
):
|
|
super().__init__(port,motors, calibration)
|
|
address = port.split(':')
|
|
self.ip = address[0]
|
|
self.port = int(address[1])
|
|
self.motors = motors
|
|
self.gripper_range = gripper_range
|
|
self.init_joint_position = joint
|
|
self.safe_disable_position = joint
|
|
self.mock = mock
|
|
if mock:
|
|
#self.rmarm = RealmanMock()
|
|
self.rmarm = RoboticArm(rm_thread_mode_e.RM_DUAL_MODE_E)
|
|
else:
|
|
self.rmarm = RoboticArm(rm_thread_mode_e.RM_DUAL_MODE_E)
|
|
self.handle = None
|
|
self.init_pose = numpy.zeros(8)
|
|
self.init_pose[-2] = 1
|
|
self.running = False
|
|
custom = rm_udp_custom_config_t()
|
|
custom.joint_speed = 0
|
|
# custom.lift_state = 1
|
|
custom.hand_state = 1
|
|
custom.plus_state = 1
|
|
custom.plus_base=1
|
|
custom.expand_state = 1
|
|
custom.arm_current_status = 1
|
|
custom.expand_state = 1
|
|
self.udp_config = rm_realtime_push_config_t(1, True, udp_port, 0, udp_ip, custom)
|
|
self.lock = threading.Lock()
|
|
self.temp_pose = numpy.zeros(8)
|
|
|
|
@property
|
|
def motor_names(self) -> list[str]:
|
|
return list(self.motors.keys())
|
|
|
|
@property
|
|
def motor_models(self) -> list[str]:
|
|
return [model for _, model in self.motors.values()]
|
|
|
|
@property
|
|
def motor_indices(self) -> list[int]:
|
|
return [idx for idx, _ in self.motors.values()]
|
|
|
|
def update_arm_state(self, state:rm_realtime_arm_joint_state_t):
|
|
joint = list(state.joint_status.joint_position)
|
|
gripper = state.expandState.pos
|
|
pose = state.waypoint.position
|
|
euler = state.waypoint.euler
|
|
pose = [pose.x, pose.y, pose.z, euler.rx, euler.ry, euler.rz, 0]
|
|
self.current_arm_state = {"joint": joint, "gripper": gripper, "pose": pose}
|
|
|
|
|
|
def connect(self, enable=True) -> bool:
|
|
'''
|
|
使能机械臂并检测使能状态,尝试5s,如果使能超时则退出程序
|
|
'''
|
|
self.handle = self.rmarm.rm_create_robot_arm(self.ip, self.port)
|
|
|
|
if self.mock:
|
|
self.rmarm.rm_set_arm_run_mode(0)#仿真模式
|
|
else:
|
|
self.rmarm.rm_set_arm_run_mode(1)
|
|
enable_flag = False
|
|
loop_flag = False
|
|
# 设置超时时间(秒)
|
|
timeout = 5
|
|
# 记录进入循环前的时间
|
|
start_time = time.time()
|
|
elapsed_time_flag = False
|
|
|
|
while not loop_flag:
|
|
elapsed_time = time.time() - start_time
|
|
print("--------------------")
|
|
|
|
if enable:
|
|
# 获取机械臂状态
|
|
ret = self.rmarm.rm_get_current_arm_state()
|
|
if ret[0] == 0: # 成功获取状态
|
|
enable_flag = True
|
|
else:
|
|
enable_flag = False
|
|
# 断开所有连接,销毁线程
|
|
RoboticArm.rm_destory()
|
|
print("使能状态:", enable_flag)
|
|
print("--------------------")
|
|
if(enable_flag == enable):
|
|
loop_flag = True
|
|
enable_flag = True
|
|
break
|
|
else:
|
|
loop_flag = False
|
|
enable_flag = False
|
|
# 检查是否超过超时时间
|
|
if elapsed_time > timeout:
|
|
print("超时....")
|
|
elapsed_time_flag = True
|
|
enable_flag = True
|
|
break
|
|
time.sleep(1)
|
|
# self.rmarm.rm_set_realtime_push(self.udp_config)
|
|
# self.arm_state_callback = rm_realtime_arm_state_callback_ptr(self.update_arm_state)
|
|
# self.rmarm.rm_realtime_arm_state_call_back(self.arm_state_callback)
|
|
self.rmarm.rm_movej(self.init_joint_position[:-1], 5, 0, 0, 1)
|
|
self.init_pose = self.getQuatPose()
|
|
resp = enable_flag
|
|
self.running = True
|
|
threading.Thread(target=self.update_pose).start()
|
|
threading.Thread(target=self.update_gripper).start()
|
|
print(f"Returning response: {resp}")
|
|
return resp
|
|
|
|
def getQuatPose(self)->tuple[int,rm_current_arm_state_t]:
|
|
state = rm_current_arm_state_t()
|
|
ret = rm_get_current_arm_state(self.handle, byref(state))
|
|
return numpy.array([
|
|
state.pose.position.x,
|
|
state.pose.position.y,
|
|
state.pose.position.z,
|
|
state.pose.quaternion.w,
|
|
state.pose.quaternion.x,
|
|
state.pose.quaternion.y,
|
|
state.pose.quaternion.z,
|
|
self.rmarm.rm_get_gripper_state()[1]['actpos']
|
|
])
|
|
def read_pose(self):
|
|
return self.init_pose
|
|
|
|
def motor_names(self):
|
|
return
|
|
|
|
def read_calibration(self):
|
|
return
|
|
def set_calibration(self):
|
|
return
|
|
|
|
def revert_calibration(self):
|
|
return
|
|
|
|
def apply_calibration(self):
|
|
"""
|
|
移动到初始位置
|
|
"""
|
|
self.write_arm(target_joint=self.init_joint_position)
|
|
|
|
def write(self, data_name: str, motor: str, value, *, normalize: bool = True, num_retry: int = 0):
|
|
return False
|
|
|
|
def write_arm(self, target_joint: list):
|
|
ret = self.rmarm.rm_movej(self.init_joint_position[:-1], 5, 0, 0, 1)
|
|
if ret != 0:
|
|
print("movej error:", ret)
|
|
return ret
|
|
|
|
ret = self.rmarm.rm_set_gripper_position(target_joint[-1], block=False, timeout=2)
|
|
return ret
|
|
|
|
def read(self) -> Dict:
|
|
"""
|
|
- 机械臂关节消息,单位1度;[-1, 1]
|
|
- 机械臂夹爪消息,[-1, 1]
|
|
"""
|
|
joint_state = self.read_pose()
|
|
|
|
return {
|
|
"joint_1": joint_state[0]/180,
|
|
"joint_2": joint_state[1]/180,
|
|
"joint_3": joint_state[2]/180,
|
|
"joint_4": joint_state[3]/180,
|
|
"joint_5": joint_state[4]/180,
|
|
"joint_6": joint_state[5]/180,
|
|
"gripper": (joint_state[6]-500)/500
|
|
}
|
|
5
|
|
def disconnect(self, disable_torque = True):
|
|
self.safe_disconnect()
|
|
|
|
def safe_disconnect(self):
|
|
"""
|
|
Move to safe disconnect position
|
|
"""
|
|
self.write_arm(target_joint=self.safe_disable_position)
|
|
self.running = False
|
|
# 断开所有连接,销毁线程
|
|
|
|
"""
|
|
向机械臂写入动作
|
|
"""
|
|
def sync_write(self,name,actionData: dict[str, Any]):
|
|
if name =="Goal_Position":
|
|
self.write_arm(target_joint=actionData)
|
|
elif name == "Goal_Velocity":#速度透传模式
|
|
values = numpy.array(list(actionData.values()))
|
|
with self.lock:
|
|
self.temp_pose += numpy.array([*values[:6],0,values[-1]])
|
|
def update_pose(self):
|
|
while self.running:
|
|
with self.lock:
|
|
self.temp_pose[-1] = self.temp_pose[-1]*1000
|
|
temp = self.init_pose + self.temp_pose
|
|
newr = R.from_rotvec(self.temp_pose[3:6].tolist())
|
|
oldr = R.from_quat(self.init_pose[3:7].tolist())
|
|
temp[3:7] = (oldr * newr).as_quat()
|
|
self.temp_pose.fill(0)
|
|
ret = self.rmarm.rm_movep_canfd(temp[:-1].tolist(), False)
|
|
if ret == 0:
|
|
self.init_pose = temp
|
|
else:
|
|
print(f"Error: {ret}")
|
|
time.sleep(0.01)
|
|
def clamp(self,value, min_value, max_value):
|
|
return max(min(value, max_value), min_value)
|
|
def update_gripper(self):
|
|
while self.running:
|
|
if self.init_pose[-1]>0:
|
|
self.rmarm.rm_set_gripper_release(500,False,0)
|
|
self.init_pose[-1] = 0
|
|
elif self.init_pose[-1]<0:
|
|
self.rmarm.rm_set_gripper_pick(500,300,False,0)
|
|
self.init_pose[-1] = 0
|
|
|
|
def sync_read(self, data_name, motors = None, *, normalize = True, num_retry = 0):
|
|
if data_name == "Present_Position":
|
|
return self.read()
|
|
|
|
|
|
@property
|
|
def is_connected(self) -> bool:
|
|
return self.handle is not None
|
|
@property
|
|
def is_calibrated(self) -> bool:
|
|
return True
|
|
|
|
def write_calibration(self, calibration_dict, cache = True):
|
|
pass
|
|
|
|
def _get_half_turn_homings(self, positions):
|
|
pass
|
|
|
|
def _find_single_motor(self, motor: str, initial_baudrate: int | None = None) -> tuple[int, int]:
|
|
return 0,self.motors[motor].id
|
|
|
|
def _encode_sign(self, data_name, ids_values):
|
|
pass
|
|
|
|
def _decode_sign(self, data_name, ids_values):
|
|
pass
|
|
|
|
def _split_into_byte_chunks(self, value, length):
|
|
pass
|
|
|
|
def broadcast_ping(self, num_retry = 0, raise_on_error = False):
|
|
pass
|
|
def _handshake(self) -> None:
|
|
pass
|
|
def _assert_protocol_is_compatible(self, instruction_name):
|
|
return True
|
|
|
|
|
|
"""
|
|
配置电机
|
|
"""
|
|
def configure_motors(self, return_delay_time=0, maximum_acceleration=254, acceleration=254) -> None:
|
|
pass
|
|
|
|
def disable_torque(self, motors: int | str | list[str] | None = None, num_retry: int = 0):
|
|
pass
|
|
|
|
def _disable_torque(self, motor: int, model: str, num_retry: int = 0):
|
|
pass
|
|
|
|
def enable_torque(self, motors = None, num_retry = 0):
|
|
pass |