P3 把 handeye 相关文件从 src/arm_application/config/ 迁到了仓库根的 handeye/, 但文档还写着老路径。这类"代码改了、文档没跟"的偏差最麻烦:新人照着文档敲 命令会直接报文件不存在,然后开始怀疑人生。 同步内容 - docs/HANDEYE.md / WORKSPACE.md / SITE_CONFIG.md / ARCHITECTURE.md: 手眼结果与示教点位改指 handeye/results|poses/<臂号>_<模式>.yaml, 临时目录改指 handeye/tmp/<臂号>/,并补上 --mode / --points / --auto-solve。 - docs/INTERFACES.md / TRACKING.md / docs/HOLE_DETECT.md / README.md: 权重那一行从 export_yolo_trt.sh 改为 weights.sh,并注明前者已是兼容转发壳。 - docs/HOLE_DETECT.md:hole_weights / workpiece_weights 的实际取值改为 models/hole_insert/detect_best_1.onnx(原文档写的是不存在的 best.pt)。 - scripts/merge_handeye_poses.py:只更新文件头注释里的示例路径。 多处改为指向 models/README.md 与 handeye/README.md,避免同一套规则在 七八个文件里各写一遍、然后各自漂移。 验证:rg 扫 docs/ README.md,已无 handeye_poses_/handeye_result_/ /tmp/handeye 之类过期路径残留。 Co-authored-by: Cursor <cursoragent@cursor.com>
8.4 KiB
入孔检测
只做视觉定位(像素孔心),不包含装配插入运动 / HMI。跟踪仍用 run_visual_servo_rt.sh。
日常改 config/site.yaml → hole_detect:(Hough、居中门控、间距都会覆盖包内 yaml)。
src/arm_application/config/hole_detect.yaml 只是缺省值。
源码:
| 文件 | 作用 |
|---|---|
src/arm_application/arm_application/hole_detector.py |
统一入口:yolo / hough / two_stage |
src/arm_application/arm_application/hole_detect_hough.py |
YOLO 工件框 + HoughCircles 左右孔 |
src/arm_application/arm_application/hole_detect_node.py |
订当前臂 D405 彩图 |
src/arm_application/config/hole_detect.yaml |
two_stage 参数 |
src/arm_application/config/hole_insert.yaml |
yolo/hough 参数 |
models/hole_insert/ |
孔模型权重(二进制不进 git) |
跑
先开该臂相机,再开检测:
bash ~/ros2/scripts/run_camera_d405.sh --arm 89 --preview
bash ~/ros2/scripts/run_hole_detect.sh --arm 89 --preview
91 把 --arm 换成 91(话题 /d405_right/...)。
site.yaml:
hole_detect:
backend: "two_stage" # two_stage | yolo | hough
weights: "/home/USER/ros2/models/hole_insert/detect_best_1.onnx"
workpiece_weights: "/home/USER/ros2/models/hole_insert/detect_best_1.onnx" # 空 = paths.yolo_weights
conf: 0.25
hough_param2: 26
hole_distance_min: 150
two_stage:用工件 YOLO 找 bbox,再在 ROI 里 Hough 找左右螺纹孔(原camera_test/hole_detect.py)。yolo:模型框直接当孔(原hole_insertHoleDetector)。hough:整幅图圆检测,不用权重。
权重放到 ~/ros2/models/hole_insert/(当前 detect_best_1.onnx,二进制不进 git,布局见 models/README.md)。two_stage 的工件模型默认就是跟踪用的 paths.yolo_weights(hole_detect.workpiece_weights 可覆盖)。
ROS
| 话题 | 类型 |
|---|---|
/hole_detect/result |
arm_msgs/msg/HoleDetections |
/hole_detect/holes |
std_msgs/Float32MultiArray [ok, n, u,v,r,conf, …] |
/hole_detect/overlay |
sensor_msgs/Image 画框画孔 |
改 hole_detect.yaml 的 Hough / 居中门控后重启 run_hole_detect.sh。two_stage 会做时序稳帧(stabilize_*)。
三阶段串联(推荐日常入口)
一条命令跑完:视觉跟踪锁定 → 拍照对孔 → 搜孔并轻轻按压。默认不拧入 (拧入是后面单独阶段)。三个阶段在同一个节点里交接,相机 / 触觉 / 手眼 TF 只起一次。
# 终端 1 —— 控制栈(一直开着)
bash ~/ros2/scripts/run_real.sh --arm 89
# 终端 2 —— 相机预览(一直开着,看跟踪画面)
bash ~/ros2/scripts/run_camera_d405.sh --arm 89 --preview
# 终端 3 —— 串联(搜到孔后轻压保持,不拧)
bash ~/ros2/scripts/run_all.sh --arm 89 # 跟踪 + 对孔 + 搜孔轻压
bash ~/ros2/scripts/run_all.sh --arm 89 --hole k2 # 换 k2
bash ~/ros2/scripts/run_all.sh --arm 89 --skip-stage1 # 臂已在工件上方,跳过跟踪
# 若这一次就要拧入(一般留给后续阶段):
bash ~/ros2/scripts/run_all.sh --arm 89 --depth 6 --pitch 1.0
run_pipeline.sh 是 run_all.sh 的别名。
跑之前确认:夹爪夹着通止规;工件在相机大概能看见的位置(一阶段会自己找)。 左指触觉如果掉线会自动降级成单指。
盯这几行就知道跑到哪了:
[pipeline] === stage1_track ===→ 等跟踪锁定一阶段已锁定 — 停跟踪、钉住当前关节,直接进二阶段[pipeline] === stage2_locate ===→ 降到孔悬停高,拍照对准[pipeline] === stage3_search ===→触面→★ 入孔→轻压到位(不拧入)
急停:终端 3 Ctrl-C。搜孔中会先抬 5mm。失败时臂保持当前位姿,看
/tmp/pipeline_stage1.log、/tmp/stage2_result.json、/tmp/stage3_result.json。
高度接口:robots[89].servo.ee_z_m(跟踪锁高)、
robots[89].servo.hole_hover_z_m(拍照悬停高)。
分阶段单独跑仍用 run_visual_servo_rt.sh、run_stage2_locate.sh、
run_stage3_search.sh,下面两节。
二阶段一次性拍照定位(stage2,与一阶段跟踪分开跑)
先悬停垂直朝下 → 拍一张照 → detect_best_1.onnx 框工件 → Hough 解 k1/k2 孔心 →
深度(孔外工作面中值)+ 内参 + 手眼换算基座坐标 → 移动夹爪/通止规轴线到目标孔正上方 →
复拍验证(≤align_max_iters 次,收敛判据 align_tol_m)→ 锁定保持。
# 前置:T1 run_real.sh --arm 89;T3 run_camera_d405.sh --arm 89 --preview
bash ~/ros2/scripts/run_stage2_locate.sh --arm 89 # 完整流程(自动拉起触觉服务,仅左臂)
bash ~/ros2/scripts/run_stage2_locate.sh --arm 89 --hover-only # 只垂直+悬停锁定,用来调高度
bash ~/ros2/scripts/run_stage2_locate.sh --arm 89 --hole k2 # 对准 k2(默认 k1)
bash ~/ros2/scripts/run_stage2_locate.sh --arm 89 --hover-z 0.28 # 临时改悬停高,不动 yaml
悬停锁高接口:site.yaml → robots[89].servo.hole_hover_z_m(91 在同名段)。
工件权重:site.yaml → hole_detect.workpiece_weights(默认 models/hole_insert/detect_best_1.onnx)。
其余二阶段参数:site.yaml → hole_detect.stage2:(对准容差、迭代次数、垂直判据等)。
| 话题 | 类型 |
|---|---|
/stage2/state |
std_msgs/String 阶段状态机 |
/stage2/result |
std_msgs/String JSON:k1/k2 基座坐标、残差、悬停高 |
/stage2/overlay |
sensor_msgs/Image 标注图(同时落盘 /tmp/stage2_overlay.png) |
三阶段柔顺搜孔(stage3,Orisys 触觉 + 力闭环螺旋)
前置:二阶段刚跑完(臂悬停在目标孔上方、垂直朝下、夹持通止规)。流程:
- verify — 垂直度核验;Orisys 双指触觉话题判活(connected=true 才算数), 并自动测 ~1.2s 静止基线 μ0/σ0(日志打印「触觉基线/派生阈值」两行)
- approach — 1.5mm 小步下降,
fnormal > μ0 + max(4σ0, touch_delta)判定触面,记录z_contact - seat — Z 力闭环建立轻触(fn ≈ 触面阈值 +
press_delta) - spiral — 阿基米德螺旋搜孔:XY 沿螺旋走(螺距
spiral_pitch_mm,保证轨迹重叠 覆盖),Z 由力闭环维持轻触;fnormal相对接触均值跌落drop_ratio持续drop_frames帧 → 尖端入孔。找不到则每次扩大spiral_expand_step_mm再搜 - center — 入孔点十字微扫(±
center_scan_mm),剪切力最小点 = 孔心 - insert — 力限下压
insert_depth_mm(超限回退微抖重试insert_retry次);screw_pitch_mm ≠ 0时按螺距同步绕世界 Z 旋入(螺纹通止规:>0 右旋 / <0 左旋; 光面规保持 0)。insert_depth_mm: 0= 只搜孔定心不插入 - hold — 锁定关节,发布结果
# 前置:T1 run_real.sh --arm 89;二阶段已完成(臂保持不动)
bash ~/ros2/scripts/run_stage3_search.sh --arm 89 # 搜孔+落座
bash ~/ros2/scripts/run_stage3_search.sh --arm 89 --depth 0 # 只搜孔定心,不插入
bash ~/ros2/scripts/run_stage3_search.sh --arm 89 --depth 6 --pitch 1.0 # M6 螺纹规旋入
bash ~/ros2/scripts/run_stage3_search.sh --arm 89 --r-max 5 # 临时改搜索半径
bash ~/ros2/scripts/run_stage3_search.sh --arm 89 --resnap # 搜前用 D405 复拍对准一次
力阈值是「相对静止均值的增量」,不用标定牛顿:Orisys 读数是形变任意单位且
夹持基线会漂移,节点每次启动自动测基线后派生全部阈值。要调灵敏度就改
site.yaml → hole_detect.stage3: 的 touch_delta / press_delta / safe_press_delta / shear_delta(日志里能看到实际派生值)。
安全(任一触发立即抬 5mm 中止):Orisys fnormal > μ0 + safe_press_delta;腕部 F/T
|F| > ft_safe_n(订 /<sn>/flexiv_robot_states,订不到只警告);触觉断流 >
tactile_timeout_s。切向力超 μ0shear + shear_delta 自动抬 0.3mm 减拖。
| 话题 | 类型 |
|---|---|
/stage3/state |
std_msgs/String 阶段状态机 |
/stage3/result |
std_msgs/String JSON:孔心基座坐标、触面 Z、插入深度、峰值力 |
结果同时落盘 /tmp/stage3_result.json。退出码:0 成功;3 未触面;4 搜孔失败;
5 插入受阻;6 安全中止;7 触觉断流。