# ============================================================================= # site.yaml — 本机唯一配置入口(不进 git;从 site.example.yaml 复制) # 路径:~/ros2/config/site.yaml # # 89 与 91 的手眼 / 腕部相机 / 夹爪 / 点位互相独立,禁止混用。 # 切换机械臂:改 robot.sn,或所有终端加同一 --arm 89|91。 # ============================================================================= robot: # >>> 切换机械臂:改成 robots 里某一台的完整 SN <<< sn: "Rizon4-063989" # Studio Tools 名(一字不差)。空=不切换。查:bash ~/ros2/scripts/rdk_switch_tool.sh --list # 左臂 89 的激活工具写在 robots[] 里(tool10120938);这里只给未覆盖的臂当缺省。 tool_name: "tool1" # 每台登记 SN + IP,以及该臂自己的相机/夹爪/手眼/点位(覆盖下面的全局段)。 robots: - sn: "Rizon4-063989" arm: left ip: "192.168.2.101" pc_ip: "192.168.2.10" iface: "eth0" label: "左臂89" # 本臂装了 Orisys 视触觉(只有左臂有)。脚本靠这一项决定要不要拉触觉服务, # 而不是靠"SN 等于某个具体值"这种写死的判断(换台臂就会失效)。 tactile: true # 2026-10-16 下午 RDK 重读 Studio tool10120938(用户再次更新 TCP)。 tool_name: "tool10120938" camera: name: d405_left # 话题 /d405_left/... 与 91 分开 serial: "352122274408" # 左腕 D405(一直在用的那台) auto_exposure: true ae_limit_us: 4000 emitter_enabled: false gripper: model: "PGEA-100-20-W" sn: "26610799" port: "/dev/ttyUSB0" stroke_mm: 20.0 # 大寰上电后必须初始化一次(写 0xA5),而初始化的动作是**手指先回零** # = 张开到满行程。如果这时爪子已经夹着通止规/工件,规会被放开、掉下去。 # 2026-09-28 现场实测过:auto_init 走一遍,开口从 19.18mm 变成 20.00mm。 # 所以这里的默认是 **false**:由人决定什么时候初始化。 # · 上电后、还没装夹 → 可以临时开一次:SITE_GRIP_AUTO_INIT=true bash scripts/run_gripper.sh --arm 89 # · 已经装夹(装配过程中)→ 保持 false;不初始化也能读状态、也能开合 # (status 里的 init=need_init 只说明"绝对位置标定没做",不影响夹持) # 想让开机必初始化(无人值守的上电流程)就显式改 true,并确认爪里是空的。 auto_init: false named_poses_file: "/home/cc/ros2/src/arm_control/config/named_poses_89.yaml" paths: handeye_file: "/home/cc/ros2/handeye/results/89_eye_in_hand.yaml" handeye_waypoints: "/home/cc/ros2/handeye/poses/89_eye_in_hand.yaml" handeye_workdir: "/home/cc/ros2/handeye/tmp/89" handeye: # Studio tcp_location(法兰系,米 + qwxyz)。与 rdk Tool.params 必须一致。 # 眼在手上:AX=XB 的手是这份 TCP,再折成 cam→法兰写 T_4x4。 # 眼在手外:AX=XB 的手同样是这份 TCP(板夹在工具上);T_4x4 仍是 cam→基座,不折。 gripper_tool: "tool10120938" gripper_tcp_xyz: [0.000537, -0.000264, 0.226587] gripper_tcp_qwxyz: [1.0, 0.0, 0.0, 0.0] # 左臂跟踪高度(只在 --arm 89 时生效,改 91 的 servo 不影响这里) # 锚点 `&servo_left`:下面的"仿真台"条目直接引用它 —— 仿真建的就是左臂, # 悬停/锁高必须与 89 **一模一样**,抄两份迟早漂移。 servo: &servo_left # 2026-09-29 底座加高后由 0.35 → 0.36:工件上表面实测抬高 10mm # (0.012 → 0.022),锁高同步 +0.011 ⇒ 相机到工件的视距**与加高前一致** # (≈0.213m,跟踪那一档已实拍验证过检出稳定),不是随手抬的。 # 跟踪锁高与 hole_hover_z_m 相同:先到这个悬高,一阶段只动 XY。 # 对孔完成后再下降。以前 ee_z 高于悬停(0.36),对孔前会先降一截, # 倾斜相机下孔位被重投影,偏差变大。 ee_z_m: 0.320 # 跟踪锁高:法兰相对基座 Z [m],与 hole_hover_z_m 同一值 min_ee_z_m: 0.20 safe_z_m: 0.45 z_min_m: 0.22 xy_track: hover # 法兰(夹爪中心) XY 压在工件上,不是画面中心 ee_vertical_down: true # 法兰 +Z 朝世界 −Z hover_offset_ee_xy_m: [0.0, 0.0] # 爪心相对法兰 XY;平行爪同轴保持 0 # >>> 二阶段孔定位悬停锁高:只改这里,91 用下面那段 <<< # 法兰 Z [m];要兼顾:够低拍清 k1/k2 螺纹孔,够高通止规尖不碰工件。 # 调试:bash ~/ros2/scripts/run_stage2_locate.sh --arm 89 --hover-only # = 工件上表面 0.022(本臂 gauge_assembly.workpiece_surface_z_m,加底座后重测) # + 工具长 0.245 + 间隙 0.002 # 2026-09-29 实测:按 0.23 悬停时工具尖端落在工件面**下** 5~10mm, # 悬停这一步就把通止规插进了工件(见本臂 gauge_assembly 段的完整说明)。 # # 2026-09-29 二次修正 —— 上面那条"工具几何"只保证**不碰工件**,它保证不了 # **相机看得见孔**,而二阶段的第一步就是拍 k1/k2。旧值 0.259 的后果实测: # 相机光心在法兰下方 0.136m(手眼 T_cam_to_gripper + 29.7° 倾角) # 法兰 0.259 → 相机离工件面 0.123m → 检出 0/16,conf 峰值 0.012(噪声) # 法兰 0.309 → 相机离工件面 0.173m → 检出 16/16,conf 0.87, # k0/k1/k2 置信度全 0.999~1.000,k1-k2 间距 156px 稳定 # gauge_fsm.hover_z_guard 的设计注释也写着"腕部相机要离工件面 ~0.17m # 才看得清孔",所以 0.309 才是这条同时满足"相机看得见"和"规尖不碰"的值 # (规尖此时在面上 64mm,比旧值更安全)。 # 最后 2mm 的贴近由 stage2 的**深度闭环轻降**(final_gap_mm)完成 —— # 它按实测间隙往下走,与绝对悬停高无关,所以抬高悬停不会让尖撞工件。 # # 2026-09-29 三次修正(工件加了底座,工作平面重新感知): # 拿 gongjian0929 在**实时图**上出 k0/k1/k2,再取各点邻域深度投到基座系: # k1 = +0.0220 / +0.0221 / +0.0223 m(多帧重复性 0.4mm 内) # k2 = +0.0223 / +0.0224 m # k0 = +0.0165 / +0.0186 m(偏低约 6mm 且抖,局部读数噪声大) # 三点"中心 vs 外环"深度差 ≤1mm ⇒ 三点都落在**上表面**上,不是孔底 ⇒ # 上表面取 k1/k2 的一致值 **0.022**(原 0.012 是加底座前的旧值)。 # 悬停随之 0.309 → 0.320(同样 +0.011),让"相机到工件面"的**视距保持 # ≈0.17m 不变** —— 0.309 那一档是实拍验证过"16/16 检出、conf 0.87、 # k1-k2 间距 156px 稳定"的工作点,改高度就是在改视距,不能随手抬。 # 几何兜底仍然满足:0.320 − 工具长 0.245 = 尖端 0.075,工件面 0.022 # ⇒ 尖端在面上方 53mm(要求 ≥ final_gap 2mm)。 hole_hover_z_m: 0.320 # ← 二阶段悬停高度调这一行 # FT 数据源:默认按 sn 推导 = //ft_sensor_wrench,由真机 # flexiv_robot_states_broadcaster 发布。但 **2026-10-08 实测该话题恒为 # 全零(0,0,0)** —— RDK 那侧的原始 FT 传感没在出数(`ft_sensor_raw` 为 0), # 于是应用里"abs(fz−基线)>2.5N 判接触"的兜底**永远判不到接触**(静默失效)。 # 同一时刻 //external_wrench_in_tcp 是有数的(≈负载 54N,且随受力变化), # 所以真机改用它。判接触只看"相对轻降起始基线的增量",与 54N 这个偏置无关, # 用这个源可直接恢复接触兜底,不需要改代码。 # 仿真**别跟着用**(仿真没有这个话题)—— 见本文件 sim 段的显式覆盖。 # 注意:这不是话题名写错,而是"哪个数据源有数"的取舍(真机 RDK 未启用原始 FT)。 ft_wrench_topic: "/Rizon4_063989/external_wrench_in_tcp" # ---- 工件几何(2026-09-29 现场实测标定)---- # 真机段的坐标都是**基座系**;本机基座与台面同高 ⇒ 台面 z ≈ 0。 gauge_assembly: # 工件**上表面**的世界 z [m]。悬停高度的几何判据(gauge_fsm.hover_z_guard) # 与 S07 下探预算都拿它当基准面。 # # ⚠️ 为什么不能留全局段的默认 0.0:0.0 = "未知",运行期退回"用 stage2 实测 # 孔位 z 兜底" —— 但兜底发生在**悬停之后**,悬停本身还是按 0.0 算的。 # 后果(2026-09-29 实测): # 悬停 0.247 − 工具长 0.245 = 工具尖端 z≈0.002,而工件面在 0.0074 # ⇒ 尖端在工件面**下** 5~10mm。悬停这一步就把通止规插进工件,接着 # S05 的横向对准等于拿规尖**拨工件**:两孔中点两拍之间被推走 19.1mm, # 残差 27.3 → 42.7mm 反向发散(日志看着像手眼标定坏了),最后被 # drag_verdict 看门狗拦下(exit=6)。 # # 实测依据(旧,加底座前):stage2 解出的两孔基座系 z = 0.00743 / 0.00715 m # (/tmp/stage2_result.json 的 k1_base_m / k2_base_m)。孔是通孔, # 深度相机在孔口附近读数会偏深 ⇒ 上表面取工件板厚 **0.012**。 # # 实测依据(2026-09-29 加底座后**重新感知**,现行值 = 0.022): # 不再靠"板厚"推,而是直接量上表面 —— 让 gongjian0929 在实时图上出 # k0/k1/k2,对每个点取 (±6px) 邻域深度的中位数,用同一帧 TF # (base←flange · flange←cam) 投到基座系: # k1 = +0.0220 / +0.0221 / +0.0223,k2 = +0.0223 / +0.0224(重复性 0.4mm) # k0 = +0.0165 / +0.0186(偏低 6mm 且抖 ⇒ 该点邻域读数不可靠,不参与定面) # 判"是不是孔底":各点中心 (±6px) 与外环 (12~20px) 深度差 ≤1mm ⇒ 齐平 # ⇒ 三点都在上表面。ROI 高度直方图同为单峰平台,p75≈0.0228、p95≈0.0247 # ⇒ 上表面 0.022(比加底座前的 0.012 高 10mm)✓ 与"工件加了底座变高"一致。 # 法兰 Z=0.356 时该点光学视距实测 0.204m(模型仍检出 conf 0.862)⇒ # 模型对 0.17~0.20m 有一档余量,但两侧都不要顶到边界。 # 复核(会同时打印实测工件面 + 悬停几何自检): # bash ~/ros2/scripts/run_stage2_locate.sh --arm 89 --hover-only # python3 scripts/lib/real_geometry_check.py --sn Rizon4-063989 workpiece_surface_z_m: 0.022 # 工具长(规尖在法兰下方的距离)—— **2026-10-08 真机感知标定**:0.223m。 # 方法:hover(法兰 0.320) 下腕部相机抓一帧,取规杆最深像素 + 深度,用 # 手眼 T_base = T_base_flange·T_flange_cam 投到基座 ⇒ 规尖 z=0.0971, # 故 0.320 − 0.0971 = 0.2229m。同帧反投影链的独立校验:k1/k2 基座 z # 精确复现上次实测 0.0219/0.0204(误差 <0.1mm),链路可信。 # 工具长 = tool1 夹爪 TCP 0.147 + 规露出 0.076。 # ⚠️ 旧值 0.245 是照抄仿真的(TCP 0.200 + 规 0.043),比真机长 22mm: # 它让几何误以为悬停时尖端只在面上方 53mm(实际 75mm),轻降预算因此 # 少 22mm,**永远到不了孔口**(现象就是"机械臂不下臂")。 # 落在本文件下方注释记录的合理区间 0.204~0.233 内。 # 复核:python3 scripts/sense_tool_length.py --sn Rizon4-063989 # python3 scripts/lib/real_geometry_check.py --sn Rizon4-063989 # 2026-10-16 下午:RDK Tool.params("tool10120938").tcp_location # [x,y,z,qw,qx,qy,qz] = [0.000537, -0.000264, 0.226587, 1, 0, 0, 0] # 示教器拟合误差:平均 0.58 / 最大 0.99 / 最小 0.29 mm。 # 下臂安全距离用 TCP +Z。上午 0.226274;再往前 0.2245 是 10-12;0.223 是 tool1。 tool_tip_below_flange_m: 0.226587 # >>> 仿真那台臂(Gazebo)也登记成 robots[] 里的一台 <<< # 为什么必须登记:应用是按"当前臂"的名字去查 frame / joint / 相机话题的 #(`sim_env.sh` 注释里说的"仿真与真机共用同一套 site.yaml")。仿真的 TF 与关节 # 名带 `sim_` 前缀(与真机区分,避免仿真数据冒充真机),所以它需要自己的一台。 # 登记之后,**所有阶段(stage2/stage3/装配…)都不改一行代码**就能跑仿真, # 只要 `--arm sim`(`scripts/run_gauge_assembly.sh --sim` 会自动选它)。 # # 真机上永远不会选到它:只有显式 `--arm sim` 才会,而没有任何真机脚本会这么写。 - sn: "sim" arm: sim label: "仿真台" # 仿真建模的就是左臂,触觉也只有左指(由 sim_tactile_adapter 发) tactile: true camera: # 话题 /sim_d405/... :与真机的 /d405_left 分开。为什么不用同一个名字: # 同域里两个来源会互相覆盖,"台上这台到底是谁"就说不清了。 # 名字里的 d405 与 URDF 里的模型名一致 → 光学系 sim_d405_color_optical_frame # 正好等于 `{camera.name}_color_optical_frame`,所以应用查 TF 不用特判。 name: sim_d405 # 仿真不需要手眼标定文件:腕部相机在 URDF 里就装在法兰上, # 而真机那份标定文件描述的是"相机相对法兰的实际安装偏差", # 套到仿真上会把相机搬到一个**错误**的位置(投影全错)。 # 留空 → 启动脚本不发静态 TF(见 gauge_assembly.launch.py 的提示)。 paths: # 仿真不"标定",而是把 URDF 里的真值**导出**成同格式的文件: # scripts/gen_sim_handeye.py(run_gauge_assembly.sh --sim 会自动跑)。 # 为什么必须给文件而不能留空:visual_servo 的 servo_node 需要 # T_flange_cam 矩阵做像素→笛卡尔换算;留空它就直接拒绝启动 #("手眼标定结果缺失"),S04 跟踪起不来。 handeye_file: "/home/cc/ros2/handeye/results/sim_eye_in_hand.yaml" # 运动参数:**大部分**与 89 共用(锚点),但**高度类**必须按仿真台的几何改写。 # # 为什么不能直接共用:真机是"机械臂装在台面同一高度",所以"法兰相对基座" # 就等于"法兰相对台面"(hole_hover_z_m=0.23 就是这么来的)。 # 仿真里机械臂站在**地面**、台面 0.40m 高、工件上表面 0.412m —— 一切 Z # 都比真机大 0.412。照抄真实值会让伺服把法兰送到 0.28m,也就是**撞进台面**。 # # 推导(与 scene.yaml 的 operating 段同源,scripts/check_sim_scene.py 会核对): # 相机到工件 0.17(像素门槛反推,不能改) # + 相机比法兰低 0.05(URDF 里 d405_z=0.05) # + 工件上表面 0.412 # ⇒ 二阶段悬停法兰高 = 0.632 # 一阶段跟踪高 = 悬停 + 0.05(通止规离工件)= 0.682 # 所以工作盒上界必须 ≥ 0.682 —— 真机那套上界 0.65 不够,S04 一跟踪就会被 # "超出工作盒"截断(现象是"越跟踪越偏",很难想到是配置差 3cm)。 # # ⚠️ 上面那套"按相机"的推导**只对了一半**(2026-09-24 修正):它没算**工具**。 # 悬停高度还要满足"工具尖端不碰工件": # 工件上表面 0.412 + 工具长 0.243(推算的标称值:TCP 0.200 + 规尖 0.043) # + 间隙 0.002 = **0.657** # 原来的 0.632 会让工具尖端落在工件面**下**(按 0.245 算是 −23mm)—— 悬停 # 这一步就把通止规捅进工件里,接着 S05 的横向对准等于拿规尖拨工件(实测把 # 工件推走 79mm),而真值孔位按工件**实测**位姿算 ⇒ 孔位跟着工具走、残差 # 恒定 11mm 永不收敛(日志看起来像"手眼标定坏了")。真机同参数同结果:只要 # 悬停按相机定、而工具比相机长,就会这样。 # 现在三道防线:stage2 启动时按 gauge_fsm.hover_z_guard 用几何兜住;S05 有 # "工件被推走"的看门狗(gauge_fsm.drag_verdict);S07 按几何自查下探预算。 # # ⚠️ 为什么这里能写 0.657 这种"贴着"的值:悬停高每抬高 1mm,S07 就多下 # 1mm,而 contact_max_depth_mm=35 是按"悬停只留 2mm 间隙"配的 # (30 台肩 + 5 余量)。抬到 0.67 会让下探变成 43mm,S07 就得**自动改大** # 预算并告警 —— 能跑,但配置就失去意义了。 # # 工具长 0.243 只是**标称**:仿真里上料脚本每次实测并落盘 # (devices/sim/gazebo/config/sim_tool_geometry.yaml), # run_gauge_assembly.sh --sim 用它当启动参数覆盖这里的值。真机由现场把 # 夹好规后读到的 TF 值写进全局段的 tool_tip_below_flange_m。 servo: <<: *servo_left # 仿真 FT:由 sim_wrist_ft 发布,与真机 //ft_sensor_wrench 同名同类型同单位。 # 必须显式覆盖 —— 上面 *servo_left 带下来的 ft_wrench_topic 是**真机**的 # external_wrench_in_tcp,仿真里没有这个话题(不覆盖 ⇒ 仿真 FT 收不到)。 ft_wrench_topic: "/sim/ft_sensor_wrench" ee_z_m: 0.72 # 一阶段跟踪锁高(法兰,世界系) # 不必随悬停一起抬:跟踪时工具尖端离面 ~60mm, # 只要**高于悬停**就不碰工件(0.72 > 0.657)。 hole_hover_z_m: 0.657 # 二阶段悬停高:**由工具几何定**,不是相机视距 # = 0.412(工件上表面)+ 0.243(工具)+ 0.002(间隙) safe_z_m: 0.76 # 安全抬升/复位高度(高于跟踪高) z_ref_m: 0.657 # 没有测量时的兜底 Z:绝不能是 0.22(会撞工件) z_min_m: 0.55 # 伺服硬下限:0.55 = 工件上表面 + 0.14 min_ee_z_m: 0.55 workspace_xyz_min: [0.08, -0.55, 0.55] workspace_xyz_max: [0.85, 0.55, 0.76] # 仿真不跑推理:感知结果由 sim_truth_perception 按已知位姿直接给出。 # 原因:ros_gz_sim 环境里没有 torch/ultralytics,且本机离线装不上 —— # perception_node 一启动就死,表现为"一阶段永远不锁定"。 # 详见 devices/sim/gazebo/README.md「仿真里哪些模块用真值替代」。 perception: source: "truth" # 几何类参数按台子改写(gauge_assembly 段的下沉覆盖)。 # 为什么需要:全局 gauge_assembly 里的值是**真机**的 —— 真机上机械臂 # 基座与台面同高,所以"工件上表面 z"就是板厚 0.012;仿真里机械臂站在 # **地面**、台面 0.40m 高、工件上表面 0.412。写死全局值会让 S07 的 # "悬停时工具尖端离面多高"自检算错(差 0.4m),现象是"一路下压判不到 # 接触",看起来像力阈值标歪了 —— 于是排查从阈值开始走偏。 gauge_assembly: workpiece_surface_z_m: 0.412 # 工具长这里写**推算的标称值**(TCP 在法兰下方 0.200 + 规尖在指腹面下方 # 0.043 = 0.243)。真值由上料脚本实测并作为启动参数覆盖:仿真里合爪后规会 # 在指间下滑到落座位置,下滑量随装夹变化(实测见过 243 / 254mm)。 # 写成固定值会让悬停高度算错(写小 → 规尖插进工件 ⇒ S05 拿规尖拨工件、 # 残差恒不收敛),所以**这个值在仿真里只是兜底**,实际用实测值。 tool_tip_below_flange_m: 0.243 - sn: "Rizon4-063991" arm: right ip: "192.168.2.100" pc_ip: "192.168.2.10" iface: "eth0" label: "右臂91" camera: name: d405_right # 话题 /d405_right/... 禁止和 89 的 /d405_left 混用 serial: "260522274442" # 右腕新 D405 auto_exposure: true ae_limit_us: 0 # 0=不封顶,让固件自己调亮 emitter_enabled: false laser_power: 0 gripper: model: "RGI-100-30-O-S-M1" # 旋转爪:run_gripper.sh --arm 91 sn: "" port: "/dev/ttyUSB1" stroke_mm: 30.0 auto_init: false named_poses_file: "/home/cc/ros2/src/arm_control/config/named_poses_91.yaml" paths: handeye_file: "/home/cc/ros2/handeye/results/91_eye_in_hand.yaml" handeye_waypoints: "/home/cc/ros2/handeye/poses/91_eye_in_hand.yaml" handeye_workdir: "/home/cc/ros2/handeye/tmp/91" # >>> 右臂跟踪高度:只改这里,89 仍用上面那段 <<< # 法兰 Z;RGI 比平行爪高,通常要比 89 略抬一点。改完重启: # bash ~/ros2/scripts/run_visual_servo_rt.sh --arm 91 --control servo: ee_z_m: 0.25 # 跟踪锁高:法兰相对基座 Z [m] ← 日常调这一行 min_ee_z_m: 0.20 safe_z_m: 0.45 z_min_m: 0.22 xy_track: hover # 用法兰系手眼 T:夹爪中心(法兰原点)压在工件正上方 ee_vertical_down: true # 法兰/RGI 轴线朝下;相机有 29° 倾角,绿框不必对准十字 hover_offset_ee_xy_m: [0.0, 0.0] # RGI 与法兰同轴;爪心偏了再改这两项 [m] # 二阶段孔定位悬停锁高(91 装了传感器后同样可用) hole_hover_z_m: 0.22 # FT 数据源同 89:真机 ft_sensor_wrench 恒零,改用有数的 external_wrench_in_tcp # (见 89 段 ft_wrench_topic 的完整说明)。 ft_wrench_topic: "/Rizon4_063991/external_wrench_in_tcp" # --control:先只降到 ee_z_m(XY 不动),到位后才 hover,用手眼把爪心压到工件正上方 paths: # >>> YOLO 权重(标准放法,不必改代码)<<< # 1) 把 .onnx / .pt 放到 /home/cc/ros2/models/yolo/ # 2) 这一行改成那个绝对路径(写 .onnx 即可;TensorRT 会找同名 .engine) # 3) backend=tensorrt 时必须先导出引擎(只需做一次): # bash ~/ros2/scripts/weights.sh convert <源文件> --name <名字> --group yolo # 4) yolo.imgsz 必须等于 ONNX 输入边长(当前 640) # 5) 改权重/imgsz 后重启:bash ~/ros2/scripts/run_visual_servo_rt.sh [--control] # 6) 换完确认标签没丢:bash ~/ros2/scripts/weights.sh doctor --check-engines # 当前:gongjian0929(2026-09-29 从 ~/下载 转入)。 # ⚠️ 这是个 **pose 模型**(YOLO26s-pose):一个工件框 + 3 个关键点 # k0=跟踪孔 / k1,k2=对孔。它同时替代了原来的跟踪模型和入孔模型, # 所以下面 hole_weights 也指向同一份。 # ultralytics 解不了 pose 的 TensorRT engine,运行时走 # arm_application/pose_detector.py 的自解码(见该模块头)。 # 仍然用 .engine(实测 ~2.6 ms/帧;onnx 只有 CPU provider,约 27 ms)。 yolo_weights: "/home/cc/ros2/models/yolo/gongjian0929.onnx" # 换模型:把 models/yolo/ 下别的 .onnx / .pt 绝对路径填进来即可。 # 未在用的模型已移出仓库(体积大且干扰 `weights.sh list`),要恢复见 models/README.md。 # 入孔模型(backend=yolo 时把孔当框)。two_stage 用下面的工件权重。 # pose 模型两处共用同一份 —— 它既能框工件,又直接给 k1/k2 关键点。 # 查悬空引用:bash ~/ros2/scripts/weights.sh doctor hole_weights: "/home/cc/ros2/models/yolo/gongjian0929.onnx" # 下面是缺省(未匹配到 robots[] 时)。真机以 robots[].paths 为准:89 / 91 各一份。 handeye_file: "/home/cc/ros2/handeye/results/89_eye_in_hand.yaml" handeye_waypoints: "/home/cc/ros2/handeye/poses/89_eye_in_hand.yaml" handeye_calibrate: "/home/cc/ros2/handeye/calibrate.yaml" handeye_workdir: "/home/cc/ros2/handeye/tmp/89" # 空=默认 DDS(SHM);勿强制 UDP-only,否则相机预览收不到 /workpiece/detection fastdds_profile: "" handeye: mode: "eye_in_hand" # >>> 标定板尺寸:**按模式各一份**(内角点数 + 方格边长 m)<<< # # 为什么必须按模式分开写(而不是两个模式共用一组键): # 眼在手上与眼在手外通常用**两块不同的物理板**,方格边长不一样。共用一组键 # 的后果不是报错,而是"跑另一种模式时静默用错方格边长"—— 标定照样跑完、 # 结果只是系统性偏,现场几乎联想不到板尺寸(历史反例见 handeye/README.md 第 3 节)。 # 内角点 = 方格数 − 1;列/行顺序不用纠结,检测端两种顺序都会试 #(见 handeye_lib.detect_chessboard)。 boards: eye_in_hand: # A5 板:12×9 方格 → 内角点 11×8 cols: 11 # 内角点列数 rows: 8 # 内角点行数 square_size_m: 0.015 # 方格边长 [m] eye_to_hand: # A4 板:12×9 方格 cols: 11 rows: 8 square_size_m: 0.020 # 运动更顺滑:慢速 + 到位等静止 + 零速 pin 后再拍照 velocity_scale: 0.05 move_time_sec: 8.0 settle_sec: 1.5 settle_qd_max: 0.015 # |qd|_max 低于此值才算静 settle_stable_sec: 0.6 settle_timeout_sec: 8.0 pin_holds: 2 pin_hold_sec: 0.45 skip_unstable_poses: true # 某点看不到棋盘则跳过,不整段中止 # 点位:paths.handeye_waypoints camera: name: "d405" serial: "352122274408" # 全局缺省=左腕;真机以 robots[].camera.serial 为准 # USB3 推荐 848x480@30;USB2 自动降到 640x480@15,深度/右目仍开 width: 848 height: 480 # 改宽高/帧率必须重启相机:bash ~/ros2/scripts/run_camera_d405.sh --preview fps: 30 enable_depth: true enable_infra: true # 同一 SDK 进程开左右红外;失败则自动退回彩图+深度 preview: true preview_stereo: true # 预览同时显示 彩图 | 深度 | 左目 | 右目(不另开相机、不抢 USB) overlay_hold_sec: 0.10 # 预览只留很短;长残留会看起来像绿框不跟 use_site_resolution: true auto_exposure: true ae_limit_us: 4000 # D405 腕部近距;91 在 robots[] 里更严 emitter_enabled: false # 腕部关红外投射,否则彩图发白 laser_power: 0 # 现场三台 RealSense(按序列号钉死,禁止按「第一台 D405」乱开) cameras: d405_left: model: d405 name: d405_left serial: "352122274408" role: left_wrist arm: left label: 左腕D405(89 平行夹爪) width: 848 # 相机分辨率以本段为准(全局 camera: 段为旧路径,P2 收敛) height: 480 emitter_enabled: false # 腕部必须关投射器,红外点阵会污染跟踪图像 d405_right: model: d405 name: d405_right serial: "260522274442" role: right_wrist arm: right label: 右腕D405(91 旋转夹爪) width: 848 height: 480 emitter_enabled: false # >>> 仿真那两台(arm: sim)<<< # 为什么仿真也要在这里登记:应用是"按当前臂"去认相机的 —— 腕部相机取 # robots[].camera.name,手外相机取这里 role=overhead* 且 arm 与当前臂一致的 # 那一条(见 gauge_assembly_node._d435_ns)。名字必须与 # devices/sim/gazebo/config/cameras.yaml 的 ros_ns 一模一样。 # 名字不带 d405_left/d435 那样的真机名:同域里两个来源会互相覆盖, # 而 eye-to-hand 标定文件是按相机名命名的 —— 重名会让"跑一次仿真把真机标定盖掉"。 sim_d405: model: d405 name: sim_d405 role: sim_wrist arm: sim label: 仿真腕部D405(= 左臂位置) width: 848 height: 480 emitter_enabled: false sim_d435: model: d435 name: sim_d435 role: overhead_eye_to_hand arm: sim label: 仿真手外D435(世界固定) width: 640 height: 480 fps: 30 emitter_enabled: true d435: model: d435 serial: "262522073161" role: overhead_eye_to_hand label: 双臂中间手外D435 name: d435 width: 1280 height: 720 fps: 30 auto_exposure: true ae_limit_us: 8000 emitter_enabled: true # 顶视深度可用投射器;只看彩图可改 false yolo: conf: 0.10 # 检测器门槛:略低于 min_conf,让运动模糊框也能进跟踪 iou: 0.45 imgsz: 640 # 必须等于所用 ONNX 的输入边长(当前 detect_best_0 = 640) device: "0" half_precision: true backend: "tensorrt" # 强制 .engine;没有则启动失败。导出:bash ~/ros2/scripts/export_yolo_trt.sh mp_infer: true # YOLO 独立进程,不和 ROS 抢 GIL min_conf: 0.12 # 新锁门槛;已锁定的框可略低于此 # 重新捕获门槛:**跳到新位置**(离上一帧框中心 >max_track_px)必须达到的置信度。 # 为什么必须比 min_conf 高得多 —— 2026-10-08 真机实测(一阶段 servo_node 日志): # 0659.551 死区保持 |e_xy|=3.0mm ← 已经收敛到 3mm # 0659.598 k0 置信度 0.00 < 0.30,本帧不用它 # 0659.6xx 重新捕获 conf=0.15 dpx=503 ← 503px 外、conf 0.15 的假框 # feat_base=(0.231,-0.484) z=-0.021 ← 跟踪点瞬间横移 427mm # 0670.985 === 一阶段已锁定 === ← "锁定"在离工件 500mm 的位置 # 那串假框实测 conf 0.13~0.47,而真 k0 是 0.87。旧写法把这里接成 min_conf=0.12 # ⇒ 0.15 的假框被原样采信、perception 的 kf.reset() 直接跳过去,伺服再拦也只是 # 拦一帧(假框帧间稳定,3 帧后 FeatureJumpGate 也会认)。 # 取 0.55:高于假框上沿 0.47,低于真 k0 0.87。宁可原地不动,也不追假点。 reacquire_conf: 0.55 vision_hz: 30.0 infer_max_width: 640 # 进 GPU 进程前缩到 640,减小进程间拷图,避免队列堵 box_ema_alpha: 1.00 # 绿框用预测补偿后的框,不再 EMA miss_clear_frames: 10 # ~0.33 s @30 Hz;期间绿框按像素速度滑行 overlay_hold_on_miss: true # 滑行失败时仍续发上一框 hold_point_on_miss: false # 眼在手上:丢检续发旧点会让臂追错位置 miss_hold_point_frames: 1 jump_px: 400.0 jump_z_m: 0.10 z_ref_m: 0.22 min_depth_m: 0.04 max_depth_m: 0.55 # 特征点的**相机量程下限**:比这更近的深度读数一定来自机械臂自身(爪上夹的 # 通止规 / 相机座),不是工件。**判据是实测的两条带,不是几何推导**: # 机械臂自身(随臂一起动):0.078 ~ 0.087 m ← 2026-10-08 被误锁的假目标 # 工件面: 0.19 ~ 0.29 m ← 锁高下正常跟踪时的 p_cam.z # 取 0.14:距近带 53mm、距远带 50mm,两边都不顶边界。 # 几何上为什么近带被占满:规尖在法兰下 0.223m 而相机只在法兰下 0.124m(handeye # cam_in_flange z=0.1236)⇒ 规杆整段都在相机近场;且相机光轴与法兰 Z 差 29.7°, # 所以"光轴视距"不等于"竖直距离",只能靠实测划带。 # 撞了会怎样:假目标在相机前 0.08m 且**随臂一起动**,伺服误差恒为 0 —— # "一阶段已锁定"会锁在自己的工具上(见上面 reacquire_conf 的记录)。 feature_min_range_m: 0.14 work_z_max_m: 0.50 meas_blend: 0.85 # 3D 跟手;抖则略降 z_alpha_min: 0.10 z_alpha_max: 0.40 z_alpha_gain: 1.0 camera_frame: "d405_color_optical_frame" object_frame: "tracked_object" depth_topic: "/d405/aligned_depth_to_color/image_raw" feature_mode: "keypoint" # keypoint=pose 模型的 k0(跟踪孔);plane=框内近表面立体点;box=单目射线 # feature_mode=keypoint 时用:跟踪用哪个关键点、它的置信度低于多少就不信它。 # k0 = 跟踪孔(见 pose_detector 的约定)。k0 不可用时**借 k0 相对检测框的归一化 # 偏移**在框上重建 k0 —— 位置连续、工件动时还跟着走。 # 刻意**不**回落"框中心"(那是另一个点,离 k0 有 ~89mm,真机上就是一次不该有的 # 横向快移,见 2026-10-08 一阶段 150s 没锁上的记录);只有在**从未**见过可信 k0 # 的冷启动帧才回落到框中心/框内深度。 keypoint_index: 0 keypoint_min_conf: 0.30 keypoint_win_px: 6 # k0 处取多大窗口的中位深度 # ---- 跟踪点可信性的三道守卫(实现:feature_guard.py,单一数据源)---- # 为什么需要:2026-10-08 真机 arm 89,一阶段真 k0(conf 0.87 uv=(410,246))被一串 # **高置信度假 k0** 顶掉,伺服 1s 内把臂从 home 带到工作盒角落 (0.083,-0.515), # 之后 148s 全是"检测漏检"、150s 超时。假 k0 的 conf 是 0.53~0.73(真值 0.87) # ⇒ 置信度没有可分区间;但它们**贴在画面边上**、且**借深度**: # 766.96 uv=(76,75) 768.16 uv=(143,92) 768.78 uv=(12,30) 770.07 uv=(431,226) keypoint_border_px: 6 # k0 离画面边缘 < 此值即判位置不可信(孔心不在画面边上) track_max_step_m: 0.04 # 已发布跟踪点的每帧位移上限 [m](实测真实运动 ≤15mm/帧) reacquire_far_px: 120 # 远于此像素距离的"重新捕获"要连续确认(dpx 那个门限) reacquire_confirm_frames: 3 # 远框要连续几帧指向同一处才采信 plane_inlier_m: 0.008 # 拟合平面内点阈值 [m] plane_band_m: 0.06 # 近表面深度带宽 [m] servo: control_hz: 100.0 # --- 基座坐标系下的末端约束(法兰,单位 m)--- ee_x_m: 0.25 ee_y_m: 0.00 ee_z_m: 0.35 # 全局缺省锁高;真机以 robots[].servo.ee_z_m 为准(89/91 分开调) ee_hold_xy: false # false=跟踪工件 XY;true=锁死 ee_x/ee_y xy_track: "hover" # hover=法兰悬停在目标正上方(任意锁高) lookat=画面中心对准目标 use_static_handeye_tf: false # 只用 handeye_result.yaml,避免 TF 树抢父子帧 ee_vertical_down: true # --- 启动回初始位(已停用:--control 不再调用 goto_ee_plane)--- safe_z_m: 0.45 # >>> 防撞安全高度:先纯 Z 抬到这里再回 home,避免扫到料框/壁 home_pose_name: "home" # 非夕初始点位(/set_named_pose:home | ready | inspect | pos_left) home_pose_speed: 0.30 # 回初始位的速度比例 (0,1],越小越稳 ee_hold_kp: 2.0 ee_w_max: 0.60 z_ref_m: 0.22 z_min_m: 0.22 approach_xy_gate_m: 0.040 # --control 先降到锁高,|ee_z-ee_z_m| 小于此值才开始 XY hover v_approach_z: 0.025 watchdog_timeout_sec: 1.2 lost_timeout_sec: 1.2 stale_hold_sec: 0.35 # 丢检空窗内用基座 KF 预测继续跟(自运动安全),超过才钉住 max_extrapolate_sec: 0.35 # 预测时间上限(图像延迟 + 前瞻) predict_enable: true # 基座系恒速预测,补视觉延迟 predict_sec: 0.16 # 额外前瞻 [s] predict_v_max: 0.80 # 工件速度上限 [m/s],防乱飞 predict_jump_m: 0.08 # 基座 XY 跳变大于此则视为"疑似假跳",转入确认流程 # 大跳变要连续多少帧指向同一新位置才采信(见 feature_jump_guard.py 的注释)。 # 30Hz 下 3 帧 ≈ 0.1s:真的重定位 0.1s 就被认;假点(乱跳)永远认不了。 # 为什么不能是 1:2026-10-08 真机,一阶段已收敛到 3mm 后 k0 掉信、跟踪点瞬间 # 横移 89mm,旧逻辑单帧就 reset 采信 → 伺服以 0.45m/s 去追假点 → 彻底丢失。 jump_confirm_frames: 3 # 特征点位置合理性:必须落在工作盒 XY 内(±此余量 [m]),否则判误检、当"没有目标"。 # 为什么需要它:2026-10-08 真机,模型在运动模糊下把特征点丢到基座后方 # (-0.10,-0.65)(工件其实在 (0.5,-0.12)),伺服一路追到工作盒边缘卡住。 feat_ws_margin_m: 0.05 kp_xy: 4.20 # 很跟手:v = kp × 误差,再经加速度整形;抖则降回 3.5 ki_xy: 0.00 kd_xy: 0.20 kp_z: 0.80 ki_z: 0.00 kd_z: 0.35 i_sep_pos_m: 0.008 deadzone_m: 0.003 # 3 mm;收紧提高对准精度。若抖动调回 0.006 deadzone_z_m: 0.012 lock_hold_sec: 0.7 # 旧死区驻留(混合模式不再用它进二阶段) # 混合模式必填。交回 D405 的等待不再用它,见 d435_handback_sec。 lock_still_sec: 3.0 # 厂家 MoveL 结束后,法兰连续这么久没再动,才打开 D405 框心。 d435_handback_sec: 2.0 # 框心对准并且法兰停稳这么久,才精拍一次小孔关键点、进对孔。实时点到此为止。 wrist_align_sec: 0.5 lock_still_move_m: 0.015 # 15 mm;超过就重计时,避免"晃一下刚好满 3 秒" e_xy_clip_m: 0.200 # 大误差仍按 v_max 走,靠 a_max 加速 v_max_xy: 0.45 # 法兰 XY 上限 450 mm/s v_max_z: 0.04 # 远处限速:目标离得比 slow_far_m 远时 XY 限速到 v_slow_xy(**与"目标真不真"无关** # 的一道防线)。为什么需要:2026-10-08 真机一阶段被一串高置信度假 k0(conf 0.53~0.73) # 顶掉,伺服 0.7s 内以 0.45m/s 把臂从 home (0.686,-0.113) 带到工作盒角落 # (0.083,-0.515),之后 148s 全是"检测漏检"、150s 超时。加了这道之后:真 k0(离 # 145mm)只是晚 ~1.8s 到位,假目标最多把臂带走几厘米。 slow_far_m: 0.05 # 目标离得超过此距离即限速 [m] v_slow_xy: 0.08 # 限速值 [m/s](真目标到位后仍回到 v_max_xy) min_ee_z_m: 0.20 workspace_xyz_min: [0.08, -0.55, 0.18] # 法兰工作盒;盒外只允许往回走,不再卡死 no traj workspace_xyz_max: [0.85, 0.55, 0.65] a_max: 3.00 # 加速度上限(真正作用在 hover 指令上) vel_lpf_alpha: 0.80 # 速度低通:越大越跟手(1=不滤波) dq_window_size: 1 # 1=不做滑窗平均,去掉 ~20ms 滞后 traj_stream_hz: 50.0 # 轨迹点流率:JTC open_loop+样条,吃得起 exec_horizon_sec: 0.08 # 每个轨迹点的执行窗口 max_qdot: 2.00 # 关节速度上限 [rad/s] min_cmd_delta_rad: 0.001 point_topic: "visual_servo/filtered_point" traj_topic: "rizon_arm_controller/joint_trajectory" # 大寰夹爪:官方 Modbus RTU,USB–RS485 插 IPC,不走 Studio/RDK。 # 左 89 PGEA → /dev/ttyUSB0;右 91 RGI → /dev/ttyUSB1。 # bash ~/ros2/scripts/run_gripper.sh --arm 89 --cli open|close|pos # bash ~/ros2/scripts/run_gripper.sh --arm 91 --cli open|close|rotate 90 # bash ~/ros2/scripts/run_dual_gripper.sh # ROS:/gripper/open|close(PGEA 单爪)或 /dual_gripper_driver/{left,right}/execute gripper: backend: "usb" # usb=工控机官方Modbus rdk=Studio设备(不用) vendor: "dh" model: "PGEA-100-20-W" sn: "26610799" # 铭牌,不进协议 device_name: "GripperDahuanModbus" port: "/dev/ttyUSB0" # 本机 MACROSILICON/pl2303 baud: 115200 # 官方默认 115200 8N1 slave_id: 1 # 官方默认从站 1 rs485_rts: "" # 空=不拉RTS;scan 若 OK 在 tx/tx_inv 再填 stroke_mm: 20.0 # --- 开合 / 力 / 速(大寰官方寄存器,超出会被驱动钳位)--- force_pct: 20 # 夹持力 区间 20–100 %(低于 20 会被钳到 20;PGEA-100 约 30–100 N) speed_pct: 50 # 开合速度 区间 1–100 % open_position: 1000 # 张开目标 区间 0–1000 ‰(1000=全开) close_position: 0 # 闭合目标 区间 0–1000 ‰(0=全合;留缝改 50–200) auto_init: true state_hz: 20.0 # 双爪共用参数(run_dual_gripper.sh / load_from_site) parallel: stroke_min: 0.0 stroke_max: 20.0 default_speed: 50 default_force: 20 force_min: 20 force_max: 100 auto_initialize: true rotary: auto_initialize: false angle_min: -32767.0 angle_max: 32767.0 max_step_deg: 30000.0 finger_open_per_mille: 1000 finger_close_per_mille: 0 default_speed: 50 default_torque: 50 default_finger_speed: 50 default_finger_force: 50 torque_min: 20 torque_max: 100 # RDK 回退字段(backend: rdk 才用) force_n: 50.0 vel_mps: 0.05 # 入孔检测。算法参数写在这里(覆盖包内 hole_detect.yaml 默认值)。 # bash ~/ros2/scripts/run_hole_detect.sh --arm 89 --preview hole_detect: backend: "two_stage" # two_stage | yolo | hough # backend=yolo 时用。 weights: "/home/cc/ros2/models/yolo/gongjian0929.onnx" # 二阶段工件检测权重。pose 模型既框工件、又直接给 k1/k2 关键点, # 所以和上面的跟踪权重是同一份。 workpiece_weights: "/home/cc/ros2/models/yolo/gongjian0929.onnx" # pose 关键点优先:检测器给了 k1/k2 就直接用,不用再找圆。 # 关键点不可信(低于 keypoint_min_conf / 两点几乎重合)时自动回落到 # 亮环 + Hough 那套老路径。 keypoint_enable: true keypoint_min_conf: 0.30 keypoint_min_dist_px: 15.0 keypoint_hole_radius_px: 12.0 # 关键点没有半径;反投影时按这个半径取孔周深度环 config: "/home/cc/ros2/src/arm_application/config/hole_detect.yaml" conf: 0.25 iou: 0.7 imgsz: 640 center_gate_enable: true center_max_offset_ratio_x: 0.15 center_max_offset_ratio_y: 0.18 center_gate_draw: true refine_hole_centers: true bbox_padding: 80 gaussian_ksize: 5 gaussian_sigma: 1.5 hough_dp: 1.2 hough_min_dist: 50 hough_param1: 100 hough_param2: 26 hough_min_radius: 6 # 悬停较高时螺纹孔半径只有几像素 hough_max_radius: 30 min_radius: 6 max_radius: 30 hole_distance_min: 90 # 悬停 0.23m 两螺纹孔约 170px;过小会配到中心孔 hole_distance_max: 700 expected_holes: 2 min_hole_sep_px: 20 max_hole_sep_px: 2000 stabilize_hold_frames: 15 stabilize_ema_alpha: 0.35 stabilize_max_jump: 60 default_depth_m: 0.10 camera_intrinsics: fx: 425.0 fy: 425.0 cx: 640.0 cy: 360.0 # ===== 二阶段一次性拍照定位(stage2_locate_node,不做实时推理)===== # bash ~/ros2/scripts/run_stage2_locate.sh --arm 89 # 完整流程 # bash ~/ros2/scripts/run_stage2_locate.sh --arm 89 --hover-only # 只悬停调高度 stage2: # 对准目标孔。pose 模型下 k1/k2 是**模型学出来的固定语义**(不是图像左右): # hole_points[0] = 模型的 k1,hole_points[1] = 模型的 k2 # 下游是 `k1_b = pts[0] if k1_label=="left" else pts[1]`,所以: # k1_label: "left" → target_hole=k1 打的是**模型的 k1**(当前设置,推荐) # k1_label: "right" → target_hole=k1 打的是模型的 k2(等价于老 Hough 的"图像左孔") # 实测这台臂的视角下模型给的是 k1 在画面右、k2 在画面左;对孔顺序要反过来就改这里。 target_hole: "k1" k1_label: "left" # ---- 对准判据:工具**用视觉量到的规尖**,不是法兰轴线(2026-10-08 真机定稿)---- # 实测(同一帧、同一条手眼链,n=8): # · 规杆轴线相对法兰轴线 = (+10.7, −0.1)mm(σ≈0.5mm);最深像素法 (+8.8,−1.8)mm # · 而 site 原来把 hover_offset_ee_xy_m 写成 [0,0]("平行爪同轴"),于是精对准 # 把**法兰**对到孔上,规尖实际离孔心 ~10mm ⇒ 轻降时 φ6 规尖骑在 φ7 孔沿上, # 腕力从 −1N 直接蹦到 −31N→−63N 卡死(现象是"轻降在离面 4mm 停住")。 # · 孔检测本身的重复性极好:k1 基座坐标 σ=(0.17,0.04)mm —— 精度不是瓶颈。 # 所以:在同一帧里同时解「规尖基座 XY」和「孔基座 XY」,两者一起走手眼链, # 链路误差与装夹变动一起抵消。规尖量取规则见 _tip_offset_from_depth(四条判据)。 align_tol_m: 0.00025 # 对准到位判据 [m]。检测重复性 0.04mm 撑得住 0.25mm。 # ⚠️ 不要回到 0.7mm:那会让"规偏 10mm"这种系统偏差 # 在容差里被放过去(原来残差 0.69mm 看着"通过",其实 # 是拿法兰对的孔)。收到 0.25mm 才逼它必须真的对上规尖。 align_max_iters: 12 # 拍照→修正→复拍 迭代上限(收紧容差后需要更多拍) align_tip_vision: true # true=精对准用「视觉规尖」;false=退回旧的「法兰+偏置」 tip_near_m: 0.15 # 规尖量取:无工件面深度时的近端上限 [m]; # 精对准同一帧有 z_wp 时改用「工件面 − 15mm」 # (3mm 挡不住噪声,会把工件面当成规尖) tip_min_m: 0.04 # 有效深度下限 [m] tip_max_m: 0.60 # 有效深度上限 [m] tip_search_band_frac: 0.25 # 中央竖带半宽 = 该比例 × 图宽(规夹在爪里,不会靠边) tip_min_px: 150 # 近端连通域最小像素数(少了就是没看到规) tip_calib_n: 4 # 定偏移需要的一致拍数(够了就冻结) tip_offset_tol_m: 0.003 # 样本相对中位偏移的最大允许偏差 [m](剔偶发外点 + 冻结判据) tip_len_min_m: 0.15 # 反投影出的「法兰z − 规尖z」必须落在此区间 —— tip_len_max_m: 0.32 # 这条同时挡住"深度读到台面/工件"(主干守卫) coarse_max_moves: 2 # 工件粗居中(bbox 中心)移动上限 # —— 粗居中的两条误检守卫(单源:代码读这两个键,见 _coarse_target_reject)—— # 为什么需要:2026-10-08 真机 arm 89,粗居中拿被画面左边缘切掉的半框 # bbox=(0,142,97,222) 反投影出盒外的 (0.280,-0.747)(偏差 211.5mm),旧逻辑 # **不判误检、只把目标截断到盒边**,臂照样横移 56mm 离开工件 ⇒ 精定位再也 # 看不到 k1/k2 ⇒ exit=4。判据改成"盒外/偏移过大 ⇒ 判误检、原地不动"。 coarse_max_offset_m: 0.08 # 目标离夹爪轴线超过此值就不像粗居中量 [m];0.12 放行了 2026-10-16 的 110mm 回撤 coarse_border_px: 2 # bbox 贴边(被画面切)即弃用 [px];见 _bbox_on_border # —— 柔顺运动(夹爪持规,防急加速/急停/过冲把规震脱):速度放慢 + 平滑刹车 —— align_speed: 0.014 # 对准段笛卡尔速度 [m/s](原0.03,放缓防冲) move_speed: 0.022 # 升降/粗居中段速度 [m/s](原0.05,放缓防震规) # 对小孔关键点:只用像素射线交到 workpiece_surface_z_m,不读深度图。 hole_visual_only: true # 本分支实验(exp/hole-keypoint-once):true=悬停拍一次,框心→k1 一次走完 # (40mm 上限)。main 保存点是 false(多走多拍)。 hole_keypoint_once: true # true=悬停走完后一条厂家 MoveL 贴面(不补拍)。对照 37b85aa 改回 false。 hole_once_lock_xy_descend: true # 贴面相对悬停:沿 -look 走 k×剩余下降 [m/m]。0=笛卡尔锁 XY 只降 Z。 # 0.018 在框心改平面交点后两条链路都 +1.3mm X 过补,插不进。 once_ff_look_per_m: 0.0 final_gap_mm: 2.0 # 不进孔时的贴面目标:规尖离面 [mm]。笛卡尔锁 XY # 时法兰 Z = 工具长 + 工件面 + 间隙。CLI --final-gap 覆盖。 hole_entry_mm: 1.0 # 规尖停在工件上表面以下 [mm]。2026-10-19 12:20 左臂: # 4mm 指令在面下 1.6mm 被挡住(厂家 302064 到不了位), # 流水线接着空等 90s。1.0mm 留在挡住点之上,仍进孔口。 # 0 = 停在 final_gap(面上)。上限 8mm(板厚 12mm)。 observe_gap_mm: 15.0 # --no-descent 时的观测下降:只降到离面这么高 [mm], # 方便看规尖偏没偏。必须 ≥ final_gap_mm。 # 左臂89:悬停间隙约 71.4mm(0.320−0.226587−0.022)⇒ 观测下降约 56.4mm。 final_descent_speed: 0.005 # 最后轻降速度 [m/s](很慢,柔顺) # ✅ 2026-10-08 定稿:下降量**只由几何决定**(法兰 FK − 工具长 − 工件面 − final_gap), # 深度读数只做交叉核对、不再参与任何决策。两段真机事故方向相反、都指向这条: # · 2026-09-29(多降→撞工件):环形深度中值报 gap0=141.0mm,几何真值只有 53mm # (probe 0.245 版),多降 ~86mm 把通止规撞进孔,工件被 +X 推走 5mm。 # ⇒ 读数往**大**错一次就撞。 # · 2026-10-08(少降→到不了 2mm):同一个读数悬停时报 144.0mm(几何真值 75mm), # 只降 24mm 后又翻成 −7.0mm,触发 SAFE_FLOOR 把轻降停在离面 ~50mm 处, # 最终日志却打印"悬在孔面上方 ~2.0mm"(其实停在 ~18mm)。 # ⇒ 读数往**小**错一次就永远到不了目标。 # 同一套读数两个方向各错一次、幅度 150mm 量级、随位姿乱跳 —— 它对"能降多少" # 和"该不该停"都没有发言权。现在它只在偏差 >10mm 时喊一句"深度测距与几何不符"。 # 现行几何:悬停 0.320 − 工具长 0.226587 − 工件面 0.022 = 71.4mm 间隙 ⇒ 降 69.4mm 到 2mm。 # 三道**物理/几何**保险:descent_budget_m(配置上限)+ 腕部 FT 接触检测(停+退 1mm) # + 任一步 _goto 走不到位就停。 # 缺几何(工具长/工件面没配)时**拒绝下降**并报错(保持悬停),绝不拿测距去赌。 final_descent_enable: true # true=对准后降到 final_gap_mm;false / --no-descent=只降到 observe_gap_mm verticalize_tol_deg: 0.8 # 垂直朝下判据 [deg] settle_sec: 0.45 # 到位后静止多久再拍照 snap_frames: 5 # 每次精拍取中值的帧数 min_depth_m: 0.06 # 有效深度范围(防孔底/桌面拉偏 XY) max_depth_m: 0.60 linger_sec: 8.0 # 完成后继续发布结果/叠图的时长 # ===== 三阶段柔顺搜孔(stage3_search_node,Orisys 触觉 + 力闭环螺旋)===== # 前置:二阶段已完成,臂悬停在目标孔上方。先 run_tactile.sh --cli 看力读数量级再调阈值。 # bash ~/ros2/scripts/run_stage3_search.sh --arm 89 # 搜孔+落座 # bash ~/ros2/scripts/run_stage3_search.sh --arm 89 --depth 0 # 只搜孔定心不插入 # bash ~/ros2/scripts/run_stage3_search.sh --arm 89 --depth 6 --pitch 1.0 # 螺纹规旋入 stage3: # —— 力阈值全是「相对静止均值的增量」(Orisys 读数是形变任意单位且基线会漂移, # 节点启动时自动测 1.2s 静止均值 μ0/方差 σ0,看日志「触觉基线/派生阈值」两行)—— touch_delta: 250.0 # 触面判定:fnormal > μ0 + max(4σ0, 本值) press_delta: 200.0 # 轻触目标按压量(触面阈值之上再加这么多) max_press_delta: 900.0 # 螺旋中允许的最大按压(超过→力闭环抬起) safe_press_delta: 2000.0 # 安全上限:fnormal > μ0+本值 立即抬升中止 shear_delta: 600.0 # 切向拖拽保护:shear > 静止切向均值+本值 → 贴面时微抬 0.15mm(Z 钉在触面) insert_press_delta: 1200.0 # 插入力上限(触面阈值之上;超过回退微抖重试) kp_z_mm_per_unit: 0.001 # Z 力闭环增益:每单位读数偏差调整的毫米数 approach_step_mm: 0.8 # 触面前每步下降量(驱动死区已修,0.5mm 微步可跟踪;小步=触面冲击小) enter_probe_mm: 2.0 # 触面深于此值→疑似直接入孔,四向微探验证后跳过螺旋 mouth_lift_mm: 2.0 # 直接入孔后抬到孔口倒角段再定心(深插横探会别死孔壁) baseline_min: 8.0 # 触觉基线健康窗下限(<此值=几乎全 0,tare/断流) baseline_max: 1500.0 # 上限(>此值=近饱和,接触余量不足)——出窗即中止并提示重启触觉 approach_ft_touch_n: 6.0 # approach 腕部 |F| 旁路触面判据(标定牛顿;自由空气≈2-3N) approach_max_mm: 60.0 # 从当前位姿最多下降这么多(找不到面就中止;悬停被抬高后仍有余量) spiral_pitch_mm: 1.0 # 螺旋螺距(< 通止规直径,保证重叠覆盖) spiral_step_mm: 0.5 # 沿螺旋的弧长步长 spiral_r_max_mm: 4.0 # 最大搜索半径(覆盖二阶段残差 1.5mm + 余量) spiral_expand_max: 1 # 找不到孔时扩大半径的次数(残差≤1.8mm 时代码不再扩大) spiral_expand_step_mm: 2.0 drop_ratio: 0.45 # 入孔判据:fnormal 相对接触均值跌落比例 drop_frames: 2 # 跌落需持续的帧数(防毛刺) center_scan: false # 入孔后十字微扫;默认关(0.8mm 扫会把已入孔的规刮偏) center_scan_mm: 0.3 insert_depth_mm: 0.0 # 默认只搜孔+轻压,不拧入;拧入阶段再用 --depth insert_retry: 3 screw_pitch_mm: 0.0 # 螺纹通止规螺距:>0 右旋 / <0 左旋 / 0=不旋(光面规) search_speed: 0.03 # 搜孔笛卡尔速度 [m/s] settle_sec: 0.25 # 每个微步后的静止时间 tactile_timeout_s: 3.0 # 触觉话题断流多久中止 ft_watchdog: true # 订阅 //flexiv_robot_states 做腕部力看门狗 ft_safe_n: 35.0 # 腕部 |F| 安全上限(真牛顿,标定过的;连续3拍超限才中止) linger_sec: 8.0 # 完成后继续发布结果的时长 # Orisys 视触觉(夹爪左右指摄像机,不是腕部 D405)。SDK 单独 pip 安装。 # bash ~/ros2/scripts/run_tactile.sh --cli # bash ~/ros2/scripts/run_tactile.sh # ============================================================================= # 感知来源:伺服用的"视觉测量"由谁产生 # # yolo 本体 perception_node(YOLO/ONNX,需要 torch/ultralytics)—— 真机 # truth 仿真真值:**整条视觉链都由场景几何算出**,不跑任何推理 # · 一阶段目标点:arm_sim_gazebo 的 sim_truth_perception # · 二阶段孔位 :arm_sim_gazebo 的 truth_holes(投影 *_ring) # external 由外部节点按**同一话题**(visual_servo/filtered_point)提供, # 本框架不起感知进程(二阶段仍走检测器) # # ⚠️ 别和 yolo.backend 混了:那个是"**怎么**推理"(tensorrt/onnx), # 这个是"**谁**给出测量"。 # # 为什么需要这个开关:仿真环境(ros_gz_sim conda)没有 torch/ultralytics, # 离线也装不上,perception_node 一起就死;但仿真里工件位姿本来就是已知的, # 没必要也没法做推理。切到 truth 后,流程能在仿真里验"控制与接口"那一半, # 而**感知那一半留给真机**——这条边界写在 devices/sim/gazebo/README.md 的 # 「仿真里哪些模块用真值替代」一节。 # ============================================================================= perception: source: "yolo" # 一阶段跟踪用哪台相机。wrist=腕部 D405(主跟踪)+ 顶视 D435 一次性兜底; # overhead=整段只用 D435(实验分支才切)。非法值启动即报错,不会静默退回。 track_camera: "wrist" # 画面内最高检测 conf 低于此 → 马上拍一张 D435,把工件 / k0 定到基座系。 # 与 perception / servo 共用这一处,不许两边各写一个 0.5。 wrist_handoff_conf: 0.5 # 顶视深度量程 + 一次性参数。腕部那组 0.04–0.55m 会把 D435 的点全丢掉。 # 缺键启动即红,不许借腕部量程、不许用代码默认冷却时间。 overhead: min_depth_m: 0.25 max_depth_m: 1.80 feature_min_range_m: 0.30 work_z_max_m: 1.50 z_ref_m: 0.70 feature_mode: "plane" # 顶视与腕部视角差太大,pose 的 k0 经常看不见;优先 k0,否则框心 hold_point_on_miss: true # 仅 overhead 整段跟踪时用;混合模式一次性不续发 oneshot_conf: 0.5 # D435 这一张也必须 conf≥此值,不许拿噪声框当 K0 cooldown_s: 1.5 # 两次一次性之间的最短间隔,避免跟 D405 抢同一条 YOLO 队列 frame_max_age_s: 0.4 # 彩图/深度比这更旧就拒绝,不许拿过期图反投影 oneshot_hold_s: 8.0 # 伺服保持这一次设定的最长时间;超时丢掉,等下一张或 D405 truth: # 真值目标物体。留空 = 场景文件里的 operating.part_object(推荐留空: # "哪个是工件"是场景的知识,换场景不该改这里)。 object: "" # 目标点相对物体**顶面中心**的偏移 [m],在物体局部系里(会跟着 yaw 转)。 # 例:把目标从板中心挪到 k1 孔 → [0.0, -0.029, 0.0] offset_m: [0.0, 0.0, 0.0] # ============================================================================= # 通止规装配(看板 S00–S11):bash ~/ros2/scripts/run_gauge_assembly.sh --arm 89 # # 这一段全是**会动的量**,也是现场最常改的地方。改完不必改代码: # 解析在 arm_application/gauge_config.py(键名、单位、类型都在一张表里校验), # 拼错键名会直接报错并给出最相近的候选名,不会静默退回默认值。 # # 单位体现在键名后缀上(_deg/_mm/_m/_s/_pct/_delta),三张表逐条校验; # 带 _delta 的是**力增量**(Orisys 读数是形变任意单位且基线会漂移, # 绝对值没有意义),必须现场标定: # bash ~/ros2/scripts/run_tactile.sh --cli # 看读数量级 # 启动时节点会打印本段**实际生效**的值(含派生结论),先核对再上电。 # ============================================================================= gauge_assembly: # ---- 总开关 ---- holes_total: 2 # 一次任务装几个孔(k1/k2);--holes N 可临时覆盖 dry_run: false # true = 只验编排与自检,不发任何运动指令 # E1(掉件)重放时等人工确认 /gauge/reload_ok 的最长时间 [s]。超时=中止。 # 不是"再等等":臂停在 home,让人来处理比继续跑安全。 reload_timeout_s: 180.0 # S00 自检每项(相机/触觉/夹爪状态)等"第一帧真数据"的最长秒数。 # 只看话题名会假阳性(发布者不在也算在),所以必须等真数据。 boot_timeout_s: 30.0 # ---- S01 回 home 后按工具系逆时针预绕到极限角 ---- # 预绕是为了给 S08 的棘轮留出回转行程:不预绕,拧两圈就贴关节限位。 extreme_angle_deg: 150.0 # 想转多少度(按工具系逆时针) extreme_speed_deg_s: 10.0 # 关节角速度 [deg/s]。2026-10-19 左臂 A7 咬合从 20 降到 10, # 限位到限位约 34s(原先约 17s)。顺时针进孔用这个速度。 unwind_open_permille: 400 # 第三步松爪只开到这个千分比。0=合,1000=全开。 # 2026-10-19 从 200 改成 400,只张开这个量。 unwind_lift_mm: 3.0 # 第三步张开后只抬的 TCP Z [mm](0.3cm)。 # 2026-10-19 指令 5mm、ZFine 后实走 +4.86mm,下降 -4.70mm,差 0.16mm。 # 空回到限位后,下降量用这次实测的抬高,不另写一个数。 # 超过 5mm 拒绝。 extreme_direction: -1 # +1=逆时针 / -1=顺时针(2026-10-19 真机 A7 转反了,关节角增大是顺时针) extreme_margin_deg: 3.0 # 距关节限位留的余量 [deg];撞限位不算失败(按 JOINT_LIMIT 继续) # 但余量太小会让规划器频繁拒收,表现为"偶尔转不到位" # ---- S02 夹持确认(预留,默认跳过)---- # 现在的触觉判据与 D435 手眼都还没就绪,先留接口:开成 true 时, # 夹爪状态字必须明确报"夹住了",否则 S02 判失败并中止。 grip_check: false grip_hold_s: 0.5 # 合爪后等多久再读状态 [s] # ---- S03 手外粗定位(S04 跟踪的兜底:跟踪丢了才来)---- # 前提是手外相机做过 eye-to-hand 标定:handeye/results/<相机>_eye_to_hand.yaml。 # 没有那个文件时 S03 会明确报"缺哪个文件、怎么生成"并回 S04,不做假装成功的反投影。 coarse_enabled: true # 手外相机名:**以 cameras 段里 role=overhead* 且 arm 与本臂一致的那条为准** #(就是 cameras 注册表,见 gauge_assembly_node._d435_ns)。这一项只在 # "注册表里没有手外相机"时当兜底;两者不一致会在 S00 打告警并以注册表为准。 # 所以真机/仿真各跑各的时候**不用**改这里 —— 曾经写死 d435 让仿真去查真机的相机。 coarse_camera: "d435" coarse_hover_m: 0.25 # 粗定位后悬停在工件上方多高 [m](可调) coarse_conf: 0.35 # 工件检测置信度阈值(0~1) coarse_timeout_s: 20.0 # 等手外相机出图的最长秒数(相机刚起来要一两秒) # ---- S05 精拍定位:等画面稳了再拍 ---- # 判据是"所有采样点到均值的距离 ≤ 容差",不是"相邻两帧差 ≤ 容差": # 跟踪抖动是零均值的,相邻帧之差会在画面明明在晃时判成稳定,拍出糊图。 snap_stable_s: 1.0 # 连续观察多久 [s] snap_tol_px: 2.0 # 允许的抖动半径 [px](越小越严,越难满) snap_tol_mm: 0.4 # 同上,但特征=法兰位移时的容差 [mm] snap_min_frames: 6 # 至少采到多少帧才敢说"稳定" # 用哪个特征判"画面稳定了": # auto = 按 perception.source 自动选(真机 yolo→bbox;仿真真值/外部→motion) # bbox = 工件检测框中心(像素)。需要检测器真的出框:仿真里合成图过不了 # 真机训练出来的模型(实测最高分 0.017),选它会让 S05 空转到超时 # motion = 法兰位移(mm)。相机装在法兰上,法兰不动画面就不动 —— 不依赖模型 snap_stable_feature: "auto" # ---- S06 正悬停(悬停高度不在这里:见 servo.hole_hover_z_m)---- align_tol_mm: 1.0 # 对位残差容差 [mm];超了回 S05 重拍(不是硬往下走) # 孔口径 mm 级时别放大这个值:放大=直接蹭孔壁 # ---- S07 柔顺接触:步进下压,切向力突变即停 ---- contact_speed_m_s: 0.02 # 每步下压速度 [m/s];这一步必须慢,快了就是一次冲击 contact_step_mm: 0.5 # 每步下压量 [mm](越大越快,越小越不会过冲) contact_max_depth_mm: 35.0 # 最多下探多少 [mm];到了还没接触判失败(不会一直压) # 为什么是 35 而不是 25:判"到底了"的是**台肩落到工件 # 上表面**那一下,而台肩在规尖上方 30mm(通端长 30, # 见 models/gauge/model.sdf)。工件板厚 12mm,所以要 # 下压 30mm 台肩才落面 —— 25mm 的上限根本到不了, # 只会报"下探 25mm 都没触发接触判据"然后 ABORT。 # 35 = 30(台肩落面)+ 5(对位残差/工件厚度公差余量)。 # 注意:这个值**不必**手工算 —— S07 启动时会按下面两个 # 工具几何量算出"台肩落面还需要多少",不够就地改大并告警。 # ---- 工具几何(S07 的"下去多少才到底"完全由这两个值决定)---- # 为什么必须显式配:流程里的"悬停高度"是按**相机**定的(要看清孔得离面 ~0.17m), # 不是按**工具**定的;而工具装在法兰下面、可能比相机长得多。两者一冲突就会出现: # 悬停法兰 0.632 − 工具长 0.243 = 尖端 0.389,工件上表面 0.412 # → **悬停这一刻工具已经插进工件 23mm** # 于是 S07 一路压到顶都等不到"台肩落面"那一下,报"没触发接触判据", # 看起来像力阈值标错了 —— 实际是几何错。真机同参数同结果。 # S07 启动时会把这个差值算出来印在日志里(负值会明确标注"已经插进工件了")。 tool_tip_below_flange_m: 0.245 # 工具尖端(规尖)在法兰下方的距离 [m] # **这是实测值,不是推算值**:夹住规之后直接读 # TF 的 flange z − 规尖 z。 # 仿真里 `scripts/sim_load_gauge.py` 每次上料后 # 会自己把它算出来打印("实测工具几何"那一段), # 现场照着填即可 —— 真机同法(夹住规后直接读 TF)。 # # 推算值(TCP 在法兰下方 0.200 + 规尖在指腹面下方 # 0.043 = 0.243)与实测 0.245 只差 2mm,说明推算 # 思路是对的;但**别再用推算**,因为下面这个理由: # # ⚠️ 这个数天生有 ±几毫米的不确定度:规是**靠摩擦** # 夹住的,每次上料在爪里的位置都会差几毫米。 # 所以它只用来**诊断**(悬停时尖端离面多高、下探 # 预算够不够),**不能**当硬约束 —— S07 的自检 # 因此是"告警 + 自动放大预算",不是"直接失败"。 # 要更准只能改夹持方式(定位台肩/加挡块),那是 # 机械方案,不是改这个数。 workpiece_surface_z_m: 0.0 # 工件**上表面**的世界 z [m](对孔基准面) # 0 = 未知 → 运行期用 stage2 实测的孔位 z 兜底 #(孔就在上表面上)。仿真里实测是 0.412。 # 只在"stage2 还没跑就想知道几何对不对"时才需要手填。 contact_tangential_delta: 300.0 # 触觉切向力突变阈值 [au,任意单位] —— **兜底判据** # ⚠️ 这是"触觉"的单位,不是牛顿:真机 Orisys 输出的 # 是形变任意单位(厂商不给牛顿换算),仿真侧由 # force_scale 定义尺度。所以这个数**不能**与下面 # contact_axial_delta_n 比大小(两者量纲不同)。 # # 它为什么只能是兜底:通止规靠**夹持摩擦**抱住,轴向力 # 传到指垫必须过摩擦面,上限 = µ·N ≈ 0.25×46N ≈ 23N # (两指合计)。而台肩落面一步(0.5mm × 5e4 N/m)就 # 产生 25N —— 必然打滑,所以触觉**物理上读不到**那一下 # 的力,读数停在 0(而它是对的)。拿它当主判据,阈值调 # 多大都不对:调小被噪声骗,调大永远达不到。 # 2026-09-24 就是照"阈值调高了"改的,白费一轮。 # 详见 gauge_fsm.wrench_touch 与 docs/GAUGE_ASSEMBLY.md §6。 contact_axial_delta_n: 8.0 # 腕部 FT 轴向力突变阈值 [N] —— **首选判据** # 腕部 FT 在夹持摩擦的**上游**(工件→规→夹爪→法兰 # →腕部关节),工具在爪里打不打滑都测得准,单位是真牛顿。 # 为什么只要 8N:这是"碰到了没有"、不是"压到位了没有"。 # 台肩一落面,接触刚度 5e4 N/m 意味着过压 0.1mm 就有 # 5N、0.2mm 有 10N —— 8N 对应约 0.16mm 过压,**在打滑 # 之前**就判到了(打滑上限 ≈23N ≈ 0.46mm),留 3 倍余量。 # 现场标定:空载慢慢压到台肩落面,看日志里每 5 步打印的 # 「腕部轴向」涨到多少,取它的 1/3(要能区分"擦到孔壁" # 与"台肩落面")。 # 数据源话题:servo.ft_wrench_topic(默认 # //ft_sensor_wrench,真机与仿真同名同类型同单位)。 contact_relief_mm: 0.5 # 判到接触后的**卸载回退** [mm] —— 别调成 0 # 为什么必须有这一步(2026-09-24 一整轮排查换来的): # 位置控制的臂撞上硬面时不会"刚碰到就停",它会顶到 # 自己的**出力上限**才停住。仿真实测:0.5mm 的一步压在 # 工件上,腕部 FT 直接读到 **+244N**,而臂这一步只走了 # 0.09mm。也就是说"判到接触"的那一刻,臂上顶着的力 # 与 contact_axial_delta_n **没有关系**(阈值只决定 # 什么时候承认接触)。 # 而规是靠**夹持摩擦**抱住的:轴向传力上限 µ·N,两指 # 合计 ≈73N(µ=0.8、握力 46N)。244N 远超它 ⇒ 规被顶出 # 爪外。本次现象:S07 判到接触 → S08 一转 → 规掉地上 # → 报 E1,而 E1 的日志只说"通止规掉了",看不到真因。 # 0.5mm 怎么来的:判到接触时真实过压量通常只有 0.0x mm # (5e4 N/m 时 10N ≈ 0.2mm),回退 0.5mm 足够把力放 # 干净,而规尖仍在孔里(通端穿板 18mm),不会脱出。 # 放不干净会在日志里明确告警(残余 > contact_hold_n)。 contact_hold_n: 5.0 # 卸载后允许的残余轴向力 [N] # 超了不失败,但会**大声报**:它说明"夹持摩擦力不够 # 承受这次接触",接着 S08 一转就会掉件。 # 真修法只有两条:① 降峰值(调小 contact_step_mm 或 # 加大 contact_relief_mm);② 加握力(改夹爪合拢力度/ # 指垫摩擦)。**不要**去调 contact_axial_delta_n。 contact_safe_delta: 2000.0 # 法向安全上限 [任意单位]:超了立刻抬起并中止(E2) # 必须 > contact_tangential_delta,否则自检就报矛盾 # ---- S08 棘轮拧入(慢转 → 松爪 → 快回转 → 夹紧,循环)---- # 停止判据:切向力 > screw_shear_delta **且** 法向力突增 > screw_normal_delta。 screw_step_deg: 120.0 # 每次慢转多少度。 # **硬约束**:2×step + 2×余量(8°) ≤ 关节7 行程。 # Rizon4/10 的关节7 是 ±175° → 行程 350° → step ≤ 167°。 # 填 180° 会**必然**贴限位(慢转+回转=360°>350°), # 测试 test_ratchet_step_of_180_never_fits_the_rizon_joint7_range # 与 S08 开头的行程核算都会拦;120° 留足余量吸收 # "回转每轮比指令少走 4~5°"的亏欠。 screw_speed_deg_s: 30.0 # 慢转速度 [deg/s] screw_back_speed_deg_s: 120.0 # 松爪后的空转回转速度 [deg/s](爪里没东西,可以快) screw_max_cycles: 12 # 最多几轮(12×120°=1440° = **4 整圈**,够 M6 螺纹规走完)。 # 必须满足 screw_max_cycles × screw_step_deg 是 360° 的整数倍: # 否则规停在螺纹中间,S09 拧出也少转几分之一圈。 screw_normal_delta: 400.0 # 拧到位时的法向力突增 [任意单位](螺纹到底/顶住) screw_shear_delta: 600.0 # 拧到位的切向力增量 [任意单位] screw_grip_force_pct: 50.0 # 夹紧力百分比 [%](**只有契约后端生效**:仿真/新驱动) screw_grip_speed_pct: 50.0 # 夹紧速度百分比 [%](同上;真机 dh_pgea 驱动不支持这两个参数) screw_release_wait_s: 0.3 # 松爪后等多久再回转 [s](等爪真的张开,否则会带着规磨) # ---- S09 反向拧出 ---- unscrew_extra_cycles: 1 # 比拧入多转几轮,确保螺纹完全脱开 # ---- 力判据怎么调(现场最常问)---- # 1) 先看量级:bash ~/ros2/scripts/run_tactile.sh --cli,手压一下看读数变化; # 2) S07 的接触判据是"相对基线的增量",基线在 S06 悬停时(还没碰工件)测; # 3) 太灵敏(刚靠近就停)→ 调大 contact_tangential_delta; # 不灵敏(压很深才停)→ 调小;同时看启动日志里的基线/实测值再定。 tactile: backend: "orisys" # orisys | mock # 传感器型号。必须与 devices/sensors/orisys/models/ 下的子目录名一致。 model: "dd03" # 型号配置随 SDK 一起放:devices/sensors/orisys/models/<型号>/config.json config_file: "/home/cc/ros2/devices/sensors/orisys/models/dd03/config.json" # 用 by-path 稳定路径(插哪个 USB 口就认哪个口,不受 video 编号漂移影响)。 # 当前:3.4.1=左指 3.4.2=右指;若左右反了,互换两行即可。 left_video: "/dev/v4l/by-path/pci-0000:00:14.0-usb-0:3.4.1:1.0-video-index0" right_video: "/dev/v4l/by-path/pci-0000:00:14.0-usb-0:3.4.2:1.0-video-index0" cuda: false # 两路进程抢 GPU 上下文会互相拖慢;CPU 解算仅 ~9ms/帧,足够 isstitch: true infer_backend: "cpu" # cupy | opencl | cpu;双路并行用 cpu 最稳 baseline_seconds: 2.0 filter_alpha: 0.25 motion_threshold: 0.0 target_fps: 30.0 publish_hz: 20.0 # 视触觉图像与力矢量场的发布频率。0 = 关闭(默认)。 # 三力走 30fps 的轻量队列;图像每帧几百 KB,单独走一条 maxsize=1 的队列, # 打开它不会影响三力的时序。需要看形变图/矢量场时设成 5~10 即可。 image_hz: 0.0 rotation_config: "" # 左右共用;有分指文件则用下面两项 left_rotation: "" # 例:orisys SDK 的 rotation_config_0.json right_rotation: "" go_home: pose: "home" speed: 0.25 network: # 2026-09-29 改线:两台臂 → 交换机 → USB-C 千兆网卡(ASIX AX88179) # MAC 6c:1f:f7:d0:6f:17。内核现在叫 eth0(不再用 enx6c1ff7d06f17)。 # 本机 192.168.2.10 ↔ 063989(左89)=.101 / 063991(右91)=.100 # NM 配置名 robot-usb-lan(静态 .10/24,不当默认路由)。 # 说明:enp4s0 仍是办公网(192.168.1.x);两台臂已不在 enp4s0 上。 prefer_iface: "eth0" # 仿真平台(当前 Gazebo 尚未接线,详见 devices/sim/gazebo/README.md) # 设计约束:仿真与真机共用同一套参数(servo.* / hole_detect.stage2.*), # 只有物理引擎自身的参数放在这里,避免「仿真调好的值搬到真机不生效」。 sim: # 这一段由 arm_sim_gazebo.sim_site 真正读取(此前是"声明了但没人读", # 改了毫无反应也不报错)。三个键的接线状况**不一样**,务必看清: # # world ✔ 已接线。优先级:命令行 --world > 这里 > 内置默认。 # 相对路径按**仓库根**解析。 # physics_enable ✘ 未接线。世界文件里的重力/步长是写死的。若这里写的值 # 与世界的实际状态不符,启动时会打印告警(不再静默)。 # joint_prefix ✘ 未接线。前缀由 urdf/arm.sim.urdf.xacro 的 arm_prefix # 决定,且 config/controllers.yaml 里硬编码了 sim_joint1..7。 # 写了个不同的值会告警。要真改,三处必须一起改。 # # 另外注意:`run_sim.sh` 走的仍是 mock 驱动(假数据 + RViz),不是物理仿真; # 物理仿真入口是 `scripts/run_sim_gz.sh`(Gazebo)。两者互不影响。 platform: "gazebo" world: "devices/sim/gazebo/worlds/assembly.world" physics_enable: true joint_prefix: "sim_" # 仿真关节前缀,必须与真实臂前缀区分