注释与提示信息全面中文化:源码 66 个 .py + 脚本/配置 45 个文件
背景:现场读代码的人只读中文。此前代码里的 docstring 是英文、行内注释是中文,
一半英文一半中文反而更难看懂 —— 新手打开一个 .py 不知道这个模块为什么存在。
范围(本仓库自己的代码,厂商上游不动)
src/arm_application/** 37 个文件:servo_node / perception_node / handeye_* / dh_pgea /
yolo_detector / kalman_filter / goto_ee_plane / dual_gripper …
src/arm_interfaces/** 11 个文件:paths / manifest / loader / config_guard / errors …
src/arm_control/** 5 个文件
src/arm_bringup/** 3 个 launch
scripts/** 6 个 .py + 29 个 .sh(scripts/ 是"唯一操作入口",用户屏幕上
看到的就是这些提示,所以提示信息也一并中文化)
config/ handeye/ devices/ src/arm_*/config/ 17 个 .yaml/.xml/.xacro/.cfg
口径(写进 docs/FRAMEWORK_RULES.md 新增的 R6.5,作为长期约束)
注释、docstring、print/日志/异常里给人看的句子 = 中文;
标识符、API 名、话题名、参数名、字段名、命令名 = 保留英文原文(用 `` `` 包起来);
单位、十六进制、协议报文、CRC 值、命令示例 = 原样保留;
src/flexiv_*/ = 厂商上游代码,不许改也不许翻译(R4.4)。
翻译保原意与原有详细程度:那些"为什么不能用另一种做法""踩过什么坑"的说明
全部完整保留 —— 它们才是这份代码里最贵的部分。
新增 scripts/check_text_only.py:只改文字时的自证工具
人工 review 靠不住(115 个文件、3000 多行 diff)。这个脚本把改动前后的源码解析成
语法树,删掉 docstring、抹平字符串常量、把 f-string 只留"插值了几次 + 插值是什么",
再比对:完全相同 = 改动范围内没有逻辑变化。
留了一个口子:f-string 的插值按内容排序后再比 —— 因为中文语序常常要和英文反着来
(f"on {port} read {idx} failed" → f"在 {port} 上读 {idx} 失败"),只换显示顺序不该判失败;
但多插一个、少插一个、换掉被插入的表达式,立刻会报出来。
本次 66 个 .py 全部通过该闸门。
验收
改动的 66 个 .py:scripts/check_text_only.py 全部通过(零逻辑变化)
45 个非 .py 文件:去注释后与 HEAD 逐字节相同(零配置变化)
docstring 总数:没有一个文件减少(不存在"翻译时顺手删了说明")
单测:arm_application 233 / arm_driver_flexiv 69 / arm_interfaces 46 /
arm_kinematics 24 / arm_sim_gazebo 236 = 608 passed,与改动前一致
全部 .sh:bash -n 通过;并额外实际执行了 --help 验证(见下一个提交:
bash -n 对 CRLF 脚本会漏报,只有真跑才暴露)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1 +1 @@
|
||||
"""Package init for arm_control."""
|
||||
"""arm_control 的包初始化。"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Publish ArmStatus + GetArmState from joint_states, TF, and Flexiv tcp_pose."""
|
||||
"""根据 joint_states、TF 与 Flexiv 的 tcp_pose 发布 ArmStatus,并提供 GetArmState 服务。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -43,7 +43,7 @@ class ArmStatusNode(Node):
|
||||
self.create_timer(1.0 / rate, self._tick)
|
||||
self.get_logger().info(
|
||||
f'ArmStatus: base={self.base_frame} ee={self.ee_frame} '
|
||||
f'tcp_topic={self.tcp_pose_topic or "(none)"}'
|
||||
f'tcp_topic={self.tcp_pose_topic or "(无)"}'
|
||||
)
|
||||
|
||||
def _on_js(self, msg: JointState) -> None:
|
||||
@@ -55,7 +55,7 @@ class ArmStatusNode(Node):
|
||||
self._last_tcp = msg
|
||||
|
||||
def _ee(self) -> Optional[PoseStamped]:
|
||||
# Prefer Flexiv-published TCP pose (reliable on real stack)
|
||||
# 优先用 Flexiv 发布的 TCP 位姿(真机栈上更可靠)
|
||||
if self._last_tcp is not None:
|
||||
return self._last_tcp
|
||||
try:
|
||||
@@ -101,7 +101,7 @@ class ArmStatusNode(Node):
|
||||
and abs(s.ee_pose.pose.position.y) < 1e-9
|
||||
and abs(s.ee_pose.pose.position.z) < 1e-9
|
||||
):
|
||||
response.message = 'ok (ee pose unavailable — check TF/tcp_pose)'
|
||||
response.message = 'ok(末端位姿不可用 —— 请检查 TF/tcp_pose)'
|
||||
else:
|
||||
response.message = 'ok'
|
||||
return response
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Service server: SetNamedPose -> FollowJointTrajectory (backend-agnostic)."""
|
||||
"""服务端:SetNamedPose -> FollowJointTrajectory(与底层后端无关)。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -65,18 +65,18 @@ class NamedPoseServer(Node):
|
||||
SetNamedPose, 'set_named_pose', self._on_set, callback_group=self._cb
|
||||
)
|
||||
self.get_logger().info(
|
||||
f'Named poses {list(self.poses)} joints={self.joint_names}'
|
||||
f'命名位姿 {list(self.poses)} joints={self.joint_names}'
|
||||
)
|
||||
|
||||
def _on_set(self, request: SetNamedPose.Request, response: SetNamedPose.Response):
|
||||
name = request.pose_name
|
||||
if name not in self.poses:
|
||||
response.success = False
|
||||
response.message = f'Unknown pose "{name}". Available: {list(self.poses)}'
|
||||
response.message = f'未知位姿"{name}"。可用: {list(self.poses)}'
|
||||
return response
|
||||
if not self._client.wait_for_server(timeout_sec=3.0):
|
||||
response.success = False
|
||||
response.message = 'follow_joint_trajectory action not available'
|
||||
response.message = 'follow_joint_trajectory action 不可用'
|
||||
return response
|
||||
|
||||
goal = FollowJointTrajectory.Goal()
|
||||
@@ -87,30 +87,30 @@ class NamedPoseServer(Node):
|
||||
goal.time_from_start = [duration]
|
||||
goal.velocity_scale = float(request.velocity_scale)
|
||||
|
||||
self.get_logger().info(f'-> pose [{name}] T={duration:.2f}s')
|
||||
self.get_logger().info(f'-> 位姿 [{name}] T={duration:.2f}s')
|
||||
send_fut = self._client.send_goal_async(goal)
|
||||
if not _wait_future(send_fut, 5.0):
|
||||
response.success = False
|
||||
response.message = 'Timed out waiting for goal accept'
|
||||
response.message = '等待目标被接受超时'
|
||||
return response
|
||||
handle = send_fut.result()
|
||||
if handle is None or not handle.accepted:
|
||||
response.success = False
|
||||
response.message = 'Goal rejected'
|
||||
response.message = '目标被拒绝'
|
||||
return response
|
||||
res_fut = handle.get_result_async()
|
||||
if not _wait_future(res_fut, 60.0):
|
||||
response.success = False
|
||||
response.message = 'Timed out waiting for trajectory result'
|
||||
response.message = '等待轨迹结果超时'
|
||||
return response
|
||||
wrapped = res_fut.result()
|
||||
if wrapped is None:
|
||||
response.success = False
|
||||
response.message = 'Empty result'
|
||||
response.message = '结果为空'
|
||||
return response
|
||||
result = wrapped.result
|
||||
response.success = bool(result.success)
|
||||
response.message = result.message or f'Moved to {name}'
|
||||
response.message = result.message or f'已移动到位姿 {name}'
|
||||
return response
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Expose arm_msgs/FollowJointTrajectory for apps; drive flexiv JTC (control_msgs)."""
|
||||
"""对应用侧暴露 arm_msgs/FollowJointTrajectory;实际驱动 Flexiv JTC(control_msgs)。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -45,19 +45,19 @@ class RealTrajectoryAdapter(Node):
|
||||
cancel_callback=lambda _g: CancelResponse.ACCEPT,
|
||||
callback_group=self._cb,
|
||||
)
|
||||
self.get_logger().info(f'Real adapter {arm} -> {ctrl}')
|
||||
self.get_logger().info(f'真机适配器 {arm} -> {ctrl}')
|
||||
|
||||
def _execute(self, goal_handle):
|
||||
result = ArmFJT.Result()
|
||||
req = goal_handle.request
|
||||
if not req.joint_names or req.num_points < 1:
|
||||
result.success = False
|
||||
result.message = 'Invalid goal'
|
||||
result.message = '目标非法'
|
||||
goal_handle.abort()
|
||||
return result
|
||||
if not self._ctrl.wait_for_server(timeout_sec=5.0):
|
||||
result.success = False
|
||||
result.message = f'Controller action not available'
|
||||
result.message = f'控制器 action 不可用'
|
||||
goal_handle.abort()
|
||||
return result
|
||||
|
||||
@@ -72,8 +72,8 @@ class RealTrajectoryAdapter(Node):
|
||||
pt = JointTrajectoryPoint()
|
||||
pt.positions = pos[idx : idx + n_j]
|
||||
idx += n_j
|
||||
# Point-to-point named poses must end at rest — nonzero end vel
|
||||
# + open_loop JTC makes the arm coast / shake after arrival.
|
||||
# 点到点的命名位姿必须在静止状态下结束 —— 末速度非零
|
||||
# 再叠加开环 JTC,会让机械臂到位后滑行/抖动。
|
||||
pt.velocities = [0.0] * n_j
|
||||
pt.accelerations = [0.0] * n_j
|
||||
sec = int(t)
|
||||
@@ -86,13 +86,13 @@ class RealTrajectoryAdapter(Node):
|
||||
send = self._ctrl.send_goal_async(ctrl_goal)
|
||||
if not _wait(send, 5.0):
|
||||
result.success = False
|
||||
result.message = 'Timed out sending to controller'
|
||||
result.message = '发送给控制器超时'
|
||||
goal_handle.abort()
|
||||
return result
|
||||
handle = send.result()
|
||||
if handle is None or not handle.accepted:
|
||||
result.success = False
|
||||
result.message = 'Controller rejected goal'
|
||||
result.message = '控制器拒绝了目标'
|
||||
goal_handle.abort()
|
||||
return result
|
||||
traj_duration = 0.0
|
||||
@@ -102,13 +102,13 @@ class RealTrajectoryAdapter(Node):
|
||||
res_fut = handle.get_result_async()
|
||||
if not _wait(res_fut, max(120.0, traj_duration * 3.0)):
|
||||
result.success = False
|
||||
result.message = 'Timed out waiting for controller'
|
||||
result.message = '等待控制器超时'
|
||||
goal_handle.abort()
|
||||
return result
|
||||
ctrl_res = res_fut.result().result
|
||||
ok = ctrl_res.error_code == CtrlFJT.Result.SUCCESSFUL
|
||||
result.success = ok
|
||||
result.message = 'OK' if ok else f'controller error_code={ctrl_res.error_code}'
|
||||
result.message = 'OK' if ok else f'控制器 error_code={ctrl_res.error_code}'
|
||||
if ok:
|
||||
goal_handle.succeed()
|
||||
else:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Bridge control_msgs/FollowJointTrajectory -> arm_msgs/FollowJointTrajectory."""
|
||||
"""把 control_msgs/FollowJointTrajectory 桥接到 arm_msgs/FollowJointTrajectory。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -44,7 +44,7 @@ class TrajectoryBridge(Node):
|
||||
cancel_callback=lambda _g: CancelResponse.ACCEPT,
|
||||
callback_group=self._cb,
|
||||
)
|
||||
self.get_logger().info(f'Bridge {ctrl} -> {arm}')
|
||||
self.get_logger().info(f'桥接 {ctrl} -> {arm}')
|
||||
|
||||
def _execute(self, goal_handle):
|
||||
result = CtrlFJT.Result()
|
||||
|
||||
@@ -5,13 +5,13 @@ controller_manager:
|
||||
joint_state_broadcaster:
|
||||
type: joint_state_broadcaster/JointStateBroadcaster
|
||||
|
||||
# Name aligned with Flexiv official bringup
|
||||
# 名字与 Flexiv 官方 bringup 对齐
|
||||
rizon_arm_controller:
|
||||
type: joint_trajectory_controller/JointTrajectoryController
|
||||
|
||||
rizon_arm_controller:
|
||||
ros__parameters:
|
||||
# Site robot Rizon4-063989 (official flexiv_description keeps hyphen)
|
||||
# 站点机器人 Rizon4-063989(官方 flexiv_description 保留连字符)
|
||||
joints:
|
||||
- Rizon4-063989_joint1
|
||||
- Rizon4-063989_joint2
|
||||
@@ -26,8 +26,8 @@ rizon_arm_controller:
|
||||
- position
|
||||
- velocity
|
||||
allow_partial_joints_goal: false
|
||||
# Must be false: true interpolates from the last *command* (already at target
|
||||
# after a "success" with no hardware motion) so the arm never moves.
|
||||
# 必须为 false:若为 true,会从最后一次 *命令* 开始插值(在硬件没动
|
||||
# 却报"成功"之后,命令已经落在目标点上),于是机械臂永远不动。
|
||||
open_loop_control: false
|
||||
constraints:
|
||||
stopped_velocity_tolerance: 0.05
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Named poses — site robot Rizon4-063989 (7-DOF), radians
|
||||
# Joint base names match flexiv_description: joint1..joint7
|
||||
# 命名位姿 — 站点机器人 Rizon4-063989(7 自由度),弧度
|
||||
# 关节基础名与 flexiv_description 一致:joint1..joint7
|
||||
named_pose_server:
|
||||
ros__parameters:
|
||||
joint_prefix: "Rizon4-063989_"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Right arm 91 (Rizon4-063991) named poses (radians).
|
||||
# Starting values match the generic home; teach cell-specific poses on 91.
|
||||
# 右臂 91(Rizon4-063991)的命名位姿(弧度)。
|
||||
# 起始值与通用 home 一致;在 91 上示教该工位专用的位姿。
|
||||
named_pose_server:
|
||||
ros__parameters:
|
||||
joint_prefix: "Rizon4-063991_"
|
||||
|
||||
@@ -8,7 +8,7 @@ from launch_ros.actions import Node
|
||||
from launch_ros.parameter_descriptions import ParameterValue
|
||||
from launch_ros.substitutions import FindPackageShare
|
||||
|
||||
# Site robot. Official flexiv_description keeps the hyphen in the SN prefix.
|
||||
# 现场机械臂。官方 flexiv_description 要求 SN 前缀里**保留连字符**。
|
||||
SITE_ROBOT_SN = os.environ.get('SITE_ROBOT_SN', 'Rizon4-063989')
|
||||
SITE_JOINT_PREFIX = os.environ.get('SITE_JOINT_PREFIX', f'{SITE_ROBOT_SN}_')
|
||||
SITE_NAMED_POSES = os.environ.get('SITE_NAMED_POSES_FILE', '').strip()
|
||||
|
||||
Reference in New Issue
Block a user