码迷,mamicode.com
首页 > 其他好文 > 详细

双摆的程序实现

时间:2015-02-27 13:21:54      阅读:193      评论:0      收藏:0      [点我收藏+]

标签:

双摆是物理中的一个概念,先给下单摆与双摆的定义:

单摆由一根不可伸长、质量不计的绳子,上端固定,下端系一个质点的装置。

双摆:是一个摆的支点装在另一摆的下部所形成的组合物体。双摆有两个摆角,所以有两个自由度。双摆是多自由度振动系统的最简单的力学模型之一,它也是一种混沌实例。

这里对双摆的实现程序与上一篇文章中三体的程序实现很是相似,要实现它的程序需要一定的数学基础。先写了一个双摆的抽象基类:

  1 // --------------------------------------------------------------------------------------
  2 
  3 inline float     rand2(float a, float b)
  4 {
  5     const float r = (float)(::rand() / ((float)RAND_MAX + 1));
  6     return r*(b-a) + a;
  7 }
  8 
  9 // --------------------------------------------------------------------------------------
 10 
 11 class IDoublePendulum
 12 {
 13 public:
 14 
 15     IDoublePendulum()
 16     {
 17         m_fGravity = 9.8f;
 18 
 19         m_m1 = 1.0f;
 20         m_m2 = 2.0f;
 21     
 22         m_l1 = 1.0f;
 23         m_l2 = 2.0f;
 24     }
 25 
 26     virtual ~IDoublePendulum() {}
 27 
 28     // 重置
 29     virtual void        Reset()
 30     {
 31         m_m1 = rand2(1.0f, 5.0f);
 32         m_m2 = rand2(1.0f, 5.0f);
 33 
 34         m_l1 = rand2(1.0f, 5.0f);
 35         m_l2 = rand2(1.0f, 5.0f);
 36     }
 37 
 38     // 按时更新
 39     virtual void        Update(float deltaTime) = NULL;
 40 
 41     // 重力系数
 42     virtual void        SetGravity(float g)
 43     {
 44         m_fGravity = g;
 45     }
 46 
 47     // 质量
 48     virtual void        SetMass1(float m)
 49     {
 50         m_m1 = m;
 51     }
 52 
 53     virtual void        SetMass2(float m)
 54     {
 55         m_m2 = m;
 56     }
 57 
 58     // 长度
 59     virtual void        SetLength1(float l)
 60     {
 61         m_l1 = l;
 62         UpdatePosition();
 63     }
 64 
 65     virtual void        SetLength2(float l)
 66     {
 67         m_l2 = l;
 68         UpdatePosition();
 69     }
 70 
 71     float               GetGravity() const
 72     {
 73         return m_fGravity;
 74     }
 75 
 76     float               GetMass1() const
 77     {
 78         return m_m1;
 79     }
 80 
 81     float               GetMass2() const
 82     {
 83         return m_m2;
 84     }
 85 
 86     float               GetLength1() const
 87     {
 88         return m_l1;
 89     }
 90 
 91     float               GetLength2() const
 92     {
 93         return m_l2;
 94     }
 95 
 96     const Vec3&         GetPosition1() const
 97     {
 98         return m_pos1;
 99     }
100 
101     const Vec3&         GetPosition2() const
102     {
103         return m_pos2;
104     }
105 
106 protected:
107     // 更新两球位置
108     virtual void        UpdatePosition() = NULL;
109 
110 protected:
111     float m_fGravity;   // 重力系数
112 
113     float m_m1, m_m2;   // 两球质量
114     float m_l1, m_l2;   // 两球距离
115 
116     Vec3 m_pos1, m_pos2;// 两球位置
117 };

从IDoublePendulum中可以看到双摆的几个输入数据是:重力系数,两摆的距离和两球质量。而计算的数据是:每一个时刻两个球的位置。

下一步是对该基类进行继承实现。

