Files
ros_contorl/scripts/README.md
T
arm_dev 6baf989595 真机侧补 stop_real.sh 与域隔离:残留的 mock 栈会把仿真/真机的 TF 盖成"静止"
实测(2026-09-28)在仿真里上料失败,一路查到 IK / TF / 时钟上,全是被冤枉的。
根因是两个真机侧一直缺的东西:

1) **真机侧没有停止脚本**。仿真侧早有 stop_sim_gz.sh(R4.10/R4.11:残留会伪造出
   "代码写错了"的现场),真机侧只有 run_real.sh 里两行 pkill。而真机栈里有一族
   **独立于 ros2_control_node 存活**的进程 —— 上一轮栈的主体早没了,只剩
   real_trajectory_adapter / named_pose_server / arm_status_node /
   joint_state_publisher 四条,两条 pkill 一条都匹配不到。
   危害不是占资源而是**冒名顶替**:残留的 adapter 照样订阅 /trajectory、
   残留的 joint_state_publisher 照样发 /joint_states(mock 的静态关节角)。
   现象极具误导性:控制器 feedback 与 reference 完全一致(控制器没坏),
   TF 却显示末端一动不动。

   新增 stop_real.sh + lib/real_procs.sh:扫描 = 点名表 + `install/` 前缀兜底,
   按 ROS_DOMAIN_ID 判别真机/仿真(两边有同名的 robot_state_publisher,
   否则停真机会把正在跑的仿真杀一半),杀完复查残留并非零退出。
   run_real.sh 启动前的清理改用它,不再各写一份会漂移的模式表。
   真机侧**有意不**用仿真的"进程组"法:真机栈是前台交互跑的,没有独立进程组,
   它的 PGID 就是调用它的 shell —— 记下来再 kill 会杀掉自己的终端。

