Files
arm_dev dfc945b7e0 P6 通止规装配:主流程节点(S00–S11 跑通)+ 入口脚本 + 真机/仿真验证
新增
----
- arm_application/gauge_assembly_node.py:主流程。每个阶段一个 _step_*,
  返回值就是状态机的事件;纯逻辑(状态机/配置/限位/夹爪后端/自检)全部
  在 gauge_*.py 里,节点只做"发指令 + 读反馈 + 报错"。
  * S00 自检报告带"缺了会怎样",必检拦住、选检告警;
  * S01 回 home → 切工具系 → 逆时针预绕,**事后用 TF 实测绕工具 Z 转了多少度**
    (转轴偏差 >5° 直接中止:以"度"为单位的动作全建立在这条假设上);
  * S03 手外粗定位:没有 eye-to-hand 标定文件时明确报"缺哪个文件、怎么生成",
    回 S04 兜底(不做假装成功的反投影);
  * S05 稳定判据用"到均值的最远距离",不用相邻帧之差;
  * S07 悬停处测基线 → 步进下压 → 两路切向任一突变即停,法向超安全上限抬起中止;
  * S08 棘轮四步(慢转→松爪→快回转→夹紧),停止判据「切向 >阈值 且 法向突增」,
    掉件走 E1 重放;S09 反向拧出(比拧入多一轮);
  * E1 重放等 /gauge/reload_ok 人工确认(超时按中止),E2 任何中止都先退安全高度。
- launch/gauge_assembly.launch.py:只做一件事 —— 发布手眼静态 TF(S05 的前提),
  其余判据全交给 S00(判断标准只有一处)。
- scripts/run_gauge_assembly.sh:--arm/--sim/--dry-run/--holes/--backend。
- gauge_assembly 入口(setup.py)+ 站点参数 reload_timeout_s。
- test/test_gauge_step_events.py:**读节点源码 AST**,把每个 _step_* 可能返回的
  事件逐个问状态机"合法吗" —— 节点与转移表是两份数据,漏登记要在 pytest 里炸,
  而不是在真机上跑到那一步才炸。

顺手修掉的两个真问题
--------------------
1. 四份 launch 里的四元数换算**已经互相抄歪了**(退化分支的公式各不相同)。
   统一收口到 handeye_lib.read_transform_yaml,并加回环测试(真实标定文件 +
   180°/270° 等分支切换角度);实测 4 个 launch 输出与库完全一致。
2. pipeline_node.AssemblyPipeline 支持 node_name 复用(本流程继承它的一阶段
   跟踪/二阶段定位链,但在图上必须是另一个节点名)。

验证(都在本机真跑过)
----------------------
  pytest(仿真环境)              → 349 passed, 14 skipped(缺 onnx 的用例跳过)
  干跑全流程                      → S00→S01→…→S11→DONE,exit 0
  仿真里不干跑(缺 /home 等)      → S00 拦住,exit 1,未发任何运动指令
另:仿真环境没有 onnx(只有真机 conda 有),相关用例改为 skip —— 让测试结果
"红就是真红"。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-24 10:12:03 +08:00

88 lines
3.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from setuptools import find_packages, setup
import os
from glob import glob
package_name = 'arm_application'
def _existing(paths):
"""只保留真实存在的常规文件。
必需:colcon 的 ``build/<pkg>/`` 里放着指向源文件的符号链接,
源文件被挪走或删掉之后这些链接就断了。``glob`` / ``os.walk`` 仍会把它们
列出来,随后 copy 会报 "doesn't exist or not a regular file",
**整个包构建失败**,而报错信息指向 build/ 下的一个幽灵文件,很难查。
这里统一过滤掉。
"""
out = []
for p in paths:
if os.path.isfile(p): # 会跟随符号链接,断链返回 False
out.append(p)
return out
def _data_files():
entries = [
('share/ament_index/resource_index/packages', ['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
(os.path.join('share', package_name, 'launch'), _existing(glob('launch/*.py'))),
(os.path.join('share', package_name, 'trajectories'), _existing(glob('trajectories/*'))),
]
for root, _dirs, files in os.walk('config'):
srcs = _existing([os.path.join(root, name) for name in files])
if not srcs:
continue
dest = os.path.join('share', package_name, root)
entries.append((dest, srcs))
return entries
setup(
name=package_name,
version='0.1.0',
packages=find_packages(exclude=['test']),
data_files=_data_files(),
install_requires=['setuptools'],
zip_safe=True,
maintainer='arm_dev',
maintainer_email='dev@example.com',
description='High-level arm application nodes.',
license='Apache-2.0',
entry_points={
'console_scripts': [
# Bringup helpers
'demo_named_poses = arm_application.demo_named_poses:main',
'move_to_pose_client = arm_application.move_to_pose_client:main',
'run_joint_trajectory = arm_application.run_joint_trajectory:main',
'goto_ee_plane = arm_application.goto_ee_plane:main',
# 相机
'realsense_color_pub = arm_application.realsense_color_pub:main',
'realsense_view = arm_application.realsense_view:main',
# 手眼标定
'handeye_teach = arm_application.handeye_teach:main',
'handeye_waypoints = arm_application.handeye_waypoints:main',
'handeye_calibrate = arm_application.handeye_calibrate:main',
# 视觉伺服(当前主链路)
'perception_node = arm_application.perception_node:main',
'servo_node = arm_application.servo_node:main',
# 夹爪
'dh_pgea = arm_application.dh_pgea_node:main',
'dh_pgea_cli = arm_application.dh_pgea:main',
'dual_gripper = arm_application.dual_gripper_node:main',
'dual_gripper_cli = arm_application.dual_gripper_cli:main',
# 入孔检测(只检测,不做插入动作)
'hole_detect = arm_application.hole_detect_node:main',
# 第二阶段:一次性孔定位(k1/k2)+ 对规
'stage2_locate = arm_application.stage2_locate_node:main',
# 第三阶段:柔顺找孔(Orisys 触觉螺旋 + 插入)
'stage3_search = arm_application.stage3_search_node:main',
# 三段式装配流水线(跟踪 → 定位 → 找孔,单进程)
'assembly_pipeline = arm_application.pipeline_node:main',
# 通止规装配(看板 S00–S11,含自检/换孔/掉件重放)
'gauge_assembly = arm_application.gauge_assembly_node:main',
'tactile = arm_application.tactile_node:main',
'tactile_cli = arm_application.tactile_cli:main',
],
},
)