接触传感器(Contact Sensor)#
接触传感器用于检测和报告两个物体(或几何体)之间的接触信息,包括接触点位置、接触力、法向量等详细数据。在机器人仿真中,接触传感器常用于足端触地检测、抓取力反馈、碰撞检测等应用场景。
🎯 功能描述#
接触传感器监测指定的几何体对或物体对之间的接触事件,并返回详细的接触信息。该传感器可以配置返回不同类型的数据,包括接触状态、接触力、接触位置、法向量等。
📋 返回值格式#
contact_data = model.get_sensor_value("contact_sensor_name", data)
# 类型:numpy.ndarray[float32]
⚙️ MJCF 配置参数#
在 MotrixSim 中,接触传感器支持以下 MJCF 配置:
基本配置格式#
<sensor>
<contact name="sensor_name"
geom1="geom1_name"
geom2="geom2_name"
data="found force pos normal tangent"
num="4"
reduce="none"
/>
</sensor>
支持的属性#
属性名 |
类型 |
必需 |
默认值 |
描述 |
|---|---|---|---|---|
name |
string |
✅ |
- |
传感器的唯一标识名称 |
geom1 |
string |
❌ |
- |
第一个几何体名称(与 geom2 配对使用) |
geom2 |
string |
❌ |
- |
第二个几何体名称(与 geom1 配对使用) |
body1 |
string |
❌ |
- |
第一个物体名称(与 body2 配对使用) |
body2 |
string |
❌ |
- |
第二个物体名称(与 body1 配对使用) |
subtree1 |
string |
❌ |
- |
第一个子树名称(与 subtree2 配对使用) |
subtree2 |
string |
❌ |
- |
第二个子树名称(与 subtree1 配对使用) |
site |
string |
❌ |
- |
参考点名称 |
data |
string |
❌ |
"found" |
返回的数据类型,用空格分隔 |
num |
int |
❌ |
1 |
最大报告接触点数量 |
reduce |
string |
❌ |
"none" |
数据约简方式 |
目标对象配置(四种方式)#
1. 几何体对(推荐)#
<contact name="floor_contact" geom1="bar" geom2="freebox"/>
2. 物体对#
<contact name="body_contact" body1="body1_name" body2="body2_name"/>
3. 子树对#
<contact name="subtree_contact" subtree1="subtree1_name" subtree2="subtree2_name"/>
4. Site 点#
<contact name="site_contact" site="site_name"/>
数据类型(data 属性)#
可组合使用以下值:
值 |
描述 |
数据占用 |
|---|---|---|
found |
接触点数量 |
1 个值 |
force |
接触力投影(法向 + 2 切向) |
每接触点 3 个值 |
torque |
接触力矩 |
每接触点 3 个值 |
dist |
穿透深度 |
每接触点 1 个值 |
pos |
接触点位置 |
每接触点 3 个值 |
normal |
法向量 |
每接触点 3 个值 |
tangent |
第一切向量 |
每接触点 3 个值 |
数据约简方式(reduce 属性)#
值 |
描述 |
|---|---|
none |
返回所有接触点数据(最多 num 个) |
mindist |
只返回距离最近的接触点 |
maxforce |
只返回接触力最大的接触点 |
netforce |
返回所有接触点的合力(简化格式) |
📐 返回数组长度与内容布局#
接触传感器返回的数组长度和内容布局取决于三个关键配置参数:data 属性(指定数据类型)、num 属性(最大接触点数量)和 reduce 属性(数据约简方式)。
数组长度计算公式#
total_size = 1 + max_num_contacts × values_per_contact
1:
found字段,表示实际接触点数量max_num_contacts: MJCF 配置中
num参数设定的最大接触点数量(数组固定大小)values_per_contact: 每个接触点的数据值数量(取决于
data配置)
重要:数组大小固定,不受实际接触点数量影响。如果实际接触点少于 max_num_contacts,剩余位置用 0 填充。
data 属性对每接触点数据量的影响#
不同的 data 类型组合会产生不同的 values_per_contact:
data 配置 |
每接触点值数量 |
数据布局(按顺序) |
|---|---|---|
|
0 |
仅接触状态 |
|
3 |
[法向力, 切向 0 力, 切向 1 力] |
|
6 |
[3 个力分量, 3 个位置坐标] |
|
9 |
[3 个力分量, 3 个位置坐标, 3 个法向量] |
|
12 |
[3 个力分量, 3 个位置坐标, 3 个法向量, 3 个切向量 0] |
|
6 |
[3 个位置坐标, 3 个法向量] |
|
6 |
[3 个力分量, 3 个力矩分量] |
注意:每个数据类型的占用空间:
found: 1 个值(所有配置都包含)force: 3 个值(法向 + 2 个切向力投影)torque: 3 个值(接触力矩)dist: 1 个值(穿透深度)pos: 3 个值(接触点位置)normal: 3 个值(法向量)tangent: 3 个值(第一切向量)
reduce 属性对接触点数量的影响#
reduce 属性决定了返回哪些接触点以及返回多少个:
reduce="none"(默认)#
行为:返回满足匹配条件的前
num个接触点,按在 mjData.contact 中出现的顺序特点:最快选项,但可能非确定性(碰撞检测算法变化可能改变接触点的身份和顺序)
接触点数量:最多
num个,可能少于num个(如果实际接触点较少)
reduce="mindist"#
行为:返回穿透深度最小的
num个接触点,按深度升序排列接触点数量:最多
num个,可能少于num个
reduce="maxforce"#
行为:返回力范数最大的
num个接触点,按力大小降序排列接触点数量:最多
num个,可能少于num个
reduce="netforce"#
行为:返回一个"合成"接触点,具有以下特性:
位置:所有匹配接触点的力加权质心
坐标系:全局坐标系(normal 和 tangent 失去原有语义)
力和力矩:计算为在计算位置施加的力和力矩,产生与所有匹配接触点相同的净效果
接触点数量:始终恰好 1 个(忽略
num设置)特殊注意:由于使用全局坐标系,数据解释与其他 reduce 类型不同
具体配置示例#
示例 1:完整接触信息(reduce="none")#
<contact name="full_contact"
geom1="bar" geom2="freebox"
data="found force pos normal tangent"
num="4"
reduce="none"/>
返回数据布局:
contact_data = model.get_sensor_value("full_contact", data)
# 形状:shape = (1 + 4×12,) = (49,) 固定大小(由num="4"决定)
# 数组大小固定,不足的接触点用0填充
# 数据结构:
contact_data[0] # 实际接触点数量 (例如:2,其余2个为填充)
# 第1个接触点(offset = 1,有效数据):
contact_data[1:4] # 力分量:[法向力, 切向0力, 切向1力]
contact_data[4:7] # 位置:[x, y, z]
contact_data[7:10] # 法向量:[nx, ny, nz]
contact_data[10:13] # 切向量0:[tx0, ty0, tz0]
# 第2个接触点(offset = 13,有效数据):
contact_data[13:16] # 力分量
contact_data[16:19] # 位置
contact_data[19:22] # 法向量
contact_data[22:25] # 切向量0
# 第3个接触点(offset = 25,填充0):
contact_data[25:37] # 全部为0(无接触)
# 第4个接触点(offset = 37,填充0):
contact_data[37:49] # 全部为0(无接触)
示例 2:简化接触信息(reduce="maxforce")#
<contact name="force_contact"
geom1="bar" geom2="freebox"
data="found force pos"
num="5"
reduce="maxforce"/>
返回数据布局:
contact_data = model.get_sensor_value("force_contact", data)
# 形状:shape = (1 + 5×6,) = (31,) 固定大小(由num="5"决定)
# 数组大小固定,最多返回力最大的5个接触点,按力降序排列
# 不足的接触点用0填充
contact_data[0] # 实际接触点数量(例如:3)
# 第i个接触点:每接触点6个值
# 只有前 contact_data[0] 个接触点包含有效数据
offset = 1 + i * 6
contact_data[offset + 0:offset + 3] # 力分量
contact_data[offset + 3:offset + 6] # 位置
# 注意:i >= contact_data[0] 时,以上数据全部为0
示例 3:仅接触状态(最简化)#
<contact name="touch_only"
geom1="bar" geom2="freebox"
data="found"
num="1"/>
返回数据布局:
contact_data = model.get_sensor_value("touch_only", data)
# 形状:shape = (1,) 始终只有1个值
contact_data[0] # 接触点数量 (0 = 无接触, 1 = 有接触)
示例 4:合力信息(reduce="netforce")#
<contact name="net_force"
geom1="bar" geom2="freebox"
data="found force pos normal tangent"
num="10"
reduce="netforce"/>
返回数据布局:
contact_data = model.get_sensor_value("net_force", data)
# 形状:shape = (1 + 1×12,) = (13,) 始终返回1个合成接触点(忽略num="10")
contact_data[0] # 始终为1(合成接触点)
# 合成接触点数据(全局坐标系):
contact_data[1:4] # 合力分量
contact_data[4:7] # 力加权质心位置
contact_data[7:10] # 法向量(全局坐标系,语义改变)
contact_data[10:13] # 切向量0(全局坐标系,语义改变)
重要注意事项#
固定数组大小:返回的数组大小是固定的,由
num参数决定:shape = (1 + num × values_per_contact,)零填充机制:如果实际接触点数量少于
num,剩余位置用 0 填充,需要使用contact_data[0]判断有效数据found 字段可靠性:始终使用
contact_data[0]来确定实际接触点数量,不要假设数组全部有效netforce 特殊处理:
reduce="netforce"的坐标系和数据解释与其他类型不同,且始终返回 1 个接触点性能优化:对于只需要接触状态的应用,使用
data="found"可以显著提高性能
🐍 Python Demo#
下面是一个完整的接触传感器可视化示例,展示了如何实时获取和渲染接触力数据。该示例来自 site_and_sensor.py 文件,演示了接触传感器的实际应用效果。
场景配置#
首先,我们定义一个包含接触传感器的场景:
<contact name="box_floor_contact" geom1="bar" geom2="freebox"
data="found force pos normal tangent" num="4" reduce="none"/>
这个配置创建了一个名为 box_floor_contact 的接触传感器,监测 bar 和 freebox 两个几何体之间的接触,返回完整的接触信息(力、位置、法向量、切向量)。
Python 代码#
下面是完整的接触传感器可视化代码:
# tag::contact_sensor_gizmos[]
# Get contact sensor data: [found, then 12 values per contact point]
contact_data = model.get_sensor_value("box_floor_contact", data)
num_contacts = int(contact_data[0])
# Process each contact point
for i in range(num_contacts):
# Each contact has 12 values
offset = 1 + i * 12
# Extract force components (scalars, not vectors!)
force_normal_mag = contact_data[offset + 0] # Normal force magnitude
force_tangent0_mag = contact_data[offset + 1] # Tangent 0 force magnitude
force_tangent1_mag = contact_data[offset + 2] # Tangent 1 force magnitude
# Extract contact position
contact_pos = contact_data[offset + 3 : offset + 6]
# Extract normal vector
normal = contact_data[offset + 6 : offset + 9]
# Extract first tangent vector
tangent0 = contact_data[offset + 9 : offset + 12]
# Compute second tangent vector: tangent1 = tangent0 × normal
tangent1 = np.cross(tangent0, normal)
# Scale force magnitudes for visualization
force_scale = 0.01
# Compute force vectors for visualization
# Normal force vector (green arrow)
normal_force_end = contact_pos + normal * force_normal_mag * force_scale
# Tangent 0 force vector (red arrow)
tangent0_force_end = contact_pos + tangent0 * force_tangent0_mag * force_scale
# Tangent 1 force vector (blue arrow)
tangent1_force_end = contact_pos + tangent1 * force_tangent1_mag * force_scale
# Draw contact point (small white sphere)
render.gizmos.draw_sphere(0.02, contact_pos, color=Color.rgb(1, 1, 1))
# Draw normal force arrow (green - perpendicular to surface)
render.gizmos.draw_arrow(contact_pos, normal_force_end, color=Color.rgb(0, 1, 0))
# Draw tangent 0 force arrow (red - friction direction 0)
render.gizmos.draw_arrow(contact_pos + normal * 0.01, tangent0_force_end, color=Color.rgb(1, 0, 0))
# Draw tangent 1 force arrow (blue - friction direction 1)
render.gizmos.draw_arrow(contact_pos + normal * 0.01, tangent1_force_end, color=Color.rgb(0, 0, 1))
# end::contact_sensor_gizmos[]
注意:这里只显示了接触传感器的相关部分,完整的示例代码还包括场景设置、其他传感器类型和渲染循环等内容。
可视化说明#
该示例通过颜色编码的箭头实时显示接触力的各个分量:
白色球体:接触点位置
绿色箭头:法向力(垂直于接触表面)
红色箭头:切向摩擦力分量 0
蓝色箭头:切向摩擦力分量 1(通过叉积计算得到)
效果演示#
该视频展示了接触传感器的实时可视化效果,包括:
物体与地面接触时的接触点检测
接触力的实时计算和显示
不同方向力的分量可视化
力的大小随物体运动变化的动态过程
📊 物理原理#
接触坐标系#
接触传感器使用局部接触坐标系来报告接触数据:
法向量(normal):垂直于接触表面,从物体 1 指向物体 2
切向量(tangent0, tangent1):构成接触平面内的正交基底
力投影:接触力在三个轴上的投影值
接触力分解#
总接触力 = 法向力 + 切向力(摩擦力)
F_total = F_normal + F_tangent0 + F_tangent1
法向力:正压力,防止物体相互穿透
切向力:摩擦力,阻止接触面之间的相对滑动
接触检测#
接触传感器基于碰撞检测系统工作:
检测两个几何体之间的空间重叠
计算接触点的位置和法向量
根据穿透深度和材料属性计算接触力
将接触力投影到局部接触坐标系
⚠️ 注意事项#
力分量是标量:
force_normal_mag、force_tangent0_mag、force_tangent1_mag是力的投影值,不是完整的力向量第二切向量:需要通过
tangent1 = cross(normal, tangent0)计算得到坐标系:所有数据都在局部接触坐标系中表示,不是全局坐标系
性能考虑:接触传感器计算量较大,建议合理设置
num属性限制报告的接触点数量配置互斥:
geom1/geom2、body1/body2、subtree1/subtree2、site四种配置方式只能使用一种