摆锤模拟仅执行一个周期

时间:2015-08-16 21:54:17

标签: java swing debugging simulation

我正在对钟摆进行模拟,但它只发出一个摆动,然后发送接近随机位置的bob。从本质上讲,它不会倒退。

我尝试使用 goingForward 布尔值来改变方向,但它仍然无效。

public class AnimationPane extends JPanel {
    // START CHANGEABLE VARIABLES
    private double startAngle = -60.0; // degrees
    private double mass = 1; // kilogrammes
    private int radius = 10; // m
    private double gravity = 9.80665; // m/s^2 // on earth: 9.80665
    // END CHANGEABLE VARIABLEs
    private BufferedImage ball;
    private BufferedImage rope;
    private int pointX = 180;
    private int pointY = 50;
    private double endAngle = Math.abs(startAngle); // absolute value of startAngle
    private double angle = startAngle; // current angle
    private double circum = (2 * Math.PI * radius); // m
    private double distance = 0; // m
    private double velocity = 0; // m/s
    private double totalEnergy = ((radius) - (Math.cos(Math.toRadians(angle)) * radius)) * gravity * mass + 0.00001;
    private double previousE;
    private int xPos = 0; // for program
    private int yPos = 0; // for program
    private boolean goingForward = true;
    private double height = 0;

    public AnimationPane() {
        try {
            ball = ImageIO.read(new File("rsz_black-circle-mask-to-fill-compass-outline.png"));
            Timer timer = new Timer(100, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    double angleRad = Math.toRadians(Math.abs(angle));

                    double potentialE = ((radius) - (Math.cos(angleRad) * radius)) * gravity * mass;
                    Double pE = new Double(potentialE);
                    height = (radius - (Math.cos(angleRad) * radius));
                    double kineticE = totalEnergy - pE;

                    if (kineticE <= 0 || angle >= endAngle) {

                        if (goingForward == true) {
                            goingForward = false;
                        }
                        else 
                        {
                            goingForward = true; 
                        }
                        kineticE = 0.1; 
                        angle = 60;
                    }

                    velocity = Math.sqrt(2 * kineticE / mass);
                    double ratio = distance / circum;

                    if (goingForward == true) {                           
                        distance = distance + (velocity / 10);
                        angle = startAngle + (360 * ratio);
                    }
                    else {
                        distance = distance - (velocity / 10);
                        angle = startAngle - (360 * ratio);
                    }                        

                    double angles = Math.toRadians(angle);

                    double xDouble = Math.sin(angles) * (radius * 10);
                    Double x = new Double(xDouble);
                    xPos = x.intValue() + 150;

                    double yDouble = Math.cos(angles) * (radius * 10);
                    Double y = new Double(yDouble);
                    yPos = y.intValue() + 50;

                    repaint();
                }

            });
            timer.setRepeats(true);
            timer.setCoalesce(true);
            timer.start();
        } catch (IOException ex) {
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        g.drawLine(xPos + 20, yPos + 20, pointX, pointY);
        g.drawImage(ball, xPos, yPos, this);

    }

}

我真的很感谢帮助这项工作。 谢谢

2 个答案:

答案 0 :(得分:1)

我无法调试您的代码,这些代码很难处理并且有时难以理解(您在代码中使用了大量的整数文字,隐藏了它们的语义,我不知道您对某些语句的意图是什么)。

因此,我使用微振动方程的解决方案重写了它。它可以工作,您可以将其作为一个干净的基础,以您想要的方式再次实现它。请注意,正如 Andy Turner 指出的那样,您不必担心前进或后退的事实。你有一个等式,你解决它,它随时给你球的位置。如果你想要一些对大角度准确的东西,我建议你去维基百科看看这种情况下的运动方程。最后一个选项,你可以在数值上解决微分方程,虽然我个人不会乍看之下知道怎么做。

package stackoverflow;

public class AnimationPane extends JPanel {
    private static final long   serialVersionUID    = 1L;
    private static final double GRAVITY             = 9.80665;

    private BufferedImage ball;

    private final Point fixedCordPoint;
    private final int cordLength;
    private final double startAngle;
    private double currentAngle; 

