应用层修复:S00 自检假阴性、S07 触觉假断流、S07 下探静默卡死
三处都是"看着像硬件问题、其实是判据问题"的假警报:
1. S00 自检假阴性(gauge_boot.py)
探测等待时用 time.sleep 不 spin → 订阅回调根本没机会执行,
于是**健康的硬件也会被判"没有数据"**。改成等待期间持续 spin。
2. S07 触觉假断流(stage3_search_node.py)
判据原本是 |fnormal| > 1.0,但力本来就可能是零或很小(真机轻触、
仿真刚接触)→ 明明在发数据却判"触觉断流"。
改成"数据够新(TACTILE_MAX_AGE_S 内)+ connected" —— 判活看的是
**有没有在发**,不是**数值大不大**。
3. S07 下探静默卡死(gauge_fsm.py / gauge_assembly_node.py)
_goto 的到位容差 ≥ 步长时,每一步都被判成"已在容差内",臂一步都不走,
而外层只看到"没进度"→ 死循环到超时,日志里看不出原因。
新增 descend_tol_m / descend_iter_budget / descend_progress:
容差从步长推导(不该手工调大),并显式判"连续 N 步零位移"。
容差 ≥ 步长的反例已进 site 回归测试,防止再犯。
4. site 分段化(export_site_env.py):89/91 两臂的 servo/handeye/点位
各一段,避免"改 91 影响 89"。
另:run_unit_tests.sh 作为统一测试入口(932→933 项),
devices/sim/gazebo/README.md 同步。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -80,7 +80,9 @@ _SIDE_WORDS = {
|
||||
"右臂": "right",
|
||||
}
|
||||
|
||||
_ARM_SECTION_KEYS = ("camera", "gripper", "paths", "servo", "handeye", "go_home", "yolo")
|
||||
_ARM_SECTION_KEYS = (
|
||||
"camera", "gripper", "paths", "servo", "handeye", "go_home", "yolo", "perception",
|
||||
)
|
||||
|
||||
|
||||
def arm_short_id(sn: str) -> str:
|
||||
|
||||
@@ -36,6 +36,15 @@ DRY_RUN=false
|
||||
HOLES=""
|
||||
BACKEND="auto"
|
||||
|
||||
# 用户**这一条命令**里有没有显式指定臂?要看原始 argv:
|
||||
# `parse_arm_args` 只设 ROBOT_SN,而调用者的 shell 可能已经 export 了 ROBOT_SN
|
||||
#(比如先 source 过 env.sh)—— 那种情况下"仿真要不要默认成 sim 台"就分不清了。
|
||||
_HAD_ARM=false
|
||||
for _a in "$@"; do
|
||||
if [[ "${_a}" == "--arm" || "${_a}" == "--sn" ]]; then _HAD_ARM=true; fi
|
||||
done
|
||||
unset _a
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "${ROOT}/scripts/lib/parse_arm_args.sh"
|
||||
parse_arm_args "$@"
|
||||
@@ -57,6 +66,15 @@ case "${BACKEND}" in
|
||||
*) echo "--backend 只能是 auto|contract|legacy,收到 ${BACKEND}" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
if [[ "${USE_SIM}" == true && "${_HAD_ARM}" == false ]]; then
|
||||
# 仿真默认选「仿真台」——就是 site.yaml 里 robots[] 的那条 `sn: sim`
|
||||
#(TF/关节名是 sim_* 前缀,相机是 sim_d405)。**必须在 source sim_env.sh 之前**:
|
||||
# 那里会把 site.yaml 导成 SITE_*,之后再改就晚了。
|
||||
# 显式 --arm 仍然优先:想用仿真核对某台真机的点位时会这么用。
|
||||
export ROBOT_SN=sim
|
||||
export ROBOT_ARM=sim
|
||||
fi
|
||||
|
||||
if [[ "${USE_SIM}" == true ]]; then
|
||||
echo "=== 仿真环境(install_sim + ROS_DOMAIN_ID=77) ==="
|
||||
# shellcheck disable=SC1091
|
||||
@@ -77,7 +95,7 @@ if [[ -n "${SITE_FASTDDS_PROFILE:-}" && -f "${SITE_FASTDDS_PROFILE}" ]]; then
|
||||
fi
|
||||
|
||||
echo "=== 通止规装配 arm=${SITE_ROBOT_ARM:-?} ${SITE_ROBOT_LABEL:-} (${SITE_ROBOT_SN}) ==="
|
||||
echo " 孔数=${HOLES:-site} 夹爪后端=${BACKEND} 干跑=${DRY_RUN}"
|
||||
echo " 孔数=${HOLES:-site} 夹爪后端=${BACKEND} 干跑=${DRY_RUN} 仿真时钟=${USE_SIM}"
|
||||
|
||||
# 清掉上一轮可能残留的装配节点(同一时间只允许一个进程在指挥这条臂)
|
||||
# 不 pkill 整个 arm_application:会连相机/触觉服务一起带走(那些是本脚本的前置)
|
||||
@@ -91,6 +109,34 @@ rm -f "${RESULT_FILE}"
|
||||
ARGS=(gripper_backend:="${BACKEND}" result_file:="${RESULT_FILE}")
|
||||
if [[ -n "${HOLES}" ]]; then ARGS+=(holes_total:="${HOLES}"); fi
|
||||
if [[ "${DRY_RUN}" == true ]]; then ARGS+=(dry_run:=true); fi
|
||||
# 仿真必须 use_sim_time:=true:本流程的"帧新不新"和 TF 查询都按节点时钟比对。
|
||||
# 忘了带的现场是"相机 13Hz 明明在发,S00 却报腕部相机无数据(过期 1.79e9s)" ——
|
||||
# 报出来的原因是假的,人只会去查相机。
|
||||
if [[ "${USE_SIM}" == true ]]; then ARGS+=(use_sim_time:=true); fi
|
||||
# 仿真里手眼是"URDF 的真值导出成文件"(不标定)。没有这份文件,S04 的
|
||||
# visual_servo 会直接拒绝启动("手眼标定结果缺失"),整条流程卡在 S04↔S03 循环。
|
||||
# 所以这里先把它刷成当前真值(已是最新则什么都不做)。
|
||||
# 手眼静态 TF 由 URDF 提供 → 本流程**不**发(否则同一对父子两个发布者)。
|
||||
if [[ "${USE_SIM}" == true ]]; then
|
||||
ARGS+=(publish_handeye_tf:=false)
|
||||
echo "=== 刷新仿真手眼真值(sim_eye_in_hand.yaml / sim_d435_eye_to_hand.yaml) ==="
|
||||
if ! python3 "${ROOT}/scripts/gen_sim_handeye.py" --arm "${ROBOT_SN:-sim}"; then
|
||||
echo "ERROR: 仿真手眼真值导出失败(上面有原因)。仿真栈起来了吗?" >&2
|
||||
echo " bash scripts/run_sim_gz.sh 之后重试。" >&2
|
||||
exit 1
|
||||
fi
|
||||
# 仿真没有"人把规塞进夹爪"这一步:不装规,整条流程会在 S07 才暴露
|
||||
#(下了 25mm 什么也没碰到,看着像力阈值问题)。所以这里上前先上料,
|
||||
# 装不上就直接停 —— 不允许带着空夹爪往下跑。
|
||||
# 真机不需要:现场由人装夹,S02 的夹持确认是预留跳过项(grip_check)。
|
||||
echo "=== 仿真上料:把通止规装进夹爪(真机由人工装夹) ==="
|
||||
if ! python3 "${ROOT}/scripts/sim_load_gauge.py"; then
|
||||
echo "ERROR: 仿真上料失败(上面有原因)。可以单独重跑:" >&2
|
||||
echo " python3 scripts/sim_load_gauge.py # 装规" >&2
|
||||
echo " python3 scripts/sim_load_gauge.py --dry-run # 只看算出来的位姿" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "=== launch gauge_assembly(自检不过会自动报缺什么) ==="
|
||||
# 不用 exec:exec 之后 Ctrl-C 直接打给 ros2 launch,这里的清理就没了;
|
||||
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
# 跑全仓库的单元测试(纯逻辑:不需要硬件、不需要 Gazebo、不发运动指令)。
|
||||
#
|
||||
# 为什么要有这个脚本,而不是让人手敲 pytest
|
||||
# ----------------------------------------
|
||||
# 测试都按**包名**导入(arm_application.* / arm_sim_gazebo.* / arm_kinematics.*),
|
||||
# 所以 PYTHONPATH 必须同时包含 src/ 下所有包目录。手敲很容易漏一个:
|
||||
# 漏了 src/arm_kinematics,`from arm_application.pipeline_node import ...` 会在
|
||||
# 收集阶段就 ModuleNotFoundError —— 现象是"测试跑了 0 个 / 1 个 error",
|
||||
# 很容易被读成"测试环境坏了",而不是"少了一个路径"。
|
||||
#
|
||||
# 用法:
|
||||
# bash scripts/run_unit_tests.sh # 全部
|
||||
# bash scripts/run_unit_tests.sh -k gauge # 只跑名字带 gauge 的
|
||||
# bash scripts/run_unit_tests.sh src/arm_sim_gazebo/test/test_sim_scene.py -x
|
||||
#
|
||||
# 环境:真机 conda 环境或仿真环境都能跑;缺可选依赖的用例自己会 skip
|
||||
#(例如 onnx/ultralytics 只在真机环境有 —— 见各测试里的 pytest.importorskip)。
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PY="${PYTHON:-python3}"
|
||||
|
||||
# 把所有 src/* 目录挂到 PYTHONPATH 最前面(包名不冲突,顺序无关)。
|
||||
paths=()
|
||||
for d in "${ROOT}"/src/*/; do
|
||||
paths+=("${d%/}")
|
||||
done
|
||||
export PYTHONPATH="$(IFS=:; echo "${paths[*]}")${PYTHONPATH:+:${PYTHONPATH}}"
|
||||
|
||||
test_dirs=()
|
||||
for d in "${ROOT}"/src/*/test; do
|
||||
[ -d "$d" ] && test_dirs+=("$d")
|
||||
done
|
||||
if [ "${#test_dirs[@]}" -eq 0 ]; then
|
||||
echo "没有找到任何测试目录(src/*/test)—— 仓库不完整?" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "${ROOT}"
|
||||
if [ "$#" -gt 0 ]; then
|
||||
exec "${PY}" -m pytest "${test_dirs[@]}" -q "$@"
|
||||
fi
|
||||
exec "${PY}" -m pytest "${test_dirs[@]}" -q
|
||||
Reference in New Issue
Block a user