.h

 1 // --------------------------------------------------------------------------------------
 2 
 3 #ifndef _DoublePendulum01_H_
 4 #define _DoublePendulum01_H_
 5 
 6 // --------------------------------------------------------------------------------------
 7 
 8 #include "IDoublePendulum.h"
 9 
10 // --------------------------------------------------------------------------------------
11 
12 class DoublePendulum01 : public IDoublePendulum
13 {
14 public:
15     DoublePendulum01()
16     {
17         m_a1 = 1.0f;
18         m_a2 = 2.0f;    
19 
20         m_v1 = 0.0f;
21         m_v2 = 0.0f;
22 
23         m_w1 = m_a1;
24         m_w2 = m_a2;
25     }
26 
27     // 重置
28     void        Reset();
29 
30     // 按时更新
31     void        Update(float deltaTime);
32 
33 protected:
34     void        UpdatePosition()
35     {
36         m_pos1.x = m_l1*sinf(m_w1);
37         m_pos1.y = -m_l1*cosf(m_w1);
38         m_pos1.z = 0.0f;
39 
40         m_pos2.x = m_pos1.x + m_l2*sinf(m_w2);
41         m_pos2.y = m_pos1.y - m_l2*cosf(m_w2);
42         m_pos2.z = 0.0f;
43     }
44 
45 private:
46     float m_a1, m_a2;   // 两球初始角度
47     float m_w1, m_w2;   // 两球当前角度
48     float m_v1, m_v2;   // 两球的角加速度
49 };
50 
51 // --------------------------------------------------------------------------------------
52 
53 #endif

CPP

 1 // --------------------------------------------------------------------------------------
 2 
 3 #include "DoublePendulum01.h"
 4 
 5 // --------------------------------------------------------------------------------------
 6 
 7 /*
 8 求解线型方程组
 9 a*x + b*y + c = 0
10 d*x + e*y + f = 0
11 */
12 inline bool SolveLinalg(float a, float b, float c, float d, float e, float f, float& x, float& y)
13 {
14     float t = b*d - a*e;
15     if (fabs(t) < FLT_EPSILON)
16     {
17         x = 0.0f;
18         y = 0.0f;
19 
20         return false;
21     }
22 
23     x = (c*e - b*f)/t;
24     y = (a*f - c*d)/t;
25 
26     return true;
27 }
28 
29 // --------------------------------------------------------------------------------------
30 
31 void        DoublePendulum01::Reset()
32 {
33     IDoublePendulum::Reset();
34 
35     m_a1 = rand2(-2.0f, 2.0f);
36     m_a2 = rand2(-2.0f, 2.0f); 
37 
38     m_v1 = 0.0f;
39     m_v2 = 0.0f;
40 
41     m_w1 = m_a1;
42     m_w2 = m_a2;
43 
44     UpdatePosition();
45 }
46 
47 void        DoublePendulum01::Update(float deltaTime)
48 {
49     float a = m_l1*m_l1*(m_m1+m_m2);
50     float b = m_l1*m_m2*m_l2*cos(m_w1-m_w2);
51     float c = m_l1*(m_m2*m_l2*sin(m_w1-m_w2)*m_v2*m_v2 + (m_m1+m_m2)*m_fGravity*sin(m_w1));
52 
53     float d = m_m2*m_l2*m_l1*cos(m_w1-m_w2);
54     float e = m_m2*m_l2*m_l2;
55     float f = m_m2*m_l2*(-m_l1*sin(m_w1-m_w2)*m_v1*m_v1 + m_fGravity*sin(m_w2));
56 
57     // 角加速度
58     float dv1;
59     float dv2;
60     SolveLinalg(a, b, c, d, e, f, dv1, dv2);
61 
62     // 角速度
63     m_v1 += dv1*deltaTime;
64     m_v2 += dv2*deltaTime;
65 
66     // 角度
67     m_w1 += m_v1*deltaTime;
68     m_w2 += m_v2*deltaTime;
69 
70     UpdatePosition();
71 }

这里使用的是角度变化实现双摆,参考的资料是:

