grpc_client/clients/get_joint_state_client.py

152 lines
5.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

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

import time
from google.protobuf import timestamp_pb2
import matplotlib.pyplot as plt # 新增
from clients._path_setup import ensure_paths
ensure_paths()
from clients.base_client import RobotClientBase
from cmvr.api import arm_command_pb2 as pb
from cmvr.api import arm_service_pb2_grpc as rpc
# 固定关节顺序
JOINT_ORDER = [
"L_SHOULDER_P", "L_SHOULDER_R", "L_SHOULDER_Y",
"L_ELBOW_R", "L_WRIST_P", "L_WRIST_Y", "L_WRIST_R",
"R_SHOULDER_P", "R_SHOULDER_R", "R_SHOULDER_Y",
"R_ELBOW_R", "R_WRIST_P", "R_WRIST_Y", "R_WRIST_R",
"HEAD_P", "HEAD_Y", "HEAD_R","WAIST_Y","WAIST_R"
]
class GetJointStateClient(RobotClientBase):
"""Continuous client to fetch robot joint states"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 时间轴
self.time_history = []
self._start_time = None
# 记录每个关节的角度、速度
self.pos_history = {name: [] for name in JOINT_ORDER}
self.vel_history = {name: [] for name in JOINT_ORDER}
def send(self, interval=0.5, device_id="right_arm"):
"""Continuously fetch joint states and print in fixed order"""
try:
if self._start_time is None:
self._start_time = time.time()
while True:
# Construct request
req = pb.JointRequest()
req.header.device_id = device_id
timestamp = timestamp_pb2.Timestamp()
timestamp.GetCurrentTime()
req.header.timestamp.CopyFrom(timestamp)
# Call RPC with timeout
try:
resp = self.stub.getJointState(req, timeout=10)
except Exception as e:
print(f"RPC call failed: {e}")
time.sleep(interval)
continue
# ====== 1. 同时取 position 和 velocity ======
joint_pos_dict = {}
joint_vel_dict = {}
state = resp.state
for name, pos, vel in zip(state.name, state.position, state.velocity):
joint_pos_dict[name] = round(pos, 12)
joint_vel_dict[name] = round(vel, 12)
# ====== 2. 记录到历史数据里,用于之后画图 ======
t = time.time() - self._start_time
self.time_history.append(t)
for name in JOINT_ORDER:
self.pos_history[name].append(joint_pos_dict.get(name, 0.0))
self.vel_history[name].append(joint_vel_dict.get(name, 0.0))
# 保持原来的打印逻辑(打印角度)
joint_list = [{"joint_name": name, "rad": joint_pos_dict.get(name, 0.0)}
for name in JOINT_ORDER]
# Print timestamp
print(f"[{time.strftime('%H:%M:%S')}]")
# Print each joint, last one without comma
for i, j in enumerate(joint_list):
if i < len(joint_list) - 1:
print(f"{j},")
else:
print(f"{j}")
time.sleep(interval)
except KeyboardInterrupt:
print("\nStopped fetching joint states.")
# 例子:退出时画几个关节的角度 & 速度曲线
try:
# 你可以根据需要改关节列表
self.plot_pos_and_vel([ "L_SHOULDER_R"])
except Exception as e:
print(f"绘图失败: {e}")
# ====== 3. 绘制速度和角度(同一张图,两行子图) ======
def plot_pos_and_vel(self, joint_names):
"""
在一张图中绘制多个关节的角度和速度:
- 上 subplot角度rad
- 下 subplot速度rad/s
joint_names: str 或 [str, ...]
"""
if isinstance(joint_names, str):
joint_names = [joint_names]
# 检查关节名合法性
invalid = [n for n in joint_names if n not in self.pos_history]
if invalid:
print(f"未知关节名: {invalid}")
print(f"可选关节: {list(self.pos_history.keys())}")
return
if not self.time_history:
print("还没有采集到任何数据,无法绘图。")
return
t = self.time_history
plt.figure(figsize=(10, 6))
# 上:角度
plt.subplot(2, 1, 1)
for name in joint_names:
plt.plot(t, self.pos_history[name], label=name)
plt.ylabel("Position (rad)")
plt.title("Joint Position")
plt.grid(True)
plt.legend()
# 下:速度
plt.subplot(2, 1, 2)
for name in joint_names:
plt.plot(t, self.vel_history[name], label=name)
plt.xlabel("Time (s)")
plt.ylabel("Velocity (rad/s)")
plt.title("Joint Velocity")
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
if __name__ == "__main__":
client = GetJointStateClient()
try:
client.send(interval=0.005)
finally:
client.close()