352270d1cc
前一个提交给了四个单项判据(USB / V4L / RDK / 手眼)。这一批把它们的**判据入口**
补齐并编排成一条命令,产出「结论 + 要人工做的事」:
scripts/lib/real_preflight.py
run_all() 按**现场该处理的顺序**排出结论,不是按代码顺序:
现场配置 → 遗留进程 → 网络 → 机械臂 RDK → 腕部相机 → USB/触觉/几何
顺序是有理由的:遗留的 ros2_control 占着 RDK,会让后面每一项的现象都指向别处,
所以先查它;机械臂能不能动是唯一的人工硬阻断,排在相机之前。
新增 check_cameras —— **本臂配置的那只腕部相机在不在 USB 上**。
这是本次实测暴露的判据缺口:2026-09-28 现场 D405 整个从 lsusb 消失
(只剩顶置 D435),而旧体检**全绿** —— check_usb 只看协商速率,遍历的是
「发现到的」设备,对「这只根本不在了」无话可说。缺 D405 之后踩的是完全不同
的一类错误(点云为空 / 找不到工件),排查方向会被带偏很远。
按 site.yaml 的配置意图核对:robots[<sn>].camera.name → cameras.<name>.model,
比**型号**而不是序列号 —— 实测这台 D435 的 sysfs serial 是空串,按序列号判会
永远判「不在」;序列号只在型号对上时做一次「是不是本臂那只」的提醒(WARN)。
修掉三处会误导现场的输出:
· _run() 改成三返回值(分离 stdout/stderr,RDK 的 C++ 日志会混进 stdout)
之后,_ps_output/网络两处还在按两返回值解包 —— 体检**第一步就崩**,
什么结论都给不出来。现在有 test_run_all_completes_without_crashing 钉住。
· 人工动作里「去查网线」出现过两条(check_network 与 rdk_status 各写一句
意思相同、字面不同的话),去重是按整串文字做的,于是人读到两条、还得判断
「这是一个问题还是两个」。措辞抽到 manual_actions.py,两边字面一致。
· 触觉 wedged 的处置与 v4l_health 同步(免密的 reset 是首选,重插是兜底)。
scripts/lib/manual_actions.py(新)
人工动作措辞的**唯一**来源。判据分在两个文件、还跑在两个不同的 Python 上
(3.11 / 3.10,见 rdk_status 的模块说明),互相 import 不了,所以抽成只依赖
标准库的公共模块。
scripts/run_real_preflight.sh(新)
一条命令的入口:只读、不动臂,不需要机械臂能连上。
实测(2026-09-28,arm 89):体检跑通并给出 NO-GO + 7 条互不重复的人工动作,
新判据抓到「D405 不在 USB 上」(旧版全绿)。同时确认了本机可用的部分:
夹爪 PGEA 初始化完成 / 全开 20.00mm、右指触觉 SDK 300 帧 @29.9Hz、
手眼矩阵自洽、enp4s0 千兆全双工。
测试:test_real_preflight 28 项通过(含跨模块的措辞去重回归、
编排端到端冒烟、腕部相机四种情形)。
Co-authored-by: Cursor <cursoragent@cursor.com>
81 lines
3.3 KiB
Bash
Executable File
81 lines
3.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 真机开工前的**一次性体检**(一条命令说清"能不能开工、不能的话人要去做什么")。
|
||
#
|
||
# bash ~/ros2/scripts/run_real_preflight.sh --arm 89
|
||
# bash ~/ros2/scripts/run_real_preflight.sh --arm 89 --json # 给脚本消费
|
||
# bash ~/ros2/scripts/run_real_preflight.sh --arm 89 --watch-usb 10
|
||
#
|
||
# 查什么(顺序就是现场该处理的顺序:根因在前,症状在后):
|
||
# 1. 现场配置:这一轮在读哪份 site.yaml、认哪台臂/哪台相机/哪个串口
|
||
# 2. 遗留进程:上一轮没退干净的栈会让这一轮的现象指向别处
|
||
# 3. 网络:机器人是否在网(含"两台臂 ARP 撞同一个 MAC"这个坑)
|
||
# 4. 机械臂 RDK:能不能使能(Manual 模式是**硬阻断**,只能人工在示教器上切)
|
||
# 5. USB:RealSense 协商速率 + Hub 是否在内核里卡死
|
||
# 6. 触觉 V4L2:设备在不在,是"没插"还是"卡死"
|
||
# 7. 几何:手眼矩阵是否自洽 + 悬停高度会不会把工具捅进工件
|
||
#
|
||
# 退出码:0 = 全绿(GO);1 = 有需要处理的项(NO-GO,看输出里"要人工做的事")。
|
||
#
|
||
# 为什么要有这个而不是让人挨个跑:这些故障**同时出现时现象会互相串**
|
||
#(Hub 卡死会让相机消失,相机消失又会让 S00 自检报"缺相机",
|
||
# 而真正拦住机械臂的其实是示教器模式)。分开查会让人先怀疑软件。
|
||
#
|
||
# 注意:这里**不加** `set -u` —— conda/RoboStack 的钩子会引用未绑定的变量。
|
||
set -eo pipefail
|
||
|
||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
WATCH_USB=0
|
||
JSON=false
|
||
|
||
# --sn/--arm 必须先解析:load_site_config.sh 靠 ROBOT_SN 决定选哪台臂
|
||
# shellcheck disable=SC1091
|
||
source "${ROOT}/scripts/lib/parse_arm_args.sh"
|
||
parse_arm_args "$@"
|
||
set -- "${PARSE_ARM_REST[@]}"
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
--watch-usb) WATCH_USB="${2:?--watch-usb 需要秒数}"; shift 2 ;;
|
||
--json) JSON=true; shift ;;
|
||
-h|--help) sed -n '2,20p' "$0"; exit 0 ;;
|
||
*) echo "Unknown: $1" >&2; exit 1 ;;
|
||
esac
|
||
done
|
||
|
||
# shellcheck disable=SC1091
|
||
source "${ROOT}/scripts/env.sh" >/dev/null 2>&1 || true
|
||
# shellcheck disable=SC1091
|
||
source "${ROOT}/scripts/lib/load_site_config.sh"
|
||
|
||
# site.yaml 不存在时不要直接失败:网络与 RDK 自检在配置前就该能用,
|
||
# 而"配置没写"本身也是体检要报出来的一项。
|
||
if [[ "${SITE_LOAD_STATUS}" == "2" ]]; then
|
||
echo "ERROR: site.yaml 有问题,先按上面的提示修好再体检。" >&2
|
||
exit 2
|
||
fi
|
||
|
||
SN="${SITE_ROBOT_SN:-${ROBOT_SN:-}}"
|
||
if [[ -z "${SN}" ]]; then
|
||
echo "ERROR: 没选到机械臂。给命令加 --arm <臂号>,或在 site.yaml 里填 robot.sn。" >&2
|
||
exit 2
|
||
fi
|
||
|
||
echo "=== 真机开工体检 arm=${SITE_ROBOT_ARM:-?} ${SITE_ROBOT_LABEL:-} (${SN}) ==="
|
||
echo
|
||
|
||
ARGS=(--sn "${SN}" --site "${SITE_CONFIG:-${ROOT}/config/site.yaml}")
|
||
[[ -n "${SITE_NET_IFACE:-}" ]] && ARGS+=(--iface "${SITE_NET_IFACE}")
|
||
[[ "${JSON}" == true ]] && ARGS+=(--json)
|
||
|
||
RC=0
|
||
if [[ "${WATCH_USB}" != "0" ]]; then
|
||
# 先单独盯一会儿 USB:Hub 半死时会"设备在 / 不在"反复跳,
|
||
# 单次采样只会看到一个瞬间,看不出它在抖。
|
||
echo "--- USB 链路盯 ${WATCH_USB}s(看掉线/重连)---"
|
||
python3 "${ROOT}/scripts/lib/usb_health.py" --watch "${WATCH_USB}" || RC=$?
|
||
echo
|
||
fi
|
||
|
||
python3 "${ROOT}/scripts/lib/real_preflight.py" "${ARGS[@]}" || RC=$?
|
||
exit "${RC}"
|