grpc_client/clients/get_joint_state_client.py

152 lines
5.1 KiB
Python
Raw Normal View History

2025-11-13 03:01:00 +08:00
import time
from google.protobuf import timestamp_pb2
2026-02-02 09:37:47 +08:00
import matplotlib.pyplot as plt # 新增
2026-02-03 13:36:52 +08:00
from clients._path_setup import ensure_paths
ensure_paths()
2025-11-13 03:01:00 +08:00
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
2025-11-13 03:01:00 +08:00
2026-02-02 09:37:47 +08:00
# 固定关节顺序
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"
]
2025-11-13 03:01:00 +08:00
class GetJointStateClient(RobotClientBase):
"""Continuous client to fetch robot joint states"""
2026-02-02 09:37:47 +08:00
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"):
2025-11-13 03:01:00 +08:00
"""Continuously fetch joint states and print in fixed order"""
try:
2026-02-02 09:37:47 +08:00
if self._start_time is None:
self._start_time = time.time()
2025-11-13 03:01:00 +08:00
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
2026-02-02 09:37:47 +08:00
# ====== 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)
2025-11-13 03:01:00 +08:00
2026-02-02 09:37:47 +08:00
# ====== 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]
2025-11-13 03:01:00 +08:00
# 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.")
2026-02-02 09:37:47 +08:00
# 例子:退出时画几个关节的角度 & 速度曲线
2026-02-03 13:36:52 +08:00
try:
# 你可以根据需要改关节列表
self.plot_pos_and_vel([ "L_SHOULDER_R"])
except Exception as e:
print(f"绘图失败: {e}")
2026-02-02 09:37:47 +08:00
# ====== 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()
2025-11-13 03:01:00 +08:00
if __name__ == "__main__":
client = GetJointStateClient()
try:
2026-02-02 09:37:47 +08:00
client.send(interval=0.005)
2025-11-13 03:01:00 +08:00
finally:
client.close()