165 lines
5.8 KiB
Python
165 lines
5.8 KiB
Python
|
||
import csv
|
||
|
||
import math
|
||
import sys
|
||
from google.protobuf import timestamp_pb2
|
||
|
||
sys.path.append("../generated")
|
||
import math
|
||
from typing import List, Dict, Literal
|
||
from generated.cmvr.api import humanoid_robot_command_pb2 as pb
|
||
from generated.cmvr.api import common_pb2
|
||
from generated.cmvr.api import humanoid_robot_service_pb2_grpc as rpc
|
||
from clients.base_client import RobotClientBase
|
||
|
||
|
||
class MoveJClient(RobotClientBase):
|
||
"""Client to send MoveJ commands to the robot"""
|
||
|
||
def send(self, joint_list, vel=1.0, acc=0.5, device_id="hc01"):
|
||
"""
|
||
joint_list: list of dicts, e.g.,
|
||
[
|
||
{"joint_name": "L_SHOULDER_P", "rad": 0.0},
|
||
{"joint_name": "L_SHOULDER_R", "rad": -1.31873},
|
||
...
|
||
]
|
||
vel: velocity
|
||
acc: acceleration
|
||
device_id: device ID
|
||
"""
|
||
# Construct JointCmd list
|
||
cmds = [pb.JointCmd(joint_name=j["joint_name"], rad=j["rad"], vel=vel) for j in joint_list]
|
||
|
||
# Construct request header
|
||
header = common_pb2.CommandHeader.Request()
|
||
header.device_id = device_id
|
||
ts = timestamp_pb2.Timestamp()
|
||
ts.GetCurrentTime()
|
||
header.timestamp.CopyFrom(ts)
|
||
|
||
# Construct MoveJ request
|
||
req = pb.MoveJ.Request(
|
||
header=header,
|
||
vel=vel,
|
||
acc=acc,
|
||
cmds=cmds
|
||
)
|
||
|
||
# Call RPC
|
||
try:
|
||
resp = self.stub.moveJ(req, timeout=10)
|
||
success = getattr(resp.header, "success", None)
|
||
error_msg = getattr(resp.header, "error_message", "")
|
||
timestamp_sec = getattr(resp.header.timestamp, "seconds", 0)
|
||
print("MoveJ RPC call succeeded")
|
||
print(f"Success: {success}")
|
||
print(f"Error message: {error_msg}")
|
||
print(f"Timestamp: {timestamp_sec}")
|
||
except Exception as e:
|
||
print("MoveJ RPC call failed:", e)
|
||
|
||
def read_joint_from_csv(self, file_name, row_num):
|
||
"""Read joint data from a CSV file and return joint list for the specified row."""
|
||
joint_order = [
|
||
'R_SHOULDER_P', 'R_SHOULDER_R', 'R_SHOULDER_Y',
|
||
'R_ELBOW_R', 'R_WRIST_P', 'R_WRIST_Y', 'R_WRIST_R'
|
||
]
|
||
|
||
try:
|
||
with open(file_name, newline='') as csvfile:
|
||
reader = csv.reader(csvfile)
|
||
# Skip header row
|
||
next(reader)
|
||
# Read specific row
|
||
for i, row in enumerate(reader, start=1):
|
||
if i == row_num:
|
||
# Assign joint values to the list
|
||
joint_list = [
|
||
{'joint_name': joint_order[j], 'rad': round(float(row[j]) * math.pi / 180, 10)}
|
||
for j in range(len(joint_order))
|
||
]
|
||
return joint_list
|
||
print(f"Row {row_num} not found.")
|
||
return []
|
||
except Exception as e:
|
||
print(f"Error reading CSV: {e}")
|
||
return []
|
||
|
||
def apply_joint_offsets(self,
|
||
joint_list: List[Dict[str, float]],
|
||
on_missing: Literal["ignore", "error", "warn"] = "ignore",
|
||
) -> List[Dict[str, float]]:
|
||
"""
|
||
将零偏置 joint_zero_offset 叠加到 joint_list 上,按 joint_name 对齐。
|
||
|
||
参数:
|
||
joint_list: [{'joint_name': str, 'rad': float}, ...] 原始指令
|
||
joint_zero_offset: [{'joint_name': str, 'rad': float}, ...] 零偏置
|
||
on_missing: 当 joint_list 中出现 zero_offset 里没有的关节名时的处理:
|
||
- "ignore": 当作偏置为 0(默认)
|
||
- "warn": 打印警告,再当作 0
|
||
- "error": 抛出 KeyError
|
||
|
||
返回:
|
||
与 joint_list 顺序一致、已叠加偏置的新列表
|
||
"""
|
||
# 构造偏置表,并检查是否有重复关节名
|
||
|
||
deg_to_rad = math.pi / 180.0
|
||
|
||
joint_zero_offset = [
|
||
{'joint_name': 'R_SHOULDER_P', 'rad': 0},
|
||
{'joint_name': 'R_SHOULDER_R', 'rad': 0 * deg_to_rad},
|
||
{'joint_name': 'R_SHOULDER_Y', 'rad': 0.2 * deg_to_rad},
|
||
{'joint_name': 'R_ELBOW_R', 'rad': 0.2 * deg_to_rad},
|
||
{'joint_name': 'R_WRIST_P', 'rad': 0.6 * deg_to_rad},
|
||
{'joint_name': 'R_WRIST_Y', 'rad': 2.5 * deg_to_rad},
|
||
{'joint_name': 'R_WRIST_R', 'rad': 0},
|
||
]
|
||
|
||
offset_map: Dict[str, float] = {}
|
||
for j in joint_zero_offset:
|
||
name = j["joint_name"]
|
||
if name in offset_map:
|
||
raise ValueError(f"Duplicated joint in zero_offset: {name}")
|
||
offset_map[name] = float(j["rad"])
|
||
|
||
joint_cmd: List[Dict[str, float]] = []
|
||
for j in joint_list:
|
||
name = j["joint_name"]
|
||
base = float(j["rad"])
|
||
if name not in offset_map:
|
||
if on_missing == "error":
|
||
raise KeyError(f"Missing zero offset for joint: {name}")
|
||
elif on_missing == "warn":
|
||
print(f"[apply_joint_offsets] WARN: Missing zero offset for {name}, using 0.0")
|
||
off = 0.0
|
||
else:
|
||
off = offset_map[name]
|
||
|
||
joint_cmd.append({"joint_name": name, "rad": base + off})
|
||
return joint_cmd
|
||
|
||
|
||
if __name__ == "__main__":
|
||
# Initialize client
|
||
client = MoveJClient()
|
||
|
||
# Specify CSV file and row number to read from
|
||
file_name = 'joint_states.csv'
|
||
row_num = 2 # Row number you want to read (e.g., 2 for the second row)
|
||
|
||
# Read joint data from CSV
|
||
joint_list = client.read_joint_from_csv(file_name, 8)
|
||
joint_list = client.apply_joint_offsets(joint_list)
|
||
print(joint_list)
|
||
|
||
if joint_list:
|
||
|
||
# Send MoveJ command with the read joint data
|
||
client.send(joint_list, vel=0.8, acc=0.8)
|
||
|
||
# Close client
|
||
client.close() |