cmvr-es-cli/cmvr/models.py
2025-10-21 09:33:47 +08:00

278 lines
9.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from dataclasses import dataclass, field
from typing import List
import numpy as np
@dataclass
class MicState:
"""麦克风状态"""
is_initialized: bool = False
is_running: bool = False
is_recording: bool = False
volume: int = 0
error_message: str = ""
@dataclass
class SpeakerState:
"""扬声器状态"""
is_initialized: bool = False
is_running: bool = False
is_decoding: bool = False
is_paused: bool = False
volume: int = 0
@dataclass
class CameraState:
"""相机状态"""
is_initialized: bool = False
is_opened: bool = False
is_streaming: bool = False
is_recording: bool = False
width: int = 0
height: int = 0
fps: int = 0
@dataclass
class CameraIntrinsics:
"""相机内参与Protobuf CameraIntrinsics对齐"""
cx: float = 0.0 # 对应Protobuf tag=1
cy: float = 0.0 # 对应Protobuf tag=2
fx: float = 0.0 # 对应Protobuf tag=3
fy: float = 0.0 # 对应Protobuf tag=4
# 畸变系数固定5个元素k1, k2, p1, p2, k3与Protobuf tag=5对齐
coeffs: List[float] = field(default_factory=lambda: [0.0]*5)
@dataclass
class FreedomState:
"""灵巧手自由度状态"""
dof_id: int = 0
angle: int = 0
speed: int = 0
force: int = 0
position: int = 0
current: int = 0
temperature: int = 0
error: int = 0
error_message: List[str] = field(default_factory=list)
@dataclass
class DexHandState:
"""灵巧手整体状态"""
is_initialized: bool = False
hands: List[FreedomState] = field(default_factory=lambda: [FreedomState() for _ in range(6)])
@dataclass
class FacialExpressionState:
"""面部表情状态"""
# 眉毛
left_eyebrow_outside_y: float = 0.0
left_eyebrow_inside_y: float = 0.0
right_eyebrow_outside_y: float = 0.0
right_eyebrow_inside_y: float = 0.0
# 眼睑
left_eye_upper_lid_y: float = 0.0
left_eye_lower_lid_y: float = 0.0
right_eye_upper_lid_y: float = 0.0
right_eye_lower_lid_y: float = 0.0
# 眼球
left_eye_ball_x: float = 0.0
left_eye_ball_y: float = 0.0
right_eye_ball_x: float = 0.0
right_eye_ball_y: float = 0.0
# 鼻子
left_nose_y: float = 0.0
right_nose_y: float = 0.0
# 嘴巴
upper_lip_y: float = 0.0
upper_lip_z: float = 0.0
lower_lip_y: float = 0.0
lower_lip_z: float = 0.0
# 左唇角
upper_left_lip_x: float = 0.0
upper_left_lip_y: float = 0.0
left_corner_lip_x: float = 0.0
left_corner_lip_y: float = 0.0
lower_left_lip_x: float = 0.0
lower_left_lip_y: float = 0.0
# 右唇角
upper_right_lip_x: float = 0.0
upper_right_lip_y: float = 0.0
right_corner_lip_x: float = 0.0
right_corner_lip_y: float = 0.0
lower_right_lip_x: float = 0.0
lower_right_lip_y: float = 0.0
# 下巴
jaw_x: float = 0.0
jaw_y: float = 0.0
@dataclass
class FingerTactileData:
"""手指触觉数据"""
data: np.ndarray = None
rows: int = 0
cols: int = 0
byteSize: int = 0
name: str = ""
def __post_init__(self):
if self.data is None and self.rows > 0 and self.cols > 0:
self.data = np.zeros((self.rows, self.cols), dtype=np.uint16)
@dataclass
class PalmTactileData:
"""手掌触觉数据"""
data: np.ndarray = None
rows: int = 8
cols: int = 14
byteSize: int = 224
name: str = "掌心"
def __post_init__(self):
if self.data is None:
self.data = np.zeros((self.rows, self.cols), dtype=np.uint16)
@dataclass
class HandTactileSensors:
"""整只手的触觉传感器数据"""
pinky_tip: FingerTactileData = None
pinky_finger: FingerTactileData = None
pinky_pad: FingerTactileData = None
ring_tip: FingerTactileData = None
ring_finger: FingerTactileData = None
ring_pad: FingerTactileData = None
middle_tip: FingerTactileData = None
middle_finger: FingerTactileData = None
middle_pad: FingerTactileData = None
index_tip: FingerTactileData = None
index_finger: FingerTactileData = None
index_pad: FingerTactileData = None
thumb_tip: FingerTactileData = None
thumb_finger: FingerTactileData = None
thumb_middle: FingerTactileData = None
thumb_pad: FingerTactileData = None
palm: PalmTactileData = None
def __post_init__(self):
"""初始化所有传感器数据"""
# 小拇指
self.pinky_tip = FingerTactileData(rows=3, cols=3, byteSize=18, name="小拇指指端")
self.pinky_finger = FingerTactileData(rows=12, cols=8, byteSize=192, name="小拇指指尖")
self.pinky_pad = FingerTactileData(rows=10, cols=8, byteSize=160, name="小拇指指腹")
# 无名指
self.ring_tip = FingerTactileData(rows=3, cols=3, byteSize=18, name="无名指指端")
self.ring_finger = FingerTactileData(rows=12, cols=8, byteSize=192, name="无名指指尖")
self.ring_pad = FingerTactileData(rows=10, cols=8, byteSize=160, name="无名指指腹")
# 中指
self.middle_tip = FingerTactileData(rows=3, cols=3, byteSize=18, name="中指指端")
self.middle_finger = FingerTactileData(rows=12, cols=8, byteSize=192, name="中指指尖")
self.middle_pad = FingerTactileData(rows=10, cols=8, byteSize=160, name="中指指腹")
# 食指
self.index_tip = FingerTactileData(rows=3, cols=3, byteSize=18, name="食指指端")
self.index_finger = FingerTactileData(rows=12, cols=8, byteSize=192, name="食指指尖")
self.index_pad = FingerTactileData(rows=10, cols=8, byteSize=160, name="食指指腹")
# 大拇指
self.thumb_tip = FingerTactileData(rows=3, cols=3, byteSize=18, name="大拇指指端")
self.thumb_finger = FingerTactileData(rows=12, cols=8, byteSize=192, name="大拇指尖")
self.thumb_middle = FingerTactileData(rows=3, cols=3, byteSize=18, name="大拇指指中")
self.thumb_pad = FingerTactileData(rows=12, cols=8, byteSize=192, name="大拇指指腹")
# 掌心
self.palm = PalmTactileData()
def get_finger_name(self, finger_type):
"""获取手指名称"""
from .enums import FingerType
names = {
FingerType.PINKY: "小拇指",
FingerType.RING: "无名指",
FingerType.MIDDLE: "中指",
FingerType.INDEX: "食指",
FingerType.THUMB: "大拇指"
}
return names.get(finger_type, "未知")
def print_summary(self):
"""打印传感器数据摘要信息"""
print(f" 小拇指指端: {self.pinky_tip.rows}x{self.pinky_tip.cols} (数据大小: {self.pinky_tip.byteSize} bytes)")
print(f" 无名指指端: {self.ring_tip.rows}x{self.ring_tip.cols} (数据大小: {self.ring_tip.byteSize} bytes)")
print(f" 中指指端: {self.middle_tip.rows}x{self.middle_tip.cols} (数据大小: {self.middle_tip.byteSize} bytes)")
print(f" 食指指端: {self.index_tip.rows}x{self.index_tip.cols} (数据大小: {self.index_tip.byteSize} bytes)")
print(f" 大拇指指端: {self.thumb_tip.rows}x{self.thumb_tip.cols} (数据大小: {self.thumb_tip.byteSize} bytes)")
print(f" 大拇指指中: {self.thumb_middle.rows}x{self.thumb_middle.cols} (数据大小: {self.thumb_middle.byteSize} bytes)")
print(f" 掌心: {self.palm.rows}x{self.palm.cols} (数据大小: {self.palm.byteSize} bytes)")
# 机器人相关数据结构
@dataclass
class JointCmd:
"""关节控制命令"""
joint_name: str = "" # 关节名称
rad: float = 0.0 # 弧度
vel: float = 0.0 # 角速度rad/s
@dataclass
class Pose3D:
"""3D位姿"""
x: float = 0.0 # X坐标
y: float = 0.0 # Y坐标
z: float = 0.0 # Z坐标
rx: float = 0.0 # 绕X轴旋转弧度
ry: float = 0.0 # 绕Y轴旋转弧度
rz: float = 0.0 # 绕Z轴旋转弧度
def to_array(self) -> np.ndarray:
"""转换为numpy数组"""
return np.array([self.x, self.y, self.z, self.rx, self.ry, self.rz])
@classmethod
def from_array(cls, arr: np.ndarray) -> 'Pose3D':
"""从numpy数组创建Pose3D"""
return cls(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5])
@dataclass
class JointState:
"""关节状态"""
name: List[str] = field(default_factory=list) # 关节名称列表
position: List[float] = field(default_factory=list) # 关节位置列表(弧度)
velocity: List[float] = field(default_factory=list) # 关节速度列表rad/s
effort: List[float] = field(default_factory=list) # 关节力矩列表
timestamp: float = 0.0 # 时间戳
def __post_init__(self):
"""确保列表长度一致"""
if len(self.name) > 0:
n_joints = len(self.name)
self.position = self.position[:n_joints] + [0.0] * (n_joints - len(self.position))
self.velocity = self.velocity[:n_joints] + [0.0] * (n_joints - len(self.velocity))
self.effort = self.effort[:n_joints] + [0.0] * (n_joints - len(self.effort))
def get_joint_by_name(self, joint_name: str) -> tuple:
"""根据关节名称获取状态"""
if joint_name in self.name:
idx = self.name.index(joint_name)
return (self.position[idx], self.velocity[idx], self.effort[idx])
return (0.0, 0.0, 0.0)
def to_dict(self) -> dict:
"""转换为字典格式"""
return {
'name': self.name,
'positions': self.position,
'velocities': self.velocity,
'efforts': self.effort,
'timestamp': self.timestamp
}