P3c:teach --auto-solve —— 采完自动 merge 并接着标定,收尾手眼手动闭环

新增 teach --auto-solve:采完点按 q 后自动 merge、重启相机、接着 calibrate,
不再需要手敲第二、三条命令。

但只有 --ros(示教器)模式能真正全程跑通,这一点如实处理而非强行自动:
标定必须通过 run_real.sh 的控制器驱动机械臂走点位,而 --rdk 软拖拽要求
RDK 独占,两者无法同时存在。这是硬件约束,脚本绕不过去。
所以 --rdk --auto-solve 会照常自动 merge,然后打印明确的两步指令,
并提示想要一条命令就改用示教器 Free Drive 模式。
硬跑一个必然失败的 calibrate 只会让人以为标定坏了。

顺带修正一处不可达代码:_need_camera / _need_files / _need_robot 内部用的是
exit(终止整个脚本)而非 return,所以 `if ! _need_camera; then echo ERROR; fi`
里的分支永远不会执行。改为直接调用,由它们自己打印原因并退出。

usage 里补上 --auto-solve 及其限制说明;handeye/README.md 第 5 节补
"一条命令跑完"小节,写明为什么 RDK 模式做不到。
usage() 的 sed 行号改为按首个非注释行自动计算,避免以后加注释又要同步。

