双摆代码运行后瞬间消失,变量返回NaN值的原因排查求助
解决双摆瞬间消失并出现NaN值的问题
我来帮你排查这个问题,你的双摆显示一瞬间就消失,之后变量出现NaN,核心问题出在角度转弧度的错误处理,以及数值计算中的几个关键细节上,下面一步步分析并修复:
问题根源分析
错误的角度转弧度逻辑
你在convDeg函数里把180度直接返回0,这完全违背了弧度转换的数学逻辑——180度对应的弧度是M_PI,不是0。这个错误会直接导致三角函数(sin/cos)计算出完全错误的值,进而让后续的加速度、速度计算出现异常,很快就会产生NaN。未对角度进行模运算
双摆的角度a1和a2会随着循环不断累加,数值会越来越大,最终超出三角函数的有效计算范围,导致计算结果异常。分母可能为0的风险
计算den的表达式里有cos(convDeg(2*a1-2*a2)),如果角度差对应的余弦值恰好让分母为0,除法运算就会直接产生NaN,而错误的弧度转换会更快触发这个情况。
修复后的代码及解释
下面是修改后的完整代码,我会标注关键修改点:
#undef __STRICT_ANSI__ #include "window.h" #include <SFML/Graphics.hpp> #include <GL/glew.h> #include <cmath> #include <iostream> namespace Window { sf::ContextSettings settings; sf::RenderWindow window(sf::VideoMode(600, 600), "Window", sf::Style::Close | sf::Style::Titlebar, settings); void init() { settings.depthBits = 24; settings.majorVersion = 4; settings.minorVersion = 6; //OpenGL 4.6 glewInit(); glViewport(0,0, 600, 600); } void close() { window.close(); } void update() { window.display(); } void checkForClose() { sf::Event windowEvent; while (window.pollEvent(windowEvent)) { if (windowEvent.type == sf::Event::Closed) { close(); } } } bool isOpen() { return window.isOpen(); } // 修复1:正确的角度转弧度函数,移除错误的180度判断 double convDeg(double deg) { return deg * M_PI / 180.0; } // 新增:将角度限制在0-360度范围内,避免数值溢出 double clampAngle(double deg) { double clamped = fmod(deg, 360.0); if (clamped < 0) clamped += 360.0; return clamped; } void runLoop() { double r1 = 100; double m1 = 40; double a1 = 90; double a1_v = 0; double r2 = 100; double m2 = 40; double a2 = 30; double a2_v = 0; double x1 = 0; double x2 = 0; double y1 = 0; double y2 = 0; double g = 0.005; double num1 = 0; double num2 = 0; double num3 = 0; double num4 = 0; double den = 0; double a1_a = 0; double a2_a = 0; while (window.isOpen()) { // 修复2:先对角度做模运算,避免数值过大 a1 = clampAngle(a1); a2 = clampAngle(a2); // 所有弧度转换使用正确的convDeg函数 num1 = -g * (2 * m1 + m2) * std::sin(convDeg(a1)); num2 = -m2 * g * std::sin(convDeg(a1 - 2*a2)); num3 = -2 * std::sin(convDeg(a1 - a2)) * m2; num4 = a2_v*a2_v*r2 + a1_v*a1_v*r1 * std::cos(convDeg(a1 - a2)); den = r1 * (2*m1 + m2 - m2 * std::cos(convDeg(2*a1 - 2*a2))); // 修复3:添加分母非零判断,避免除以0产生NaN if (std::fabs(den) > 1e-6) { a1_a = (num1 + num2 + num3*num4) / den; } else { a1_a = 0; // 分母接近0时,暂时将加速度设为0 } num1 = 2 * std::sin(convDeg(a1 - a2)); num2 = (a1_v*a1_v*r1*(m1 + m2)); num3 = g * (m1 + m2) * std::cos(convDeg(a1)); num4 = a2_v*a2_v*r2*m2 * std::cos(convDeg(a1 - a2)); den = r2 * (2*m1 + m2 - m2 * std::cos(convDeg(2*a1 - 2*a2))); // 同样添加分母非零判断 if (std::fabs(den) > 1e-6) { a2_a = (num1*(num2 + num3 + num4)) / den; } else { a2_a = 0; } x1 = 300 + (r1 * std::sin(convDeg(a1))); y1 = 300 + (r1 * std::cos(convDeg(a1))); x2 = x1 + (r2 * std::sin(convDeg(a2))); y2 = y1 + (r2 * std::cos(convDeg(a2))); a1_v += a1_a; a2_v += a2_a; a1 += a1_v; a2 += a2_v; sf::Vertex pOne[] = { sf::Vertex(sf::Vector2f(300,300)), sf::Vertex(sf::Vector2f(x1,y1)), }; sf::Vertex pTwo[] = { sf::Vertex(sf::Vector2f(x1,y1)), sf::Vertex(sf::Vector2f(x2,y2)), }; window.clear(); window.draw(pOne, 2, sf::Lines); window.draw(pTwo, 2, sf::Lines); update(); checkForClose(); // 可以保留调试输出,观察数值变化 // std::cout << std::sin(convDeg(a1)) << "\n"; } } }
关键修改说明
- 修正
convDeg函数:移除了错误的180度判断,严格按照数学公式进行弧度转换,这是解决问题的核心。 - 新增角度模运算函数
clampAngle:将角度限制在0-360度范围内,避免角度数值无限累加导致三角函数计算异常。 - 添加分母非零判断:在计算加速度前检查分母的绝对值是否大于一个极小值(
1e-6),如果接近0则将加速度设为0,避免除以0产生NaN。 - 保留原有绘图逻辑:确保每次循环都重新绘制双摆,避免显示异常。
经过这些修改后,双摆应该能正常持续运动,不会再出现瞬间消失和NaN的问题了。
内容的提问来源于stack exchange,提问作者Don
相关产品推荐
相关产品推荐

