P5c-5 触觉适配器落地:Gazebo 关节力 → arm_msgs/TactileStatus

链路:指尖关节 force_torque(持续发布)→ ros_gz_bridge → sim_tactile_adapter
→ /tactile/{left,right}/status。字段口径与真机 tactile_node 完全同构:
20Hz、frame_id=<side>_tactile、fnormal clamp 到非负、所有力都是"相对静止
基线的增量"。所以 compliant_descent 与 stage3_search_node 一行不改就能在
仿真里跑 —— 这是"同一套接口、换后端"契约的一部分。

新增 sim_tactile.py 把三件纯数据变换独立出来(轴向投影、桥接参数、基线
跟踪),因为这三处的错误全都**不会报错**,只会让力的语义静默错位:
轴向映射反了照样"有力、会响应、松开归零",基线把接触吃掉了则表现为
"明明夹住了 fnormal 恒为 0"。抽出来才有单测可钉(新增 25 条)。

已实测验收(关闭夹爪夹静态薄板,60×120×80mm):
    空载   fnormal ~1e-8、contact=false(基线相减到位)
    夹住   contact=true、fnormal=714.7/102.5,切向同时起变化
    松开   contact 立刻回 false(靠 contact_timeout_s,因为 gz 的接触消息
           是事件式的,不接触时一条都不发),力回落到 ~1e-8
    启动期 check_tactile_collisions 无告警

check_tactile_collisions 顺带被负向单测逼出两处"会被静默骗过"的判据:
sdformat 对缺失的 contact/topic 会补 __default_topic__、对缺失的 collision
会补 __default__,所以只判"空"是抓不到的 —— 必须连占位值一起判。

force_scale 口径写入 config/tactile.yaml:仿真输出 = Gazebo 牛顿 × force_scale。
要共用真机那套 250/600 阈值必须先标定它,否则要么永不触发要么一碰就炸
(实测位置控制器硬压可到几百,量级与真机完全不同)。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
arm_dev
2026-09-23 16:32:56 +08:00
parent 768da34cf4
commit 01315773e7
10 changed files with 1547 additions and 3 deletions
+4
View File
@@ -42,6 +42,10 @@ setup(
# 话题(改名 + 光学系 frame_id + 深度 米→毫米)。
# 由 gazebo_sim.launch.py 拉起,一般不手跑;单独调试时可以用。
'sim_camera_adapter = arm_sim_gazebo.sim_camera_adapter:main',
# 仿真触觉适配器:把桥接过来的 gz 关节力/接触传感器变成
# arm_msgs/TactileStatus(轴向投影 + 基线相减 + 恒定发布)。
# 由 gazebo_sim.launch.py 拉起;单独调试时可以用。
'sim_tactile_adapter = arm_sim_gazebo.sim_tactile_adapter:main',
# 独立小工具:只看归一化做了什么,不启动 Gazebo。
# 排查"为什么 Gazebo 说我的 link 不存在"时非常省时间。
'urdf_normalize = arm_sim_gazebo.urdf_normalize:main',