角运动学系统设计与实现
▌ 物理基础回顾
关键公式链
τ = 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旋转”现象 | 浮点数精度误差累积 | 约束微小角速度强制置零 |
▌ 工程洞见
- 能量守恒检查工具
void MonitorEnergy() { kinetic = 0.5f * mass*v.SqrLength() + 0.5f*I*ω*ω; if (abs(kinetic - last_kinetic)/kinetic > 0.2f) LogWarning("能量突变!"); }
- 异步更新策略
对高速旋转物体使用子步长积分:
Δt_{sub} = \frac{\theta_{threshold}}{\omega_{current}}
- 网格分辨率启发式规则
自动设置时间步长上限:
max_dt = min(0.001f, 0.1f / (ω + 1.0f));
通过这套角运动模型的实现,不仅能模拟真实物体的旋转行为,还为后续的碰撞响应、关节系统等高级特性奠定了数学基础。接下来的重点应放在碰撞求解器的稳定性优化上,特别是要解决旋转导致的穿透修正问题。