【免费下载链接】webots
Webots Robot Simulator
本文以 Webots 参考文档 Physics 节点条目为核心,系统讲解该节点的五个字段(density、mass、centerOfMass、inertiaMatrix、damping)的取值规则与生效逻辑,并结合同步源码(src/webots/nodes/WbPhysics.cpp、src/webots/nodes/WbSolid.cpp、src/webots/nodes/utils/WbSolidMerger.cpp)剖析“物理模式 vs 运动学模式”的判定、刚体隐式合并与关节创建等底层机制,帮助你正确为场景中的物体、机器人和设备配置物理属性。
Physics 节点定义与字段总览
Physics 节点用于向物理引擎(Webots 内置 ODE)指定刚体的物理参数:质量、重心位置和质量分布。它是绝大多数 Webots 世界的基本组成,只有纯运动学(kinematics)仿真才会省略它。Physics 节点必须放置在一个 Solid 节点(或任何 Solid 派生节点)的 physics 字段中——该字段是 NULL 还是指向一个 Physics 节点,直接决定了这个 Solid 是以 physics(物理)模式还是 kinematics(运动学)模式被仿真:
Physics {
SFFloat density 1000 # {-1, [0, inf)}
SFFloat mass -1 # {-1, [0, inf)}
MFVec3f centerOfMass [ ] # any vector
MFVec3f inertiaMatrix [ ] # any two vectors
SFNode damping NULL # {Damping, PROTO}
}
各字段的作用如下:
| 字段 | 类型 | 默认值 | 作用 |
|---|---|---|---|
density | SFFloat | 1000 | 所在 Solid 的密度,kg/m³;为 -1 时表示未知,必须改用 mass |
mass | SFFloat | -1 | 所在 Solid 的总质量,kg;为 -1 时表示未知,必须改用 density |
centerOfMass | MFVec3f | 空 | 重心位置(米,Solid 局部坐标系);为空则自动由 boundingObject 计算 |
inertiaMatrix | MFVec3f | 空 | 手动指定惯性矩阵;必须为空或恰好 2 个向量 |
damping | SFNode | NULL | 指向一个 Damping 节点,定义该 Solid 的线性/角速度阻尼 |
注意:在旧版 Webots 中,
coulombFriction、bounce、bounceVelocity和forceDependentSlip曾直接定义在Physics节点里。现在这些接触参数必须写在 ContactProperties 节点中。从源码看,coulombFriction、forceDependentSlip等字段仅存在于WbContactProperties类中(见 WbContactProperties.hpp),WbPhysics类(WbPhysics.hpp)只持有density、mass、centerOfMass、inertiaMatrix、damping五个用户字段。
字段一:density 与 mass 的互斥关系
density的值应为 -1 或正数,单位为 kg/m³。取 -1 表示密度未知,此时必须指定mass。若density为正,则Solid的总质量 = 密度 ×boundingObject中全部几何基元的总体积。需要特别注意:Webots 忽略基元之间是否相交,每个基元的体积被简单累加后再乘以密度。mass的值应为 -1 或正数,单位为 kg。取 -1 表示总质量未知,此时必须指定density。如果机器人规格书直接给出了整机质量,直接填mass比反推density更精确。
源码印证了这条互斥规则。WbPhysics.cpp 中的 checkMassAndDensity() 会给出两条解析警告:两者同时为 -1 时提示“Either the 'mass' or the 'density' must be specified”;两者同时为正时提示“the 'density' will be ignored”(即以 mass 为准)。此外,checkDensity() 表明当 mass 非正时会把 density 重置回默认值 1000,checkMass() 则把 mass 重置为 1 或 -1——这与字段注释中 {density: 默认 1000}、{mass: 默认 -1} 的取值表一致。
字段二:centerOfMass
centerOfMass 定义 Solid 的重心位置:
- 字段为空(0 个向量):重心自动根据
boundingObject计算; - 字段含 1 个向量:以该向量(单位米,
Solid局部坐标系)为准,覆盖自动计算; - 若该向量不为
[0 0 0],Webots 3D 窗口中会用一个深红/绿/蓝十字标出重心位置(对应 WbSolid.cpp 中mCenterOfMassTransform节点的可见性逻辑)。
centerOfMass 最多接受 1 个向量,多余向量会被丢弃并产生警告,见 WbPhysics.cpp 中 preFinalize() 的校验代码。
字段三:inertiaMatrix 与两种配置模式
inertiaMatrix 可用于手动指定 Solid 的惯性矩阵。字段要么为空(默认,此时 Webots 依据 boundingObject 中几何基元的位置与朝向自动计算),要么恰好包含 2 个向量:
- 第 1 个向量
[I11, I22, I33]表示主惯性矩(principals moments of inertia); - 第 2 个向量
[I12, I13, I23]表示惯性乘积(products of inertia)。
二者共同构成 3×3 惯性矩阵:
[ I11 I12 I13 ]
[ I12 I22 I23 ]
[ I13 I23 I33 ]
Ixx 以 kg·m² 为单位,且主惯性矩必须为正;该矩阵是相对于 centerOfMass 定义的。内部实现中,这 6 个值不经换算直接传给 ODE 的 dMassSetParameters 函数——这一点在 WbSolid.cpp 的 setOdeInertiaMatrix() 中可以清楚看到:
dMassSetParameters(mOdeMass, p->mass(), 0.0, 0.0, 0.0, v0.x(), v0.y(), v0.z(), v1.x(), v1.y(), v1.z());
...
dMassTranslate(mOdeMass, com.x(), com.y(), com.z()); // 平移到 Solid 坐标系原点
如果指定了 inertiaMatrix,则 mass 字段必须同时指定;源码在 checkInertiaMatrix() 中做了三重校验:主惯性矩必须全为正(否则调试版 ODE 会触发断言失败)、矩阵必须正定(dIsPositiveDefinite)、且必须同时提供 centerOfMass 和正的 mass。
综合来看,从源码结构可以归纳出 WbPhysics 的三种工作模式(定义于 WbPhysics.hpp 的 ModeType 枚举):
| 模式 | 触发条件(updateMode()) |
|---|---|
CUSTOM_INERTIA_MATRIX | mass > 0 且 centerOfMass 恰有 1 个向量且惯性矩阵合法 |
BOUNDING_OBJECT_BASED | mass 或 density 为正,且 inertiaMatrix 为空 |
INVALID | 其余情况,此时 ODE 沿用最近一次有效配置 |
一个容易忽略的细节是:在 BOUNDING_OBJECT_BASED 模式下,计算出的惯性矩阵是以 boundingObject 自身质心为基准的,Solid 的行为等效于“把质量分布平移到 centerOfMass 处,而物理边界不动”(见 WbPhysics.hpp 顶部注释)。
字段四:damping
damping 字段指向一个 Damping 节点,用于定义作用在该 Solid 上的速度阻尼参数。Damping 的 linear/angular 字段取值范围为 [0, 1],分别对线速度和角速度每秒衰减相应比例。合并刚体的阻尼值会按各 Solid 分量体积加权平均(合并逻辑见 WbSolidMerger.cpp 中 setupOdeBody() 调用的 setOdeDamping())。仓库中 tests/physics/worlds/damping.wbt 展示了在多个 Solid 上分别配置不同 linear 阻尼(0、0.4、0.8)的对比测试场景。
如何决定一个物体是否需要 Physics 节点
若 Solid 含有 Physics 节点,该物体以 physics 模式仿真:引擎会计算作用在刚体上的力以及刚体属性(质量、转动惯量);反之,physics 字段为 NULL 时物体以 kinematics 模式仿真:只模拟运动本身而不考虑引发运动的力。典型差异是:运动学模式下物体可以瞬时达到目标速度,而物理模式下惯性阻力会使物体逐渐加速。
是否给某个物体配置 Physics,取决于你想建模现实中的哪一面。
被动静止物体
- 如果一个被动物体在仿真中绝不应移动(墙、地板),就把
physics字段留空。此时不会在该物体上模拟任何接触力,它永远不会动。 - 地板尤其必须不带
Physics节点,否则在重力作用下地板本身会掉下来。 - 反之,如果被动物体会被推、踢、掉落(足球被踢出滚动、箱子被推倒或堆叠),就必须给它加
Physics节点,摩擦力和重力才会作用其上。
机器人
关节式机器人、人形机器人、车辆等都构建为 Solid(或其子类)的层级结构。腿或轮子产生的接触力、摩擦力的仿真,以及抓取式机械手/夹爪的接触力,通常是机器人运动仿真的核心。因此机器人的机械部件(腿、轮、臂、手等)一般需要 Physics 节点。
说明:可以把一个
Robot或顶层Solid的physics字段设为NULL,将其基座固定于静态环境。这对基座锚定在固定位置上的机械臂仿真很有用。更一般地,你可以定义一个以某个顶层Solid为根的更大“静态基座”:从该顶层Solid出发、整棵子树中所有Solid都不含Physics节点。
说明:采用差速轮配置的
Robot是特例:即使没有Physics节点也能移动,因为 Webots 对此类机器人使用了特殊的运动学算法;但一旦存在Physics节点,就切换回常规物理仿真算法。从源码看,WbSolid::createJoint()的默认行为是创建 ODE 固定关节(WbSolid.cpp 中dJointCreateFixed(world, 0)),即Solid与父级刚体之间默认以固定关节连接。而一个Robot要被识别为差速轮机器人,必须满足:机器人有 2 个HingeJoint,两轴平行对齐,且两个关节endPoint字段中Solid的 bounding object 必须是半径相同的 Cylinder。
隐式 Solid 合并与关节
先明确“Solid 子节点”的广义定义:给定一个 Solid,其子节点既包括直接放进 children 列表的节点,也包括放在 children 中某个 Joint 的 endPoint 字段里的 Solid;该定义可延伸到从 Solid 的 children 列表开始、包含 Joint 或 Solid 的嵌套 Group。
若一个广义子 Solid 与其父 Solid 之间没有关节相连,且两者都有 Physics 节点,则它们在物理引擎层面被合并:ODE 只获得一个 body 来同时表示父与子。该过程递归进行,直到某个通过关节指向上层 Solid 的最高祖先,或最高无 Physics 节点的祖先之前为止。这样一来,即使一个刚性质组件由大量小 Solid 拼装而成,也不会拖累物理仿真速度。
注意:发生刚体合并时,刚性质组件中只有最高祖先持有 ODE body(即非空
dBodyID),并承载整组组件的物理属性。这一点会影响 physics plugin 的设计方式。源码印证:WbSolidMerger.cpp 中每个合并器用dBodyCreate创建一个 ODE body,appendSolid()把各子Solid的dMass收集进mMergedSolids映射表,最终由mergeMasses()汇总到这一个 body 上。
设计机器人节点树结构时,关于 Physics 节点有一条必须牢记的规则:
规则 (1):如果一个
Solid节点的父节点和子节点都有Physics节点,那么它自己也必须带有Physics节点。
其推论是:在机器人树中,只有叶子节点以及 static basis(静态基座,见上文)内的节点才允许 physics 字段为 NULL。顶层 Robot 节点通常必须带 Physics,否则其任何子节点都无法使用物理仿真。
同时要注意:每个 Physics 节点都会显著增加世界的复杂度、拖慢仿真速度,因此 Physics 节点数量应尽可能少。即使是很复杂的轮式或关节式机器人,部分 physics 字段也可以保持为空。以一台双腿关节机器人为例(高度简化的模型),两条腿需要 Physics 节点,因为它们与地面接触产生的力是机器人移动的来源;若腿上没有 Physics,接触力无从产生,机器人就不会动。按规则 (1),腿有了 Physics,父级 Robot 也必须有 Physics,否则仿真无法工作(腿会“脱落”):
Robot {
...
children [
DEF LEG1_HINGE HingeJoint {
...
endPoint DEF LEG1 Solid {
physics Physics {
}
}
}
DEF LEG2_HINGE HingeJoint {
...
endPoint DEF LEG2 Solid {
physics Physics {
}
}
}
]
physics Physics {
}
}
现在假设给机器人加一个 Camera。若相机质量很小、且希望忽略相机与其他物体的碰撞,则相机的 physics 字段留空即可:
Robot {
...
children [
DEF CAM Camera {
...
}
DEF LEG1_HINGE HingeJoint {
...
endPoint DEF LEG1 Solid {
...
physics Physics {
}
}
}
DEF LEG2_HINGE HingeJoint {
...
endPoint DEF LEG2 Solid {
physics Physics {
}
}
}
]
physics Physics {
}
}
再假设相机需要伺服化,例如水平旋转。此时只需把相机放进控制其水平位置的 HingeJoint 的 endPoint 字段。若假设电机质量小、转动惯量无关紧要,相机的 Physics 节点同样可以省略:
Robot {
...
children [
DEF CAMERA_HINGE HingeJoint {
...
device DEF CAM_MOTOR RotationalMotor {
...
}
endPoint DEF CAM Camera {
...
}
}
DEF LEG1_HINGE HingeJoint {
...
endPoint DEF LEG1 Solid {
...
physics Physics {
}
}
}
DEF LEG2_HINGE HingeJoint {
...
endPoint DEF LEG2 Solid {
physics Physics {
}
}
}
]
physics Physics {
}
}
设备(Devices)
大多数设备节点在没有 Physics 节点的情况下也能工作,但可选地加一个 Physics 节点来仿真设备自身的重量和转动惯量。一般建议:除非设备在仿真机器人中代表可观的质量或体积,否则保持设备的 physics 字段为空。这条建议适用于:Accelerometer、Altimeter、Camera、Compass、DistanceSensor、Emitter、GPS、LED、LightSensor、Pen、Receiver。
注意:InertialUnit、Connector 和 VacuumGripper 的行为不同——它们要求父节点存在
Physics节点才能工作(例如 WbVacuumGripper.cpp 在吸附失败时会报告“neither the VacuumGripper node nor the solid object have Physics nodes”)。也可以在设备本身上指定Physics节点,但代价是给仿真额外增加一个 body。TouchSensor 是另一个特例:作为“力传感器”使用时需要
Physics节点;仅作为“碰撞器”(bumper)使用时则不一定需要。从源码看,WbTouchSensor::createJoint()(WbTouchSensor.cpp)会在forceBehavior()为真时创建dJointFeedback以收集接触力。
实战建议小结
- 地板、墙体等永久静止物体一律不加
Physics,否则地板会在重力下坠落;需要被推动/踢动的物体(足球、木箱)必须加。 - 遵守规则 (1):父、子都有
Physics的中间Solid必须自己带Physics;顶层Robot一般必须带Physics。 - 质量已知优先写
mass,否则写density;二者同时为正时density被忽略且会产生警告。 - 需要精确惯性行为时提供
inertiaMatrix+centerOfMass+ 正的mass三件套,并注意主惯性矩为正、矩阵正定。 - 传感器、摄像头、发射器等轻设备保持
physics为空;只有InertialUnit、Connector、VacuumGripper及作力传感器用的TouchSensor例外。 - 控制
Physics节点总数:利用隐式刚体合并机制,把同一刚性质组件拆成多个小Solid不会额外增加 ODE body 数量,但每个独立Physics都会增加求解开销。 - 摩擦、反弹等接触参数请写到 ContactProperties,速度阻尼写到
Physics.damping指向的 Damping 节点。
【免费下载链接】webots
Webots Robot Simulator
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/gitblog_00091/article/details/157404437



