牛顿摇篮:与钟摆的弹性碰撞

本文关键字:碰撞 钟摆 摇篮 | 更新日期: 2023-09-27 18:35:15

现在

在我的程序中,我有 5 个摆动的钟摆,它们都由滑块值一次修改。这些值被绘制到从并传递到类钟摆上。要更改值,将运行更新按钮,并且有一个默认选项来使用有效的设置。这些值也通过一些简单的标签显示在屏幕上。零按钮将所有值(长度除外)设置为零。

现在我的下一个任务是让钟摆"逼真"碰撞,并重现牛顿摇篮式效果。我已经研究了粒子上的弹性碰撞解决方案,例如,这里有一个很好的例子:球对球碰撞 - 检测和处理

我也在这里研究了它背后的原始物理学,但我的大脑无法掌握事物的原始物理学方面。

所以我想知道是否有人可以帮助我弄清楚碰撞的理论。
我目前的想法是,当坐标相遇时,速度将简单地改变极性,例如在相对的钟摆中+10到-10。我在这里说得对吗?

这是我的钟摆课:

class Pendulum 
{
    //all public static so they are actually global (can be used between classes and not just global to this class).
    public static int length = 10;//length of arm /** can't be less than 0 or will break.
    public static double angle = 0; //pendulums arm angle
    public static double aAcc = 0.00; //Angle acceleration
    public static double aVel = 0.00; //anglular velocity
    public static double damping = 0.000; //friction //friction
    public static double gravity = 0.0; //make gravity a constant
    int originX = 0;
    int originY = 0; //allways 0
    int bobX; // = frmWidth / 2;
    int bobY; // = (int)length; Drawn in pendulm as don't really need to be global. Are currently for flexibilty.
    Timer timer; //global for flexibility
    public Pendulum(int frmWidth, int frmHeight)
    {
        timer = new Timer() { Interval = 30 };
        timer.Tick += delegate(object sender, EventArgs e)
        {
            //-----------------drawing variables------------------------------//
            originX = frmWidth / 2;
            originY = 0;
            //to be relative to origin we go:
            bobX = originX + (int)(Math.Sin(angle) * length);
            bobY = originY + (int)(Math.Cos(angle) * length);
            //gravity
            aAcc = (-1 * gravity / length) * Math.Sin(angle); //calculate acceleration
            aVel += aAcc;//increment velcocity
            angle += aVel;//incrment angle
            aVel *= damping;//friction action, pendulum eventually 0's
        };
        timer.Start();
    }
    public void DrawPendulum(Graphics g) 
    {
            g.DrawLine(Pens.Black, originX, originY, bobX, bobY);
            g.FillEllipse(Brushes.Red, bobX - 8, bobY, 20, 20); //-8 to make it look more central!
    }
}

这是表单代码:

public partial class frmPendulum : Form
{
    private Timer timer;
    private Pendulum p1 = null;
    private Pendulum p2 = null;
    private Pendulum p3 = null;
    private Pendulum p4 = null;
    private Pendulum p5 = null;

    public frmPendulum()
    {
        InitializeComponent();
        this.Shown += new EventHandler(frmPendulum_Shown);
        this.Paint += new PaintEventHandler(frmPendulum_Paint);
    }
    void frmPendulum_Shown(object sender, EventArgs e)
    {
        p1 = new Pendulum(this.ClientRectangle.Width, this.ClientRectangle.Height);
        p2 = new Pendulum(this.ClientRectangle.Width + 40, this.ClientRectangle.Height);
        p3 = new Pendulum(this.ClientRectangle.Width - 40, this.ClientRectangle.Height);
        p4 = new Pendulum(this.ClientRectangle.Width - 80, this.ClientRectangle.Height);
        p5 = new Pendulum(this.ClientRectangle.Width + 80, this.ClientRectangle.Height);
        timer = new Timer() { Interval = 100 };
        timer.Tick += delegate(object s2, EventArgs e2)
        {
                this.Refresh();
                Value.Text = string.Format("Length: " + Pendulum.length + "{0}Angle: " + Pendulum.angle + "{0}Acc: " + Pendulum.aAcc + "{0}Vel: " + Pendulum.aVel + "{0}Damping: " + Pendulum.damping + "{0}Gravity: " + Pendulum.gravity, Environment.NewLine);
                value2.Text = string.Format("Length: " + tbrLength.Value + "{0}Angle: " + ((double)tbrAngle.Value) / 100.0 + "{0}Vel: " + ((double)tbrAVel.Value) / 100.0 + "{0}Damping: " + ((double)tbrDamp.Value) / 100.0 + "{0}Gravity: " + ((double)tbrGrav.Value) / 100.0, Environment.NewLine);
        };
        timer.Start();
    }
    void frmPendulum_Paint(object sender, PaintEventArgs e)
    {
        switch (tbrNoOfPend.Value)
        {
            case 1:
                if (p1 != null) //if used because the Paint() event could occur BEFORE "p1"etc. has been instantiated.
                {
                    p1.DrawPendulum(e.Graphics);
                }
                break;
            case 2:
                if (p2 != null)
                {
                    p2.DrawPendulum(e.Graphics);
                }
                goto case 1;
            case 3:
                if (p3 != null)
                {
                    p3.DrawPendulum(e.Graphics);
                }
                goto case 2;
            case 4:
                if (p4 != null)
                {
                    p4.DrawPendulum(e.Graphics);
                }
                goto case 3;
            case 5:
                if (p5 != null)
                {
                    p5.DrawPendulum(e.Graphics);
                }
                goto case 4;
            default:
                break;
        }
    }