测试:224 passed
This commit is contained in:
arm_dev
2026-09-23 11:55:08 +08:00
parent e4f513392d
commit adc72955c7
2 changed files with 88 additions and 2 deletions
+21
View File
@@ -160,6 +160,27 @@ bash ~/ros2/scripts/run_handeye.sh show --arm 89
两者共用同一份 `poses/*.yaml`,顺序是 `teach`(采)→ `merge`(存)→ `calibrate`(算)。 两者共用同一份 `poses/*.yaml`,顺序是 `teach`(采)→ `merge`(存)→ `calibrate`(算)。
### 一条命令跑完(手动模式 + 自动求解)
```bash
# 需要先在另一个终端跑 run_real.sh
bash ~/ros2/scripts/run_handeye.sh teach --ros --auto-solve --arm 89
```
采完点按 `q` 之后,脚本会自动 `merge`、重启相机、接着 `calibrate`,
不用再敲第二条命令。
> ⚠️ **`--auto-solve` 只在 `--ros`(示教器)模式能全程跑通。**
>
> 原因是一个硬件约束:标定必须通过 `run_real.sh` 的控制器驱动机械臂走点位,
> 而 `--rdk` 软拖拽要求 **RDK 独占**,两者无法同时存在。
> 所以 `--rdk --auto-solve` 会照常自动 merge,然后明确告诉你剩下那两条命令 ——
> **这是物理限制,不是脚本能绕过的**。
>
> 想要真正一条命令,就用示教器 Free Drive:先在示教器上打开 Free Drive,
> 再用 `--ros --auto-solve`。
### 切换 eye-in-hand / eye-to-hand ### 切换 eye-in-hand / eye-to-hand
```bash ```bash
+67 -2
View File
@@ -12,12 +12,19 @@
# bash ~/ros2/scripts/run_handeye.sh calibrate # bash ~/ros2/scripts/run_handeye.sh calibrate
# bash ~/ros2/scripts/run_handeye.sh waypoints # bash ~/ros2/scripts/run_handeye.sh waypoints
# bash ~/ros2/scripts/run_handeye.sh teach # RDK 软拖拽(推荐) # bash ~/ros2/scripts/run_handeye.sh teach # RDK 软拖拽(推荐)
# bash ~/ros2/scripts/run_handeye.sh teach --ros --auto-solve
# # 示教器模式 + 采完自动 merge 并直接标定(一条命令跑完)
# bash ~/ros2/scripts/run_handeye.sh teach --points 12 # 目标采 12 个点 # bash ~/ros2/scripts/run_handeye.sh teach --points 12 # 目标采 12 个点
# bash ~/ros2/scripts/run_handeye.sh teach --append # 接着上次点位继续采 # bash ~/ros2/scripts/run_handeye.sh teach --append # 接着上次点位继续采
# bash ~/ros2/scripts/run_handeye.sh teach --ros # 示教器 Free Drive # bash ~/ros2/scripts/run_handeye.sh teach --ros # 示教器 Free Drive
# bash ~/ros2/scripts/run_handeye.sh merge [taught.txt] # bash ~/ros2/scripts/run_handeye.sh merge [taught.txt]
# bash ~/ros2/scripts/run_handeye.sh show # 看当前配置与点数 # bash ~/ros2/scripts/run_handeye.sh show # 看当前配置与点数
# #
# 关于 --auto-solve(采完自动标定)的重要限制:
# 只有 --ros(示教器)模式能真正一条命令跑完。因为标定必须在 run_real.sh 的
# 控制器上走点位,而 --rdk 软拖拽要 RDK 独占,两者互斥 —— 这是硬件约束。
# --rdk --auto-solve 会照常自动 merge,然后明确告诉你接下来那两条命令。
#
# 全局选项(放在子命令前或后都可以): # 全局选项(放在子命令前或后都可以):
# --arm 89|91|left|right 选臂(默认 89) # --arm 89|91|left|right 选臂(默认 89)
# --mode eye_in_hand|eye_to_hand # --mode eye_in_hand|eye_to_hand
@@ -46,7 +53,7 @@ CMD="${1:-}"
shift || true shift || true
usage() { usage() {
sed -n '2,36p' "$0" sed -n '2,40p' "$0"
} }
# shellcheck disable=SC1091 # shellcheck disable=SC1091
@@ -237,12 +244,14 @@ cmd_teach() {
local backend="rdk" local backend="rdk"
local append=false local append=false
local points=0 local points=0
local auto_solve=false
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
--rdk) backend="rdk"; shift ;; --rdk) backend="rdk"; shift ;;
--ros|--pendant) backend="ros"; shift ;; --ros|--pendant) backend="ros"; shift ;;
--append|--keep) append=true; shift ;; --append|--keep) append=true; shift ;;
--points) points="${2:?--points 需要一个数字}"; shift 2 ;; --points) points="${2:?--points 需要一个数字}"; shift 2 ;;
--auto-solve) auto_solve=true; shift ;;
*) echo "Unknown teach arg: $1" >&2; exit 1 ;; *) echo "Unknown teach arg: $1" >&2; exit 1 ;;
esac esac
done done
@@ -280,6 +289,9 @@ cmd_teach() {
fi fi
echo " 结束后: bash ~/ros2/scripts/run_handeye.sh merge" echo " 结束后: bash ~/ros2/scripts/run_handeye.sh merge"
echo " bash ~/ros2/scripts/run_handeye.sh calibrate" echo " bash ~/ros2/scripts/run_handeye.sh calibrate"
if [[ "${auto_solve}" == true ]]; then
echo " --auto-solve: 采完会自动 merge,并视后端决定是否直接标定"
fi
TEACH_ARGS=( TEACH_ARGS=(
-p backend:="${backend}" -p backend:="${backend}"
-p robot_sn:="${SITE_ROBOT_SN:-Rizon4-063989}" -p robot_sn:="${SITE_ROBOT_SN:-Rizon4-063989}"
@@ -301,7 +313,60 @@ cmd_teach() {
-p exposure_us:="${SITE_CAM_EXPOSURE_US:-0}" -p exposure_us:="${SITE_CAM_EXPOSURE_US:-0}"
-p gain:="${SITE_CAM_GAIN:-0}" -p gain:="${SITE_CAM_GAIN:-0}"
) )
exec ros2 run arm_application handeye_teach --ros-args "${TEACH_ARGS[@]}" ros2 run arm_application handeye_teach --ros-args "${TEACH_ARGS[@]}" || true
# ---- 采点结束 ----
if [[ "${auto_solve}" != true ]]; then
echo
echo "采点结束。接着跑(或下次加 --auto-solve 让它自动接):"
echo " bash ~/ros2/scripts/run_handeye.sh merge --arm ${SITE_ROBOT_ARM:-89}"
echo " bash ~/ros2/scripts/run_handeye.sh calibrate --arm ${SITE_ROBOT_ARM:-89}"
return 0
fi
echo
echo "=== --auto-solve: 自动 merge ==="
if [[ ! -s "${TAUGHT}" ]]; then
echo "ERROR: 没采到任何点(${TAUGHT} 为空),中止。" >&2
return 1
fi
if ! python3 "${ROOT}/scripts/merge_handeye_poses.py" "${TAUGHT}" "${WP}"; then
echo "ERROR: merge 失败,点还在 ${TAUGHT},可手动重跑 merge。" >&2
return 1
fi
echo " 已写入 ${WP}"
# RDK 软拖拽与控制器互斥:软拖拽要 RDK 独占,标定必须在 run_real.sh 的
# 控制器上走点位,两者无法同时存在。这是硬件约束,脚本绕不过去,
# 所以这里如实停下并给出下一步,而不是硬跑一个必然失败的 calibrate。
if [[ "${backend}" == "rdk" ]]; then
echo
echo "⚠️ 当前是 RDK 拖拽模式(--rdk),无法自动接着求解。"
echo " 原因:软拖拽需要 RDK 独占,而标定需要通过 run_real.sh 的控制器走点位,"
echo " 两者不能同时存在。"
echo
echo " 继续两步即可:"
echo " 1) 另开终端: bash ~/ros2/scripts/run_real.sh --arm ${SITE_ROBOT_ARM:-89}"
echo " 2) 回到这里: bash ~/ros2/scripts/run_handeye.sh calibrate --arm ${SITE_ROBOT_ARM:-89}"
echo
echo " 想真正一条命令跑完,请改用示教器 Free Drive 模式:"
echo " bash ~/ros2/scripts/run_handeye.sh teach --ros --auto-solve"
return 0
fi
# --ros(示教器)后端:控制器本来就在跑,可以无缝接上标定。
# 但 teach 阶段把相机杀掉了(USB 独占),标定拍照要用,得先拉回来。
echo
echo "=== 重启相机(teach 阶段杀掉了它,标定拍照需要)==="
nohup bash "${ROOT}/scripts/run_camera_d405.sh" --arm "${SITE_ROBOT_ARM:-89}" \
--no-preview > /tmp/run_handeye_camera.log 2>&1 &
echo " pid=$! log=/tmp/run_handeye_camera.log"
# _need_camera 内部失败时会自己打印原因并 exit,不需要在这里再判一次。
_need_camera
echo
echo "=== --auto-solve: 自动 calibrate ==="
cmd_calibrate
} }
case "${CMD}" in case "${CMD}" in