6baf989595
实测(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>
85 lines
6.7 KiB
Markdown
85 lines
6.7 KiB
Markdown
# 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` 一开始就是对的。
|