http://sebug.net/paper/books/scipydoc/double_pendulum.html

它里面的理论有些难,我看得也似懂非懂的。好在公式就在那里,只要会用,不求会懂,程序就能实现。

这个程序是在2D的,双摆可以出现在三维空间中.我也试着写了下,使用力学来进行模拟,不过效果不太好:

.h

技术分享
 1 class DoublePendulum02 : public IDoublePendulum
 2 {
 3 public:
 4     DoublePendulum02()
 5     {
 6         m_fPotentialEnergy1 = 0.0f;
 7         m_fPotentialEnergy2 = 0.0f;
 8         m_velocity1 = Vec3(0.0f, 0.0f, 0.0f);
 9         m_velocity2 = Vec3(0.0f, 0.0f, 0.0f);
10     }
11 
12     // 重置
13     void        Reset();
14 
15     // 按时更新
16     void        Update(float deltaTime);
17 
18 protected:
19     void        UpdatePosition()
20     {
21         Vec3 v = m_pos2 - m_pos1;
22         v.Normalize();
23 
24         m_pos1.Normalize();
25         m_pos1 *= m_l1;
26 
27         m_pos2 = m_pos1 + v*m_l2;
28     }
29 
30 private:
31     float m_fPotentialEnergy1;      // 重力势能1
32     float m_fPotentialEnergy2;      // 重力势能2
33     float m_fAmountOfEnergy;        // 总能量
34     Vec3 m_velocity1, m_velocity2;  // 两球当前速度
35 };
View Code

.cpp

