arm_dev
|
55acdb638d
|
空回微抬改成一条厂家 MoveL,并按实测抬高降回。默认 Z50 会在还差约 0.5mm 时就报到位,微升降改用 ZFine 和到位等级 1。合到 0 时固件常报 grip=1,手指已在合拢端就继续顺时针,避免最后一转永远不发。快转样条按峰速不超过 0.90 rad/s 排程,避免时间到了还差几十度。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-10-19 19:02:55 +08:00 |
|
arm_dev
|
d8942defd1
|
初步完成平行夹爪旋转入孔-不断的张开转到逆时针极限再闭合转到顺时针极限
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-10-19 15:44:09 +08:00 |
|
arm_dev
|
7af9e3d4b6
|
贴面后不再抬回悬高;MoveL 切回关节后不再把整段位移当前进速度。
搜孔开局会抬到悬高再落回 hole_hover_z_m,螺纹还没咬合臂就离开孔口。没有接触传感器,到位只认 final_gap 算出的法兰高度,贴面后停在这个 Z。09:25 厂家 MoveL 切回 NRT_JOINT_POSITION 的第一拍把整段关节差算成 1.686 rad/s,前瞻再推 0.07 rad,光心从 5 mm 冲到 276 mm 后急停。切回后钉住结束瞬间的关节角并清零速度,JTC 还指着出发前的关节时不放行。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-10-19 11:30:49 +08:00 |
|
arm_dev
|
7942014763
|
主界面一屏要同时看臂、夹爪、相机和触觉,按钮再被拉高就会叠到画面上。相机按原分辨率走回调取最新帧,场景一动不再等红外齐帧把彩色丢掉。模型和相机都留在工位窗口里,启动默认不再弹 RViz。
验收:test_listed_arms 与 test_camera_preview 共 20 项通过。D435 1280×720 发布约 29 帧/秒。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-10-15 16:20:35 +08:00 |
|
arm_dev
|
5c7966d8cd
|
悬停走完后常数前馈会往 +X 过补约 1.3mm、插不进,贴面改成笛卡尔锁 XY、只降 Z 的厂家 MoveL;D435 交回也不再杀栈,只等法兰停稳 0.5 秒。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-10-15 10:23:52 +08:00 |
|
arm_dev
|
37b85aa09b
|
悬停一次走完后下降会漂几毫米,这一保存点把观察高补交、接触门控和厂家 MoveL 接到能插进去的状态,后面好对照锁 XY 纯 Z。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-10-13 19:27:19 +08:00 |
|
arm_dev
|
1b264c0654
|
把现场确认能用的对孔方案落成保存点:小孔只用视觉关键点交到工件面、多走多拍闭环;D435 走到点后杀栈冷启动再停稳 3 秒才交 D405,避免赶路时带臂晃。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-10-13 14:57:54 +08:00 |
|
arm_dev
|
f882c4309c
|
克隆下来必须带上正在用的视觉权重和本机 site.yaml,否则感知只能指到一台机器上的路径,别人起不来。
顶视迁臂改走厂家 MoveL,D405 框心对准后再对孔;夹爪停在 caught 时 open 不再把相同目标当成已经张开,init 会真的回零。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-10-12 17:08:43 +08:00 |
|
arm_dev
|
b903161113
|
左臂换成 tool10120938 后,手眼仍按旧 tool1 和 8×11 板求解,顶视迁臂又按短跳 rest-to-rest,真机上要么整体偏一截,要么中途弹射启停。
眼在手上改用该 TCP 做 AX=XB,再折回相机→法兰;A5 内角点改为 11×8,09-04 / 10-09 旧结果留在 backup。一阶段 D435 迁臂改成一条 FollowJointTrajectory(余弦加减速,近处降速,XY 进门槛后再单独收垂直),跟踪锁高与悬停齐平,避免倾斜相机下先降一截把孔位重投影偏掉。--no-descent 只降到离面 15 mm;跳过三阶段时触觉 USB 打不开不再空等几分钟。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-10-12 15:04:23 +08:00 |
|
arm_dev
|
7d22f777c8
|
把当前控制栈收成一份可克隆的源码:腕部看不见时用顶视一次定到基座,二阶段对孔和手外标定跟着进同一份树。
现场 site.yaml、权重和编译产物仍不入库;模板只留占位符。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-10-09 14:51:03 +08:00 |
|
arm_dev
|
55e5f8f20b
|
--sim 时按仿真台别名导出手眼真值:--arm 89 --sim 照着文档敲必然失败
`--arm 89 --sim` 是个**合法且有意的**用法(run_gauge_assembly.sh 里
_HAD_ARM 那段写着"想用仿真核对某台真机的点位时会这么用"),但手眼真值导出
传的是 ${ROBOT_SN} —— 于是帧名成了 Rizon4-063989_flange,而仿真 URDF 里是
sim_flange。导出先干等 20s,再报"仿真栈起来了吗?",把人引去重启一个明明在
跑的仿真。文档 §7 里写的就是这条命令,所以照着敲 100% 失败。
真值必须按**仿真台**的帧名导出,与"用哪台臂的点位"无关 → 固定用仿真台别名
(SIM_ARM_ALIAS 可覆盖,默认 sim)。
顺带把 §7 那组验收命令补成真能跑的:
· 以前只写"先编",没说**怎么起仿真**,也没说**起之前要清干净** ——
这次就是在这两步上栽的,新增 §6.5 把三步(编 / 清 / 起)写全;
· "arm_application 子集 454 passed"配的那条裸 `PYTHONPATH=... pytest`
命令实测已经收集阶段就 error(导入不到 ament),改成走 run_unit_tests.sh
并更新数字;总测试数 1118 → 1128。
验证:`bash scripts/run_gauge_assembly.sh --arm 89 --sim --dry-run` 现在
state=DONE ok=True 2/2 孔、退出码 0(修改前必然退出码 1)。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-09-28 12:25:26 +08:00 |
|
arm_dev
|
02c26cfb86
|
测试运行器:位置参数真的只跑指定路径;文档改成实际能跑通的命令与数字
scripts/run_unit_tests.sh
用法注释里写的是「bash run_unit_tests.sh src/<pkg>/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 <cursoragent@cursor.com>
|
2026-09-28 10:42:56 +08:00 |
|
arm_dev
|
bda2fa7129
|
通止规装配:配置段进 site.yaml + 中文手册 + 成败判定「不看 $?」
配置(site.yaml → gauge_assembly:,38 键全部带中文注释)
------------------------------------------------------
- 键名/单位/类型/交叉矛盾由 gauge_config.py 校验:拼错键名报错并给最相近的候选,
单位后缀(_deg/_mm/_m/_s/_pct/_delta)逐条对表,`contact_safe_delta ≤
contact_tangential_delta` 这类矛盾启动就拦(R1.2:没接线的键不许静默存在)。
- 借机清掉 4 个**没接线**的键(require_preview / track_*_s / hover_m:前两个是
stage1 那层的、后两个是 servo 那层的,放在这里等于第二份真相),
并把 contact_speed_m_s / coarse_timeout_s / 夹爪力速 真的接上。
- site.example.yaml 由 site.yaml 生成(gen_site_example.py --check 通过)。
成败判定:`ros2 launch` 的退出码**恒为 0**
------------------------------------------
实测(Humble):子进程 exit 3,`ros2 launch` 仍返回 0。用 `$?` 判成败的脚本会把
"装配失败"记成"成功"——没人会去查一个说成功的脚本。改成:
- 节点把结论**落盘**(--result-file,默认 /tmp/gauge_assembly_result.json,原子写、
先落盘再发话题),任何"没跑到收尾"的退出也会补一份 ok=false;
- 脚本读结论文件判成败:0=成功、1=失败、**文件不存在也算失败**并说清;
- 新增 test_gauge_result_gate.py:把脚本里那段判成败的 Python 抽出来,
对成功/失败/干跑/缺文件/坏 JSON 五种输入断言退出码 —— 判据本身也有测试。
顺带修掉两个"看着像玄学"的现象
------------------------------
- **流程打完了终端不回提示符**:launch 里的静态 TF 发布者是常驻的,主节点退出
带不走它 —— 挂 OnProcessExit → Shutdown。
- **"已装 1/2 孔"**:结果里 hole_index 是 0 基当前孔号,不是"装完几个",
新增 holes_done 给人和脚本看。
文档(全部中文,含"怎么调参/还没做")
------------------------------------
- 新增 docs/GAUGE_ASSEMBLY.md:状态机全图、每步判据表、E1 掉件处理(**按实现**:
掉件后该孔作废、换下一个孔;最后一个孔掉件则序号被夹住=重试本孔)、
话题契约、调参顺序、验收命令、踩过的坑。
- FRAMEWORK_RULES 新增 R5.8(同一份数学只许一份 + 守卫测试)、R5.9(自检的
期望类型与调用处同源)、R5.10(缺依赖 skip,别留常年红)、R6.9(硬退出前打
traceback)、R6.10(ros2 launch 退出码恒 0)+ R5.7 补真机/仿真"生效方式不同"
(真机软链即改即生效,仿真拷贝必须重编)+ 5 条症状反例、.cursor/rules 同步。
- WORKSPACE / INTERFACES / SITE_CONFIG / HANDBOOK / 两份 README 索引与决策表补齐。
验证(本机真跑)
----------------
pytest(仿真环境) 436 passed, 14 skipped(缺 onnx 跳过)
干跑 --sim --dry-run → 脚本退出码 0,结论 ok=true 已装 2/2 孔
不干跑 --sim → 脚本退出码 1,S00 拦住,未发任何运动指令
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-09-24 10:30:11 +08:00 |
|
arm_dev
|
6922b18187
|
仿真夹爪适配器:服务接口对齐真机,开口按正运动学换算成真毫米
为什么
------
应用层只认 arm_msgs/srv/GripperExecute(开口毫米),真机与仿真必须同一套接口。
但 GN01 的 URDF 里 <mimic> 是**线性近似**:直接按线性系数下发 finger_width_joint,
"命令开 20 mm"在 Gazebo 里量出来是 21.75 mm —— 抓不住工件,而且看起来像算法问题。
实测(2026-09-24,Gazebo 内量两指尖间距):
线性下发 20 mm → 实际 21.74 mm(偏差 1.74 mm,超 2 mm 抓取容差的前半步)
走 FK 反解 → 实际 20.00 mm(≤0.5 mm)
怎么改
------
- 新增 sim_gripper_kin.py:解析 URDF 正运动学,算出"主关节值 ↔ 真实开口 mm"的
单调曲线,反解用二分。曲线非单调/零行程时**主动报错**,不静默退化成线性。
- sim_gripper.py:开口换算全部走该曲线;容差统一改成毫米。
- sim_gripper_adapter.py:反馈改取**从动关节**(见下);is_gripped 按毫米判定。
- 新增 check_sim_gripper.py:三层验收(服务/开口一致/指尖真实几何行程 + 对称性)。
踩到的坑
--------
- finger_width_joint 挂在无质量的 unused_finger_width_link 上,与手指**没有机械
连接**:命令到 0.100,它的 /joint_states 恒为 1.4e-08,6 个从动关节才准到
0.7854。反馈若取它,/gripper/status 会永远报"开口 0 mm"。
- 残留进程会伪造出"代码写错了"的现场:旧 sim_gripper_adapter 与新实例抢话题,
验收读到"指尖间距 0.03 mm"(真实 127 mm);孤儿 static_transform_publisher
用 latched /tf_static 把旧相机外参按在新仿真上。
→ 新增 scripts/lib/sim_procs.sh;run_sim_gz.sh 用 setsid 独立进程组,
PGID 记进 log_sim/sim.pgid,stop 时整组 kill;启动前预检拒绝带病启动。
- 验收脚本自己也错过两次(把 TF 的米当毫米比 → 127 mm 显示成 0.13 mm;
用会转动指垫的中点漂移判对称 → 必然位移 16 mm)。判据改成"两侧各走多远",
单位只在 _dist_mm 里换算一次。
验证
----
python3 -m pytest src/arm_sim_gazebo/test/ -q → 307 passed
python3 scripts/check_sim_gripper.py → 三层全过
第 2 层:请求/反馈开口最大偏差 < 0.5 mm
第 3 层:期望行程 vs 实际行程、左右行程差,均 < 1.5 mm
bash scripts/stop_sim_gz.sh --list → 整组列出(含 static tf)
文档
----
- 仿真手册:新增 §4.4(开口 mm 怎么换算)、坑 12(宽度关节不动、
反馈必须取从动关节);停止一节改写成进程组语义。
- FRAMEWORK_RULES:新增 R4.11(残留进程会伪造现场)、R4.12(验收判据要挑
与实现无关的物理量);R4.9 表补夹爪;清单补 13/14 两条。
- HANDBOOK:新增 §3.4「在仿真里开合夹爪」,补 §4.4 的两条新教训。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-09-24 09:46:09 +08:00 |
|
arm_dev
|
f5ed44a987
|
配置:消灭代码里的硬编码臂 SN,并给"换臂漏改"加一把尺子
## 为什么
"设备 ID 全部可配置"这条要求在代码里只做了一半:site.yaml 能配,
但**代码和静态配置里到处躺着 Rizon4-063989 当兜底**。这类兜底的危害
不是"改起来麻烦",而是它把"配置没生效"伪装成"配置生效了":
* `robot_sn` 默认 `Rizon4-063989` → 换臂后节点去订**上一台臂**的关节话题,
节点起来了、不报错、什么都不动;
* launch 里用 `ee_frame == "Rizon4-063989_flange"` 当"用户没指定"的哨兵 →
换个站点这个判断永远不成立,于是 site.yaml 里的 ee_frame 再也不会被用上,
静态 TF 挂错坐标系、点还照样查得到;
* `if SN == "Rizon4-063989"` 决定要不要拉触觉服务 → 换臂后触觉静默消失。
## 改了什么
1. **删掉所有"兜底成某台臂"的默认值**(15 处脚本 + 6 个 launch + 6 个节点)。
没配就为空 / 直接 `:?` 报错,宁可起不来也不要连错机器:
- `scripts/lib/export_site_env.py`:别名解析改为从 `robots[]` 现算
(`arm_short_id` / `resolve_robot_sn` / `describe_arms`),选不出臂时
抛 `ValueError`;`main()` 捕获后打人话 + 退出码 2(供 shell `eval`);
- 手眼文件/路点/工作目录由当前臂 short id 派生,不再写死 `89_*`;
- `ROBOT_HAS_TACTILE` 改为读 site.yaml 的 `robots[].tactile`;
- `arm_application/site_io.py` 新增 `joint_prefix_default()` /
`base_frame_default()` / `ee_frame_default()`:优先读 `SITE_*`,
退而由 `SITE_ROBOT_SN` 现算,**不写死 SN**;
- `arm.urdf.xacro` 的 `robot_sn` 默认值改成明显的占位符 `UNSET_ARM_SN`
(关节名会带出来,报错信息自解释);
- `ros2_control.launch.py` / `move_group.launch.py` **原先根本不传
robot_sn**,靠 xacro 写死默认值"蒙对"本机,现在都从环境取。
2. **新增 `scripts/check_arm_prefixes.py`**:Flexiv 把 SN 编进关节名,
所以有一批一级配置(SRDF/controllers/joint_names/bringup/servo/...)
必须跟着当前臂走。漏改的症状是"MoveIt 规划成功但机械臂不动",很难查。
这个脚本逐文件逐行列出对不上的地方,并直接给出修法。
`--list` 还会说明 SN 来自 site.yaml 还是被 `ROBOT_SN` 覆盖。
3. **文档**:`FRAMEWORK_RULES.md` 新增 R1.1.1(两类含臂名文件怎么分、
换臂四步)+ 检查清单第 12 条 + 反例 2 条;`HANDBOOK.md` 新增 3.6.1
与症状表 2 行。
4. **顺带修掉的真 bug**(都是这次改动带出来、被 pyflakes + 真跑抓住的):
- `convert_flexiv_traj.py` / `rdk_drag_bridge.py` 用了 `os` 但没 import
→ `--help` 直接 NameError;
- `check_arm_prefixes.py` 第一版正则用 `\b` 收尾,而关节名是
`Rizon4-063989_joint1`(`_` 属于 \w,`\b` 不成立)→ SRDF 里 24 处
硬编码**一处都没报**,还打印 OK。已加负向测试钉住;
- 删掉若干死代码/死 import(`pos_line`/`flat`/`names`、`IfCondition` 等)。
## 验收(都真跑过)
- `python3 -m pyflakes <全部改过的 py>` → 干净
- 逐包 pytest:233 + 69 + 78 + 24 + 236 = **640 passed**
- `env -u ROBOT_SN python3 scripts/check_arm_prefixes.py` → OK(8 文件)
- 换成 SN=Rizon4-777001 的临时 site.yaml → 报出**8 个**文件不一致并给修法(退出码 1)
- 故意不给 SITE_* → 各 launch 的 `robot_sn` / `ee_frame` / `handeye_file`
默认值为 `''`,不再悄悄指向上一台臂
- `ros2 launch ... --show-args` 对 stack/sim/ros2_control/mock_control/
move_group/display/visual_servo **全部通过**
- `convert_flexiv_traj.py --help` 正常;拿 `xunhuan.traj` 真转 5 个路点,
输出 `joint_prefix: "Rizon4-063989_"` 来自 site.yaml
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-09-24 09:18:44 +08:00 |
|
arm_dev
|
fac2d5384d
|
P5b 新手手册:这台机器在干什么 / 改哪里 / 怎么验 / 出问题查哪
此前仓库有规则文档(FRAMEWORK_RULES)、有目录表(WORKSPACE)、有各模块说明,
但**缺一份"从零进来的人读什么"**:业务目标没有一处讲清("通止规装配"散落在
7 个文件里各提一句),测试怎么跑在 docs/ 里一次都没写过,改一个东西该动哪个文件
要靠翻源码猜。
docs/HANDBOOK.md(416 行,新增)
1. 这台机器在干什么 —— 通止规是检验螺纹的量具(通端应旋到底、止端应旋不进);
为什么值得自动化(留下可追溯的力曲线);三段主线的数据流;日志三行判进度。
★ 1.4 节是**承诺书**:逐项标 ✅/⚠️/❌,并给出"哪条命令能证明它已就绪"。
如实写明还没做的四项:自动拉起全部服务、夹爪持规自检、D435 手眼标定、
装配合格判定。指不出验证命令的,就老实标 ❌(R6.1)。
2. 系统长什么样 —— 三层各有硬边界;两块**独占资源**(RDK、D405 USB)
以及各自的冲突表现;从像素到关节的全链路加上"每步的口径写错会怎么表现"。
3. 我要改东西,改哪里 —— 决策表(改 X → 动 Y → 用 Z 验),覆盖换臂/悬高/夹爪/
入孔/触觉/相机/仿真场景/权重/新品牌设备/命名位姿。每行都外链权威文档,
不复制内容(M2)。
4. 怎么验 —— 三层验收(不报错不算验收 / 物理量命中真值 / 任务成功)+ 命令清单 +
**单元测试怎么跑**(此前 docs/ 里没有任何一处写过)。含 608 项分包清单。
4.4 节专讲"验收工具自己也会失效",把 M1/R5.3/R6.6 串成一条线。
5. 出问题按现象查 —— 三处权威文档的分工表 + 15 行"现象 → 先怀疑什么",
覆盖 8 条仿真坑与全部触觉坑(坑 1/2/5/6/7/9/10)。
6. 术语表 + 未做的事(按优先级)+ 往下读的路线。
顺带写进规则的(docs/FRAMEWORK_RULES.md)
R6.7 验收命令必须真跑过 —— 本仓库已经因此踩过两次(CRLF 死脚本被五处文档推荐)
R6.8 install/ 构建产物会互相污染 —— 见下面这个发现
第 7 节清单加两条(第 10 条真跑过命令、第 11 条构建环境对不对)
第 8 节反例表加三行症状
★ 顺带发现一个哑弹(R6.8,**已记录未修**)
colcon build 会把当时激活的那个 python 的绝对路径写进每个入口脚本的 shebang。
实测:install/ 里 31/59 条指向仿真环境 ros_gz_sim,install_sim/ 里 31/55 条
(后者是对的)—— 说明真机 install/ 是在仿真 conda 环境里构建出来的,违反 R5.7。
$ head -1 install/arm_interfaces/lib/arm_interfaces/device_check
#!/home/cc/anaconda3/envs/ros_gz_sim/bin/python3.11
为什么现在没炸:两个 conda 环境的 Python 依赖当前完全相同(pyrealsense2 / serial /
cv2 / numpy / torch / ultralytics / tensorrt 都在),指错了也照样 import 成功。
这是一颗哑弹 —— 哪天某个环境多装或少装一个包,真机栈就会以"模块找不到"的形式炸,
而人是不会想到去查 shebang 的。
修法(写进 R6.8,待执行):在真机 conda 环境里重建 install/,再
`grep -l ros_gz_sim install/*/lib/*/* | wc -l` 应当变成 0。
预防:构建脚本开头应断言当前 conda 环境与目标一致(待补)。
验收
- 文档内所有相对链接与反引号路径引用均存在(脚本检查,0 坏链)
- 文档里给的每条命令都实际执行过:
ros2 run arm_interfaces device_check 通过
device_check --list-brands 列出 5 类设备
scripts/show_site_config.sh 存在
check_sim_cameras.py / check_sim_scene.py / check_text_only.py 存在
for d in src/*/test ... pytest 循环 608 passed
- 单测 608 passed,与改动前一致(本次只改文档)
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-09-24 09:05:09 +08:00 |
|