EG/plugins/user/rigid_body_physics/constraints/constraint_manager.py
2025-12-12 16:16:15 +08:00

1022 lines
41 KiB
Python

"""
约束和关节系统模块
负责各种物理约束和关节的创建和管理
"""
from panda3d.core import Point3, Vec3, TransformState, LVector3f
from panda3d.bullet import BulletConstraint
from panda3d.bullet import BulletPoint2PointConstraint, BulletHingeConstraint
from panda3d.bullet import BulletSliderConstraint, BulletConeTwistConstraint
from panda3d.bullet import BulletGeneric6DofConstraint, BulletGeneric6DofSpringConstraint
from panda3d.bullet import BulletGearConstraint, BulletFixedConstraint
import math
import time
from collections import deque
class ConstraintManager:
"""
约束管理器类
管理各种类型的物理约束和关节
"""
CONSTRAINT_POINT2POINT = 'point2point'
CONSTRAINT_HINGE = 'hinge'
CONSTRAINT_SLIDER = 'slider'
CONSTRAINT_CONETWIST = 'conetwist'
CONSTRAINT_6DOF = '6dof'
CONSTRAINT_6DOF_SPRING = '6dof_spring'
CONSTRAINT_GEAR = 'gear'
CONSTRAINT_FIXED = 'fixed'
def __init__(self):
"""
初始化约束管理器
"""
self.constraints = []
self.constraint_groups = {} # 约束组
self.constraint_limits = {} # 约束限制
self.constraint_motors = {} # 约束马达
self.constraint_springs = {} # 约束弹簧
print("约束管理器初始化完成")
def create_point2point_constraint(self, body_a, body_b, pivot_a, pivot_b,
collision=True, breaking_threshold=0.0,
disable_collision_between_linked_bodies=True):
"""
创建点对点约束
Args:
body_a (RigidBody): 第一个刚体
body_b (RigidBody): 第二个刚体
pivot_a (Point3): 第一个刚体上的连接点
pivot_b (Point3): 第二个刚体上的连接点
collision (bool): 是否允许两个刚体碰撞
breaking_threshold (float): 破裂阈值
disable_collision_between_linked_bodies (bool): 是否禁用连接刚体间的碰撞
Returns:
BulletPoint2PointConstraint: 点对点约束对象
"""
constraint = BulletPoint2PointConstraint(
body_a.bullet_body, body_b.bullet_body, pivot_a, pivot_b)
# 设置约束属性
constraint.setOverrideEnabled(not collision)
if breaking_threshold > 0:
constraint.setBreakingImpulseThreshold(breaking_threshold)
# 禁用连接刚体间的碰撞
if disable_collision_between_linked_bodies:
body_a.bullet_body.setIgnoreCollisionCheck(body_b.bullet_body, True)
# 保存约束信息
constraint_info = {
'type': self.CONSTRAINT_POINT2POINT,
'constraint': constraint,
'body_a': body_a,
'body_b': body_b,
'properties': {
'pivot_a': pivot_a,
'pivot_b': pivot_b,
'collision': collision,
'breaking_threshold': breaking_threshold,
'disable_collision': disable_collision_between_linked_bodies
},
'creation_time': time.time(),
'is_enabled': True,
'group': None,
'tags': set(),
'feedback': None
}
self.constraints.append(constraint_info)
return constraint
def create_hinge_constraint(self, body_a, body_b, pivot_a, pivot_b, axis_a, axis_b,
collision=True, breaking_threshold=0.0, limits=None,
motor=None, disable_collision_between_linked_bodies=True):
"""
创建铰链约束
Args:
body_a (RigidBody): 第一个刚体
body_b (RigidBody): 第二个刚体
pivot_a (Point3): 第一个刚体上的连接点
pivot_b (Point3): 第二个刚体上的连接点
axis_a (Vec3): 第一个刚体上的轴向
axis_b (Vec3): 第二个刚体上的轴向
collision (bool): 是否允许两个刚体碰撞
breaking_threshold (float): 破裂阈值
limits (tuple): 旋转限制 (min_angle, max_angle) 或 None
motor (dict): 马达参数 {'target_velocity': float, 'max_impulse': float}
disable_collision_between_linked_bodies (bool): 是否禁用连接刚体间的碰撞
Returns:
BulletHingeConstraint: 铰链约束对象
"""
constraint = BulletHingeConstraint(
body_a.bullet_body, body_b.bullet_body, pivot_a, pivot_b, axis_a, axis_b)
# 设置约束属性
constraint.setOverrideEnabled(not collision)
if breaking_threshold > 0:
constraint.setBreakingImpulseThreshold(breaking_threshold)
# 设置旋转限制
if limits:
min_angle, max_angle = limits
constraint.setLimit(min_angle, max_angle)
# 设置马达
if motor:
target_velocity = motor.get('target_velocity', 0.0)
max_impulse = motor.get('max_impulse', 1.0)
constraint.enableAngularMotor(True, target_velocity, max_impulse)
# 禁用连接刚体间的碰撞
if disable_collision_between_linked_bodies:
body_a.bullet_body.setIgnoreCollisionCheck(body_b.bullet_body, True)
# 保存约束信息
constraint_info = {
'type': self.CONSTRAINT_HINGE,
'constraint': constraint,
'body_a': body_a,
'body_b': body_b,
'properties': {
'pivot_a': pivot_a,
'pivot_b': pivot_b,
'axis_a': axis_a,
'axis_b': axis_b,
'collision': collision,
'breaking_threshold': breaking_threshold,
'limits': limits,
'motor': motor,
'disable_collision': disable_collision_between_linked_bodies
},
'creation_time': time.time(),
'is_enabled': True,
'group': None,
'tags': set(),
'feedback': None,
'motor_enabled': motor is not None,
'current_angle': 0.0,
'angular_velocity': 0.0
}
self.constraints.append(constraint_info)
return constraint
def create_slider_constraint(self, body_a, body_b, frame_a=None, frame_b=None,
collision=True, breaking_threshold=0.0, limits=None,
motor=None, disable_collision_between_linked_bodies=True):
"""
创建滑动约束
Args:
body_a (RigidBody): 第一个刚体
body_b (RigidBody): 第二个刚体
frame_a (TransformState): 第一个刚体上的参考帧
frame_b (TransformState): 第二个刚体上的参考帧
collision (bool): 是否允许两个刚体碰撞
breaking_threshold (float): 破裂阈值
limits (tuple): 滑动限制 (linear_min, linear_max, angular_min, angular_max) 或 None
motor (dict): 马达参数 {'target_linear_velocity': float, 'max_linear_force': float,
'target_angular_velocity': float, 'max_angular_force': float}
disable_collision_between_linked_bodies (bool): 是否禁用连接刚体间的碰撞
Returns:
BulletSliderConstraint: 滑动约束对象
"""
# 如果没有提供参考帧,创建默认的
if frame_a is None:
frame_a = TransformState.makePos(Point3(0, 0, 0))
if frame_b is None:
frame_b = TransformState.makePos(Point3(0, 0, 0))
constraint = BulletSliderConstraint(
body_a.bullet_body, body_b.bullet_body, frame_a, frame_b, useLinearReferenceFrameA=True)
# 设置约束属性
constraint.setOverrideEnabled(not collision)
if breaking_threshold > 0:
constraint.setBreakingImpulseThreshold(breaking_threshold)
# 设置滑动限制
if limits:
linear_min, linear_max, angular_min, angular_max = limits
constraint.setLowerLinLimit(linear_min)
constraint.setUpperLinLimit(linear_max)
constraint.setLowerAngLimit(angular_min)
constraint.setUpperAngLimit(angular_max)
# 设置马达
if motor:
# 滑动约束的马达设置比较复杂,需要分别设置线性和角马达
pass
# 禁用连接刚体间的碰撞
if disable_collision_between_linked_bodies:
body_a.bullet_body.setIgnoreCollisionCheck(body_b.bullet_body, True)
# 保存约束信息
constraint_info = {
'type': self.CONSTRAINT_SLIDER,
'constraint': constraint,
'body_a': body_a,
'body_b': body_b,
'properties': {
'frame_a': frame_a,
'frame_b': frame_b,
'collision': collision,
'breaking_threshold': breaking_threshold,
'limits': limits,
'motor': motor,
'disable_collision': disable_collision_between_linked_bodies
},
'creation_time': time.time(),
'is_enabled': True,
'group': None,
'tags': set(),
'feedback': None,
'motor_enabled': motor is not None,
'current_linear_position': 0.0,
'current_angular_position': 0.0
}
self.constraints.append(constraint_info)
return constraint
def create_cone_twist_constraint(self, body_a, body_b, frame_a=None, frame_b=None,
collision=True, breaking_threshold=0.0, limits=None,
motor=None, disable_collision_between_linked_bodies=True):
"""
创建锥形扭转约束
Args:
body_a (RigidBody): 第一个刚体
body_b (RigidBody): 第二个刚体
frame_a (TransformState): 第一个刚体上的参考帧
frame_b (TransformState): 第二个刚体上的参考帧
collision (bool): 是否允许两个刚体碰撞
breaking_threshold (float): 破裂阈值
limits (tuple): 限制 (swing_span1, swing_span2, twist_span) 或 None
motor (dict): 马达参数
disable_collision_between_linked_bodies (bool): 是否禁用连接刚体间的碰撞
Returns:
BulletConeTwistConstraint: 锥形扭转约束对象
"""
# 如果没有提供参考帧,创建默认的
if frame_a is None:
frame_a = TransformState.makePos(Point3(0, 0, 0))
if frame_b is None:
frame_b = TransformState.makePos(Point3(0, 0, 0))
constraint = BulletConeTwistConstraint(
body_a.bullet_body, body_b.bullet_body, frame_a, frame_b)
# 设置约束属性
constraint.setOverrideEnabled(not collision)
if breaking_threshold > 0:
constraint.setBreakingImpulseThreshold(breaking_threshold)
# 设置限制
if limits:
swing_span1, swing_span2, twist_span = limits
constraint.setLimit(swing_span1, swing_span2, twist_span)
# 禁用连接刚体间的碰撞
if disable_collision_between_linked_bodies:
body_a.bullet_body.setIgnoreCollisionCheck(body_b.bullet_body, True)
# 保存约束信息
constraint_info = {
'type': self.CONSTRAINT_CONETWIST,
'constraint': constraint,
'body_a': body_a,
'body_b': body_b,
'properties': {
'frame_a': frame_a,
'frame_b': frame_b,
'collision': collision,
'breaking_threshold': breaking_threshold,
'limits': limits,
'motor': motor,
'disable_collision': disable_collision_between_linked_bodies
},
'creation_time': time.time(),
'is_enabled': True,
'group': None,
'tags': set(),
'feedback': None
}
self.constraints.append(constraint_info)
return constraint
def create_6dof_constraint(self, body_a, body_b, frame_a=None, frame_b=None,
collision=True, breaking_threshold=0.0, linear_limits=None,
angular_limits=None, motors=None, springs=None,
disable_collision_between_linked_bodies=True):
"""
创建6自由度约束
Args:
body_a (RigidBody): 第一个刚体
body_b (RigidBody): 第二个刚体
frame_a (TransformState): 第一个刚体上的参考帧
frame_b (TransformState): 第二个刚体上的参考帧
collision (bool): 是否允许两个刚体碰撞
breaking_threshold (float): 破裂阈值
linear_limits (tuple): 线性限制 (x_min, x_max, y_min, y_max, z_min, z_max) 或 None
angular_limits (tuple): 角度限制 (x_min, x_max, y_min, y_max, z_min, z_max) 或 None
motors (dict): 马达参数 {'linear': {axis: {'target_velocity', 'max_force'}},
'angular': {axis: {'target_velocity', 'max_force'}}}
springs (dict): 弹簧参数 {'linear': {axis: {'stiffness', 'damping', 'equilibrium'}},
'angular': {axis: {'stiffness', 'damping', 'equilibrium'}}}
disable_collision_between_linked_bodies (bool): 是否禁用连接刚体间的碰撞
Returns:
BulletGeneric6DofConstraint: 6自由度约束对象
"""
# 如果没有提供参考帧,创建默认的
if frame_a is None:
frame_a = TransformState.makePos(Point3(0, 0, 0))
if frame_b is None:
frame_b = TransformState.makePos(Point3(0, 0, 0))
constraint = BulletGeneric6DofConstraint(
body_a.bullet_body, body_b.bullet_body, frame_a, frame_b, useLinearReferenceFrameA=True)
# 设置约束属性
constraint.setOverrideEnabled(not collision)
if breaking_threshold > 0:
constraint.setBreakingImpulseThreshold(breaking_threshold)
# 设置线性限制
if linear_limits:
x_min, x_max, y_min, y_max, z_min, z_max = linear_limits
constraint.setLinearLowerLimit(Vec3(x_min, y_min, z_min))
constraint.setLinearUpperLimit(Vec3(x_max, y_max, z_max))
# 设置角度限制
if angular_limits:
x_min, x_max, y_min, y_max, z_min, z_max = angular_limits
constraint.setAngularLowerLimit(Vec3(x_min, y_min, z_min))
constraint.setAngularUpperLimit(Vec3(x_max, y_max, z_max))
# 设置马达
if motors:
linear_motors = motors.get('linear', {})
angular_motors = motors.get('angular', {})
for axis in range(3):
# 线性马达
if axis in linear_motors:
motor_info = linear_motors[axis]
constraint.setTargetVelocity(axis, motor_info.get('target_velocity', 0.0))
constraint.setMaxMotorForce(axis, motor_info.get('max_force', 1.0))
constraint.setParam(axis, 1) # 启用马达
# 角马达
if axis in angular_motors:
motor_info = angular_motors[axis]
constraint.setTargetVelocity(axis + 3, motor_info.get('target_velocity', 0.0))
constraint.setMaxMotorForce(axis + 3, motor_info.get('max_force', 1.0))
constraint.setParam(axis + 3, 1) # 启用马达
# 禁用连接刚体间的碰撞
if disable_collision_between_linked_bodies:
body_a.bullet_body.setIgnoreCollisionCheck(body_b.bullet_body, True)
# 保存约束信息
constraint_info = {
'type': self.CONSTRAINT_6DOF,
'constraint': constraint,
'body_a': body_a,
'body_b': body_b,
'properties': {
'frame_a': frame_a,
'frame_b': frame_b,
'collision': collision,
'breaking_threshold': breaking_threshold,
'linear_limits': linear_limits,
'angular_limits': angular_limits,
'motors': motors,
'springs': springs,
'disable_collision': disable_collision_between_linked_bodies
},
'creation_time': time.time(),
'is_enabled': True,
'group': None,
'tags': set(),
'feedback': None,
'motor_enabled': motors is not None,
'spring_enabled': springs is not None
}
self.constraints.append(constraint_info)
return constraint
def create_6dof_spring_constraint(self, body_a, body_b, frame_a=None, frame_b=None,
collision=True, breaking_threshold=0.0, linear_limits=None,
angular_limits=None, springs=None, motors=None,
disable_collision_between_linked_bodies=True):
"""
创建6自由度弹簧约束
Args:
body_a (RigidBody): 第一个刚体
body_b (RigidBody): 第二个刚体
frame_a (TransformState): 第一个刚体上的参考帧
frame_b (TransformState): 第二个刚体上的参考帧
collision (bool): 是否允许两个刚体碰撞
breaking_threshold (float): 破裂阈值
linear_limits (tuple): 线性限制 (x_min, x_max, y_min, y_max, z_min, z_max) 或 None
angular_limits (tuple): 角度限制 (x_min, x_max, y_min, y_max, z_min, z_max) 或 None
springs (dict): 弹簧参数 {'linear': {axis: {'stiffness', 'damping', 'equilibrium'}},
'angular': {axis: {'stiffness', 'damping', 'equilibrium'}}}
motors (dict): 马达参数
disable_collision_between_linked_bodies (bool): 是否禁用连接刚体间的碰撞
Returns:
BulletGeneric6DofSpringConstraint: 6自由度弹簧约束对象
"""
# 如果没有提供参考帧,创建默认的
if frame_a is None:
frame_a = TransformState.makePos(Point3(0, 0, 0))
if frame_b is None:
frame_b = TransformState.makePos(Point3(0, 0, 0))
constraint = BulletGeneric6DofSpringConstraint(
body_a.bullet_body, body_b.bullet_body, frame_a, frame_b, useLinearReferenceFrameA=True)
# 设置约束属性
constraint.setOverrideEnabled(not collision)
if breaking_threshold > 0:
constraint.setBreakingImpulseThreshold(breaking_threshold)
# 设置线性限制
if linear_limits:
x_min, x_max, y_min, y_max, z_min, z_max = linear_limits
constraint.setLinearLowerLimit(Vec3(x_min, y_min, z_min))
constraint.setLinearUpperLimit(Vec3(x_max, y_max, z_max))
# 设置角度限制
if angular_limits:
x_min, x_max, y_min, y_max, z_min, z_max = angular_limits
constraint.setAngularLowerLimit(Vec3(x_min, y_min, z_min))
constraint.setAngularUpperLimit(Vec3(x_max, y_max, z_max))
# 设置弹簧
if springs:
linear_springs = springs.get('linear', {})
angular_springs = springs.get('angular', {})
for axis in range(3):
# 线性弹簧
if axis in linear_springs:
spring_info = linear_springs[axis]
constraint.setStiffness(axis, spring_info.get('stiffness', 0.0))
constraint.setDamping(axis, spring_info.get('damping', 0.0))
constraint.setEquilibriumPoint(axis, spring_info.get('equilibrium', 0.0))
constraint.enableSpring(axis, True)
# 角弹簧
if axis in angular_springs:
spring_info = angular_springs[axis]
constraint.setStiffness(axis + 3, spring_info.get('stiffness', 0.0))
constraint.setDamping(axis + 3, spring_info.get('damping', 0.0))
constraint.setEquilibriumPoint(axis + 3, spring_info.get('equilibrium', 0.0))
constraint.enableSpring(axis + 3, True)
# 禁用连接刚体间的碰撞
if disable_collision_between_linked_bodies:
body_a.bullet_body.setIgnoreCollisionCheck(body_b.bullet_body, True)
# 保存约束信息
constraint_info = {
'type': self.CONSTRAINT_6DOF_SPRING,
'constraint': constraint,
'body_a': body_a,
'body_b': body_b,
'properties': {
'frame_a': frame_a,
'frame_b': frame_b,
'collision': collision,
'breaking_threshold': breaking_threshold,
'linear_limits': linear_limits,
'angular_limits': angular_limits,
'springs': springs,
'motors': motors,
'disable_collision': disable_collision_between_linked_bodies
},
'creation_time': time.time(),
'is_enabled': True,
'group': None,
'tags': set(),
'feedback': None,
'spring_enabled': springs is not None,
'motor_enabled': motors is not None
}
self.constraints.append(constraint_info)
return constraint
def create_gear_constraint(self, body_a, body_b, axis_a, axis_b, ratio=1.0,
collision=True, breaking_threshold=0.0):
"""
创建齿轮约束
Args:
body_a (RigidBody): 第一个刚体
body_b (RigidBody): 第二个刚体
axis_a (Vec3): 第一个刚体上的轴向
axis_b (Vec3): 第二个刚体上的轴向
ratio (float): 齿轮比
collision (bool): 是否允许两个刚体碰撞
breaking_threshold (float): 破裂阈值
Returns:
BulletGearConstraint: 齿轮约束对象
"""
constraint = BulletGearConstraint(
body_a.bullet_body, body_b.bullet_body, axis_a, axis_b, ratio)
# 设置约束属性
constraint.setOverrideEnabled(not collision)
if breaking_threshold > 0:
constraint.setBreakingImpulseThreshold(breaking_threshold)
# 保存约束信息
constraint_info = {
'type': self.CONSTRAINT_GEAR,
'constraint': constraint,
'body_a': body_a,
'body_b': body_b,
'properties': {
'axis_a': axis_a,
'axis_b': axis_b,
'ratio': ratio,
'collision': collision,
'breaking_threshold': breaking_threshold
},
'creation_time': time.time(),
'is_enabled': True,
'group': None,
'tags': set(),
'feedback': None
}
self.constraints.append(constraint_info)
return constraint
def create_fixed_constraint(self, body_a, body_b, frame_a=None, frame_b=None,
collision=True, breaking_threshold=0.0,
disable_collision_between_linked_bodies=True):
"""
创建固定约束
Args:
body_a (RigidBody): 第一个刚体
body_b (RigidBody): 第二个刚体
frame_a (TransformState): 第一个刚体上的参考帧
frame_b (TransformState): 第二个刚体上的参考帧
collision (bool): 是否允许两个刚体碰撞
breaking_threshold (float): 破裂阈值
disable_collision_between_linked_bodies (bool): 是否禁用连接刚体间的碰撞
Returns:
BulletFixedConstraint: 固定约束对象
"""
# 如果没有提供参考帧,创建默认的
if frame_a is None:
frame_a = TransformState.makePos(Point3(0, 0, 0))
if frame_b is None:
frame_b = TransformState.makePos(Point3(0, 0, 0))
constraint = BulletFixedConstraint(
body_a.bullet_body, body_b.bullet_body, frame_a, frame_b)
# 设置约束属性
constraint.setOverrideEnabled(not collision)
if breaking_threshold > 0:
constraint.setBreakingImpulseThreshold(breaking_threshold)
# 禁用连接刚体间的碰撞
if disable_collision_between_linked_bodies:
body_a.bullet_body.setIgnoreCollisionCheck(body_b.bullet_body, True)
# 保存约束信息
constraint_info = {
'type': self.CONSTRAINT_FIXED,
'constraint': constraint,
'body_a': body_a,
'body_b': body_b,
'properties': {
'frame_a': frame_a,
'frame_b': frame_b,
'collision': collision,
'breaking_threshold': breaking_threshold,
'disable_collision': disable_collision_between_linked_bodies
},
'creation_time': time.time(),
'is_enabled': True,
'group': None,
'tags': set(),
'feedback': None
}
self.constraints.append(constraint_info)
return constraint
def remove_constraint(self, constraint):
"""
移除约束
Args:
constraint: 约束对象
"""
for constraint_info in self.constraints:
if constraint_info['constraint'] == constraint:
# 重新启用刚体间的碰撞
if constraint_info['properties'].get('disable_collision', False):
body_a = constraint_info['body_a']
body_b = constraint_info['body_b']
body_a.bullet_body.setIgnoreCollisionCheck(body_b.bullet_body, False)
self.constraints.remove(constraint_info)
return True
return False
def get_constraint_info(self, constraint):
"""
获取约束信息
Args:
constraint: 约束对象
Returns:
dict: 约束信息字典
"""
for constraint_info in self.constraints:
if constraint_info['constraint'] == constraint:
return constraint_info
return None
def set_motor(self, constraint, axis, target_velocity, max_impulse):
"""
设置约束马达
Args:
constraint: 约束对象
axis (int): 轴向 (0=X, 1=Y, 2=Z for linear, 3=X, 4=Y, 5=Z for angular)
target_velocity (float): 目标速度
max_impulse (float): 最大冲量
"""
constraint_info = self.get_constraint_info(constraint)
if not constraint_info:
return
constraint_type = constraint_info['type']
if constraint_type == self.CONSTRAINT_HINGE:
constraint.setMotorTargetVelocity(target_velocity)
constraint.setMaxMotorImpulse(max_impulse)
constraint.enableAngularMotor(True)
constraint_info['motor_enabled'] = True
elif constraint_type == self.CONSTRAINT_6DOF:
constraint.setTargetVelocity(axis, target_velocity)
constraint.setMaxMotorForce(axis, max_impulse)
constraint.setParam(axis, 1) # 启用马达
constraint_info['motor_enabled'] = True
elif constraint_type == self.CONSTRAINT_6DOF_SPRING:
constraint.setTargetVelocity(axis, target_velocity)
constraint.setMaxMotorForce(axis, max_impulse)
constraint.setParam(axis, 1) # 启用马达
constraint_info['motor_enabled'] = True
def disable_motor(self, constraint):
"""
禁用约束马达
Args:
constraint: 约束对象
"""
constraint_info = self.get_constraint_info(constraint)
if not constraint_info:
return
constraint_type = constraint_info['type']
if constraint_type == self.CONSTRAINT_HINGE:
constraint.enableAngularMotor(False)
constraint_info['motor_enabled'] = False
elif constraint_type == self.CONSTRAINT_6DOF:
# 禁用所有轴的马达
for axis in range(6):
constraint.setParam(axis, 0)
constraint_info['motor_enabled'] = False
elif constraint_type == self.CONSTRAINT_6DOF_SPRING:
# 禁用所有轴的马达
for axis in range(6):
constraint.setParam(axis, 0)
constraint_info['motor_enabled'] = False
def set_spring(self, constraint, axis, stiffness, damping, equilibrium=0.0):
"""
设置约束弹簧
Args:
constraint: 约束对象
axis (int): 轴向 (0=X, 1=Y, 2=Z for linear, 3=X, 4=Y, 5=Z for angular)
stiffness (float): 刚度
damping (float): 阻尼
equilibrium (float): 平衡点
"""
constraint_info = self.get_constraint_info(constraint)
if not constraint_info:
return
constraint_type = constraint_info['type']
if constraint_type == self.CONSTRAINT_6DOF_SPRING:
constraint.setStiffness(axis, stiffness)
constraint.setDamping(axis, damping)
constraint.setEquilibriumPoint(axis, equilibrium)
constraint.enableSpring(axis, True)
constraint_info['spring_enabled'] = True
def disable_spring(self, constraint, axis):
"""
禁用约束弹簧
Args:
constraint: 约束对象
axis (int): 轴向
"""
constraint_info = self.get_constraint_info(constraint)
if not constraint_info:
return
constraint_type = constraint_info['type']
if constraint_type == self.CONSTRAINT_6DOF_SPRING:
constraint.enableSpring(axis, False)
constraint_info['spring_enabled'] = False
def set_limit(self, constraint, axis, lower_limit, upper_limit):
"""
设置约束限制
Args:
constraint: 约束对象
axis (int): 轴向
lower_limit (float): 下限
upper_limit (float): 上限
"""
constraint_info = self.get_constraint_info(constraint)
if not constraint_info:
return
constraint_type = constraint_info['type']
if constraint_type == self.CONSTRAINT_6DOF:
if axis < 3: # 线性轴
current_lower = constraint.getLinearLowerLimit()
current_upper = constraint.getLinearUpperLimit()
if axis == 0:
current_lower.setX(lower_limit)
current_upper.setX(upper_limit)
elif axis == 1:
current_lower.setY(lower_limit)
current_upper.setY(upper_limit)
elif axis == 2:
current_lower.setZ(lower_limit)
current_upper.setZ(upper_limit)
constraint.setLinearLowerLimit(current_lower)
constraint.setLinearUpperLimit(current_upper)
else: # 角轴
current_lower = constraint.getAngularLowerLimit()
current_upper = constraint.getAngularUpperLimit()
axis_index = axis - 3
if axis_index == 0:
current_lower.setX(lower_limit)
current_upper.setX(upper_limit)
elif axis_index == 1:
current_lower.setY(lower_limit)
current_upper.setY(upper_limit)
elif axis_index == 2:
current_lower.setZ(lower_limit)
current_upper.setZ(upper_limit)
constraint.setAngularLowerLimit(current_lower)
constraint.setAngularUpperLimit(current_upper)
def enable_constraint(self, constraint, enabled=True):
"""
启用或禁用约束
Args:
constraint: 约束对象
enabled (bool): 是否启用
"""
constraint_info = self.get_constraint_info(constraint)
if not constraint_info:
return
constraint_info['is_enabled'] = enabled
def add_constraint_to_group(self, constraint, group_name):
"""
将约束添加到组
Args:
constraint: 约束对象
group_name (str): 组名
"""
constraint_info = self.get_constraint_info(constraint)
if not constraint_info:
return
constraint_info['group'] = group_name
if group_name not in self.constraint_groups:
self.constraint_groups[group_name] = []
if constraint not in self.constraint_groups[group_name]:
self.constraint_groups[group_name].append(constraint)
def remove_constraint_from_group(self, constraint, group_name):
"""
将约束从组中移除
Args:
constraint: 约束对象
group_name (str): 组名
"""
constraint_info = self.get_constraint_info(constraint)
if not constraint_info:
return
constraint_info['group'] = None
if group_name in self.constraint_groups and constraint in self.constraint_groups[group_name]:
self.constraint_groups[group_name].remove(constraint)
def add_constraint_tag(self, constraint, tag):
"""
为约束添加标签
Args:
constraint: 约束对象
tag (str): 标签
"""
constraint_info = self.get_constraint_info(constraint)
if not constraint_info:
return
constraint_info['tags'].add(tag)
def remove_constraint_tag(self, constraint, tag):
"""
为约束移除标签
Args:
constraint: 约束对象
tag (str): 标签
"""
constraint_info = self.get_constraint_info(constraint)
if not constraint_info:
return
constraint_info['tags'].discard(tag)
def get_constraints_by_tag(self, tag):
"""
根据标签获取约束
Args:
tag (str): 标签
Returns:
list: 约束列表
"""
result = []
for constraint_info in self.constraints:
if tag in constraint_info['tags']:
result.append(constraint_info['constraint'])
return result
def get_constraints_by_group(self, group_name):
"""
根据组名获取约束
Args:
group_name (str): 组名
Returns:
list: 约束列表
"""
return self.constraint_groups.get(group_name, [])
def get_constraint_state(self, constraint):
"""
获取约束状态
Args:
constraint: 约束对象
Returns:
dict: 约束状态
"""
constraint_info = self.get_constraint_info(constraint)
if not constraint_info:
return None
state = {
'type': constraint_info['type'],
'is_enabled': constraint_info['is_enabled'],
'group': constraint_info['group'],
'tags': list(constraint_info['tags']),
'creation_time': constraint_info['creation_time'],
'age': time.time() - constraint_info['creation_time']
}
# 添加特定于约束类型的状态信息
if constraint_info['type'] == self.CONSTRAINT_HINGE:
state['current_angle'] = constraint.getHingeAngle()
state['motor_enabled'] = constraint_info.get('motor_enabled', False)
elif constraint_info['type'] == self.CONSTRAINT_6DOF:
state['motor_enabled'] = constraint_info.get('motor_enabled', False)
state['spring_enabled'] = constraint_info.get('spring_enabled', False)
elif constraint_info['type'] == self.CONSTRAINT_6DOF_SPRING:
state['motor_enabled'] = constraint_info.get('motor_enabled', False)
state['spring_enabled'] = constraint_info.get('spring_enabled', False)
return state
def clear_all_constraints(self):
"""
清除所有约束
"""
# 重新启用所有刚体间的碰撞
for constraint_info in self.constraints:
if constraint_info['properties'].get('disable_collision', False):
body_a = constraint_info['body_a']
body_b = constraint_info['body_b']
body_a.bullet_body.setIgnoreCollisionCheck(body_b.bullet_body, False)
self.constraints.clear()
self.constraint_groups.clear()
self.constraint_limits.clear()
self.constraint_motors.clear()
self.constraint_springs.clear()
print("所有约束已清除")
def update_constraint_feedback(self, time_delta):
"""
更新约束反馈信息
Args:
time_delta (float): 时间增量
"""
for constraint_info in self.constraints:
constraint = constraint_info['constraint']
constraint_type = constraint_info['type']
# 更新约束反馈信息
if constraint_type == self.CONSTRAINT_HINGE:
constraint_info['current_angle'] = constraint.getHingeAngle()
# 可以添加更多反馈信息
elif constraint_type == self.CONSTRAINT_SLIDER:
constraint_info['current_linear_position'] = constraint.getLinearPos()
constraint_info['current_angular_position'] = constraint.getAngularPos()
def get_constraint_statistics(self):
"""
获取约束统计信息
Returns:
dict: 统计信息
"""
type_counts = {}
group_counts = {}
tag_counts = {}
for constraint_info in self.constraints:
# 统计类型
constraint_type = constraint_info['type']
type_counts[constraint_type] = type_counts.get(constraint_type, 0) + 1
# 统计组
group = constraint_info['group']
if group:
group_counts[group] = group_counts.get(group, 0) + 1
# 统计标签
for tag in constraint_info['tags']:
tag_counts[tag] = tag_counts.get(tag, 0) + 1
return {
'total_constraints': len(self.constraints),
'type_distribution': type_counts,
'group_distribution': group_counts,
'tag_distribution': tag_counts,
'groups': list(self.constraint_groups.keys())
}