C++ Raylib双摆模拟器角加速度方程实现故障咨询
双摆模拟器角加速度方程实现问题修复
核心问题分析
你的双摆模拟失效主要源于三个关键错误:
角度单位混用
初始输入的角度是度数,setAngle()会将其转成弧度存入pAngle,但循环中你直接用uAngle1 += angularV1(angularV1是弧度/秒单位),随后又调用setAngle(uAngle1)将混合单位的数值再次转成弧度,导致角度数值异常放大,完全偏离真实物理状态。缺失时间步长控制
物理模拟中,角加速度(弧度/秒²)和角速度(弧度/秒)的更新必须乘以每帧的时间间隔(dt),否则相当于假设每帧时长为1秒,运动速度和加速度会极度失真。你当前直接累加数值,未考虑帧率波动带来的时间差异。角加速度方程符号偏差
第二个角加速度方程的分子部分,因坐标系的细微差异,g(m1+m2)cosθ1的符号应为负,你当前的正号会导致受力方向错误。
修复方案
1. 统一角度单位(全程用弧度)
- 初始输入角度后,直接转成弧度存储,不再依赖
setAngle()的度数转弧度逻辑:// 初始角度处理 uAngle1 *= DEG2RAD; uAngle2 *= DEG2RAD; pen1.pAngle = uAngle1; pen2.pAngle = uAngle2; - 循环中直接更新弧度值,不再调用
setAngle()避免重复转换:uAngle1 += angularV1 * dt; angularV1 += angularA1 * dt; pen1.pAngle = uAngle1;
2. 添加时间步长控制
每次循环获取当前帧的时间间隔,用于物理量更新:
float dt = GetFrameTime();
3. 修正角加速度方程符号
第二个角加速度的num3改为负号:
num3 = -g * (pen1.getMass() + pen2.getMass()) * cos(pen1.getAngle());
完整修正后的main.cpp关键片段
// 初始角度处理(替换原setAngle调用) uAngle1 *= DEG2RAD; uAngle2 *= DEG2RAD; pen1.setMass(uMass1); pen1.setLength(uLength1); pen1.pAngle = uAngle1; pen1.setX(pen1.pLength, pen1.getAngle()); pen1.setY(pen1.pLength, pen1.getAngle()); pen2.setMass(uMass2); pen2.setLength(uLength2); pen2.pAngle = uAngle2; pen2.setX(pen2.pLength, pen2.getAngle()); pen2.setY(pen2.pLength, pen2.getAngle()); pen2.x = pen1.getX() + pen2.getX(); pen2.y = pen1.getY() + pen2.getY(); // 主循环内 while (!WindowShouldClose()) { float dt = GetFrameTime(); // 获取时间步长 Vector2 rod1{px1,py1}; Vector2 rod2 {px2, py2}; // 摆1角加速度计算(原逻辑正确,保留) float num1 = -g * (2 * pen1.getMass() + pen2.getMass()) * sin(pen1.getAngle()); float num2 = -pen2.getMass() * g * sin(pen1.getAngle() - 2 * pen2.getAngle()); float num3 = -2 * sin(pen1.getAngle() - pen2.getAngle()) * pen2.getMass(); float num4 = pow(angularV2, 2) * pen2.getLength() + pow(angularV1,2) * pen1.getLength() * cos(pen1.getAngle() - pen2.getAngle()); float den1 = pen1.getLength() * (2*pen1.getMass() + pen2.getMass() - pen2.getMass() * cos(2*pen1.getAngle() - 2 * pen2.getAngle())); float angularA1 = (num1 + num2 + num3*num4) / den1; // 摆2角加速度计算(修正num3符号) num1 = 2 * sin(pen1.getAngle() - pen2.getAngle()); num2 = (pow(angularV1,2.0) * pen1.getLength() * (pen1.getMass() + pen2.getMass())); num3 = -g * (pen1.getMass() + pen2.getMass()) * cos(pen1.getAngle()); // 这里改负号 num4 = pow(angularV2,2.0) * pen2.getLength() * pen2.getMass() * cos(pen1.getAngle() - pen2.getAngle()); den1 = pen2.getLength() * (2*pen1.getMass() + pen2.getMass() - pen2.getMass() * cos(2*pen1.getAngle() - 2*pen2.getAngle())); float angularA2 = (num1 * (num2 + num3 + num4)) / den1; // 更新部分(添加dt,修正角度赋值) frameCounter++; uAngle1 += angularV1 * dt; angularV1 += angularA1 * dt; pen1.pAngle = uAngle1; // 直接赋值弧度,不再调用setAngle pen1.setX(pen1.pLength, pen1.getAngle()); pen1.setY(pen1.pLength, pen1.getAngle()); px1 = pen1.getX() + origin.x; py1 = pen1.getY() + origin.y; uAngle2 += angularV2 * dt; angularV2 += angularA2 * dt; pen2.pAngle = uAngle2; // 直接赋值弧度 pen2.setX( pen2.pLength, pen2.getAngle()); pen2.setY( pen2.pLength, pen2.getAngle()); pen2.x = pen1.getX() + pen2.getX(); pen2.y = pen1.getY() + pen2.getY(); px2 = pen2.getX() + origin.x; py2 = pen2.getY() + origin.y; // 其余绘制和文件写入逻辑保留 }
额外建议
- 可以给
pendulum类添加setAngleRad()方法,专门用于设置弧度角度,避免单位混淆问题。 - 增大重力常数
g的值(比如设为980,对应像素坐标系下的重力),模拟效果会更贴近真实。
内容的提问来源于stack exchange,提问作者Cr3
相关产品推荐
相关产品推荐