    private final double pulsation;
    private final Point ballPos = new Point();
    private int time = 1;

    public AnimationPane(Point fixedCordPoint, int cordLength, double startAngleRadians) {
        this.fixedCordPoint = new Point(fixedCordPoint);
        this.cordLength     = cordLength;
        this.pulsation      = Math.sqrt(GRAVITY / cordLength);
        this.startAngle     = startAngleRadians;
        this.currentAngle   = startAngleRadians;
        this.ball           = loadImage(new File("ball.jpg"));
    }

    private BufferedImage loadImage(File file) {
        try {
            return ImageIO.read(file);
        } catch (IOException e) {
            throw new RuntimeException("Could not load file : " + file, e);
        }
    }

    public void start() {
        Timer timer = new Timer(100, event -> {
            ballPos.x = fixedCordPoint.x + (int) Math.round(Math.sin(currentAngle) * cordLength);
            ballPos.y = fixedCordPoint.y + (int) Math.round(Math.cos(currentAngle) * cordLength);
            repaint();
            currentAngle = startAngle * Math.cos(pulsation * time);
            time++;
        });
        timer.setRepeats(true);
        timer.setCoalesce(true);
        timer.start();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawLine(ballPos.x, ballPos.y, fixedCordPoint.x, fixedCordPoint.y);
        g.drawImage(ball, ballPos.x - ball.getWidth() / 2, ballPos.y - ball.getHeight() / 2, this);
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        AnimationPane pendulumAnimationPane = new AnimationPane(new Point(160, 25), 180, - Math.PI / 10);
        frame.setContentPane(pendulumAnimationPane);
        frame.setSize(400,400);
        frame.setVisible(true);

        pendulumAnimationPane.start();
    }
}

答案 1 :(得分:0)

我真的无法遵循您的代码。如果我不得不说出我认为问题出在哪里,我会说它是distance变量,并在ratio中使用它:这似乎被定义为在钟摆开始时为零。当你切换钟摆的方向时,我认为需要将其重新设定为零...不知何故。

您还应该从模拟中分离出GUI代码。您可以在调试时将其打印出x和y坐标。例如,您可以将值写入CSV文件,这样您就可以在Matlab(或其他)中可视化它们,以确保您的模拟随着时间的推移而正确。

我会提出两个建议来帮助你进行模拟:

  1. 不要在能量方面对其进行建模,这是一个标量,这意味着你必须使用额外的状态,如方向标记&#34;模拟它的走向;
  2. 您将需要一个额外的状态变量。存储x和y是多余的,因为这些可以直接从角度导出(和R,尽管这是常数)。您正在为二阶系统建模,因此您需要第二个状态变量来模拟系统瞬间的运动及其位置。例如,您可以对角度和角速度进行建模。
  3. 当然,不要乱解学位 - 坚持弧度:)

    (关于速度问题,你的名字变量实际上是速度 - 你不知道它移动的方向,而且这些信息与系统的动态密切相关。)

    使用角度和角速度的方法的推导是非常简单的,虽然我的数学生锈,足以提醒它使用而不仔细检查!

    rotational form of Newton's second law of motion是:

    Torque = moment of inertia * angular acceleration
    
    • 鲍勃上的扭矩由-mgR sin angle给出(m是质量,g是重力,R是摆的长度,angle是摆锤从垂直方向的角位移)。减号是因为力趋于使鲍勃返回垂直;
    • 点质量的惯性矩是mR^2 - 如果摆锤与摆锤的半径相比非常长,这是一个合理的近似值。

    因此角加速度为-g sin theta / R。希望这应该看起来像简单的谐波运动 - 与平衡距离成比例的加速度(对于小θ,sin theta约为theta)并指向它。

    您现在可以将它们组合成一个数值方案来模拟钟摆。例如,使用Euler's method

    New angle            = old angle                + dt * old angular velocity
    New angular velocity = old angular velocity vel - dt * g * sin(angle) / R
    

    dt是你的时间步。

    当然,您可以使用诸如Runge-Kutta之类的高阶方法来减少模拟中的数值误差,但它(稍微)更多地涉及。