2) **真机侧不强制 ROS_DOMAIN_ID**。sim_env.sh 早就强制 77 并写明"隔离不能靠
   『环境里恰好没设』",真机侧没有对应保护:先 source 过 sim_env.sh 的 shell 里
   再起真机栈,真机栈就落到**仿真的域**上,节点名撞车、/joint_states 与 /tf
   各有两个来源互相盖。补 lib/ros_domain.sh(真机 0 / 覆盖用 REAL_ROS_DOMAIN_ID),
   env.sh、run_real.sh、run_sim.sh 三处共用一份实现 —— 成对强制,否则一侧的
   强制只是把运气挪到了另一侧。

   run_sim.sh 特别容易漏:它名为 sim,用的却是**真机的 install/**、节点名与真机
   完全一样,而且当时不管域;这次也钉到真机域 0。run_sim_gz.sh 文件头那句
   "两者互不影响"是错的,一并改掉。

验证:stop_real.sh --list 在有仿真在跑时为空(REAL_PROC_INCLUDE_SIM=1 能看到
并按"域 77"标出);伪装一个 install/arm_control/lib/arm_control/arm_status_node
的孤儿进程能被点名。新增 6 项单测钉住域的四种输入 + 一个不许误报。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-28 12:25:16 +08:00

85 lines
6.7 KiB
Markdown
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.
# scripts — 唯一操作入口
实现细节在 [docs/WORKSPACE.md](../docs/WORKSPACE.md) 和 [docs/](../docs/)。
**每天:** `run_real.sh` · `run_camera_d405.sh` · `run_visual_servo_rt.sh` · `go_home.sh`
停:`stop_real.sh`(**别敲 `pkill -f`** —— 先匹配到调用它的 shell,会"秒返回、什么都没停")
- 为什么单独有它:真机栈里有一族**独立于 `ros2_control_node` 存活**的进程
(`real_trajectory_adapter` / `named_pose_server` / `arm_status_node` /
`joint_state_publisher`)。它们会**冒名顶替**新一轮的栈 —— 残留的照样订阅
`/trajectory`、照样发 `/joint_states`(mock 的静态关节角),现象是
"控制器一切正常,TF 却显示臂没动"。见 `docs/FRAMEWORK_RULES.md` R4.10b。
- 它按 `install/` 前缀兜底扫,并**按 `ROS_DOMAIN_ID` 避开仿真进程**
(两边有同名的 `robot_state_publisher`)—— 所以停真机不会把正在跑的仿真杀一半。
- `--list` 只列出、不动手。
**三阶段串联:** `run_all.sh --arm 89 [--hole k2] [--skip-stage1]`(`run_pipeline.sh` 是它的旧名别名)
**手眼:** `run_handeye.sh`(`teach` / `merge` / `calibrate` / `show`)
**夹爪:** `run_gripper.sh --arm 89|91` · `run_dual_gripper.sh`
**入孔:** `run_hole_detect.sh --arm … [--preview]`
**触觉:** `run_tactile.sh [--cli]`
**通止规装配:** `run_gauge_assembly.sh --arm 89 [--dry-run] [--holes N] [--backend …]`
- 一台状态机走完 S00–S11(自检 → 回 home/预绕 → 粗定位 → 跟踪 → 稳定拍照 →
悬停 → 柔顺接触 → 棘轮拧入 → 拧出 → 换孔 → 复位)。
- `--dry-run` **不发任何运动指令**,只验编排与自检 —— 改完配置先这么跑。
- 前置服务(臂 / 腕部相机 / 夹爪 / 触觉)要自己先起;缺哪项由 **S00 自检逐项报出**
并停住(不代你启动,也不"带病往下跑")。
- 掉件(E1):流程停在 home 等人,把规塞回爪里后发一次
`ros2 topic pub --once /gauge/reload_ok std_msgs/msg/Bool "{data: true}"`。
- 判据、调参顺序、还没做的部分:[docs/GAUGE_ASSEMBLY.md](../docs/GAUGE_ASSEMBLY.md)
**权重:** `weights.sh`(`list` / `labels` / `convert` / `doctor` / `show`)
- 转换:`weights.sh convert <文件> --name <模型名>` —— PT→ONNX→TensorRT,自动改名归位 + 固化标签
- 标签:`weights.sh labels <模型名>` 看,`--set "工件,通止规"` 改
- 体检:`weights.sh doctor --check-engines`(能查出 engine 与 TensorRT 版本不匹配这类静默失效)
- 规则写在 [models/README.md](../models/README.md)。`export_yolo_trt.sh` 现在是它的兼容转发壳。
**配置:** `show_site_config.sh` · `lib/export_site_env.py`
**仿真:** `run_sim_gz.sh`(Gazebo 物理仿真,唯一能做接触/力控验证的入口)
停:`stop_sim_gz.sh`(**别敲 `pkill -f gz`** —— 会杀掉调用它的 shell,见脚本文件头)
- 环境是独立 conda 环境 `ros_gz_sim`(`install_sim_env.sh` 装、`sim_env.sh` 激活),与真机环境隔离
- 相机验收:`python3 scripts/check_sim_cameras.py` —— **别用 `ros2 topic hz` 或肉眼看图验收相机**,
仿真相机的错误几乎全是静默的(深度单位、frame_id、内参、位姿方向),都能出图却是错的。
这条命令把深度反投影到 world,要求命中已知平面(地面/台面)才算通过。
- 场景验收:`python3 scripts/check_sim_scene.py` —— 同样别靠肉眼。它管四件事:
物体的摆放(悬空/穿模/出界/互相嵌入)、场景里的像素门槛与**真机生效值**是否漂移、
孔检测的成像几何(亮环/孔间距换算成像素后够不够门槛)、以及粗定位用的手外相机
能不能**完整**看见工件。第二条尤其重要:门槛抄了两份,不一致时仿真会"通过"而真机不认。
- 注意 `run_sim.sh` 不是 Gazebo,它走 mock 驱动(假数据 + RViz),永远不会产生接触力。
它用的是**真机的 `install/`**、节点名与真机完全一样,所以它跑在**真机的域 0**上
(不是仿真的 77)—— 别把它当成"另一个仿真"去和 `run_sim_gz.sh` 一起看。
**环境:** `env.sh` · `build_all.sh`
**RDK:** `test_rdk_python.sh` · `rdk_switch_tool.sh` · `rdk_drag_bridge.py`
**改注释/写文档后自检:** `python3 scripts/check_text_only.py`
- 用途:证明这次改动**只动了文字**(注释、docstring、提示信息),没碰逻辑。
它把改动前后的源码解析成语法树,删掉 docstring、抹平字符串常量后比对;
"翻译 f-string 时漏掉一个 `{}` 插值""顺手改了个变量名"这类问题会被抓出来。
- `python3 scripts/check_text_only.py` 查所有改过的 `.py`;也可以直接跟文件名。
基准是 `git HEAD`,所以要在**提交之前**跑。老手滑手滑的代价比跑一条命令高得多。
- 规矩写在 [docs/FRAMEWORK_RULES.md](../docs/FRAMEWORK_RULES.md) 的 R6.5。
一句话:**注释/docstring/提示信息用中文;标识符、话题名、字段名保留英文**
(但 `src/flexiv_*/` 是厂商上游代码,不许翻译,见 R4.4)。
**搁置未用:** `fetch_isaac_flexiv.sh`(拉 Flexiv 的 Isaac Sim / RDK 源码到 `thirdparty/`;Isaac 路线目前搁置,Gazebo 是选定平台)
`lib/parse_arm_args.sh` 解析 `--arm 89|91`。
`lib/load_site_config.sh` 把 `config/site.yaml` 变成 `SITE_*`。
`lib/ros_domain.sh` 真机/仿真的 DDS 域隔离(真机 0 / 仿真 77,`ROS_DOMAIN_ID` 从仿真
环境漏进来时改回并喊出来)—— `env.sh`、`run_real.sh`、`run_sim.sh` 共用一个实现。
`lib/real_procs.sh` 真机进程扫描(点名表 + `install/` 兜底 + 按域避开仿真),
`run_real.sh` 与 `stop_real.sh` 共用一份,不会各写一份漂移的模式表。
## 排查脚本卡住时的常识
几个脚本开头会等外部条件(控制器 ACTIVE、相机出图、触觉就绪)。**正确的判活顺序是
先 `ros2 service list` / `ros2 topic list` 看目标在不在,再去 call / echo**:
```bash
# 对:服务不存在时立刻返回
timeout 3 ros2 service list | grep -q '^/controller_manager/list_controllers$' || exit 1
timeout 3 ros2 service call /controller_manager/list_controllers ...
# 错:服务不存在时每次 call 都要耗满超时才返回,
# 30 次循环 = 白等两三分钟(实测 63s),看起来就像脚本卡死了
for _i in $(seq 1 30); do timeout 5 ros2 service call ... ; done
```
`run_all.sh` / `run_stage2_locate.sh` / `run_stage3_search.sh` 已按正确顺序改过;
`run_handeye.sh` 一开始就是对的。