279 lines
11 KiB
Python
279 lines
11 KiB
Python
|
|
import grpc
|
|||
|
|
import numpy as np
|
|||
|
|
from typing import List, Optional, Tuple
|
|||
|
|
from typing import Dict
|
|||
|
|
|
|||
|
|
from .enums import CMVRErrorCode, RobotCartesian, RobotJointIndexDirection
|
|||
|
|
from .models import JointCmd, Pose3D, JointState
|
|||
|
|
|
|||
|
|
class HumanoidRobotClient:
|
|||
|
|
"""人形机器人客户端"""
|
|||
|
|
|
|||
|
|
def __init__(self, device_id: str, stub):
|
|||
|
|
"""
|
|||
|
|
初始化机器人客户端
|
|||
|
|
|
|||
|
|
参数:
|
|||
|
|
device_id: 设备ID
|
|||
|
|
stub: gRPC stub实例
|
|||
|
|
"""
|
|||
|
|
self.device_id = device_id
|
|||
|
|
self.stub = stub
|
|||
|
|
self.generated = None # 推迟导入
|
|||
|
|
|
|||
|
|
def _import_generated(self):
|
|||
|
|
"""推迟导入 generated 模块"""
|
|||
|
|
if self.generated is None:
|
|||
|
|
try:
|
|||
|
|
# 从正确的路径导入生成的模块
|
|||
|
|
from generated.cmvr.api import common_pb2, humanoid_robot_command_pb2
|
|||
|
|
self.generated = type('GeneratedModules', (), {
|
|||
|
|
'common_pb2': common_pb2,
|
|||
|
|
'humanoid_robot_command_pb2': humanoid_robot_command_pb2
|
|||
|
|
})
|
|||
|
|
except ImportError as e:
|
|||
|
|
print(f"导入生成的模块失败: {e}")
|
|||
|
|
print("请确保已生成 protobuf 代码")
|
|||
|
|
raise
|
|||
|
|
return self.generated
|
|||
|
|
|
|||
|
|
def _create_command_header(self):
|
|||
|
|
"""创建命令头"""
|
|||
|
|
generated = self._import_generated()
|
|||
|
|
header = generated.common_pb2.CommandHeader.Request()
|
|||
|
|
header.device_id = self.device_id
|
|||
|
|
header.timestamp.GetCurrentTime()
|
|||
|
|
return header
|
|||
|
|
|
|||
|
|
# ==================== 基本控制接口 ====================
|
|||
|
|
|
|||
|
|
def torqueOn(self) -> CMVRErrorCode:
|
|||
|
|
try:
|
|||
|
|
generated = self._import_generated()
|
|||
|
|
request = generated.common_pb2.CommandHeader.Request()
|
|||
|
|
request.CopyFrom(self._create_command_header())
|
|||
|
|
|
|||
|
|
response = self.stub.torqueOn(request)
|
|||
|
|
|
|||
|
|
return CMVRErrorCode.CMVR_SUCCESS if response.success else CMVRErrorCode.CMVR_RPC_FAILED
|
|||
|
|
|
|||
|
|
except grpc.RpcError as e:
|
|||
|
|
print(f"开启使能失败: {e}")
|
|||
|
|
return CMVRErrorCode.CMVR_RPC_FAILED
|
|||
|
|
|
|||
|
|
def torqueOff(self) -> CMVRErrorCode:
|
|||
|
|
try:
|
|||
|
|
generated = self._import_generated()
|
|||
|
|
request = generated.common_pb2.CommandHeader.Request()
|
|||
|
|
request.CopyFrom(self._create_command_header())
|
|||
|
|
|
|||
|
|
response = self.stub.torqueOff(request)
|
|||
|
|
|
|||
|
|
return CMVRErrorCode.CMVR_SUCCESS if response.success else CMVRErrorCode.CMVR_RPC_FAILED
|
|||
|
|
|
|||
|
|
except grpc.RpcError as e:
|
|||
|
|
print(f"关闭使能失败: {e}")
|
|||
|
|
return CMVRErrorCode.CMVR_RPC_FAILED
|
|||
|
|
|
|||
|
|
# ==================== 关节空间运动 ====================
|
|||
|
|
|
|||
|
|
def moveJ(self, joint_commands: List[JointCmd],
|
|||
|
|
overall_vel: float = 0.1, overall_acc: float = 0.5) -> Tuple[CMVRErrorCode, Optional[str]]:
|
|||
|
|
try:
|
|||
|
|
generated = self._import_generated()
|
|||
|
|
# 创建MoveJ请求
|
|||
|
|
request = generated.humanoid_robot_command_pb2.MoveJ.Request()
|
|||
|
|
request.header.CopyFrom(self._create_command_header())
|
|||
|
|
request.vel = overall_vel
|
|||
|
|
request.acc = overall_acc
|
|||
|
|
|
|||
|
|
# 添加关节命令
|
|||
|
|
for cmd in joint_commands:
|
|||
|
|
joint_cmd = request.cmds.add()
|
|||
|
|
joint_cmd.joint_name = cmd.joint_name
|
|||
|
|
joint_cmd.rad = cmd.rad
|
|||
|
|
joint_cmd.vel = cmd.vel
|
|||
|
|
|
|||
|
|
# 调用RPC接口
|
|||
|
|
response = self.stub.moveJ(request)
|
|||
|
|
|
|||
|
|
return CMVRErrorCode.CMVR_SUCCESS if response.header.success else CMVRErrorCode.CMVR_RPC_FAILED
|
|||
|
|
|
|||
|
|
except grpc.RpcError as e:
|
|||
|
|
return CMVRErrorCode.CMVR_RPC_FAILED
|
|||
|
|
|
|||
|
|
# ==================== 直线空间运动 ====================
|
|||
|
|
|
|||
|
|
def moveL(self, ee_link: str, target_pose: Pose3D,
|
|||
|
|
vel: float = 0.1, acc: float = 0.5) -> Tuple[CMVRErrorCode, Optional[str]]:
|
|||
|
|
"""
|
|||
|
|
直线空间运动 (MoveL)
|
|||
|
|
|
|||
|
|
参数:
|
|||
|
|
ee_link: 末端执行器连杆名称
|
|||
|
|
target_pose: 目标位姿 (Pose3D)
|
|||
|
|
vel: 速度 (m/s)
|
|||
|
|
acc: 加速度 (m/s²)
|
|||
|
|
|
|||
|
|
返回:
|
|||
|
|
Tuple[CMVRErrorCode, Optional[str]]: 错误码和错误消息
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
generated = self._import_generated()
|
|||
|
|
# 创建MoveL请求
|
|||
|
|
request = generated.humanoid_robot_command_pb2.MoveL.Request()
|
|||
|
|
request.header.CopyFrom(self._create_command_header())
|
|||
|
|
request.ee_link = ee_link
|
|||
|
|
request.vel = vel
|
|||
|
|
request.acc = acc
|
|||
|
|
|
|||
|
|
# 设置目标位姿
|
|||
|
|
request.target_pose.x = target_pose.x
|
|||
|
|
request.target_pose.y = target_pose.y
|
|||
|
|
request.target_pose.z = target_pose.z
|
|||
|
|
request.target_pose.rx = target_pose.rx
|
|||
|
|
request.target_pose.ry = target_pose.ry
|
|||
|
|
request.target_pose.rz = target_pose.rz
|
|||
|
|
|
|||
|
|
# 调用RPC接口
|
|||
|
|
response = self.stub.moveL(request)
|
|||
|
|
return CMVRErrorCode.CMVR_SUCCESS if response.header.success else CMVRErrorCode.CMVR_RPC_FAILED
|
|||
|
|
|
|||
|
|
except grpc.RpcError as e:
|
|||
|
|
print(f"直线空间运动失败: {e}")
|
|||
|
|
return CMVRErrorCode.CMVR_RPC_FAILED
|
|||
|
|
|
|||
|
|
# ==================== 关节速度控制 ====================
|
|||
|
|
|
|||
|
|
def speedJ(self, joint_name: str, velocity: float,
|
|||
|
|
direction: RobotJointIndexDirection = RobotJointIndexDirection.FORWARD,
|
|||
|
|
acc: float = 0.5) -> Tuple[CMVRErrorCode, Optional[str]]:
|
|||
|
|
"""
|
|||
|
|
关节速度控制 (SpeedJ)
|
|||
|
|
|
|||
|
|
参数:
|
|||
|
|
joint_name: 关节名称
|
|||
|
|
velocity: 速度 (rad/s)
|
|||
|
|
direction: 运动方向
|
|||
|
|
acc: 加速度 (rad/s²)
|
|||
|
|
|
|||
|
|
返回:
|
|||
|
|
Tuple[CMVRErrorCode, Optional[str]]: 错误码和错误消息
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
generated = self._import_generated()
|
|||
|
|
# 创建SpeedJ请求
|
|||
|
|
request = generated.humanoid_robot_command_pb2.SpeedJ.Request()
|
|||
|
|
request.header.CopyFrom(self._create_command_header())
|
|||
|
|
request.joint_name = joint_name
|
|||
|
|
request.vel = abs(velocity) # 确保速度为正值
|
|||
|
|
request.acc = acc
|
|||
|
|
request.direction = direction.value
|
|||
|
|
|
|||
|
|
# 调用RPC接口
|
|||
|
|
response = self.stub.speedJ(request)
|
|||
|
|
return CMVRErrorCode.CMVR_SUCCESS if response.header.success else CMVRErrorCode.CMVR_RPC_FAILED
|
|||
|
|
|
|||
|
|
except grpc.RpcError as e:
|
|||
|
|
print(f"关节速度控制失败: {e}")
|
|||
|
|
return CMVRErrorCode.CMVR_RPC_FAILED, str(e)
|
|||
|
|
|
|||
|
|
# ==================== 笛卡尔速度控制 ====================
|
|||
|
|
|
|||
|
|
def speedL(self, ee_link: str, velocity: float,
|
|||
|
|
cartesian: RobotCartesian = RobotCartesian.X,
|
|||
|
|
direction: RobotJointIndexDirection = RobotJointIndexDirection.FORWARD,
|
|||
|
|
acc: float = 0.5) -> Tuple[CMVRErrorCode, Optional[str]]:
|
|||
|
|
"""
|
|||
|
|
笛卡尔速度控制 (SpeedL)
|
|||
|
|
|
|||
|
|
参数:
|
|||
|
|
ee_link: 末端执行器连杆名称
|
|||
|
|
velocity: 速度 (m/s 或 rad/s,取决于cartesian类型)
|
|||
|
|
cartesian: 笛卡尔坐标轴
|
|||
|
|
direction: 运动方向
|
|||
|
|
acc: 加速度 (m/s² 或 rad/s²)
|
|||
|
|
|
|||
|
|
返回:
|
|||
|
|
Tuple[CMVRErrorCode, Optional[str]]: 错误码和错误消息
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
generated = self._import_generated()
|
|||
|
|
# 创建SpeedL请求
|
|||
|
|
request = generated.humanoid_robot_command_pb2.SpeedL.Request()
|
|||
|
|
request.header.CopyFrom(self._create_command_header())
|
|||
|
|
request.ee_link = ee_link
|
|||
|
|
request.vel = abs(velocity)
|
|||
|
|
request.acc = acc
|
|||
|
|
request.cartesian = cartesian.value
|
|||
|
|
request.direction = direction.value
|
|||
|
|
|
|||
|
|
# 调用RPC接口
|
|||
|
|
response = self.stub.speedL(request)
|
|||
|
|
return CMVRErrorCode.CMVR_SUCCESS if response.header.success else CMVRErrorCode.CMVR_RPC_FAILED
|
|||
|
|
|
|||
|
|
except grpc.RpcError as e:
|
|||
|
|
print(f"笛卡尔速度控制失败: {e}")
|
|||
|
|
return CMVRErrorCode.CMVR_RPC_FAILED, str(e)
|
|||
|
|
|
|||
|
|
# ==================== 状态获取 ====================
|
|||
|
|
|
|||
|
|
def get_joint_states(self) -> Tuple[CMVRErrorCode, List[JointState]]:
|
|||
|
|
"""
|
|||
|
|
获取机器人所有关节状态
|
|||
|
|
|
|||
|
|
返回:
|
|||
|
|
Tuple[CMVRErrorCode, List[JointState]]: 错误码和关节状态对象列表
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
generated = self._import_generated()
|
|||
|
|
# 创建关节状态请求
|
|||
|
|
request = generated.humanoid_robot_command_pb2.JointRequest()
|
|||
|
|
request.header.CopyFrom(self._create_command_header())
|
|||
|
|
|
|||
|
|
# 调用RPC接口
|
|||
|
|
response = self.stub.getJointState(request)
|
|||
|
|
|
|||
|
|
if not response.header.success:
|
|||
|
|
print(f"RPC调用失败: {response.header}")
|
|||
|
|
return CMVRErrorCode.CMVR_RPC_FAILED, []
|
|||
|
|
|
|||
|
|
# 检查是否有状态数据
|
|||
|
|
if not response.state:
|
|||
|
|
print("响应中没有关节状态数据")
|
|||
|
|
return CMVRErrorCode.CMVR_NO_DATA, []
|
|||
|
|
|
|||
|
|
# 解析所有JointState
|
|||
|
|
joint_states = []
|
|||
|
|
for proto_joint_state in response.state:
|
|||
|
|
# 解析响应数据到JointState对象
|
|||
|
|
# 注意proto字段名和Python dataclass字段名的映射
|
|||
|
|
joint_state = JointState(
|
|||
|
|
name=list(proto_joint_state.name), # proto: name -> Python: name
|
|||
|
|
position=list(proto_joint_state.position), # proto: position -> Python: position
|
|||
|
|
velocity=list(proto_joint_state.velocity), # proto: velocity -> Python: velocity
|
|||
|
|
effort=list(proto_joint_state.effort), # proto: effort -> Python: effort
|
|||
|
|
timestamp=proto_joint_state.timestamp # proto: timestamp -> Python: timestamp
|
|||
|
|
)
|
|||
|
|
joint_states.append(joint_state)
|
|||
|
|
|
|||
|
|
if not joint_states:
|
|||
|
|
print("解析后没有有效的关节状态数据")
|
|||
|
|
return CMVRErrorCode.CMVR_NO_DATA, []
|
|||
|
|
|
|||
|
|
return CMVRErrorCode.CMVR_SUCCESS, joint_states
|
|||
|
|
|
|||
|
|
except grpc.RpcError as e:
|
|||
|
|
print(f"gRPC错误 - 获取关节状态失败: {e}")
|
|||
|
|
if hasattr(e, 'code'):
|
|||
|
|
if e.code() == grpc.StatusCode.NOT_FOUND:
|
|||
|
|
return CMVRErrorCode.CMVR_NOT_FOUND, []
|
|||
|
|
elif e.code() == grpc.StatusCode.UNAVAILABLE:
|
|||
|
|
return CMVRErrorCode.CMVR_CONNECTION_FAILED, []
|
|||
|
|
return CMVRErrorCode.CMVR_RPC_FAILED, []
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"未知错误 - 获取关节状态失败: {e}")
|
|||
|
|
import traceback
|
|||
|
|
traceback.print_exc()
|
|||
|
|
return CMVRErrorCode.CMVR_UNKNOWN_ERROR, []
|