diff --git a/.idea/grpc_client.iml b/.idea/grpc_client.iml
index 2c80e12..9c1850b 100644
--- a/.idea/grpc_client.iml
+++ b/.idea/grpc_client.iml
@@ -2,9 +2,10 @@
+
-
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 366343e..6ce4790 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -3,5 +3,5 @@
-
+
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..cc17057
--- /dev/null
+++ b/README.md
@@ -0,0 +1,44 @@
+# grpc_client
+
+## Environment
+
+- Python: 3.9.25
+
+## Setup
+
+```bash
+pip install -r requirements.txt
+```
+
+## Generate gRPC code
+
+From repo root:
+
+```bash
+python scripts/gen_grpc_py.py
+```
+
+This reads `.proto` files under `protos/` and writes Python code to `generated/`.
+
+## Run (module mode)
+
+```bash
+python -m clients.movej_client
+python -m clients.get_joint_state_client
+python -m clients.get_pose_client
+python -m clients.torque_on_client
+python -m clients.torque_off_client
+python -m clients.torque_key_client
+python -m clients.press_client
+python -m clients.save_joint_to_csv
+python -m clients.http_server
+```
+
+## Runtime parameters
+
+- gRPC server address/port: set in `clients/base_client.py` (default `192.168.0.222:50052`).
+- HTTP server host/port: set in `clients/http_server.py` (default `0.0.0.0:8000`).
+- `device_id` defaults to `hc01` in most clients; change in code if needed.
+
+
+
diff --git a/clients/__pycache__/__init__.cpython-39.pyc b/clients/__pycache__/__init__.cpython-39.pyc
index bf63648..e6ee043 100644
Binary files a/clients/__pycache__/__init__.cpython-39.pyc and b/clients/__pycache__/__init__.cpython-39.pyc differ
diff --git a/clients/__pycache__/_path_setup.cpython-39.pyc b/clients/__pycache__/_path_setup.cpython-39.pyc
new file mode 100644
index 0000000..889b4a6
Binary files /dev/null and b/clients/__pycache__/_path_setup.cpython-39.pyc differ
diff --git a/clients/__pycache__/base_client.cpython-39.pyc b/clients/__pycache__/base_client.cpython-39.pyc
index 985fd70..a76c039 100644
Binary files a/clients/__pycache__/base_client.cpython-39.pyc and b/clients/__pycache__/base_client.cpython-39.pyc differ
diff --git a/clients/__pycache__/get_joint_state_client.cpython-39.pyc b/clients/__pycache__/get_joint_state_client.cpython-39.pyc
new file mode 100644
index 0000000..c8ceaa5
Binary files /dev/null and b/clients/__pycache__/get_joint_state_client.cpython-39.pyc differ
diff --git a/clients/__pycache__/hand_client.cpython-39.pyc b/clients/__pycache__/hand_client.cpython-39.pyc
index 2a93c5c..5fa591a 100644
Binary files a/clients/__pycache__/hand_client.cpython-39.pyc and b/clients/__pycache__/hand_client.cpython-39.pyc differ
diff --git a/clients/__pycache__/http_server.cpython-39.pyc b/clients/__pycache__/http_server.cpython-39.pyc
index c5a6359..b7b9f9b 100644
Binary files a/clients/__pycache__/http_server.cpython-39.pyc and b/clients/__pycache__/http_server.cpython-39.pyc differ
diff --git a/clients/__pycache__/movej_client.cpython-39.pyc b/clients/__pycache__/movej_client.cpython-39.pyc
index 90a9f1c..d2776e4 100644
Binary files a/clients/__pycache__/movej_client.cpython-39.pyc and b/clients/__pycache__/movej_client.cpython-39.pyc differ
diff --git a/clients/_path_setup.py b/clients/_path_setup.py
new file mode 100644
index 0000000..7b8ff6f
--- /dev/null
+++ b/clients/_path_setup.py
@@ -0,0 +1,11 @@
+from pathlib import Path
+import sys
+
+
+def ensure_paths() -> None:
+ root = Path(__file__).resolve().parents[1]
+ generated = root / "generated"
+ for path in (root, generated):
+ path_str = str(path)
+ if path_str not in sys.path:
+ sys.path.insert(0, path_str)
diff --git a/clients/base_client.py b/clients/base_client.py
index 6896f03..995770b 100644
--- a/clients/base_client.py
+++ b/clients/base_client.py
@@ -1,13 +1,14 @@
-import grpc
import time
-import sys
-from pathlib import Path
-BASE_DIR = Path(__file__).resolve().parent.parent # 上一级目录
-sys.path.append(str(BASE_DIR / "generated"))
-sys.path.append(str(BASE_DIR / "generated" / "cmvr"))
-from cmvr.api import humanoid_robot_service_pb2_grpc
+import grpc
+
+from clients._path_setup import ensure_paths
+
+ensure_paths()
+
from cmvr.api import dexhand_service_pb2_grpc
+from cmvr.api import humanoid_robot_service_pb2_grpc
+
class RobotClientBase:
def __init__(self, address="192.168.0.222:50052", timeout=2, retries=1):
diff --git a/clients/cal.py b/clients/cal.py
deleted file mode 100644
index efb6443..0000000
--- a/clients/cal.py
+++ /dev/null
@@ -1,165 +0,0 @@
-
-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()
\ No newline at end of file
diff --git a/clients/get_joint_state_client.py b/clients/get_joint_state_client.py
index e856b76..bf85d4b 100644
--- a/clients/get_joint_state_client.py
+++ b/clients/get_joint_state_client.py
@@ -1,14 +1,14 @@
-import sys
import time
from google.protobuf import timestamp_pb2
-
import matplotlib.pyplot as plt # 新增
-sys.path.append("../generated")
+from clients._path_setup import ensure_paths
+
+ensure_paths()
-from generated.cmvr.api import humanoid_robot_service_pb2_grpc as rpc
-from generated.cmvr.api import humanoid_robot_command_pb2 as pb
from clients.base_client import RobotClientBase
+from cmvr.api import humanoid_robot_command_pb2 as pb
+from cmvr.api import humanoid_robot_service_pb2_grpc as rpc
# 固定关节顺序
JOINT_ORDER = [
@@ -88,12 +88,11 @@ class GetJointStateClient(RobotClientBase):
except KeyboardInterrupt:
print("\nStopped fetching joint states.")
# 例子:退出时画几个关节的角度 & 速度曲线
- # try:
- # # 你可以根据需要改关节列表
- # self.plot_pos_and_vel([ "R_SHOULDER_P", "R_SHOULDER_R", "R_SHOULDER_Y",
- # "R_ELBOW_R", "R_WRIST_P", "R_WRIST_Y", "R_WRIST_R"])
- # except Exception as e:
- # print(f"绘图失败: {e}")
+ 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):
diff --git a/clients/get_pose_client.py b/clients/get_pose_client.py
index aebc3d4..95bff89 100644
--- a/clients/get_pose_client.py
+++ b/clients/get_pose_client.py
@@ -1,12 +1,14 @@
-import sys
import time
+
+from clients._path_setup import ensure_paths
+
+ensure_paths()
+
from google.protobuf import timestamp_pb2
-sys.path.append("../generated")
-
-from generated.cmvr.api import humanoid_robot_service_pb2_grpc as rpc
-from generated.cmvr.api import humanoid_robot_command_pb2 as pb
from clients.base_client import RobotClientBase
+from cmvr.api import humanoid_robot_command_pb2 as pb
+from cmvr.api import humanoid_robot_service_pb2_grpc as rpc
class GetPoseClient(RobotClientBase):
diff --git a/clients/hand_client.py b/clients/hand_client.py
index dc212e4..75e0951 100644
--- a/clients/hand_client.py
+++ b/clients/hand_client.py
@@ -1,12 +1,12 @@
-import sys
+from clients._path_setup import ensure_paths
+
+ensure_paths()
+
from google.protobuf import timestamp_pb2
-sys.path.append("../generated")
-
-
-from generated.cmvr.api import dexhand_command_pb2 as pb
-from generated.cmvr.api import common_pb2
from clients.base_client import RobotClientBase
+from cmvr.api import common_pb2
+from cmvr.api import dexhand_command_pb2 as pb
handshake = [
diff --git a/clients/http_server.py b/clients/http_server.py
index 9489167..78edb30 100644
--- a/clients/http_server.py
+++ b/clients/http_server.py
@@ -1,5 +1,9 @@
from __future__ import annotations
+from clients._path_setup import ensure_paths
+
+ensure_paths()
+
import time
from threading import Lock
from fastapi import FastAPI
@@ -130,4 +134,4 @@ if __name__ == "__main__":
host="0.0.0.0",
port=8000,
reload=False
- )
\ No newline at end of file
+ )
diff --git a/clients/movej_client.py b/clients/movej_client.py
index e6c3440..4285b84 100644
--- a/clients/movej_client.py
+++ b/clients/movej_client.py
@@ -1,18 +1,18 @@
-import sys
+import math
import time
+from typing import Dict, List, Literal
+
+from clients._path_setup import ensure_paths
+
+ensure_paths()
from google.protobuf import timestamp_pb2
-from clients.hand_client import DexHandClient
-
-sys.path.append("../generated")
-
-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
-import math
-from typing import List, Dict, Literal
+from clients.hand_client import DexHandClient
+from cmvr.api import common_pb2
+from cmvr.api import humanoid_robot_command_pb2 as pb
+from cmvr.api import humanoid_robot_service_pb2_grpc as rpc
@@ -212,7 +212,7 @@ head_screen_joint_list = [
]
singal_joint_list = [
-{'joint_name': 'R_WRIST_P', 'rad': 0.217252876821},
+{'joint_name': 'L_SHOULDER_R', 'rad': 0},
]
hand_touch = [
@@ -263,6 +263,18 @@ hello_r_joint_list = [
+test_joint_list = [
+ # {'joint_name': 'R_SHOULDER_P', 'rad':0},
+ {'joint_name': 'R_SHOULDER_R', 'rad': 0},
+ # {'joint_name': 'R_SHOULDER_Y', 'rad': 0},
+ # {'joint_name': 'R_ELBOW_R', 'rad':0},
+ # {'joint_name': 'R_WRIST_P', 'rad': 0},
+ # {'joint_name': 'R_WRIST_Y', 'rad': 0},
+ # {'joint_name': 'R_WRIST_R', 'rad': 0},
+]
+
+
+
#
@@ -363,8 +375,8 @@ if __name__ == "__main__":
# Initialize client
# action_air(client)
- action_hello(client)
- # client.send(head_front_joint_list, vel=1.0, acc=1.0)
+ # action_hello(client)
+ client.send(test_joint_list, vel=1.0, acc=1.0)
# client.send(go_home_joint_list, vel=3.0, acc=3.0)
# action_music(client)
diff --git a/clients/press_client.py b/clients/press_client.py
index 40bcef9..87bf02e 100644
--- a/clients/press_client.py
+++ b/clients/press_client.py
@@ -1,10 +1,11 @@
-import sys
+from clients._path_setup import ensure_paths
+
+ensure_paths()
+
from google.protobuf import timestamp_pb2
-sys.path.append("../generated")
-
-from generated.cmvr.api import hlc_command_pb2 as pb
from clients.base_client import RobotClientBase
+from cmvr.api import hlc_command_pb2 as pb
class TouchClient(RobotClientBase):
diff --git a/clients/save_joint_to_csv.py b/clients/save_joint_to_csv.py
index 04611c4..28540f1 100644
--- a/clients/save_joint_to_csv.py
+++ b/clients/save_joint_to_csv.py
@@ -1,13 +1,15 @@
-import sys
import csv
import math
+
+from clients._path_setup import ensure_paths
+
+ensure_paths()
+
from google.protobuf import timestamp_pb2
-sys.path.append("../generated")
-
-from generated.cmvr.api import humanoid_robot_service_pb2_grpc as rpc
-from generated.cmvr.api import humanoid_robot_command_pb2 as pb
from clients.base_client import RobotClientBase
+from cmvr.api import humanoid_robot_command_pb2 as pb
+from cmvr.api import humanoid_robot_service_pb2_grpc as rpc
class GetJointStateClient(RobotClientBase):
diff --git a/clients/torque_key_client.py b/clients/torque_key_client.py
index 21ddf48..045695e 100644
--- a/clients/torque_key_client.py
+++ b/clients/torque_key_client.py
@@ -1,19 +1,17 @@
-
import sys
-import tty
import termios
+import tty
+
+from clients._path_setup import ensure_paths
+
+ensure_paths()
+
from google.protobuf import timestamp_pb2
-# sys.path.append("../generated")
-sys.path.append("/home/lgv/cmvr/0-workspace/grpc_client/generated") # 指向 generated
-sys.path.append("/home/lgv/cmvr/0-workspace/grpc_client/generated/cmvr") # 指向 cmvr 顶层
-
-from cmvr.api import humanoid_robot_service_pb2_grpc as rpc
-from cmvr.api import humanoid_robot_command_pb2 as pb
+from clients.base_client import RobotClientBase
from cmvr.api import common_pb2
-
-
-from base_client import RobotClientBase
+from cmvr.api import humanoid_robot_command_pb2 as pb
+from cmvr.api import humanoid_robot_service_pb2_grpc as rpc
class TorqueClient(RobotClientBase):
diff --git a/clients/torque_off_client.py b/clients/torque_off_client.py
index c8cd09b..293edf3 100644
--- a/clients/torque_off_client.py
+++ b/clients/torque_off_client.py
@@ -1,12 +1,13 @@
-import sys
+from clients._path_setup import ensure_paths
+
+ensure_paths()
+
from google.protobuf import timestamp_pb2
-sys.path.append("../generated")
-
-from generated.cmvr.api import humanoid_robot_service_pb2_grpc as rpc
-from generated.cmvr.api import humanoid_robot_command_pb2 as pb
-from generated.cmvr.api import common_pb2
from clients.base_client import RobotClientBase
+from cmvr.api import common_pb2
+from cmvr.api import humanoid_robot_command_pb2 as pb
+from cmvr.api import humanoid_robot_service_pb2_grpc as rpc
class TorqueOffClient(RobotClientBase):
diff --git a/clients/torque_on_client.py b/clients/torque_on_client.py
index a39e47d..df42df1 100644
--- a/clients/torque_on_client.py
+++ b/clients/torque_on_client.py
@@ -1,12 +1,13 @@
-import sys
+from clients._path_setup import ensure_paths
+
+ensure_paths()
+
from google.protobuf import timestamp_pb2
-sys.path.append("../generated")
-
-from generated.cmvr.api import humanoid_robot_service_pb2_grpc as rpc
-from generated.cmvr.api import humanoid_robot_command_pb2 as pb
-from generated.cmvr.api import common_pb2
from clients.base_client import RobotClientBase
+from cmvr.api import common_pb2
+from cmvr.api import humanoid_robot_command_pb2 as pb
+from cmvr.api import humanoid_robot_service_pb2_grpc as rpc
class TorqueOnClient(RobotClientBase):
diff --git a/generated/cmvr/__pycache__/__init__.cpython-39.pyc b/generated/cmvr/__pycache__/__init__.cpython-39.pyc
index 526278d..b1b4d0b 100644
Binary files a/generated/cmvr/__pycache__/__init__.cpython-39.pyc and b/generated/cmvr/__pycache__/__init__.cpython-39.pyc differ
diff --git a/generated/cmvr/api/__pycache__/__init__.cpython-39.pyc b/generated/cmvr/api/__pycache__/__init__.cpython-39.pyc
index df87170..a78f275 100644
Binary files a/generated/cmvr/api/__pycache__/__init__.cpython-39.pyc and b/generated/cmvr/api/__pycache__/__init__.cpython-39.pyc differ
diff --git a/generated/cmvr/api/__pycache__/common_pb2.cpython-39.pyc b/generated/cmvr/api/__pycache__/common_pb2.cpython-39.pyc
index 235d56c..33fbda6 100644
Binary files a/generated/cmvr/api/__pycache__/common_pb2.cpython-39.pyc and b/generated/cmvr/api/__pycache__/common_pb2.cpython-39.pyc differ
diff --git a/generated/cmvr/api/__pycache__/dexhand_command_pb2.cpython-39.pyc b/generated/cmvr/api/__pycache__/dexhand_command_pb2.cpython-39.pyc
index ca3379f..b70627f 100644
Binary files a/generated/cmvr/api/__pycache__/dexhand_command_pb2.cpython-39.pyc and b/generated/cmvr/api/__pycache__/dexhand_command_pb2.cpython-39.pyc differ
diff --git a/generated/cmvr/api/__pycache__/dexhand_service_pb2_grpc.cpython-39.pyc b/generated/cmvr/api/__pycache__/dexhand_service_pb2_grpc.cpython-39.pyc
index 41cc4b1..3d840e8 100644
Binary files a/generated/cmvr/api/__pycache__/dexhand_service_pb2_grpc.cpython-39.pyc and b/generated/cmvr/api/__pycache__/dexhand_service_pb2_grpc.cpython-39.pyc differ
diff --git a/generated/cmvr/api/__pycache__/humanoid_robot_command_pb2.cpython-39.pyc b/generated/cmvr/api/__pycache__/humanoid_robot_command_pb2.cpython-39.pyc
index 14150d6..1856ba5 100644
Binary files a/generated/cmvr/api/__pycache__/humanoid_robot_command_pb2.cpython-39.pyc and b/generated/cmvr/api/__pycache__/humanoid_robot_command_pb2.cpython-39.pyc differ
diff --git a/generated/cmvr/api/__pycache__/humanoid_robot_service_pb2_grpc.cpython-39.pyc b/generated/cmvr/api/__pycache__/humanoid_robot_service_pb2_grpc.cpython-39.pyc
index 93203b0..1caa4b6 100644
Binary files a/generated/cmvr/api/__pycache__/humanoid_robot_service_pb2_grpc.cpython-39.pyc and b/generated/cmvr/api/__pycache__/humanoid_robot_service_pb2_grpc.cpython-39.pyc differ
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..4d66caa
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,8 @@
+fastapi==0.124.4
+grpcio==1.76.0
+grpcio-tools==1.76.0
+matplotlib==3.9.4
+protobuf==6.33.2
+pydantic==2.12.5
+requests==2.32.5
+uvicorn==0.38.0
diff --git a/run_client.py b/run_client.py
index 0c317e8..06e6b9f 100644
--- a/run_client.py
+++ b/run_client.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
-统一启动 gRPC 客户端脚本
-自动设置 PYTHONPATH,让 generated 下的 cmvr 包可用
-用法:
+Unified entrypoint for running client scripts.
+
+Usage:
python run_client.py get_joint_state_client
python run_client.py movej_client
"""
@@ -16,20 +16,25 @@ PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
GENERATED_DIR = os.path.join(PROJECT_ROOT, "generated")
CLIENTS_DIR = os.path.join(PROJECT_ROOT, "clients")
-# 添加 generated 到 PYTHONPATH
-sys.path.insert(0, GENERATED_DIR)
-os.environ["PYTHONPATH"] = GENERATED_DIR + os.pathsep + os.environ.get("PYTHONPATH", "")
-
if len(sys.argv) < 2:
- print("请指定要运行的客户端脚本名,例如:get_joint_state_client")
+ print("Please provide a client script name, e.g. get_joint_state_client")
sys.exit(1)
client_name = sys.argv[1]
client_script = os.path.join(CLIENTS_DIR, f"{client_name}.py")
if not os.path.isfile(client_script):
- print(f"客户端脚本不存在: {client_script}")
+ print(f"Client script not found: {client_script}")
sys.exit(1)
-# 支持传递额外参数给客户端
-subprocess.run([sys.executable, client_script] + sys.argv[2:])
+module_name = f"clients.{client_name}"
+env = os.environ.copy()
+extra_paths = [PROJECT_ROOT, GENERATED_DIR]
+existing = env.get("PYTHONPATH", "")
+env["PYTHONPATH"] = os.pathsep.join(extra_paths + ([existing] if existing else []))
+
+subprocess.run(
+ [sys.executable, "-m", module_name] + sys.argv[2:],
+ cwd=PROJECT_ROOT,
+ env=env,
+)