技术分享
  1 // --------------------------------------------------------------------------------------
  2 
  3 #include "DoublePendulum02.h"
  4 
  5 // --------------------------------------------------------------------------------------
  6 
  7 void        DoublePendulum02::Reset()
  8 {
  9     IDoublePendulum::Reset();
 10 
 11     m_pos1.x = rand2(-1.0f, 1.0f);
 12     m_pos1.y = rand2(-1.0f, 0.0f);
 13     m_pos1.z = rand2(-1.0f, 1.0f);
 14     m_pos2.x = rand2(-1.0f, 1.0f);
 15     m_pos2.y = rand2(-1.0f, 0.0f);
 16     m_pos2.z = rand2(-1.0f, 1.0f);
 17     m_pos2 += m_pos1;
 18 
 19     m_velocity1 = Vec3(0.0f, 0.0f, 0.0f);
 20     m_velocity2 = Vec3(0.0f, 0.0f, 0.0f);
 21 
 22     UpdatePosition();
 23 
 24     m_fPotentialEnergy1 = m_m1*m_fGravity*m_pos1.y;
 25     m_fPotentialEnergy2 = m_m2*m_fGravity*m_pos2.y;
 26     m_fAmountOfEnergy = m_fPotentialEnergy1 + m_fPotentialEnergy2;
 27 }
 28 
 29 void        DoublePendulum02::Update(float t)
 30 {
 31     Vec3 line2 = m_pos2 - m_pos1;
 32     line2.Normalize();
 33 
 34     float xzsq = line2.x*line2.x + line2.z*line2.z;
 35     float xz = sqrtf(xzsq);
 36 
 37     // 下面的物体当前的加速度
 38     Vec3 dir2(-line2.x, xzsq/line2.y,-line2.z);
 39     dir2.Normalize();
 40     Vec3 acc2 = dir2*(m_fGravity*xz/m_l2);
 41 
 42     m_pos2.x = m_pos2.x + m_velocity2.x*t + 0.5f*acc2.x*t*t;
 43     m_pos2.y = m_pos2.y + m_velocity2.y*t + 0.5f*acc2.y*t*t;
 44     m_pos2.z = m_pos2.z + m_velocity2.z*t + 0.5f*acc2.z*t*t;
 45 
 46     m_velocity2.x += acc2.x*t;
 47     m_velocity2.y += acc2.y*t;
 48     m_velocity2.z += acc2.z*t;
 49 
 50     // 上面的物体受到下面绳子的拉力
 51     Vec3 force = line2*(-m_m2*m_fGravity*line2.y/m_l2);
 52     // 加上重力
 53     force.y -= m_m1*m_fGravity;
 54 
 55     // 上面的物体
 56     Vec3 line1 = m_pos1;
 57     line1.Normalize();
 58     xzsq = line1.x*line1.x + line1.z*line1.z;
 59     xz = sqrtf(xzsq);
 60     Vec3 dir1(-line1.x, xzsq/line1.y,-line1.z);
 61     dir1.Normalize();
 62 
 63     float dot = dir1|force;
 64     Vec3 acc1 = dir1*(dot/m_m1);
 65 
 66     m_pos1.x = m_pos1.x + m_velocity1.x*t + 0.5f*acc1.x*t*t;
 67     m_pos1.y = m_pos1.y + m_velocity1.y*t + 0.5f*acc1.y*t*t;
 68     m_pos1.z = m_pos1.z + m_velocity1.z*t + 0.5f*acc1.z*t*t;
 69 
 70     m_velocity1.x += acc1.x*t;
 71     m_velocity1.y += acc1.y*t;
 72     m_velocity1.z += acc1.z*t;
 73 
 74     UpdatePosition();
 75 
 76     dot = dir1|m_velocity1;
 77     m_velocity1 = dir1*dot;
 78 
 79     dot = dir2|m_velocity2;
 80     m_velocity2 = dir2*dot;
 81 
 82     float fKineticEnergy1 = 0.5f * m_m1*m_velocity1.MagnitudeSquared();
 83     float fKineticEnergy2 = 0.5f * m_m2*m_velocity2.MagnitudeSquared();
 84 
 85     m_fPotentialEnergy1 = m_m1*m_fGravity*m_pos1.y;
 86     m_fPotentialEnergy2 = m_m2*m_fGravity*m_pos2.y;
 87 
 88     float fKineticEnergy = m_fAmountOfEnergy - m_fPotentialEnergy1 - m_fPotentialEnergy2;
 89 
 90     float r = fKineticEnergy/(fKineticEnergy1 + fKineticEnergy2);
 91     if (r > 0.00001f)
 92     {
 93         fKineticEnergy1 *= r;
 94         fKineticEnergy2 *= r;
 95     }
 96     else
 97     {
 98         fKineticEnergy1 = 1.0f;
 99         fKineticEnergy2 = 1.0f;
100     }
101 
102     float v = sqrt(2*fKineticEnergy1/m_m1);
103     float w = m_velocity1.Magnitude();
104     if (w < FLT_EPSILON)
105     {
106         m_velocity1.x = v;
107     }
108     else
109     {
110         m_velocity1 *= v/w;
111     }
112 
113     v = sqrt(2*fKineticEnergy2/m_m2);
114     w = m_velocity2.Magnitude();
115     if (w < FLT_EPSILON)
116     {
117         m_velocity2.x = v;
118     }
119     else
120     {
121         m_velocity2 *= v/w;
122     }
123 }
View Code

软件截图:

技术分享

技术分享

软件下载地址:

http://files.cnblogs.com/files/WhyEngine/DoublePendulum.7z

使用说明:

程序启动后,会出现三个随机大小的球体在运动.

鼠标右键用于控制视角
键盘U用于开关UI用户界面.
通过UI用户界面可以设置两个球体的质量,连线长度,设置引力系数,设置时间缩放速度,设置球体的显示大小.

键盘1,2用于开关两个球体运动轨迹的显示

键盘G,用于开关三维网格的显示
键盘C,用于开关坐标轴的显示
键盘P,用于暂停
键盘R,用于重置,这时会随机为两个球体设置质量与初速度.

最后发一幅通过双摆绘制的图画:

技术分享

相关文章:三体三体

 

双摆的程序实现

标签:

原文地址:http://www.cnblogs.com/WhyEngine/p/4303078.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!