copy code file
This commit is contained in:
parent
2bc6a24f94
commit
8b9282c647
@ -15,14 +15,14 @@ Already a pro? Just edit this README.md and make it your own. Want to make it ea
|
|||||||
|
|
||||||
```
|
```
|
||||||
cd existing_repo
|
cd existing_repo
|
||||||
git remote add origin http://192.168.1.100:18088/smart_bench/cmvr-es-cli.git
|
git remote add origin http://10.148.20.36:18088/smart_bench/cmvr-es-cli.git
|
||||||
git branch -M main
|
git branch -M main
|
||||||
git push -uf origin main
|
git push -uf origin main
|
||||||
```
|
```
|
||||||
|
|
||||||
## Integrate with your tools
|
## Integrate with your tools
|
||||||
|
|
||||||
- [ ] [Set up project integrations](http://192.168.1.100:18088/smart_bench/cmvr-es-cli/-/settings/integrations)
|
- [ ] [Set up project integrations](http://10.148.20.36:18088/smart_bench/cmvr-es-cli/-/settings/integrations)
|
||||||
|
|
||||||
## Collaborate with your team
|
## Collaborate with your team
|
||||||
|
|
||||||
|
|||||||
15
cmvr/__init__.py
Normal file
15
cmvr/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
from .client import CMVRGrpcClient
|
||||||
|
from .enums import CMVRErrorCode, DeviceState, FingerType
|
||||||
|
from .models import (
|
||||||
|
MicState, SpeakerState, CameraState,
|
||||||
|
RH56DFTPDexHand, DexHandState, FacialExpressionState,
|
||||||
|
FingerTactileData, PalmTactileData, HandTactileSensors
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'CMVRGrpcClient',
|
||||||
|
'CMVRErrorCode', 'DeviceState', 'FingerType',
|
||||||
|
'MicState', 'SpeakerState', 'CameraState',
|
||||||
|
'RH56DFTPDexHand', 'DexHandState', 'FacialExpressionState',
|
||||||
|
'FingerTactileData', 'PalmTactileData', 'HandTactileSensors'
|
||||||
|
]
|
||||||
222
cmvr/biohead_client.py
Normal file
222
cmvr/biohead_client.py
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
import grpc
|
||||||
|
import time
|
||||||
|
from typing import Tuple
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from .enums import CMVRErrorCode
|
||||||
|
from .models import FacialExpressionState
|
||||||
|
|
||||||
|
class BioHeadClient:
|
||||||
|
"""仿生头客户端"""
|
||||||
|
|
||||||
|
def __init__(self, device_id: str, stub):
|
||||||
|
self.device_id = device_id
|
||||||
|
self.stub = stub
|
||||||
|
self.generated = None # 推迟导入
|
||||||
|
self.request_queue = [] # 请求队列
|
||||||
|
self.response_iterator = None # 响应迭代器
|
||||||
|
self.stream_active = False # 流是否活跃
|
||||||
|
|
||||||
|
def _import_generated(self):
|
||||||
|
"""推迟导入 generated"""
|
||||||
|
if self.generated is None:
|
||||||
|
try:
|
||||||
|
# 从正确的路径导入生成的模块
|
||||||
|
from generated.cmvr.api import common_pb2, biohead_command_pb2
|
||||||
|
self.generated = type('GeneratedModules', (), {
|
||||||
|
'common_pb2': common_pb2,
|
||||||
|
'biohead_command_pb2': biohead_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
|
||||||
|
# 使用正确的方式设置时间戳
|
||||||
|
from google.protobuf.timestamp_pb2 import Timestamp
|
||||||
|
timestamp = Timestamp()
|
||||||
|
timestamp.GetCurrentTime()
|
||||||
|
header.timestamp.CopyFrom(timestamp)
|
||||||
|
return header
|
||||||
|
|
||||||
|
def set_expression(self, expression: FacialExpressionState) -> CMVRErrorCode:
|
||||||
|
"""设置表情"""
|
||||||
|
try:
|
||||||
|
generated = self._import_generated()
|
||||||
|
# 创建请求
|
||||||
|
request = generated.biohead_command_pb2.SetFacialExpression.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
|
||||||
|
# 填充表情数据
|
||||||
|
expr = request.expression
|
||||||
|
expr.eyebrow.left_outside_y = expression.left_eyebrow_outside_y
|
||||||
|
expr.eyebrow.left_inside_y = expression.left_eyebrow_inside_y
|
||||||
|
expr.eyebrow.right_outside_y = expression.right_eyebrow_outside_y
|
||||||
|
expr.eyebrow.right_inside_y = expression.right_eyebrow_inside_y
|
||||||
|
|
||||||
|
expr.eyelid.left_upper_y = expression.left_eye_upper_lid_y
|
||||||
|
expr.eyelid.left_lower_y = expression.left_eye_lower_lid_y
|
||||||
|
expr.eyelid.right_upper_y = expression.right_eye_upper_lid_y
|
||||||
|
expr.eyelid.right_lower_y = expression.right_eye_lower_lid_y
|
||||||
|
|
||||||
|
expr.eyeball.left_x = expression.left_eye_ball_x
|
||||||
|
expr.eyeball.left_y = expression.left_eye_ball_y
|
||||||
|
expr.eyeball.right_x = expression.right_eye_ball_x
|
||||||
|
expr.eyeball.right_y = expression.right_eye_ball_y
|
||||||
|
|
||||||
|
expr.nose.left_y = expression.left_nose_y
|
||||||
|
expr.nose.right_y = expression.right_nose_y
|
||||||
|
|
||||||
|
expr.mouth.upper_lip_y = expression.upper_lip_y
|
||||||
|
expr.mouth.upper_lip_z = expression.upper_lip_z
|
||||||
|
expr.mouth.lower_lip_y = expression.lower_lip_y
|
||||||
|
expr.mouth.lower_lip_z = expression.lower_lip_z
|
||||||
|
|
||||||
|
expr.mouth.left_lip.upper_x = expression.upper_left_lip_x
|
||||||
|
expr.mouth.left_lip.upper_y = expression.upper_left_lip_y
|
||||||
|
expr.mouth.left_lip.corner_x = expression.left_corner_lip_x
|
||||||
|
expr.mouth.left_lip.corner_y = expression.left_corner_lip_y
|
||||||
|
expr.mouth.left_lip.lower_x = expression.lower_left_lip_x
|
||||||
|
expr.mouth.left_lip.lower_y = expression.lower_left_lip_y
|
||||||
|
|
||||||
|
expr.mouth.right_lip.upper_x = expression.upper_right_lip_x
|
||||||
|
expr.mouth.right_lip.upper_y = expression.upper_right_lip_y
|
||||||
|
expr.mouth.right_lip.corner_x = expression.right_corner_lip_x
|
||||||
|
expr.mouth.right_lip.corner_y = expression.right_corner_lip_y
|
||||||
|
expr.mouth.right_lip.lower_x = expression.lower_right_lip_x
|
||||||
|
expr.mouth.right_lip.lower_y = expression.lower_right_lip_y
|
||||||
|
|
||||||
|
expr.jaw.x = expression.jaw_x
|
||||||
|
expr.jaw.y = expression.jaw_y
|
||||||
|
|
||||||
|
# 发送请求
|
||||||
|
response = self.stub.SetExpression(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 start_stream(self):
|
||||||
|
"""开始流式会话"""
|
||||||
|
try:
|
||||||
|
if not self.stream_active:
|
||||||
|
# 创建一个请求迭代器生成器
|
||||||
|
self.request_queue = []
|
||||||
|
self.response_iterator = self.stub.StreamExpression(self._stream_request_generator())
|
||||||
|
self.stream_active = True
|
||||||
|
return CMVRErrorCode.CMVR_SUCCESS
|
||||||
|
except grpc.RpcError as e:
|
||||||
|
print(f"开始流式会话失败: {e}")
|
||||||
|
return CMVRErrorCode.CMVR_RPC_FAILED
|
||||||
|
|
||||||
|
def _stream_request_generator(self):
|
||||||
|
"""流式请求生成器"""
|
||||||
|
generated = self._import_generated()
|
||||||
|
while self.stream_active:
|
||||||
|
if self.request_queue:
|
||||||
|
request = self.request_queue.pop(0)
|
||||||
|
yield request
|
||||||
|
if request.eof:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# 如果没有请求,等待一小段时间
|
||||||
|
time.sleep(0.01)
|
||||||
|
|
||||||
|
def stream_expression(self, expression, is_eof=False):
|
||||||
|
"""流式控制表情"""
|
||||||
|
try:
|
||||||
|
if not self.stream_active:
|
||||||
|
result = self.start_stream()
|
||||||
|
if result != CMVRErrorCode.CMVR_SUCCESS:
|
||||||
|
return result
|
||||||
|
|
||||||
|
generated = self._import_generated()
|
||||||
|
|
||||||
|
# 创建请求
|
||||||
|
request = generated.biohead_command_pb2.StreamFacialExpression.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
request.expr.CopyFrom(expression)
|
||||||
|
request.eof = is_eof
|
||||||
|
|
||||||
|
# 将请求添加到队列
|
||||||
|
self.request_queue.append(request)
|
||||||
|
|
||||||
|
# 读取最新的响应
|
||||||
|
try:
|
||||||
|
feedback = next(self.response_iterator)
|
||||||
|
return CMVRErrorCode.CMVR_SUCCESS if feedback.header.success else CMVRErrorCode.CMVR_RPC_FAILED
|
||||||
|
except StopIteration:
|
||||||
|
return CMVRErrorCode.CMVR_SUCCESS
|
||||||
|
|
||||||
|
except grpc.RpcError as e:
|
||||||
|
print(f"流式控制表情失败: {e}")
|
||||||
|
return CMVRErrorCode.CMVR_RPC_FAILED
|
||||||
|
|
||||||
|
def end_stream(self):
|
||||||
|
"""结束流式会话"""
|
||||||
|
try:
|
||||||
|
if self.stream_active:
|
||||||
|
# 发送结束信号
|
||||||
|
generated = self._import_generated()
|
||||||
|
request = generated.biohead_command_pb2.StreamFacialExpression.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
request.eof = True
|
||||||
|
|
||||||
|
self.request_queue.append(request)
|
||||||
|
|
||||||
|
# 等待所有响应
|
||||||
|
for feedback in self.response_iterator:
|
||||||
|
if not feedback.header.success:
|
||||||
|
return CMVRErrorCode.CMVR_RPC_FAILED
|
||||||
|
|
||||||
|
self.stream_active = False
|
||||||
|
self.request_queue = []
|
||||||
|
self.response_iterator = None
|
||||||
|
|
||||||
|
return CMVRErrorCode.CMVR_SUCCESS
|
||||||
|
except grpc.RpcError as e:
|
||||||
|
print(f"结束流式会话失败: {e}")
|
||||||
|
return CMVRErrorCode.CMVR_RPC_FAILED
|
||||||
|
|
||||||
|
def get_system_status(self):
|
||||||
|
"""获取系统状态"""
|
||||||
|
try:
|
||||||
|
generated = self._import_generated()
|
||||||
|
request = generated.biohead_command_pb2.GetStatus.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
|
||||||
|
response = self.stub.GetSystemStatus(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 emergency_stop(self):
|
||||||
|
"""紧急停止"""
|
||||||
|
try:
|
||||||
|
generated = self._import_generated()
|
||||||
|
request = generated.biohead_command_pb2.EmergencyStop.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
|
||||||
|
response = self.stub.EmergencyStop(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 close(self):
|
||||||
|
"""关闭连接"""
|
||||||
|
if self.stream_active:
|
||||||
|
self.end_stream()
|
||||||
138
cmvr/camera_client.py
Normal file
138
cmvr/camera_client.py
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
import grpc
|
||||||
|
import numpy as np
|
||||||
|
from PIL import Image
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
from . import generated
|
||||||
|
from .enums import CMVRErrorCode
|
||||||
|
from .models import CameraState
|
||||||
|
|
||||||
|
class CameraClient:
|
||||||
|
"""相机客户端"""
|
||||||
|
|
||||||
|
def __init__(self, device_id: str, stub):
|
||||||
|
self.device_id = device_id
|
||||||
|
self.stub = stub
|
||||||
|
|
||||||
|
def _create_command_header(self):
|
||||||
|
"""创建命令头"""
|
||||||
|
header = generated.common_pb2.CommandHeader.Request()
|
||||||
|
header.device_id = self.device_id
|
||||||
|
header.timestamp.GetCurrentTime()
|
||||||
|
return header
|
||||||
|
|
||||||
|
def get_status(self) -> Tuple[CMVRErrorCode, CameraState]:
|
||||||
|
"""获取相机状态"""
|
||||||
|
try:
|
||||||
|
request = generated.camera_service_pb2.GetCameraStateCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
|
||||||
|
response = self.stub.GetStatus(request)
|
||||||
|
|
||||||
|
if not response.header.success:
|
||||||
|
return CMVRErrorCode.CMVR_RPC_FAILED, CameraState()
|
||||||
|
|
||||||
|
state = CameraState(
|
||||||
|
is_initialized=response.state.is_initialized,
|
||||||
|
is_opened=response.state.is_opened,
|
||||||
|
is_streaming=response.state.is_streaming,
|
||||||
|
is_recording=response.state.is_recording,
|
||||||
|
width=response.state.width,
|
||||||
|
height=response.state.height,
|
||||||
|
fps=response.state.fps
|
||||||
|
)
|
||||||
|
|
||||||
|
return CMVRErrorCode.CMVR_SUCCESS, state
|
||||||
|
|
||||||
|
except grpc.RpcError as e:
|
||||||
|
print(f"获取相机状态失败: {e}")
|
||||||
|
return CMVRErrorCode.CMVR_RPC_FAILED, CameraState()
|
||||||
|
|
||||||
|
def start_camera(self) -> CMVRErrorCode:
|
||||||
|
"""启动相机"""
|
||||||
|
try:
|
||||||
|
request = generated.camera_service_pb2.StartCameraCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
|
||||||
|
response = self.stub.StartCamera(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 stop_camera(self) -> CMVRErrorCode:
|
||||||
|
"""停止相机"""
|
||||||
|
try:
|
||||||
|
request = generated.camera_service_pb2.StopCameraCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
|
||||||
|
response = self.stub.StopCamera(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 get_rgb_image(self) -> Tuple[CMVRErrorCode, np.ndarray, int, int]:
|
||||||
|
"""获取RGB图像"""
|
||||||
|
try:
|
||||||
|
request = generated.camera_service_pb2.GetRGBImageCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
|
||||||
|
response = self.stub.GetRGBImage(request)
|
||||||
|
|
||||||
|
if not response.header.success:
|
||||||
|
return CMVRErrorCode.CMVR_RPC_FAILED, np.array([]), 0, 0
|
||||||
|
|
||||||
|
width = response.color_frame.width
|
||||||
|
height = response.color_frame.height
|
||||||
|
size = len(response.color_frame.data)
|
||||||
|
|
||||||
|
# 将字节数据转换为numpy数组
|
||||||
|
img_data = np.frombuffer(response.color_frame.data, dtype=np.uint8)
|
||||||
|
|
||||||
|
# 重塑为图像格式 (height, width, channels)
|
||||||
|
channels = 3 # 假设是RGB图像
|
||||||
|
if size == width * height * channels:
|
||||||
|
img_array = img_data.reshape((height, width, channels))
|
||||||
|
else:
|
||||||
|
# 可能是灰度图或其他格式
|
||||||
|
img_array = img_data.reshape((height, width))
|
||||||
|
|
||||||
|
return CMVRErrorCode.CMVR_SUCCESS, img_array, width, height
|
||||||
|
|
||||||
|
except grpc.RpcError as e:
|
||||||
|
print(f"获取图像失败: {e}")
|
||||||
|
return CMVRErrorCode.CMVR_RPC_FAILED, np.array([]), 0, 0
|
||||||
|
|
||||||
|
def start_record(self, video_path: str) -> CMVRErrorCode:
|
||||||
|
"""开始录像"""
|
||||||
|
try:
|
||||||
|
request = generated.camera_service_pb2.StartCameraRecordingCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
request.video_path = video_path
|
||||||
|
|
||||||
|
response = self.stub.StartRecording(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 stop_record(self) -> CMVRErrorCode:
|
||||||
|
"""停止录像"""
|
||||||
|
try:
|
||||||
|
request = generated.camera_service_pb2.StopCameraRecordingCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
|
||||||
|
response = self.stub.StopRecording(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
|
||||||
73
cmvr/client.py
Normal file
73
cmvr/client.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import grpc
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
from .enums import CMVRErrorCode
|
||||||
|
from .biohead_client import BioHeadClient
|
||||||
|
|
||||||
|
|
||||||
|
class CMVRGrpcClient:
|
||||||
|
"""CMVR gRPC 主客户端"""
|
||||||
|
|
||||||
|
def __init__(self, server_address: str):
|
||||||
|
self.server_address = server_address
|
||||||
|
self.connected = False
|
||||||
|
|
||||||
|
# 创建 gRPC 通道
|
||||||
|
self.channel = grpc.insecure_channel(server_address)
|
||||||
|
|
||||||
|
# 初始化各服务的存根
|
||||||
|
self.generated = None # 推迟导入
|
||||||
|
self.biohead_stub = None
|
||||||
|
self.biohead_map: Dict[str, BioHeadClient] = {}
|
||||||
|
|
||||||
|
# 检查连接状态
|
||||||
|
try:
|
||||||
|
grpc.channel_ready_future(self.channel).result(timeout=5)
|
||||||
|
self.connected = True
|
||||||
|
self._import_generated() # 在连接成功后才导入 generated
|
||||||
|
self.biohead_stub = self.generated.biohead_service_pb2_grpc.BioHeadServiceStub(self.channel)
|
||||||
|
except grpc.FutureTimeoutError:
|
||||||
|
print(f"连接服务器超时: {server_address}")
|
||||||
|
self.connected = False
|
||||||
|
|
||||||
|
def _import_generated(self):
|
||||||
|
"""推迟导入 generated"""
|
||||||
|
if self.generated is None:
|
||||||
|
try:
|
||||||
|
# 从正确的路径导入生成的模块
|
||||||
|
from generated.cmvr.api import biohead_service_pb2_grpc
|
||||||
|
from generated.cmvr.api import common_pb2, biohead_command_pb2
|
||||||
|
self.generated = type('GeneratedModules', (), {
|
||||||
|
'biohead_service_pb2_grpc': biohead_service_pb2_grpc,
|
||||||
|
'common_pb2': common_pb2,
|
||||||
|
'biohead_command_pb2': biohead_command_pb2
|
||||||
|
})
|
||||||
|
except ImportError as e:
|
||||||
|
print(f"导入生成的模块失败: {e}")
|
||||||
|
print("请确保已生成 protobuf 代码")
|
||||||
|
raise
|
||||||
|
return self.generated
|
||||||
|
|
||||||
|
def is_connected(self) -> bool:
|
||||||
|
"""检查连接状态"""
|
||||||
|
return self.connected
|
||||||
|
|
||||||
|
def get_biohead(self, device_id: str) -> BioHeadClient:
|
||||||
|
"""获取仿生头客户端"""
|
||||||
|
if device_id not in self.biohead_map:
|
||||||
|
self.biohead_map[device_id] = BioHeadClient(device_id, self.biohead_stub)
|
||||||
|
return self.biohead_map[device_id]
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""关闭所有连接"""
|
||||||
|
# 关闭所有流式连接
|
||||||
|
for biohead in self.biohead_map.values():
|
||||||
|
biohead.close()
|
||||||
|
|
||||||
|
# 关闭 gRPC 通道
|
||||||
|
self.channel.close()
|
||||||
|
|
||||||
|
# 清空所有映射
|
||||||
|
self.biohead_map.clear()
|
||||||
|
|
||||||
|
self.connected = False
|
||||||
89
cmvr/dexhand_client.py
Normal file
89
cmvr/dexhand_client.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import grpc
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
from . import generated
|
||||||
|
from .enums import CMVRErrorCode
|
||||||
|
from .models import DexHandState, RH56DFTPDexHand
|
||||||
|
|
||||||
|
class DexHandClient:
|
||||||
|
"""灵巧手客户端"""
|
||||||
|
|
||||||
|
def __init__(self, device_id: str, stub):
|
||||||
|
self.device_id = device_id
|
||||||
|
self.stub = stub
|
||||||
|
|
||||||
|
def _create_command_header(self):
|
||||||
|
"""创建命令头"""
|
||||||
|
header = generated.common_pb2.CommandHeader.Request()
|
||||||
|
header.device_id = self.device_id
|
||||||
|
header.timestamp.GetCurrentTime()
|
||||||
|
return header
|
||||||
|
|
||||||
|
def get_status(self) -> Tuple[CMVRErrorCode, DexHandState]:
|
||||||
|
"""获取灵巧手状态"""
|
||||||
|
try:
|
||||||
|
request = generated.dexhand_service_pb2.GetDexHandStateCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
|
||||||
|
response = self.stub.GetStatus(request)
|
||||||
|
|
||||||
|
if not response.header.success:
|
||||||
|
return CMVRErrorCode.CMVR_RPC_FAILED, DexHandState()
|
||||||
|
|
||||||
|
state = DexHandState(
|
||||||
|
is_initialized=response.state.is_initialized
|
||||||
|
)
|
||||||
|
|
||||||
|
# 填充手指状态
|
||||||
|
for i, hand_state in enumerate(response.state.hands):
|
||||||
|
if i < len(state.hands):
|
||||||
|
state.hands[i] = RH56DFTPDexHand(
|
||||||
|
angle=hand_state.angle,
|
||||||
|
speed=hand_state.speed,
|
||||||
|
force=hand_state.force,
|
||||||
|
position=hand_state.position,
|
||||||
|
current=hand_state.current,
|
||||||
|
temperature=hand_state.temperature,
|
||||||
|
error=hand_state.error,
|
||||||
|
error_message=list(hand_state.error_message)
|
||||||
|
)
|
||||||
|
|
||||||
|
return CMVRErrorCode.CMVR_SUCCESS, state
|
||||||
|
|
||||||
|
except grpc.RpcError as e:
|
||||||
|
print(f"获取灵巧手状态失败: {e}")
|
||||||
|
return CMVRErrorCode.CMVR_RPC_FAILED, DexHandState()
|
||||||
|
|
||||||
|
def set_position(self, dof_id: int, value: float) -> CMVRErrorCode:
|
||||||
|
"""设置位置"""
|
||||||
|
try:
|
||||||
|
request = generated.dexhand_service_pb2.SetDexHandPositionsCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
|
||||||
|
# 添加自由度值
|
||||||
|
freedom_value = request.values.add()
|
||||||
|
freedom_value.id = dof_id
|
||||||
|
freedom_value.value = value
|
||||||
|
|
||||||
|
response = self.stub.SetDexHandPos(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 set_preset_act(self, preset_act_id: int) -> CMVRErrorCode:
|
||||||
|
"""设置预设动作"""
|
||||||
|
try:
|
||||||
|
request = generated.dexhand_service_pb2.SetDexHandPresetActCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
request.presetactid = preset_act_id
|
||||||
|
|
||||||
|
response = self.stub.SetDexHandPresetAct(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
|
||||||
27
cmvr/enums.py
Normal file
27
cmvr/enums.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class CMVRErrorCode(Enum):
|
||||||
|
"""错误码定义"""
|
||||||
|
CMVR_SUCCESS = 0
|
||||||
|
CMVR_CONNECT_FAILED = 1
|
||||||
|
CMVR_INVALID_PARAM = 2
|
||||||
|
CMVR_RPC_FAILED = 3
|
||||||
|
CMVR_NOT_CONNECTED = 4
|
||||||
|
CMVR_INTERNAL_ERROR = 5
|
||||||
|
|
||||||
|
class DeviceState(Enum):
|
||||||
|
"""设备状态"""
|
||||||
|
STATE_INIT = 0
|
||||||
|
STATE_READY = 1
|
||||||
|
STATE_RUNNING = 2
|
||||||
|
STATE_ERROR = 3
|
||||||
|
STATE_ESTOP = 4
|
||||||
|
STATE_STOP = 5
|
||||||
|
|
||||||
|
class FingerType(Enum):
|
||||||
|
"""手指类型枚举"""
|
||||||
|
PINKY = 0 # 小拇指
|
||||||
|
RING = 1 # 无名指
|
||||||
|
MIDDLE = 2 # 中指
|
||||||
|
INDEX = 3 # 食指
|
||||||
|
THUMB = 4 # 大拇指
|
||||||
74
cmvr/micphone_client.py
Normal file
74
cmvr/micphone_client.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import grpc
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
from . import generated
|
||||||
|
from .enums import CMVRErrorCode
|
||||||
|
from .models import MicState
|
||||||
|
|
||||||
|
class MicphoneClient:
|
||||||
|
"""麦克风客户端"""
|
||||||
|
|
||||||
|
def __init__(self, device_id: str, stub):
|
||||||
|
self.device_id = device_id
|
||||||
|
self.stub = stub
|
||||||
|
|
||||||
|
def _create_command_header(self):
|
||||||
|
"""创建命令头"""
|
||||||
|
header = generated.common_pb2.CommandHeader.Request()
|
||||||
|
header.device_id = self.device_id
|
||||||
|
header.timestamp.GetCurrentTime()
|
||||||
|
return header
|
||||||
|
|
||||||
|
def get_status(self) -> Tuple[CMVRErrorCode, MicState]:
|
||||||
|
"""获取麦克风状态"""
|
||||||
|
try:
|
||||||
|
request = generated.microphone_service_pb2.GetMicStateCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
|
||||||
|
response = self.stub.GetStatus(request)
|
||||||
|
|
||||||
|
if not response.header.success:
|
||||||
|
return CMVRErrorCode.CMVR_RPC_FAILED, MicState()
|
||||||
|
|
||||||
|
state = MicState(
|
||||||
|
is_initialized=response.state.is_initialized,
|
||||||
|
is_running=response.state.is_running,
|
||||||
|
is_recording=response.state.is_recording,
|
||||||
|
volume=response.state.volume,
|
||||||
|
error_message=response.state.error_message
|
||||||
|
)
|
||||||
|
|
||||||
|
return CMVRErrorCode.CMVR_SUCCESS, state
|
||||||
|
|
||||||
|
except grpc.RpcError as e:
|
||||||
|
print(f"获取麦克风状态失败: {e}")
|
||||||
|
return CMVRErrorCode.CMVR_RPC_FAILED, MicState()
|
||||||
|
|
||||||
|
def start_record(self, audio_path: str) -> CMVRErrorCode:
|
||||||
|
"""开始录音"""
|
||||||
|
try:
|
||||||
|
request = generated.microphone_service_pb2.StartMicRecordingCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
request.file_path = audio_path
|
||||||
|
|
||||||
|
response = self.stub.StartRecord(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 stop_record(self) -> CMVRErrorCode:
|
||||||
|
"""停止录音"""
|
||||||
|
try:
|
||||||
|
request = generated.microphone_service_pb2.StopMicRecordingCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
|
||||||
|
response = self.stub.StopRecord(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
|
||||||
197
cmvr/models.py
Normal file
197
cmvr/models.py
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
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 RH56DFTPDexHand:
|
||||||
|
"""灵巧手自由度状态"""
|
||||||
|
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[RH56DFTPDexHand] = field(default_factory=lambda: [RH56DFTPDexHand() 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, "未知")
|
||||||
134
cmvr/speaker_client.py
Normal file
134
cmvr/speaker_client.py
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
import grpc
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
from . import generated
|
||||||
|
from .enums import CMVRErrorCode
|
||||||
|
from .models import SpeakerState
|
||||||
|
|
||||||
|
class SpeakerClient:
|
||||||
|
"""扬声器客户端"""
|
||||||
|
|
||||||
|
def __init__(self, device_id: str, stub):
|
||||||
|
self.device_id = device_id
|
||||||
|
self.stub = stub
|
||||||
|
|
||||||
|
def _create_command_header(self):
|
||||||
|
"""创建命令头"""
|
||||||
|
header = generated.common_pb2.CommandHeader.Request()
|
||||||
|
header.device_id = self.device_id
|
||||||
|
header.timestamp.GetCurrentTime()
|
||||||
|
return header
|
||||||
|
|
||||||
|
def get_status(self) -> Tuple[CMVRErrorCode, SpeakerState]:
|
||||||
|
"""获取扬声器状态"""
|
||||||
|
try:
|
||||||
|
request = generated.speaker_service_pb2.GetSpeakerStateCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
|
||||||
|
response = self.stub.GetStatus(request)
|
||||||
|
|
||||||
|
if not response.header.success:
|
||||||
|
return CMVRErrorCode.CMVR_RPC_FAILED, SpeakerState()
|
||||||
|
|
||||||
|
state = SpeakerState(
|
||||||
|
is_initialized=response.state.is_initialized,
|
||||||
|
is_running=response.state.is_running,
|
||||||
|
is_decoding=response.state.is_decoding,
|
||||||
|
is_paused=response.state.is_paused,
|
||||||
|
volume=response.state.volume
|
||||||
|
)
|
||||||
|
|
||||||
|
return CMVRErrorCode.CMVR_SUCCESS, state
|
||||||
|
|
||||||
|
except grpc.RpcError as e:
|
||||||
|
print(f"获取扬声器状态失败: {e}")
|
||||||
|
return CMVRErrorCode.CMVR_RPC_FAILED, SpeakerState()
|
||||||
|
|
||||||
|
def play_audio(self, audio_path: str) -> CMVRErrorCode:
|
||||||
|
"""播放音频"""
|
||||||
|
try:
|
||||||
|
request = generated.speaker_service_pb2.PlayAudioCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
request.audio_path = audio_path
|
||||||
|
|
||||||
|
response = self.stub.PlayAudio(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 pause_audio(self) -> CMVRErrorCode:
|
||||||
|
"""暂停播放"""
|
||||||
|
try:
|
||||||
|
request = generated.speaker_service_pb2.PauseSpeakerCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
|
||||||
|
response = self.stub.PausePlayback(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 resume_audio(self) -> CMVRErrorCode:
|
||||||
|
"""继续播放"""
|
||||||
|
try:
|
||||||
|
request = generated.speaker_service_pb2.ResumeSpeakerCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
|
||||||
|
response = self.stub.ResumePlayback(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 stop_audio(self) -> CMVRErrorCode:
|
||||||
|
"""停止播放"""
|
||||||
|
try:
|
||||||
|
request = generated.speaker_service_pb2.StopSpeakerCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
|
||||||
|
response = self.stub.StopPlayback(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 set_volume(self, volume: float) -> CMVRErrorCode:
|
||||||
|
"""设置音量"""
|
||||||
|
try:
|
||||||
|
request = generated.speaker_service_pb2.SetSpeakerVolumeCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
request.volume = volume
|
||||||
|
|
||||||
|
response = self.stub.SetVolume(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 get_volume(self) -> Tuple[CMVRErrorCode, float]:
|
||||||
|
"""获取音量"""
|
||||||
|
try:
|
||||||
|
request = generated.speaker_service_pb2.GetSpeakerVolumeCommand.Request()
|
||||||
|
request.header.CopyFrom(self._create_command_header())
|
||||||
|
|
||||||
|
response = self.stub.GetVolume(request)
|
||||||
|
|
||||||
|
if not response.header.success:
|
||||||
|
return CMVRErrorCode.CMVR_RPC_FAILED, 0.0
|
||||||
|
|
||||||
|
return CMVRErrorCode.CMVR_SUCCESS, response.volume
|
||||||
|
|
||||||
|
except grpc.RpcError as e:
|
||||||
|
print(f"获取音量失败: {e}")
|
||||||
|
return CMVRErrorCode.CMVR_RPC_FAILED, 0.0
|
||||||
135
example_usage.py
Normal file
135
example_usage.py
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import random # 导入 random 模块
|
||||||
|
|
||||||
|
# 添加项目根目录到 Python 路径
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
# 直接导入生成的模块
|
||||||
|
from generated.cmvr.api import biohead_command_pb2
|
||||||
|
from cmvr import CMVRGrpcClient, CMVRErrorCode, FacialExpressionState
|
||||||
|
|
||||||
|
def test_biohead():
|
||||||
|
"""测试仿生头功能"""
|
||||||
|
print("=== 测试仿生头功能 ===")
|
||||||
|
|
||||||
|
# 初始化客户端
|
||||||
|
client = CMVRGrpcClient("192.168.1.6:50051")
|
||||||
|
if not client.is_connected():
|
||||||
|
print("连接服务器失败")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 获取仿生头
|
||||||
|
biohead = client.get_biohead("bio_head")
|
||||||
|
|
||||||
|
# 创建表情
|
||||||
|
expression = FacialExpressionState()
|
||||||
|
|
||||||
|
# 设置眉毛,使用随机数并打印
|
||||||
|
expression.left_eyebrow_outside_y = random.uniform(0, 1)
|
||||||
|
expression.left_eyebrow_inside_y = random.uniform(0, 1)
|
||||||
|
expression.right_eyebrow_outside_y = random.uniform(0, 1)
|
||||||
|
expression.right_eyebrow_inside_y = random.uniform(0, 1)
|
||||||
|
print(f"眉毛设置: 左外 {expression.left_eyebrow_outside_y}, 左内 {expression.left_eyebrow_inside_y}, 右外 {expression.right_eyebrow_outside_y}, 右内 {expression.right_eyebrow_inside_y}")
|
||||||
|
|
||||||
|
# 设置眼睑,使用随机数并打印
|
||||||
|
expression.left_eye_upper_lid_y = random.uniform(0, 1)
|
||||||
|
expression.left_eye_lower_lid_y = random.uniform(0, 1)
|
||||||
|
expression.right_eye_upper_lid_y = random.uniform(0, 1)
|
||||||
|
expression.right_eye_lower_lid_y = random.uniform(0, 1)
|
||||||
|
print(f"眼睑设置: 左上 {expression.left_eye_upper_lid_y}, 左下 {expression.left_eye_lower_lid_y}, 右上 {expression.right_eye_upper_lid_y}, 右下 {expression.right_eye_lower_lid_y}")
|
||||||
|
|
||||||
|
# 设置眼球,使用随机数并打印
|
||||||
|
expression.left_eye_ball_x = random.uniform(0, 1)
|
||||||
|
expression.left_eye_ball_y = random.uniform(0, 1)
|
||||||
|
expression.right_eye_ball_x = random.uniform(0, 1)
|
||||||
|
expression.right_eye_ball_y = random.uniform(0, 1)
|
||||||
|
print(f"眼球设置: 左X {expression.left_eye_ball_x}, 左Y {expression.left_eye_ball_y}, 右X {expression.right_eye_ball_x}, 右Y {expression.right_eye_ball_y}")
|
||||||
|
|
||||||
|
# 设置嘴巴,使用随机数并打印
|
||||||
|
expression.upper_lip_y = random.uniform(0, 1)
|
||||||
|
expression.lower_lip_y = random.uniform(0, 1)
|
||||||
|
print(f"嘴巴设置: 上唇 {expression.upper_lip_y}, 下唇 {expression.lower_lip_y}")
|
||||||
|
|
||||||
|
# 设置下巴,使用随机数并打印
|
||||||
|
expression.jaw_x = random.uniform(0, 1)
|
||||||
|
expression.jaw_y = random.uniform(0, 1)
|
||||||
|
print(f"下巴设置: X {expression.jaw_x}, Y {expression.jaw_y}")
|
||||||
|
|
||||||
|
# 设置表情
|
||||||
|
result = biohead.set_expression(expression)
|
||||||
|
if result == CMVRErrorCode.CMVR_SUCCESS:
|
||||||
|
print("设置表情成功")
|
||||||
|
else:
|
||||||
|
print(f"设置表情失败,错误码: {result}")
|
||||||
|
|
||||||
|
# 测试流式控制
|
||||||
|
print("开始流式控制表情 (5次眨眼)...")
|
||||||
|
result = biohead.start_stream()
|
||||||
|
if result != CMVRErrorCode.CMVR_SUCCESS:
|
||||||
|
print(f"开始流式会话失败,错误码: {result}")
|
||||||
|
else:
|
||||||
|
for i in range(5):
|
||||||
|
# 创建 protobuf 消息用于流式传输
|
||||||
|
proto_expr = biohead_command_pb2.FacialExpression()
|
||||||
|
|
||||||
|
# 睁眼,使用随机数并打印
|
||||||
|
proto_expr.eyelid.left_upper_y = random.uniform(0, 1)
|
||||||
|
proto_expr.eyelid.left_lower_y = random.uniform(0, 1)
|
||||||
|
proto_expr.eyelid.right_upper_y = random.uniform(0, 1)
|
||||||
|
proto_expr.eyelid.right_lower_y = random.uniform(0, 1)
|
||||||
|
print(f"第{i+1}次流式睁眼设置: 左上 {proto_expr.eyelid.left_upper_y}, 左下 {proto_expr.eyelid.left_lower_y}, 右上 {proto_expr.eyelid.right_upper_y}, 右下 {proto_expr.eyelid.right_lower_y}")
|
||||||
|
|
||||||
|
result = biohead.stream_expression(proto_expr)
|
||||||
|
if result == CMVRErrorCode.CMVR_SUCCESS:
|
||||||
|
print(f"第{i+1}次流式睁眼成功")
|
||||||
|
else:
|
||||||
|
print(f"第{i+1}次流式睁眼失败,错误码: {result}")
|
||||||
|
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
# 闭眼,使用随机数并打印
|
||||||
|
proto_expr.eyelid.left_upper_y = random.uniform(0, 1)
|
||||||
|
proto_expr.eyelid.left_lower_y = random.uniform(0, 1)
|
||||||
|
proto_expr.eyelid.right_upper_y = random.uniform(0, 1)
|
||||||
|
proto_expr.eyelid.right_lower_y = random.uniform(0, 1)
|
||||||
|
print(f"第{i+1}次流式闭眼设置: 左上 {proto_expr.eyelid.left_upper_y}, 左下 {proto_expr.eyelid.left_lower_y}, 右上 {proto_expr.eyelid.right_upper_y}, 右下 {proto_expr.eyelid.right_lower_y}")
|
||||||
|
|
||||||
|
result = biohead.stream_expression(proto_expr)
|
||||||
|
if result == CMVRErrorCode.CMVR_SUCCESS:
|
||||||
|
print(f"第{i+1}次流式闭眼成功")
|
||||||
|
else:
|
||||||
|
print(f"第{i+1}次流式闭眼失败,错误码: {result}")
|
||||||
|
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
|
# 结束流式会话
|
||||||
|
result = biohead.end_stream()
|
||||||
|
if result == CMVRErrorCode.CMVR_SUCCESS:
|
||||||
|
print("结束流式会话成功")
|
||||||
|
else:
|
||||||
|
print(f"结束流式会话失败,错误码: {result}")
|
||||||
|
|
||||||
|
print("流式控制完成")
|
||||||
|
|
||||||
|
# 获取系统状态
|
||||||
|
result = biohead.get_system_status()
|
||||||
|
if result == CMVRErrorCode.CMVR_SUCCESS:
|
||||||
|
print("获取系统状态成功")
|
||||||
|
else:
|
||||||
|
print(f"获取系统状态失败,错误码: {result}")
|
||||||
|
|
||||||
|
# 测试紧急停止
|
||||||
|
result = biohead.emergency_stop()
|
||||||
|
if result == CMVRErrorCode.CMVR_SUCCESS:
|
||||||
|
print("紧急停止成功")
|
||||||
|
else:
|
||||||
|
print(f"紧急停止失败,错误码: {result}")
|
||||||
|
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_biohead()
|
||||||
74
generate_proto.py
Normal file
74
generate_proto.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def generate_proto():
|
||||||
|
# 获取当前目录
|
||||||
|
current_dir = Path(__file__).parent
|
||||||
|
proto_dir = current_dir / "protos"
|
||||||
|
generated_dir = current_dir / "generated"
|
||||||
|
|
||||||
|
# 创建生成的目录
|
||||||
|
generated_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# 编译所有 .proto 文件
|
||||||
|
proto_files = []
|
||||||
|
for root, _, files in os.walk(proto_dir):
|
||||||
|
for file in files:
|
||||||
|
if file.endswith(".proto"):
|
||||||
|
proto_files.append(os.path.join(root, file))
|
||||||
|
|
||||||
|
for proto_path in proto_files:
|
||||||
|
# 计算相对路径
|
||||||
|
rel_path = os.path.relpath(proto_path, proto_dir)
|
||||||
|
output_dir = generated_dir
|
||||||
|
|
||||||
|
cmd = [
|
||||||
|
"python", "-m", "grpc_tools.protoc",
|
||||||
|
f"-I{proto_dir}",
|
||||||
|
f"--python_out={output_dir}",
|
||||||
|
f"--grpc_python_out={output_dir}",
|
||||||
|
proto_path
|
||||||
|
]
|
||||||
|
|
||||||
|
print(f"Generating code for {rel_path}...")
|
||||||
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
print(f"Error generating {rel_path}:")
|
||||||
|
print(result.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("Proto files generated successfully!")
|
||||||
|
|
||||||
|
# 修复生成的代码中的导入路径
|
||||||
|
fix_imports(generated_dir)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def fix_imports(generated_dir):
|
||||||
|
"""修复生成的代码中的导入路径"""
|
||||||
|
for root, _, files in os.walk(generated_dir):
|
||||||
|
for file in files:
|
||||||
|
if file.endswith(".py"):
|
||||||
|
file_path = os.path.join(root, file)
|
||||||
|
with open(file_path, 'r', encoding='utf-8') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
# 替换导入路径
|
||||||
|
content = content.replace(
|
||||||
|
"from cmvr.api import",
|
||||||
|
"from generated.cmvr.api import"
|
||||||
|
)
|
||||||
|
content = content.replace(
|
||||||
|
"import cmvr.api.",
|
||||||
|
"import generated.cmvr.api."
|
||||||
|
)
|
||||||
|
|
||||||
|
with open(file_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
print(f"Fixed imports in {file_path}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generate_proto()
|
||||||
177
protos/cmvr/api/biohead_command.proto
Normal file
177
protos/cmvr/api/biohead_command.proto
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "cmvr/api/common.proto"; // 导入通用命令头定义,确保与其他服务接口一致
|
||||||
|
|
||||||
|
package cmvr.api;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 面部表情控制参数定义
|
||||||
|
* 包含生物头部机器人面部各部位的运动参数,用于精确控制面部表情
|
||||||
|
*/
|
||||||
|
message FacialExpression {
|
||||||
|
/**
|
||||||
|
* 眉毛控制参数
|
||||||
|
* 左右眉毛的内外侧垂直位置(0.0-1.0标准化值)
|
||||||
|
*/
|
||||||
|
message Eyebrow {
|
||||||
|
float left_outside_y = 1; // 左眉外侧垂直位置
|
||||||
|
float left_inside_y = 2; // 左眉内侧垂直位置
|
||||||
|
float right_outside_y = 3; // 右眉外侧垂直位置
|
||||||
|
float right_inside_y = 4; // 右眉内侧垂直位置
|
||||||
|
}
|
||||||
|
Eyebrow eyebrow = 3; // 眉毛参数封装
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 眼睑控制参数
|
||||||
|
* 左右眼睑的上下位置(0.0-1.0标准化值)
|
||||||
|
*/
|
||||||
|
message Eyelid {
|
||||||
|
float left_upper_y = 1; // 左上眼睑垂直位置
|
||||||
|
float left_lower_y = 2; // 左下眼睑垂直位置
|
||||||
|
float right_upper_y = 3; // 右上眼睑垂直位置
|
||||||
|
float right_lower_y = 4; // 右下眼睑垂直位置
|
||||||
|
}
|
||||||
|
Eyelid eyelid = 4; // 眼睑参数封装
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 眼球控制参数
|
||||||
|
* 左右眼球的二维坐标(-1.0到1.0标准化值,中心为0)
|
||||||
|
*/
|
||||||
|
message Eyeball {
|
||||||
|
float left_x = 1; // 左眼球水平位置
|
||||||
|
float left_y = 2; // 左眼球垂直位置
|
||||||
|
float right_x = 3; // 右眼球水平位置
|
||||||
|
float right_y = 4; // 右眼球垂直位置
|
||||||
|
}
|
||||||
|
Eyeball eyeball = 5; // 眼球参数封装
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鼻子控制参数
|
||||||
|
* 鼻子左右侧的垂直位置(0.0-1.0标准化值)
|
||||||
|
*/
|
||||||
|
message Nose {
|
||||||
|
float left_y = 1; // 鼻子左侧垂直位置
|
||||||
|
float right_y = 2; // 鼻子右侧垂直位置
|
||||||
|
}
|
||||||
|
Nose nose = 6; // 鼻子参数封装
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 嘴巴控制参数
|
||||||
|
* 包含嘴唇整体位置和左右唇角细节
|
||||||
|
*/
|
||||||
|
message Mouth {
|
||||||
|
float upper_lip_y = 1; // 上唇垂直位置
|
||||||
|
float upper_lip_z = 2; // 上唇前后位置(Z轴)
|
||||||
|
float lower_lip_y = 3; // 下唇垂直位置
|
||||||
|
float lower_lip_z = 4; // 下唇前后位置(Z轴)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 左唇角控制参数
|
||||||
|
* 包含上唇、唇角、下唇的坐标点
|
||||||
|
*/
|
||||||
|
message LeftLip {
|
||||||
|
float upper_x = 1; // 左上唇水平位置
|
||||||
|
float upper_y = 2; // 左上唇垂直位置
|
||||||
|
float corner_x = 3; // 左唇角水平位置
|
||||||
|
float corner_y = 4; // 左唇角垂直位置
|
||||||
|
float lower_x = 5; // 左下唇水平位置
|
||||||
|
float lower_y = 6; // 左下唇垂直位置
|
||||||
|
}
|
||||||
|
LeftLip left_lip = 5; // 左唇角参数封装
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 右唇角控制参数
|
||||||
|
* 结构与左唇角对称
|
||||||
|
*/
|
||||||
|
message RightLip {
|
||||||
|
float upper_x = 1; // 右上唇水平位置
|
||||||
|
float upper_y = 2; // 右上唇垂直位置
|
||||||
|
float corner_x = 3; // 右唇角水平位置
|
||||||
|
float corner_y = 4; // 右唇角垂直位置
|
||||||
|
float lower_x = 5; // 右下唇水平位置
|
||||||
|
float lower_y = 6; // 右下唇垂直位置
|
||||||
|
}
|
||||||
|
RightLip right_lip = 6; // 右唇角参数封装
|
||||||
|
}
|
||||||
|
Mouth mouth = 7; // 嘴巴参数封装
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下巴控制参数
|
||||||
|
* 下巴的二维移动坐标(X/Y轴)
|
||||||
|
*/
|
||||||
|
message Jaw {
|
||||||
|
float x = 1; // 下巴水平位置
|
||||||
|
float y = 2; // 下巴垂直位置
|
||||||
|
}
|
||||||
|
Jaw jaw = 8; // 下巴参数封装
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置面部表情命令(集成通用命令头)
|
||||||
|
* 用于单次设置生物头部的面部表情
|
||||||
|
*/
|
||||||
|
message SetFacialExpression {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1; // 通用命令头,包含设备ID和时间戳
|
||||||
|
FacialExpression expression = 2; // 具体面部表情参数
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1; // 通用反馈头,包含操作状态和时间戳
|
||||||
|
string execution_id = 2; // 表情执行任务ID
|
||||||
|
float execution_time_ms = 3; // 表情执行耗时(毫秒)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流式面部表情命令(集成通用命令头)
|
||||||
|
* 用于连续发送多个面部表情,实现表情动画效果
|
||||||
|
*/
|
||||||
|
message StreamFacialExpression {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1; // 通用命令头
|
||||||
|
FacialExpression expr = 2; // 执行
|
||||||
|
bool eof = 3; // 标记是否为流结束
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1; // 通用反馈头
|
||||||
|
FacialExpression expr_diff = 2; // 执行误差
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取生物头部状态命令(集成通用命令头)
|
||||||
|
* 用于查询当前头部各部位的状态和位置
|
||||||
|
*/
|
||||||
|
message GetStatus {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1; // 通用命令头
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1; // 通用反馈头
|
||||||
|
bool is_moving = 2; // 是否有部位在移动
|
||||||
|
string last_request_id = 3; // 上一次请求的ID
|
||||||
|
repeated float current_positions = 4; // 当前各部位位置参数
|
||||||
|
bool camera_recording = 5; // 相机是否在录制
|
||||||
|
string active_recording_id = 6; // 活跃录制任务的ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 紧急停止命令(集成通用命令头)
|
||||||
|
* 用于立即停止所有面部动作
|
||||||
|
*/
|
||||||
|
message EmergencyStop {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1; // 通用命令头
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1; // 通用反馈头
|
||||||
|
string stopped_processes = 2; // 已停止的进程列表
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
22
protos/cmvr/api/biohead_service.proto
Normal file
22
protos/cmvr/api/biohead_service.proto
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package cmvr.api;
|
||||||
|
import "cmvr/api/biohead_command.proto";
|
||||||
|
|
||||||
|
// 生物头部机器人服务接口
|
||||||
|
service BioHeadService {
|
||||||
|
// 设置面部表情
|
||||||
|
rpc SetExpression(SetFacialExpression.Request) returns (SetFacialExpression.Feedback){};
|
||||||
|
|
||||||
|
// 流式表情控制
|
||||||
|
//rpc StreamExpression(StreamFacialExpression.Request) returns (StreamFacialExpression.Feedback){};
|
||||||
|
|
||||||
|
rpc StreamExpression (stream StreamFacialExpression.Request) returns (stream StreamFacialExpression.Feedback);
|
||||||
|
|
||||||
|
|
||||||
|
// 获取状态
|
||||||
|
rpc GetSystemStatus(GetStatus.Request) returns (GetStatus.Feedback){};
|
||||||
|
|
||||||
|
// 紧急停止
|
||||||
|
rpc EmergencyStop(EmergencyStop.Request) returns (EmergencyStop.Feedback){};
|
||||||
|
}
|
||||||
161
protos/cmvr/api/camera_command.proto
Normal file
161
protos/cmvr/api/camera_command.proto
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "cmvr/api/common.proto";
|
||||||
|
|
||||||
|
package cmvr.api;
|
||||||
|
|
||||||
|
message FrameData {
|
||||||
|
enum FrameType {
|
||||||
|
U8C1 = 0;
|
||||||
|
U16C1 = 1;
|
||||||
|
U8C3 = 2;
|
||||||
|
U16C3 = 3;
|
||||||
|
F16C1 = 4;
|
||||||
|
F32C1 = 5;
|
||||||
|
}
|
||||||
|
bytes data = 1;
|
||||||
|
int32 width = 2;
|
||||||
|
int32 height = 3;
|
||||||
|
FrameType type = 4;
|
||||||
|
string codec = 5;
|
||||||
|
bool is_key_frame = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CameraState {
|
||||||
|
bool is_initialized = 1;
|
||||||
|
bool is_opened = 2;
|
||||||
|
bool is_streaming = 3;
|
||||||
|
bool is_recording = 4;
|
||||||
|
bool is_error = 5;
|
||||||
|
string error_message = 6;
|
||||||
|
int32 fps = 7;
|
||||||
|
int32 width = 8;
|
||||||
|
int32 height = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetCameraStateCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
CameraState state = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message StartCameraCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message StopCameraCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetRGBImageCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
FrameData color_frame = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetDepthImageCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
FrameData depth_frame = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetRGBDImagesCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
FrameData color_frame = 2;
|
||||||
|
FrameData depth_frame = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message StartCameraRecordingCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
string video_path = 2;
|
||||||
|
}
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message StopCameraRecordingCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetRGBImageStreamCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
bool eof = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
FrameData color_frame = 2;
|
||||||
|
int32 seq_no = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetDepthImageStreamCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
bool eof = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
FrameData depth_frame = 2;
|
||||||
|
int32 seq_no = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetRGBDImagesStreamCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
bool eof = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
FrameData color_frame = 2;
|
||||||
|
FrameData depth_frame = 3;
|
||||||
|
int32 seq_no = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
21
protos/cmvr/api/camera_service.proto
Normal file
21
protos/cmvr/api/camera_service.proto
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "cmvr/api/camera_command.proto";
|
||||||
|
|
||||||
|
package cmvr.api;
|
||||||
|
|
||||||
|
service CameraService {
|
||||||
|
rpc GetStatus(GetCameraStateCommand.Request) returns (GetCameraStateCommand.Feedback) {}
|
||||||
|
rpc StartCamera(StartCameraCommand.Request) returns (StartCameraCommand.Feedback) {}
|
||||||
|
rpc StopCamera(StopCameraCommand.Request) returns (StopCameraCommand.Feedback) {}
|
||||||
|
rpc GetRGBImage(GetRGBImageCommand.Request) returns (GetRGBImageCommand.Feedback) {}
|
||||||
|
rpc GetDepthImage(GetDepthImageCommand.Request) returns (GetDepthImageCommand.Feedback) {}
|
||||||
|
rpc GetRGBDImages(GetRGBDImagesCommand.Request) returns (GetRGBDImagesCommand.Feedback) {}
|
||||||
|
rpc StartRecording(StartCameraRecordingCommand.Request) returns (StartCameraRecordingCommand.Feedback) {}
|
||||||
|
rpc StopRecording(StopCameraRecordingCommand.Request) returns (StopCameraRecordingCommand.Feedback) {}
|
||||||
|
|
||||||
|
rpc GetRGBImageStream(stream GetRGBImageStreamCommand.Request) returns (stream GetRGBImageStreamCommand.Feedback) {}
|
||||||
|
rpc GetDepthImageStream(stream GetDepthImageStreamCommand.Request) returns (stream GetDepthImageStreamCommand.Feedback) {}
|
||||||
|
rpc GetRGBDImagesStream(stream GetRGBDImagesStreamCommand.Request) returns (stream GetRGBDImagesStreamCommand.Feedback) {}
|
||||||
|
}
|
||||||
30
protos/cmvr/api/common.proto
Normal file
30
protos/cmvr/api/common.proto
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package cmvr.api;
|
||||||
|
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
|
message DeviceLifecycle {
|
||||||
|
enum Lifecycle {
|
||||||
|
STATE_INIT = 0;
|
||||||
|
STATE_READY = 1;
|
||||||
|
STATE_RUNNING = 2;
|
||||||
|
STATE_ERROR = 3;
|
||||||
|
STATE_ESTOP = 4;
|
||||||
|
STATE_STOP = 5;
|
||||||
|
}
|
||||||
|
Lifecycle state = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CommandHeader {
|
||||||
|
message Request {
|
||||||
|
string device_id = 1; // 目标设备名称
|
||||||
|
google.protobuf.Timestamp timestamp = 2; // 请求时间
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
bool success = 1; // 是否成功
|
||||||
|
string error_message = 2; // 错误信息(成功时为空)
|
||||||
|
google.protobuf.Timestamp timestamp = 3; // 回复时间
|
||||||
|
}
|
||||||
|
}
|
||||||
149
protos/cmvr/api/dexhand_command.proto
Normal file
149
protos/cmvr/api/dexhand_command.proto
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "cmvr/api/common.proto";
|
||||||
|
|
||||||
|
package cmvr.api;
|
||||||
|
|
||||||
|
message FreedomValue {
|
||||||
|
int32 id = 1; // 自由度id 0-6 分别对应六个自由度 小拇指 无名指 中指 食指 大拇指弯曲 大拇指旋转
|
||||||
|
float value = 2; // 值 0-1百分比
|
||||||
|
}
|
||||||
|
|
||||||
|
message RH56DFTPDexHand {
|
||||||
|
int32 dof_id = 1; // 自由度ID,唯一标识不同的关节或自由度
|
||||||
|
int32 angle = 2; // 自由度角度
|
||||||
|
int32 speed = 3; // 自由度速度
|
||||||
|
int32 force = 4; // 实际受力
|
||||||
|
int32 position = 5; // 执行器位置
|
||||||
|
int32 current = 6; // 执行器实际电流
|
||||||
|
int32 temperature = 7; // 执行器温度
|
||||||
|
int32 error = 8; // 执行器故障信息
|
||||||
|
repeated string error_message = 9; // 错误消息列表
|
||||||
|
}
|
||||||
|
// 传感器数据
|
||||||
|
// 传感器数据
|
||||||
|
message SensorData {
|
||||||
|
// 手指类型(掌心作为特殊类型)
|
||||||
|
enum FingerType {
|
||||||
|
PINKY = 0; // 小拇指
|
||||||
|
RING = 1; // 无名指
|
||||||
|
MIDDLE_FINGER = 2; // 中指(避免与PartType.MIDDLE冲突)
|
||||||
|
INDEX = 3; // 食指
|
||||||
|
THUMB = 4; // 大拇指
|
||||||
|
PALM = 5; // 掌心
|
||||||
|
}
|
||||||
|
|
||||||
|
// 部位类型(指中改为THUMB_MIDDLE以避免冲突)
|
||||||
|
enum PartType {
|
||||||
|
TIP = 0; // 指端
|
||||||
|
FINGER = 1; // 指尖
|
||||||
|
PAD = 2; // 指腹
|
||||||
|
THUMB_MIDDLE = 3; // 大拇指指中(仅大拇指有)
|
||||||
|
PALM_PAD = 4; // 掌心部位(对应PalmTactileData)
|
||||||
|
}
|
||||||
|
|
||||||
|
FingerType finger_type = 4; // 手指类型(掌心使用PALM)
|
||||||
|
PartType part_type = 5; // 部位类型
|
||||||
|
string sensor_name = 6; // 传感器名称(如"小拇指指端")
|
||||||
|
|
||||||
|
message RowData {
|
||||||
|
repeated int32 values = 1 [packed = true]; // 一维数组,存放每行的浮点数值
|
||||||
|
}
|
||||||
|
repeated RowData data = 1; // 二维数组,每行数据是 RowData 类型
|
||||||
|
int32 rows = 2; // 行
|
||||||
|
int32 cols = 3; // 列
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
message DexHandState {
|
||||||
|
bool is_initialized = 1; // 是否初始化
|
||||||
|
repeated RH56DFTPDexHand hands = 2; // 包含多个自由度状态
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetDexHandStateCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
DexHandState state = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetDexHandPositionsCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
repeated FreedomValue values = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetDexHandAnglesCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
repeated FreedomValue values = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetDexHandForceCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
repeated FreedomValue values = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetDexHandSpeedCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
repeated FreedomValue values = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetDexHandPresetActCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
int32 presetActId = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetSensorDataCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
repeated SensorData sensor = 2;//所有传感器的数据
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetSensorDataStreamCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
repeated SensorData sensor = 2;//所有传感器的数据
|
||||||
|
}
|
||||||
|
}
|
||||||
18
protos/cmvr/api/dexhand_service.proto
Normal file
18
protos/cmvr/api/dexhand_service.proto
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "cmvr/api/dexhand_command.proto";
|
||||||
|
|
||||||
|
package cmvr.api;
|
||||||
|
|
||||||
|
|
||||||
|
service DexHandService {
|
||||||
|
// 基本控制
|
||||||
|
rpc GetStatus(GetDexHandStateCommand.Request) returns (GetDexHandStateCommand.Feedback);
|
||||||
|
rpc SetDexHandPos(SetDexHandPositionsCommand.Request) returns (SetDexHandPositionsCommand.Feedback);
|
||||||
|
rpc SetDexHandAngle(SetDexHandAnglesCommand.Request) returns (SetDexHandAnglesCommand.Feedback);
|
||||||
|
rpc SetDexHandForce(SetDexHandForceCommand.Request) returns (SetDexHandForceCommand.Feedback);
|
||||||
|
rpc SetDexHandSpeed(SetDexHandSpeedCommand.Request) returns (SetDexHandSpeedCommand.Feedback);
|
||||||
|
rpc SetDexHandPresetAct(SetDexHandPresetActCommand.Request) returns (SetDexHandPresetActCommand.Feedback);
|
||||||
|
rpc GetSensorData(GetSensorDataCommand.Request) returns (GetSensorDataCommand.Feedback);
|
||||||
|
rpc GetSensorDataStream(stream GetSensorDataStreamCommand.Request) returns (stream GetSensorDataStreamCommand.Feedback);
|
||||||
|
}
|
||||||
67
protos/cmvr/api/geometry.proto
Normal file
67
protos/cmvr/api/geometry.proto
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package cmvr.api;
|
||||||
|
|
||||||
|
|
||||||
|
message Vec2 {
|
||||||
|
double x = 1;
|
||||||
|
double y = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Vec3 {
|
||||||
|
double x = 1;
|
||||||
|
double y = 2;
|
||||||
|
double z = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SE2Pose {
|
||||||
|
Vec2 position = 1; // (m)
|
||||||
|
double angle = 2; // (rad)
|
||||||
|
}
|
||||||
|
|
||||||
|
message SE2Velocity {
|
||||||
|
Vec2 linear = 1; // (m/s)
|
||||||
|
double angular = 2; // (rad/s)
|
||||||
|
}
|
||||||
|
|
||||||
|
message Quaternion {
|
||||||
|
double x = 1;
|
||||||
|
double y = 2;
|
||||||
|
double z = 3;
|
||||||
|
double w = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message EulerAngleZYX {
|
||||||
|
double z = 1;
|
||||||
|
double y = 2;
|
||||||
|
double x = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SE3Pose {
|
||||||
|
Vec3 position = 1; // (m)
|
||||||
|
oneof rotation {
|
||||||
|
Quaternion quaternion = 2;
|
||||||
|
EulerAngleZYX euler = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message Inertial {
|
||||||
|
// Mass (kg)
|
||||||
|
double mass = 1;
|
||||||
|
|
||||||
|
// Center of mass (m)
|
||||||
|
Vec3 center_of_mass = 2;
|
||||||
|
|
||||||
|
// Inertia tensor
|
||||||
|
Inertia inertia = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inertia tensor components (kg*m^2)
|
||||||
|
message Inertia {
|
||||||
|
double ixx = 1;
|
||||||
|
double iyy = 2;
|
||||||
|
double izz = 3;
|
||||||
|
double ixy = 4;
|
||||||
|
double ixz = 5;
|
||||||
|
double iyz = 6;
|
||||||
|
}
|
||||||
32
protos/cmvr/api/humanoid_robot.proto
Normal file
32
protos/cmvr/api/humanoid_robot.proto
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
package cmvr.api;
|
||||||
|
import "cmvr/api/common.proto";
|
||||||
|
|
||||||
|
message JointCmd {
|
||||||
|
string joint_name = 1; // 关节名称
|
||||||
|
double rad = 2; // 弧度
|
||||||
|
double vel = 3; // 角速度,rad/s
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
message MoveJ{
|
||||||
|
message Request{
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
repeated JointCmd cmds = 2;
|
||||||
|
double vel = 3;
|
||||||
|
double acc = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Response{
|
||||||
|
CommandHeader.Feedback header= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
service HumanoidRobotService{
|
||||||
|
rpc torqueOff(CommandHeader.Request) returns (CommandHeader.Feedback);
|
||||||
|
rpc torqueOn(CommandHeader.Request) returns (CommandHeader.Feedback);
|
||||||
|
rpc moveJ(MoveJ.Request) returns (MoveJ.Response);
|
||||||
|
}
|
||||||
81
protos/cmvr/api/microphone_command.proto
Normal file
81
protos/cmvr/api/microphone_command.proto
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "cmvr/api/common.proto";
|
||||||
|
|
||||||
|
package cmvr.api;
|
||||||
|
|
||||||
|
message MicState {
|
||||||
|
bool is_initialized = 1;
|
||||||
|
bool is_running = 2;
|
||||||
|
bool is_recording = 3;
|
||||||
|
int32 volume = 4;
|
||||||
|
string error_message = 5;
|
||||||
|
}
|
||||||
|
message GetMicStateCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
MicState state = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
message StartMicRecordingCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
string file_path = 2;
|
||||||
|
}
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message StopMicRecordingCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message PauseMicRecordingCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message ResumeMicRecordingCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetMicPhoneVolumeCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
int32 volume = 2; // 音量值(0 ~ 100)
|
||||||
|
}
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetMicPhoneVolumeCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
int32 volume = 2; // 当前音量值
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
20
protos/cmvr/api/microphone_service.proto
Normal file
20
protos/cmvr/api/microphone_service.proto
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "cmvr/api/microphone_command.proto";
|
||||||
|
|
||||||
|
package cmvr.api;
|
||||||
|
|
||||||
|
|
||||||
|
service MicPhoneService {
|
||||||
|
// 基本控制
|
||||||
|
rpc GetStatus(GetMicStateCommand.Request) returns (GetMicStateCommand.Feedback);
|
||||||
|
rpc StartRecord(StartMicRecordingCommand.Request) returns (StartMicRecordingCommand.Feedback);
|
||||||
|
rpc StopRecord(StopMicRecordingCommand.Request) returns (StopMicRecordingCommand.Feedback);
|
||||||
|
rpc PauseRecord(PauseMicRecordingCommand.Request) returns (PauseMicRecordingCommand.Feedback);
|
||||||
|
rpc ResumeRecord(ResumeMicRecordingCommand.Request) returns (ResumeMicRecordingCommand.Feedback);
|
||||||
|
|
||||||
|
// 音量控制
|
||||||
|
rpc SetVolume(SetMicPhoneVolumeCommand.Request) returns (SetMicPhoneVolumeCommand.Feedback);
|
||||||
|
rpc GetVolume(GetMicPhoneVolumeCommand.Request) returns (GetMicPhoneVolumeCommand.Feedback);
|
||||||
|
|
||||||
|
}
|
||||||
99
protos/cmvr/api/speaker_command.proto
Normal file
99
protos/cmvr/api/speaker_command.proto
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "cmvr/api/common.proto";
|
||||||
|
|
||||||
|
package cmvr.api;
|
||||||
|
|
||||||
|
|
||||||
|
// 音频数据格式(与麦克风类似,但用于输出)
|
||||||
|
message AudioData {
|
||||||
|
enum AudioFormat {
|
||||||
|
PCM = 0;
|
||||||
|
MP3 = 1;
|
||||||
|
AAC = 2;
|
||||||
|
WAV = 3;
|
||||||
|
}
|
||||||
|
bytes data = 1; // 音频二进制数据
|
||||||
|
int32 sample_rate = 2; // 采样率(Hz)
|
||||||
|
int32 channels = 3; // 声道数
|
||||||
|
AudioFormat format = 4; // 音频格式
|
||||||
|
string codec = 5; // 编码方式
|
||||||
|
}
|
||||||
|
|
||||||
|
// 扬声器状态
|
||||||
|
message SpeakerState {
|
||||||
|
bool is_initialized = 1; // 是否已初始化
|
||||||
|
bool is_running = 2; // 是否正在播放
|
||||||
|
bool is_decoding = 3;
|
||||||
|
bool is_paused = 5;
|
||||||
|
int32 volume = 6; // 当前音量(0 ~ 100)
|
||||||
|
string error_message = 7; // 错误信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// 各种扬声器命令
|
||||||
|
message GetSpeakerStateCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
SpeakerState state = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message PlayAudioCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
string audio_path = 2;
|
||||||
|
}
|
||||||
|
message Feedback { CommandHeader.Feedback header = 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
message StopSpeakerCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message PauseSpeakerCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message ResumeSpeakerCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetSpeakerVolumeCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
int32 volume = 2; // 音量值(0 ~ 100)
|
||||||
|
}
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetSpeakerVolumeCommand {
|
||||||
|
message Request {
|
||||||
|
CommandHeader.Request header = 1;
|
||||||
|
}
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
int32 volume = 2; // 当前音量值
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
20
protos/cmvr/api/speaker_service.proto
Normal file
20
protos/cmvr/api/speaker_service.proto
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "cmvr/api/speaker_command.proto";
|
||||||
|
|
||||||
|
package cmvr.api;
|
||||||
|
|
||||||
|
|
||||||
|
service SpeakerService {
|
||||||
|
// 基本控制
|
||||||
|
rpc GetStatus(GetSpeakerStateCommand.Request) returns (GetSpeakerStateCommand.Feedback);
|
||||||
|
rpc PlayAudio(PlayAudioCommand.Request) returns (PlayAudioCommand.Feedback);
|
||||||
|
rpc StopPlayback(StopSpeakerCommand.Request) returns (StopSpeakerCommand.Feedback);
|
||||||
|
rpc PausePlayback(PauseSpeakerCommand.Request) returns (PauseSpeakerCommand.Feedback);
|
||||||
|
rpc ResumePlayback(ResumeSpeakerCommand.Request) returns (ResumeSpeakerCommand.Feedback);
|
||||||
|
|
||||||
|
// 音量控制
|
||||||
|
rpc SetVolume(SetSpeakerVolumeCommand.Request) returns (SetSpeakerVolumeCommand.Feedback);
|
||||||
|
rpc GetVolume(GetSpeakerVolumeCommand.Request) returns (GetSpeakerVolumeCommand.Feedback);
|
||||||
|
|
||||||
|
}
|
||||||
50
protos/cmvr/api/system_command.proto
Normal file
50
protos/cmvr/api/system_command.proto
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "cmvr/api/common.proto";
|
||||||
|
|
||||||
|
package cmvr.api;
|
||||||
|
|
||||||
|
enum DeviceType {
|
||||||
|
AGV = 0;
|
||||||
|
Battery = 1;
|
||||||
|
Camera = 2;
|
||||||
|
DexHand = 3;
|
||||||
|
Gripper = 4;
|
||||||
|
Microphone = 5;
|
||||||
|
Robot = 6;
|
||||||
|
Speaker = 7;
|
||||||
|
Unknown = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeviceList {
|
||||||
|
string device_id = 1;
|
||||||
|
DeviceType device_type = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetSystemInfoCommand {
|
||||||
|
message Request {}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
string system_name = 2;
|
||||||
|
string version = 3;
|
||||||
|
string description = 4;
|
||||||
|
string os = 5;
|
||||||
|
string kernel_version = 6;
|
||||||
|
string architecture = 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetSystemStatusCommand {
|
||||||
|
message Request {}
|
||||||
|
|
||||||
|
message Feedback {
|
||||||
|
CommandHeader.Feedback header = 1;
|
||||||
|
float cpu_usage = 2;
|
||||||
|
float mem_total_mb = 3;
|
||||||
|
float mem_used_mb = 4;
|
||||||
|
float disk_total_gb = 5;
|
||||||
|
float disk_used_gb = 6;
|
||||||
|
repeated DeviceList device_list = 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
protos/cmvr/api/system_service.proto
Normal file
11
protos/cmvr/api/system_service.proto
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "cmvr/api/system_command.proto";
|
||||||
|
|
||||||
|
package cmvr.api;
|
||||||
|
|
||||||
|
|
||||||
|
service SystemService {
|
||||||
|
rpc GetSystemInfo(GetSystemInfoCommand.Request) returns (GetSystemInfoCommand.Feedback) {}
|
||||||
|
rpc GetSystemStatus(GetSystemStatusCommand.Request) returns (GetSystemStatusCommand.Feedback) {}
|
||||||
|
}
|
||||||
17
protos/cmvr/api/test_service.proto
Normal file
17
protos/cmvr/api/test_service.proto
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package cmvr.api;
|
||||||
|
|
||||||
|
message TestReqeust {
|
||||||
|
string data = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TestResponse {
|
||||||
|
string data = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
service TestService{
|
||||||
|
rpc Call (TestReqeust) returns (TestResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
55
protos/cmvr/msgs/can_card_parameter.proto
Normal file
55
protos/cmvr/msgs/can_card_parameter.proto
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package cmvr.msgs;
|
||||||
|
|
||||||
|
message CANCardParameter {
|
||||||
|
enum CANCardBrand {
|
||||||
|
FAKE_CAN = 0;
|
||||||
|
ESD_CAN = 1;
|
||||||
|
SOCKET_CAN_RAW = 2;
|
||||||
|
HERMES_CAN = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CANCardType {
|
||||||
|
PCI_CARD = 0;
|
||||||
|
USB_CARD = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CANChannelId {
|
||||||
|
CHANNEL_ID_ZERO = 0;
|
||||||
|
CHANNEL_ID_ONE = 1;
|
||||||
|
CHANNEL_ID_TWO = 2;
|
||||||
|
CHANNEL_ID_THREE = 3;
|
||||||
|
CHANNEL_ID_FOUR = 4;
|
||||||
|
CHANNEL_ID_FIVE = 5;
|
||||||
|
CHANNEL_ID_SIX = 6;
|
||||||
|
CHANNEL_ID_SEVEN = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CANInterface {
|
||||||
|
NATIVE = 0;
|
||||||
|
VIRTUAL = 1;
|
||||||
|
SLCAN = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum BAUDRATE {
|
||||||
|
BCAN_BAUDRATE_1M = 0;
|
||||||
|
BCAN_BAUDRATE_500K = 1;
|
||||||
|
BCAN_BAUDRATE_250K = 2;
|
||||||
|
BCAN_BAUDRATE_150K = 3;
|
||||||
|
BCAN_BAUDRATE_NUM = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAN卡驱动类型配置 | 根据所用的CAN卡硬件型号或驱动类型配置
|
||||||
|
optional CANCardBrand brand = 1;
|
||||||
|
// CAN卡硬件接口类型配置 | 根据所用的CAN卡硬件接口类型或驱动类型配置
|
||||||
|
optional CANCardType type = 2;
|
||||||
|
// CAN卡端口号配置 | 根据所连接的CAN卡端口号配置
|
||||||
|
optional CANChannelId channel_id = 3;
|
||||||
|
// CAN卡软件接口配置
|
||||||
|
optional CANInterface interface = 4;
|
||||||
|
// CAN卡端口数量配置
|
||||||
|
optional uint32 num_ports = 5;
|
||||||
|
// CAN卡波特率配置
|
||||||
|
optional BAUDRATE baudrate = 6;
|
||||||
|
}
|
||||||
202
protos/cmvr/msgs/canopen.proto
Normal file
202
protos/cmvr/msgs/canopen.proto
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package cmvr.msgs;
|
||||||
|
|
||||||
|
message SdoFrame {
|
||||||
|
uint32 node_id = 1; // 节点ID
|
||||||
|
CommandSpecifier cs = 2; // SDO命令字
|
||||||
|
ObIndex index = 3; // 对象字典索引
|
||||||
|
ObSubIndex sub_index = 4; // 子索引
|
||||||
|
uint32 data = 5; // 数据区
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PdoBaseId{
|
||||||
|
|
||||||
|
PDO_BASE_ID_UNSPECIFIED = 0;
|
||||||
|
|
||||||
|
RPDO1_BASE_ID_200 = 0x200;
|
||||||
|
RPDO2_BASE_ID_300 = 0x300;
|
||||||
|
RPDO3_BASE_ID_400 = 0x400;
|
||||||
|
RPDO4_BASE_ID_500 = 0x500;
|
||||||
|
|
||||||
|
|
||||||
|
TPDO1_BASE_ID_180 = 0x180;
|
||||||
|
TPDO2_BASE_ID_280 = 0x280;
|
||||||
|
TPDO3_BASE_ID_380 = 0x380;
|
||||||
|
TPDO4_BASE_ID_480 = 0x480;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PDO 传输类型
|
||||||
|
enum TransmissionType {
|
||||||
|
|
||||||
|
// 同步传输
|
||||||
|
SYNC_EVENT_DRIVEN = 0x00; // 非循环同步
|
||||||
|
SYNC_CYCLIC = 0x01; // 循环同步
|
||||||
|
|
||||||
|
// 远程请求触发
|
||||||
|
REMOTE_SYNC = 0xFC; // 远程同步
|
||||||
|
REMOTE_ASYNC = 0xFD; // 远程异步
|
||||||
|
|
||||||
|
// 异步传输
|
||||||
|
ASYNC_MANUFACTURER_SPECIFIC = 0xFE; // 异步,制造商特定事件
|
||||||
|
ASYNC_DEVICE_SPECIFIC = 0xFF; // 异步,设备子协议特定事件
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
enum CommandSpecifier {
|
||||||
|
CS_NO = 0; // 默认无效
|
||||||
|
CS_WRITE_ONE_BYTE = 0x2F; // 写 1 字节
|
||||||
|
CS_WRITE_TWO_BYTES = 0x2B; // 写 2 字节
|
||||||
|
CS_WRITE_THREE_BYTES = 0x27; // 写 3 字节
|
||||||
|
CS_WRITE_FOUR_BYTES = 0x23; // 写 4 字节
|
||||||
|
CS_WRITE_SUCCESS_RESPONSE = 0x60; // 写成功响应
|
||||||
|
|
||||||
|
CS_READ_REQUEST = 0x40; // 发起读取请求
|
||||||
|
CS_READ_RESPONSE_ONE_BYTE = 0x4F; // 响应 1 字节
|
||||||
|
CS_READ_RESPONSE_TWO_BYTES = 0x4B;// 响应 2 字节
|
||||||
|
CS_READ_RESPONSE_THREE_BYTES = 0x47;
|
||||||
|
CS_READ_RESPONSE_FOUR_BYTES = 0x43;
|
||||||
|
|
||||||
|
CS_EXCEPTION_RESPONSE = 0x80; // 异常响应
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
enum NmtState {
|
||||||
|
// 0x00 - 正在初始化(Initializing)
|
||||||
|
NMT_INITIALIZING = 0x00;
|
||||||
|
|
||||||
|
// 0x01 - 未连接 / 应用复位(Reset Application)
|
||||||
|
NMT_RESET_APPLICATION = 0x01;
|
||||||
|
|
||||||
|
// 0x02 - 正在连接(Connecting)
|
||||||
|
NMT_CONNECTING = 0x02;
|
||||||
|
|
||||||
|
// 0x03 - 准备(Preparing)
|
||||||
|
NMT_PREPARING = 0x03;
|
||||||
|
|
||||||
|
// 0x04 - 停止状态(Stopped),开启 SDO,关闭 PDO
|
||||||
|
NMT_STOPPED = 0x04;
|
||||||
|
|
||||||
|
// 0x05 - 运行状态(Operational),开启 SDO、PDO、NMT
|
||||||
|
NMT_OPERATIONAL = 0x05;
|
||||||
|
|
||||||
|
// 0x7F - 预运行状态(Pre-Operational),开启 SDO,关闭 PDO
|
||||||
|
NMT_PRE_OPERATIONAL = 0x7F;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
enum NmtCommand {
|
||||||
|
// 未指定命令
|
||||||
|
NMT_COMMAND_UNSPECIFIED = 0x00;
|
||||||
|
|
||||||
|
// 0x01 - 启动远程节点(Start Remote Node)
|
||||||
|
NMT_START_REMOTE_NODE = 0x01;
|
||||||
|
|
||||||
|
// 0x02 - 停止远程节点(Stop Remote Node)
|
||||||
|
NMT_STOP_REMOTE_NODE = 0x02;
|
||||||
|
|
||||||
|
// 0x80 - 进入预操作状态(Enter Pre-Operational)
|
||||||
|
NMT_ENTER_PRE_OPERATIONAL = 0x80;
|
||||||
|
|
||||||
|
// 0x81 - 复位节点(Reset Node)
|
||||||
|
NMT_RESET_NODE = 0x81;
|
||||||
|
|
||||||
|
// 0x82 - 复位通信(Reset Communication)
|
||||||
|
NMT_RESET_COMMUNICATION = 0x82;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 索引
|
||||||
|
enum ObIndex {
|
||||||
|
INDEX_ZERO = 0;
|
||||||
|
USER_SAVE_PARA_2000 = 0x2000; // 下发命令 1 保存参数
|
||||||
|
POSITION_OFFSET_2008 = 0x2008; // 位置偏移,子索引 0x00,用于设置零点、起始位置
|
||||||
|
// Error Codes
|
||||||
|
ERROR_CODE_6007 = 0x6007;
|
||||||
|
ERROR_CODE_603F = 0x603F;
|
||||||
|
|
||||||
|
// Control and Status
|
||||||
|
CONTROL_WORD_6040 = 0x6040;
|
||||||
|
STATUS_WORD_6041 = 0x6041;
|
||||||
|
|
||||||
|
// Operation Modes
|
||||||
|
OPERATION_MODE_6060 = 0x6060;
|
||||||
|
MODE_DISPLAY_6061 = 0x6061;
|
||||||
|
|
||||||
|
// Actual Values
|
||||||
|
ACTUAL_POSITION_6064 = 0x6064;
|
||||||
|
ACTUAL_SPEED_606C = 0x606C;
|
||||||
|
ACTUAL_CURRENT_6078 = 0x6078;
|
||||||
|
|
||||||
|
// Torque-related
|
||||||
|
TARGET_TORQUE_6071 = 0x6071;
|
||||||
|
MAX_TORQUE_6072 = 0x6072;
|
||||||
|
DEMAND_TORQUE_6074 = 0x6074;
|
||||||
|
|
||||||
|
// Position-related
|
||||||
|
TARGET_POSITION_607A = 0x607A;
|
||||||
|
SOFTWARE_POSITION_LIMIT_607D = 0x607D; // Sub-indexes: 1, 2
|
||||||
|
|
||||||
|
// Speed-related
|
||||||
|
MAX_SPEED_607F = 0x607F;
|
||||||
|
PROFILE_SPEED_6081 = 0x6081;
|
||||||
|
PROFILE_ACCELERATION_6083 = 0x6083;
|
||||||
|
PROFILE_DECELERATION_6084 = 0x6084;
|
||||||
|
|
||||||
|
// Same as DEMAND_TORQUE? Verify correctness.
|
||||||
|
// TORQUE_SLOPE_6074 = 0x6074;
|
||||||
|
|
||||||
|
// PID Control
|
||||||
|
CURRENT_LOOP_PID_60F6 = 0x60F6; // Sub-indexes: 1, 2
|
||||||
|
SPEED_LOOP_PID_60F9 = 0x60F9; // Sub-indexes: 1, 2
|
||||||
|
POSITION_LOOP_PID_60FB = 0x60FB; // Sub-indexes: 1, 2, 3
|
||||||
|
|
||||||
|
// Target Speed
|
||||||
|
TARGET_SPEED_60FF = 0x60FF;
|
||||||
|
|
||||||
|
QUICK_STOP_OPTION_605A = 0x605A;
|
||||||
|
QUICK_STOP_DECEL_6085 = 0x6085;
|
||||||
|
|
||||||
|
// -------------------------
|
||||||
|
// PDO 通信参数对象(Communication Object)
|
||||||
|
RPDO1_COMM_1400 = 0x1400;
|
||||||
|
RPDO2_COMM_1401 = 0x1401;
|
||||||
|
RPDO3_COMM_1402 = 0x1402;
|
||||||
|
RPDO4_COMM_1403 = 0x1403;
|
||||||
|
|
||||||
|
TPDO1_COMM_1800 = 0x1800;
|
||||||
|
TPDO2_COMM_1801 = 0x1801;
|
||||||
|
TPDO3_COMM_1802 = 0x1802;
|
||||||
|
TPDO4_COMM_1803 = 0x1803;
|
||||||
|
|
||||||
|
// PDO 映射对象(Mapping Object)
|
||||||
|
RPDO1_MAP_1600 = 0x1600;
|
||||||
|
RPDO2_MAP_1601 = 0x1601;
|
||||||
|
RPDO3_MAP_1602 = 0x1602;
|
||||||
|
RPDO4_MAP_1603 = 0x1603;
|
||||||
|
|
||||||
|
TPDO1_MAP_1A00 = 0x1A00;
|
||||||
|
TPDO2_MAP_1A01 = 0x1A01;
|
||||||
|
TPDO3_MAP_1A02 = 0x1A02;
|
||||||
|
TPDO4_MAP_1A03 = 0x1A03;
|
||||||
|
|
||||||
|
PRODUCER_HEARTBEAT_TIME = 0x1017;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 子索引
|
||||||
|
enum ObSubIndex {
|
||||||
|
SUB_INDEX_0 = 0;
|
||||||
|
SUB_INDEX_1 = 1;
|
||||||
|
SUB_INDEX_2 = 2;
|
||||||
|
SUB_INDEX_3 = 3;
|
||||||
|
SUB_INDEX_4 = 4;
|
||||||
|
SUB_INDEX_5 = 5;
|
||||||
|
SUB_INDEX_6 = 6;
|
||||||
|
SUB_INDEX_7 = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
29
protos/cmvr/msgs/error_code.proto
Normal file
29
protos/cmvr/msgs/error_code.proto
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package cmvr.msgs;
|
||||||
|
|
||||||
|
// Error codes enum for API's categorized by modules.
|
||||||
|
enum ErrorCode {
|
||||||
|
// No error, returns on success.
|
||||||
|
OK = 0;
|
||||||
|
|
||||||
|
// Canbus module error codes start from here.
|
||||||
|
CANBUS_ERROR = 2000;
|
||||||
|
CAN_CLIENT_ERROR_BASE = 2100;
|
||||||
|
CAN_CLIENT_ERROR_OPEN_DEVICE_FAILED = 2101;
|
||||||
|
CAN_CLIENT_ERROR_FRAME_NUM = 2102;
|
||||||
|
CAN_CLIENT_ERROR_SEND_FAILED = 2103;
|
||||||
|
CAN_CLIENT_ERROR_RECV_FAILED = 2104;
|
||||||
|
|
||||||
|
// motor
|
||||||
|
MOTOR_ERROR = 3000;
|
||||||
|
MOTOR_ERROR_SET_ZERO = 3001;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
message StatusPb {
|
||||||
|
ErrorCode error_code = 1;
|
||||||
|
string msg = 2;
|
||||||
|
}
|
||||||
118
protos/cmvr/msgs/motor.proto
Normal file
118
protos/cmvr/msgs/motor.proto
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
import "cmvr/msgs/canopen.proto";
|
||||||
|
package cmvr.msgs;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* --------------------------------------------------------
|
||||||
|
* CAN OPEN 定义
|
||||||
|
* --------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 电机控制模式(Operation Mode)
|
||||||
|
enum RunMode {
|
||||||
|
// 未指定
|
||||||
|
RUN_MODE_UNSPECIFIED = 0;
|
||||||
|
|
||||||
|
// Profile Position Mode - 位置模式
|
||||||
|
RUN_MODE_PROFILE_POSITION = 1;
|
||||||
|
|
||||||
|
// Velocity Mode - 简单速度模式
|
||||||
|
RUN_MODE_VELOCITY = 2;
|
||||||
|
|
||||||
|
// Profile Velocity Mode - 速度模式(带轮廓)
|
||||||
|
RUN_MODE_PROFILE_VELOCITY = 3;
|
||||||
|
|
||||||
|
// Torque Mode - 转矩模式
|
||||||
|
RUN_MODE_TORQUE = 4;
|
||||||
|
|
||||||
|
// Homing Mode - 回零模式
|
||||||
|
RUN_MODE_HOMING = 5;
|
||||||
|
|
||||||
|
// Interpolation Mode - 位置插补模式
|
||||||
|
RUN_MODE_INTERPOLATED_POSITION = 7;
|
||||||
|
|
||||||
|
// Cyclic Synchronous Position Mode - 周期位置模式
|
||||||
|
RUN_MODE_CYCLIC_SYNC_POSITION = 8;
|
||||||
|
|
||||||
|
// Cyclic Synchronous Velocity Mode - 周期速度模式
|
||||||
|
RUN_MODE_CYCLIC_SYNC_VELOCITY = 9;
|
||||||
|
|
||||||
|
// Cyclic Synchronous Current Mode - 周期转矩模式
|
||||||
|
RUN_MODE_CYCLIC_SYNC_CURRENT = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 电机状态数据
|
||||||
|
message MotorStatus {
|
||||||
|
RunMode run_mode = 1; // 当前运行模式
|
||||||
|
|
||||||
|
|
||||||
|
int32 current = 2; // 当前电流(单位:mA)
|
||||||
|
int32 target_current = 3; // 目标电流(单位:mA)
|
||||||
|
int32 speed = 4; // 当前速度,转换为度每秒:(speed / 100 / 减速比) * 360
|
||||||
|
int32 target_speed = 5; // 目标速度,单位同上
|
||||||
|
int32 position = 6; // 当前电机位置(位置转角度:(position / 65536 / 减速比) * 360)
|
||||||
|
int32 target_position = 7; // 目标位置(非位置模式无效)
|
||||||
|
uint32 error_state = 8; // 错误状态位(见位说明)
|
||||||
|
|
||||||
|
int32 speed_kp = 9; // 速度环Kp,含模式和速度信息(bit复用)
|
||||||
|
int32 speed_ki = 10; // 速度环Ki,含模式和速度信息(bit复用)
|
||||||
|
int32 speed_kd = 11; // 速度环Kd
|
||||||
|
|
||||||
|
int32 position_kp = 12; // 位置环Kp
|
||||||
|
int32 position_ki = 13; // 位置环Ki
|
||||||
|
int32 position_kd = 14; // 位置环Kd
|
||||||
|
|
||||||
|
int32 bus_voltage = 15; // 母线电压
|
||||||
|
|
||||||
|
int32 max_abs_current = 16; // 最大电流绝对值(单位:mA)
|
||||||
|
int32 max_pos_current = 17; // 最大正电流(单位:mA)
|
||||||
|
int32 min_neg_current = 18; // 最小负电流(单位:mA)
|
||||||
|
|
||||||
|
int32 max_pos_accel = 19; // 最大正向加速度
|
||||||
|
int32 min_neg_accel = 20; // 最小负向加速度
|
||||||
|
int32 max_pos_velocity = 21; // 最大正向速度(转化为度/秒)
|
||||||
|
int32 min_neg_velocity = 22; // 最小负向速度(转化为度/秒)
|
||||||
|
|
||||||
|
int32 max_pos_position = 23; // 最大正向位置(角度)
|
||||||
|
int32 min_neg_position = 24; // 最小负向位置(角度)
|
||||||
|
|
||||||
|
int32 motor_temp = 25; // 电机温度(单位:摄氏度)
|
||||||
|
int32 board_temp = 26; // 电路板温度(单位:摄氏度)
|
||||||
|
|
||||||
|
int32 current_kp = 27; // 电流P
|
||||||
|
int32 current_ki = 28; // 电流I
|
||||||
|
int32 current_kd = 29; // 电流D
|
||||||
|
|
||||||
|
int32 motor_type = 30; // 电机型号(复合字段,包含刹车、ACTUATOR_TYPE等)
|
||||||
|
int32 motor_version = 31; // 电机版本号(软件+硬件,16进制格式)
|
||||||
|
int32 software_version = 32;// 软件版本号(16进制格式)
|
||||||
|
|
||||||
|
int32 position_offset = 33; // 当前位置偏移(编码器位置 - 偏移值)
|
||||||
|
bytes csp_data = 34; // 获取CSP,8字节数据
|
||||||
|
int32 encoder_voltage = 35; // 编码器电池电压(新版硬件支持)
|
||||||
|
int32 encoder_state = 36; // 编码器状态(双编码器=外圈位置,单编码器=状态)
|
||||||
|
|
||||||
|
int32 overvoltage_limit = 37; // 过压阈值(单位:V)
|
||||||
|
int32 undervoltage_limit = 38; // 欠压阈值(单位:V)
|
||||||
|
int32 coil_over_temp = 39; // 电机线圈过温阈值(单位:℃)
|
||||||
|
int32 driver_over_temp = 40; // 驱动板过温阈值(单位:℃)
|
||||||
|
|
||||||
|
/* ---------------------
|
||||||
|
* CANOPEN 特有的参数
|
||||||
|
* --------------------*/
|
||||||
|
SdoFrame sdo_response = 41;
|
||||||
|
NmtState nmt_state = 42;
|
||||||
|
// 只保留值,具体值在使用的时候使用联合体去解析
|
||||||
|
uint32 ctrl_word = 43;
|
||||||
|
uint32 status_word = 44;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
14
protos/cmvr/msgs/robot_detail.proto
Normal file
14
protos/cmvr/msgs/robot_detail.proto
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "cmvr/msgs/canopen.proto";
|
||||||
|
import "cmvr/msgs/motor.proto";
|
||||||
|
package cmvr.msgs;
|
||||||
|
|
||||||
|
|
||||||
|
// 包含机器人的所有信息
|
||||||
|
|
||||||
|
message RobotDetail{
|
||||||
|
|
||||||
|
// key 电机 nodeid
|
||||||
|
map<uint32, MotorStatus> motors = 1; // node_id => status
|
||||||
|
}
|
||||||
6
requirements.txt
Normal file
6
requirements.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
grpcio==1.60.0
|
||||||
|
grpcio-tools==1.60.0
|
||||||
|
protobuf==4.25.3
|
||||||
|
Pillow==10.1.0
|
||||||
|
numpy==1.24.4
|
||||||
|
opencv-python==4.9.0.80
|
||||||
Loading…
Reference in New Issue
Block a user