2D物理(13)角运动学系统设计与实现

By pocaster

角运动学系统设计与实现


▌ 物理基础回顾

关键公式链

τ = r × F = I * α     \\ (力矩=转动惯量×角加速度)
α = τ / I             \\ (角加速度=力矩/转动惯量)
Δω = α * Δt           \\ (角速度变化)
Δθ = ω * Δt + 0.5 * α * Δt² \\ (角度位移)

I. 刚体数据结构扩展

1. 新增角运动属性

struct RigidBody {
    // 平移运动
    Vec2 position;
    Vec2 velocity;
    Vec2 force_accum;  // 累积力
  
    // 旋转运动
    float rotation;     // 当前旋转角度(弧度)
    float angular_vel;  // 角速度 (rad/s ←)
    float torque_accum; // 累积力矩
  
    // 惯性属性
    float inverse_mass; // 1/mass(用于优化计算)
    float inverse_inertia; // 1/I (动态更新)
  
    // ... 物理材质等其他属性
};

2. 构造初始化

void RigidBody::Initialize(float mass, const Shape* shape) {
    inverse_mass = (mass != 0) ? 1.0f/mass : 0;
  
    // 基于形状类型计算转动惯量
    float inertia = shape->ComputeMomentOfInertia(mass);
    inverse_inertia = (inertia != 0) ? 1.0f/inertia : 0;
  
    // 初始状态清零
    angular_vel = 0;
    torque_accum = 0;
    // ...其他初始化
}

II. 力矩计算系统

1. 力的施加接口

void RigidBody::ApplyForceAtPoint(const Vec2& force, 
                                const Vec2& world_point) {
    // (1) 累积线加速度
    force_accum += force;
  
    // (2) 计算相对质心的力矩臂
    Vec2 r = world_point - position;
    torque_accum += Cross(r, force);  // τ = r×F
}

2. 矢量叉乘的物理意义

描述

float Cross(const Vec2& a, const Vec2& b) {
    return a.x*b.y - a.y*b.x; // 行列式计算
}

III. 积分器实现

1. 半隐式欧拉积分法

void RigidBody::Integrate(float dt) {
    if (inverse_mass == 0) return; // 静态物体跳过
  
    // 线运动积分
    Vec2 acceleration = force_accum * inverse_mass;
    velocity += acceleration * dt;
    position += velocity * dt;
  
    // 角运动积分
    float angular_accel = torque_accum * inverse_inertia;
    angular_vel += angular_accel * dt;
    rotation += angular_vel * dt;
  
    // 清除累积值
    force_accum.SetZero();
    torque_accum = 0;
}

2. 积分优化策略

参数 计算时机 刷新规则
inverse_mass 创建/质量变化时 物体质量改变时更新
inverse_inertia 创建/形状变化时 形状修改时自动重新计算

IV. 角运动与碰撞耦合

当发生碰撞时,需要同时处理线速度和角速度的响应:

1. 碰撞点速度计算

Vec2 GetVelocityAtPoint(const Vec2& world_point) const {
    Vec2 r = world_point - position;
    return velocity + Vec2(-r.y, r.x) * angular_vel; // v = v0 + ω×r
}

2. 冲量计算(旋转分量)

void ResolveCollision(CollisionInfo& info) {
    // 接触点相对质心的径向量
    Vec2 r1 = info.point - body1->position;
    Vec2 r2 = info.point - body2->position;
  
    // 计算旋转效应因子
    float rotational_factor = 
          Cross(r1, info.normal)*Cross(r1, info.normal)*body1->inverse_inertia + 
          Cross(r2, info.normal)*Cross(r2, info.normal)*body2->inverse_inertia;
  
    // 综合冲量计算公式
    float j = ...(包含旋转因子的完整冲量计算)... 
  
    // 应用角速度变化
    body1->angular_vel += Cross(r1, j*info.normal) * body1->inverse_inertia;
    body2->angular_vel -= Cross(r2, j*info.normal) * body2->inverse_inertia;
}

V. 数值稳定性手段

1. 角速度阻尼

const float ANGULAR_DAMPING = 0.99f;

void RigidBody::Integrate(float dt) {
    // ...原有积分...
  
    // 防止无限旋转
    angular_vel *= powf(ANGULAR_DAMPING, dt);
}

2. 最大角速度限制

const float MAX_ANGULAR_VEL = 50.0f; // rad/s ≈ 7958 deg/s

angular_vel = Clamp(angular_vel, -MAX_ANGULAR_VEL, MAX_ANGULAR_VEL);

VI 测试用例

1. 自由下落的旋转方块

TEST(RigidBodyTest, RotatingFall) {
    Box box(1.0f, 1.0f);
    RigidBody body;
    body.Initialize(2.0f, &box);
  
    // 在右端施加向上力
    body.ApplyForceAtPoint(Vec2(0, 100), Vec2(1,0)); 
    body.Integrate(0.1f);
  
    EXPECT_GT(body.angular_vel, 0); // 应产生正方向旋转
    EXPECT_GT(body.velocity.y, 0);  // 整体向上加速
}

2. 角动量守恒验证

TEST(RigidBodyTest, AngularMomentum) {
    // 无外力情况
    RigidBody body1 = /* 初始化有角速度的物体 */;
    RigidBody body2 = /* 初始化静止物体 */;
  
    float initial_L = body1.I()*body1.omega + body2.I()*body2.omega;
    Simulate(5.0f); // 模拟5秒
    float final_L = body1.I()*body1.omega + body2.I()*body2.omega;
  
    ASSERT_NEAR(initial_L, final_L, 1e-3); // 动量应守恒
}

VII. 可视化调试策略

1. 角速度矢量显示

在物体中心绘制旋转箭头:

void DebugDraw::DrawAngularVelocity(const RigidBody* body) {
    float arrow_len = body->angular_vel * 0.1f; // 缩放因子
    Vec2 tip = body->position + Vec2(-arrow_len, 0).Rotate(body->rotation);
    DrawArrow(body->position, tip, RED);
}

2. 力矩作用线

绘制力施加点与方向的连线:

// 在ApplyForce时记录作用点
void RigidBody::ApplyForceAtPoint(...) {
    debug_force_lines.emplace_back(world_point, force);
}

VIII. 常见问题解决方案

问题现象 原因分析 修复策略
物体受力后不旋转 碰撞点处于质心轴线上(r=0) 设计非对称碰撞测试用例
旋转速度异常增大 数值积分导致能量增益 增加角速度阻尼
碰撞后物体会抖动 冲量分辨率不一致 使用顺序冲量法(SAP)优化求解器
小物体的”Z旋转”现象 浮点数精度误差累积 约束微小角速度强制置零

▌ 工程洞见

  1. 能量守恒检查工具
    void MonitorEnergy() {
     kinetic = 0.5f * mass*v.SqrLength() + 0.5f*I*ω*ω;
     if (abs(kinetic - last_kinetic)/kinetic > 0.2f) 
         LogWarning("能量突变!");
    }
    
  2. 异步更新策略 对高速旋转物体使用子步长积分:
    Δt_{sub} = \frac{\theta_{threshold}}{\omega_{current}}
    
  3. 网格分辨率启发式规则 自动设置时间步长上限:
    max_dt = min(0.001f, 0.1f / (ω + 1.0f)); 
    

通过这套角运动模型的实现,不仅能模拟真实物体的旋转行为,还为后续的碰撞响应、关节系统等高级特性奠定了数学基础。接下来的重点应放在碰撞求解器的稳定性优化上,特别是要解决旋转导致的穿透修正问题。

Tags: GameDev Public