    private void btnDefault_Click(object sender, EventArgs e)
    {
        //sets values to a good calibration by default.
        Pendulum.length = 50;
        Pendulum.angle = Math.PI / 2; //pendulums arm angle
        Pendulum.aAcc = 0.00; //Angle acceleration
        Pendulum.aVel = 0.00; //anglular velocity
        Pendulum.damping = 0.995; //friction //friction
        Pendulum.gravity = 0.4; //make gravity a constant
        UpdateSliders();
    }
    private void UpdateValues()
    {
        /** The trackbars only use integer values so to increment in decimals certain values have to be divided by 100 so they are correct in the simulation.
         * For example is I want the gravity to be 0.4 my track bars value will have to be 40 / 100 = 0.40 
         * Another example will be angle that will go from -3 to 3 incrementing in decimals we go from -300 to 300 /100 = 3 **/
        Pendulum.length = tbrLength.Value; //length is ok as it is an integer
        Pendulum.angle = ((double)tbrAngle.Value) / 100.0; //both numerator and denominator must be of the same type.
       // acceleration is calculated so isn't sent
        Pendulum.aVel = ((double)tbrAVel.Value) / 100.0; 
        Pendulum.damping = ((double)tbrDamp.Value) / 100.0; 
        Pendulum.gravity = ((double)tbrGrav.Value) / 100.0; 
    }
    private void UpdateSliders()
    {
        tbrLength.Value = Pendulum.length;
        tbrAngle.Value = (int)(Pendulum.angle * 100.0); //pendulums arm angle
        //tbrAAcc.Value = (int) Pendulum.aAcc; //Removed acceleration as it is re-calculated anyway.
        tbrAVel.Value = (int)(Pendulum.aVel* 100.0); //anglular velocity
        tbrDamp.Value = (int)(Pendulum.damping * 100.0); //friction //friction
        tbrGrav.Value = (int)(Pendulum.gravity * 100.0); //make gravity a constant
    }
    private void btnUpdateValues_Click(object sender, EventArgs e)
    {
        UpdateValues();
        //this.Shown += new EventHandler(frmPendulum_Shown);
        //this.Paint += new PaintEventHandler(frmPendulum_Paint);
    }
    private void button1_Click(object sender, EventArgs e)
    {
        //zero's everything except length.
        Pendulum.angle = 0; //pendulums arm angle
        Pendulum.aAcc = 0.00; //Angle acceleration
        Pendulum.aVel = 0.00; //anglular velocity
        Pendulum.damping = 0; //friction //friction
        Pendulum.gravity = 0; //make gravity a constant
        UpdateSliders();
    }
}

牛顿摇篮:与钟摆的弹性碰撞

好吧,似乎

你可以自己弄清楚事情的代码方面,老实说,我真的不是 c# 专家,但我会尝试处理这方面的物理方面。

牛顿摇篮很酷的地方在于,无论两个端部之间有多少个球,能量都会守恒或至少近似守恒。在你继续之前,你需要决定你想要的现实程度。众所周知,真正的牛顿摇篮不会永远摆动,但确定每次碰撞损失多少能量是一个非常复杂的物理问题。

如果你想确定每次碰撞损失了多少能量,你将需要很多额外的细节,然后需要通过实际测量钟摆的高度和各种东西来实验计算。

从这里开始,我将假设您正在谈论能量和动量都守恒的完全弹性碰撞。

在处理弹性碰撞时,您需要了解以下几点:

M1*V1 = M2*V2 according to momentum; M is mass, V is velocity
E_kinetic = 1/2*m*v^2 
E_potential = mgh where m = mass, g = acceleration due to gravity, and h = height

现在来看一些钟摆理论:

当你把钟摆拉回来时,你把它拉回某个角度θ,但这个角度只对确定你在y方向上抬高钟摆的高度才重要。钟摆的长度非常重要,就像支撑质量的绳子的长度一样。一旦你弄清楚了你把钟摆抬高了多高,使用θ计算或只知道y位移,你就可以计算摆锤在摆动底部的速度,在牛顿摇篮中,摆锤是钟摆接触下一个球的地方。

要使用θ计算钟摆的高度,请将钟摆的长度

乘以θ的余弦,然后从摆锤的长度中减去该高度。或者在数学中:

l = length of pendulum
theta = angle of the pulled back pendulum relative to the vertical axis
h = height
now actual calculations:
h = l - l*cos(theta)

有了这个h,我们现在可以使用之前的能量公式来确定钟摆在其路径底部的速度。

由于能量在这个系统中是守恒的,当我们把它拉回来时,球是静止的,我们知道它的最大势能在其拉回路径的顶部,当我们把它拉回来时没有任何动能,因为当我们保持它时它的速度为零。

KE = PE
PE = m * g * h
KE = 1/2 * m * v^2
m * g * h = 1/2 * m * v^2
v cancels
g * h = 1/2 * v^2
v = sqrt(2 * g * h)
(g = 9.8 m/s^2 or 32 f/s^2)

现在你有了V,你可以弄清楚其他不同质量的钟摆对被你的第一个钟摆撞击会有什么反应。

程序而言,无聊的是,如果你假设弹性碰撞,钟摆中的中间球并不重要。

此外,摇篮另一端的钟摆将摆动到您拉动第一个钟摆的确切高度。虽然在现实中会有一些能量损失。如果你想弄清楚以某种作弊的方式损失了多少能量,你可以看看牛顿摇篮在停止移动之前会点击多少次。

假设系统从 100 焦耳的能量

开始,在它们停止移动之前需要 100 次球碰撞,你知道每次它们碰撞时都会损失大约 1 焦耳的能量。系统启动能量可以使用任一能量公式计算,但您必须记住重新添加质量。

一旦你知道每次碰撞损失了多少能量,你就反过来计算这些公式,看看另一边会上升多高。

Calculate the kinetic energy
subtract your calculated loss
plug into the potential energy formula
determine the height from the potential energy formula