From 02c26cfb861fa849ffe74e5b2db2280b2ae82aa1 Mon Sep 17 00:00:00 2001 From: arm_dev Date: Mon, 28 Sep 2026 10:42:56 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E8=BF=90=E8=A1=8C=E5=99=A8?= =?UTF-8?q?=EF=BC=9A=E4=BD=8D=E7=BD=AE=E5=8F=82=E6=95=B0=E7=9C=9F=E7=9A=84?= =?UTF-8?q?=E5=8F=AA=E8=B7=91=E6=8C=87=E5=AE=9A=E8=B7=AF=E5=BE=84=EF=BC=9B?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E6=94=B9=E6=88=90=E5=AE=9E=E9=99=85=E8=83=BD?= =?UTF-8?q?=E8=B7=91=E9=80=9A=E7=9A=84=E5=91=BD=E4=BB=A4=E4=B8=8E=E6=95=B0?= =?UTF-8?q?=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scripts/run_unit_tests.sh 用法注释里写的是「bash run_unit_tests.sh src//test/test_x.py -x」,但实现是 `pytest "${test_dirs[@]}" "$@"` —— 传进来的路径只是被**追加**到「全部测试目录」 后面,实际仍然跑整仓库。用法示例就是承诺,它不成立会让人以为「我只改了 x, 怎么蹦出别的包的失败」。改成:位置参数里**存在的路径**当作「只跑这些」, 其余(-k / -x / --tb=… 及其取值)原样透传。 docs/HANDBOOK.md §4.3 原文让人逐个包手敲 `python3 -m pytest src/xxx/test`。这条命令在本仓库**会失败**, 而且是两种最容易被误读的失败: · 少挂一个 src 包 → 收集阶段 ModuleNotFoundError,现象是「0 个测试 / 1 个 error」, 像测试环境坏了; · 更隐蔽:install/ 里那份**旧的**同名模块会先被找到。实测直接跑 src/arm_sim_gazebo/test 报 `cannot import name 'RESET_GIVE_UP'`,导的是 install/ 里的旧副本,源码里明明有这个符号 —— 测的根本不是你改的那份。 改为统一用 run_unit_tests.sh(它把 src/* 挂到 PYTHONPATH 最前面),并把这段 原因写进文档。顺带把过期的统计数字更新为实测值:608 → 1118 项, 分项 arm_application 233→547、arm_sim_gazebo 236→400、arm_interfaces 46→78。 实测:src/arm_sim_gazebo/test 直跑 = 2 个收集错误;经运行器 = 400 passed。 单文件 28 passed;-k real_preflight = 28 passed / 1 skipped; 无参数全量 = 1118 passed / 1 skipped。 docs/QUICKSTART.md 「第一次实机」以体检开场(它只读、不需要机械臂能动),并列出四项判据各抓什么, 免得现场跳过它直接起栈。 Co-authored-by: Cursor --- docs/HANDBOOK.md | 34 +++++++++++++++++++++------------- docs/QUICKSTART.md | 23 +++++++++++++++++++++++ scripts/run_unit_tests.sh | 22 +++++++++++++++++++--- 3 files changed, 63 insertions(+), 16 deletions(-) diff --git a/docs/HANDBOOK.md b/docs/HANDBOOK.md index b433894..3a0b445 100644 --- a/docs/HANDBOOK.md +++ b/docs/HANDBOOK.md @@ -338,6 +338,7 @@ source scripts/env.sh # 真机环境 source scripts/sim_env.sh # 仿真环境(独立 conda) # —— 设备与配置 —— +bash scripts/run_real_preflight.sh --arm 89 # 真机开工体检(只读,含 RDK/相机/USB/几何) ros2 run arm_interfaces device_check # 设备契约 + site.yaml 必填项 bash scripts/show_site_config.sh # site.yaml 解析结果 python3 scripts/check_arm_prefixes.py # 含臂名的配置是否跟着当前臂(换臂必跑) @@ -356,29 +357,36 @@ bash scripts/weights.sh doctor --check-engines ### 4.3 单元测试怎么跑 -**每个功能包都有自己的测试**,改完对应包就跑一下。当前 pytest 收集 **608 项**。 +**每个功能包都有自己的测试**,改完对应包就跑一下。当前 pytest 收集 **1118 项** +(另有 1 项按环境 skip)。 ```bash source scripts/env.sh # 或 sim_env.sh -# 逐包跑(跳过需要厂商 flexivrdk 包的 flexiv_rdk,那个环境里没有) -for d in src/*/test; do - case "$d" in src/flexiv_rdk/*) continue;; esac - printf '%-28s ' "$d"; python3 -m pytest "$d" -q -p no:cacheprovider 2>&1 | tail -1 -done - -# 只跑一个包 -python3 -m pytest src/arm_sim_gazebo/test -q +bash scripts/run_unit_tests.sh # 全部 +bash scripts/run_unit_tests.sh src/arm_kinematics/test # 只跑一个包 +bash scripts/run_unit_tests.sh src/arm_application/test/test_real_preflight.py +bash scripts/run_unit_tests.sh -k gauge # 只跑名字带 gauge 的 ``` -改动前后对比数量是最省事的回归判据 —— 现在应该是(pytest 收集数,共 608 项): +> 用这个脚本,别直接 `python3 -m pytest src/xxx/test`。脚本会把 `src/*` 全部 +> 挂到 `PYTHONPATH` 最前面,两个原因: +> +> 1. 测试按**包名**导入,少挂一个包会在收集阶段 `ModuleNotFoundError` —— +> 现象是"0 个测试 / 1 个 error",很容易被读成测试环境坏了。 +> 2. 更隐蔽:`install/` 里那份**旧的**同名模块会先被找到(它在 `PYTHONPATH` 上)。 +> 实测 `src/arm_sim_gazebo/test` 直接跑会报 +> `cannot import name 'RESET_GIVE_UP'` —— 导的是 `install/` 里的旧副本, +> 源码里明明有这个符号。挂上 `src/` 之后才测的是你改的那份。 + +按包统计(改动前后对比数量是最省事的回归判据): | 包 | 测试数 | 覆盖什么 | |----|--------|----------| -| `arm_application` | 233 | 纯逻辑:滤波器、解析器、几何、参数合并 | -| `arm_sim_gazebo` | 236 | 场景校验、URDF 归一化、相机内参、触觉适配 | +| `arm_application` | 547 | 纯逻辑:滤波器、解析器、几何、参数合并、装配状态机 | +| `arm_sim_gazebo` | 400 | 场景校验、URDF 归一化、相机内参、触觉适配 | +| `arm_interfaces` | 78 | 路径解析、清单校验、配置守卫 | | `arm_driver_flexiv` | 69 | 驱动适配、下压控制、超时 | -| `arm_interfaces` | 46 | 路径解析、清单校验、配置守卫 | | `arm_kinematics` | 24 | 正逆运动学 | > `src/flexiv_rdk/test` 需要厂商的 `flexivrdk` Python 包,仿真环境里没有, diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index a423feb..f3c7aa7 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -45,6 +45,29 @@ source scripts/env.sh ## 第一次实机 +**开工前先体检**(不需要机械臂能动,只读): + +```bash +bash ~/ros2/scripts/run_real_preflight.sh --arm 89 +``` + +它按"现场该处理的顺序"给结论,最后汇总**要人工做的事**。它专门盯这几类 +"报错的地方不是坏的地方"的问题: + +| 检查项 | 抓的是 | +| --- | --- | +| 现场配置 | 这一轮到底在读哪份 site.yaml、认哪台臂 | +| 遗留进程 | 上一轮没退干净的 ros2_control(它占着 RDK,会让 RDK 报"连不上") | +| 机器人在网 | ping 不通;以及**两个 IP 指向同一个 MAC**(两台臂出厂 MAC 相同时会互相抢转发) | +| 机械臂 RDK | 连不上 / 示教器在 Manual(这个 SW 切不了,只能人到现场拨) | +| 腕部相机 | 本臂配置的那只 D405 **在不在 USB 上** —— 它掉了以后报错会变成"点云为空" | +| 触觉 / USB / 几何 | 触觉是"没插"还是"内核卡死"、RealSense 协商速率、手眼矩阵与悬停高度 | + +结论是 `NO-GO` 时**不要往下走**:硬阻断项(RDK、腕部相机)会让后面每一步 +都以别的样子失败。 + +然后是常规步骤: + 1. 示教器:AUTO + Remote Ethernet,清故障。关 Studio。 2. `bash ~/ros2/scripts/test_rdk_python.sh`(可选) 3. T1 `run_real.sh`,T2 `run_camera_d405.sh --preview` diff --git a/scripts/run_unit_tests.sh b/scripts/run_unit_tests.sh index 91c82b9..8439eb8 100755 --- a/scripts/run_unit_tests.sh +++ b/scripts/run_unit_tests.sh @@ -38,7 +38,23 @@ if [ "${#test_dirs[@]}" -eq 0 ]; then fi cd "${ROOT}" -if [ "$#" -gt 0 ]; then - exec "${PY}" -m pytest "${test_dirs[@]}" -q "$@" +# 位置参数里**存在的路径**当作"只跑这些",其余(-k / -x / --tb=… 及其取值) +# 原样透传给 pytest。 +# +# 为什么要分开:以前是 `pytest "${test_dirs[@]}" "$@"` —— 传进来的路径只是被 +# **追加**到"全部测试目录"后面,于是 `run_unit_tests.sh src/x/test` 实际跑的是 +# **整仓库**。脚本头部的用法示例看起来像"只跑这个文件",实际不是,很容易被读成 +# "我只改了 x,怎么蹦出别的包的失败"。用法示例就是承诺,得让它成立。 +paths=() +extra=() +for a in "$@"; do + if [[ "${a}" != -* && -e "${a}" ]]; then + paths+=("${a}") + else + extra+=("${a}") + fi +done +if [ "${#paths[@]}" -gt 0 ]; then + exec "${PY}" -m pytest "${paths[@]}" -q "${extra[@]}" fi -exec "${PY}" -m pytest "${test_dirs[@]}" -q +exec "${PY}" -m pytest "${test_dirs[@]}" -q "${extra[@]}"