Methoden Berechnung von Anfangsgeschwindigkeit und Winkel

CptK

Bekanntes Mitglied
Hallo, ich habe folgende Klasse, die eine Kanonenkugel erstellt:
Java:
package Data;

import java.util.Timer;
import java.util.TimerTask;

public class Kanonenkugel {
  
    private Timer timer;
    private double position_x;  //aktuelle position (x;y)
    private double position_y;
    private double bewegung_x; //speed in x bzw. y richtung
    private double bewegung_y;
    private double wind = 0;  //wind
    private double schwerkraft;  //fallbeschleunigung
    private double masse; //masse des objekts
    private double aero;  //luftwiderstand
    private int size;

    public Kanonenkugel(int start_x, int start_y, double wurf_x, double wurf_y, double masse, double aero, double schwerkraft, int size) {
        this.position_x=start_x;
        this.position_y=start_y;
        this.bewegung_x=wurf_x;
        this.bewegung_y=wurf_y;
        this.masse=masse;
        this.aero=aero;
        this.schwerkraft=schwerkraft;
        this.size = size;
      
        timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                nextStep();
            }
        }, 0, 20);  
    }
  
    public void nextStep() {
        bewegung_x+=wind*aero;
        bewegung_y+=schwerkraft*masse;
        position_x+=bewegung_x;
        position_y+=bewegung_y;
    }
  
    public int getX() {
        return (int)position_x;
    }
  
    public int getY() {
        return (int)position_y;
    }
  
    public int getSize() {
        return size;
    }

}

Mithilfe von einem Abschusswinkel und einer Abschussgeschwindigkeit werden die beiden Vektoren der Bewegung berechnet:
Code:
   double s = ?;  //Anfangsgeschwindigkeit
            double w =?;  //Abschusswinkel
            //Berechnet die Kräfte in x und y Richtung
            double sx=Math.sin(Math.toRadians(w))*s;
            double sy=-Math.cos(Math.toRadians(w))*s;

Mein Problem ist jetzt folgendes:
Ich habe einen Anfangspunkt A(x|y) und einen Zielpunkt Z(x|y).
Wie berechne ich jetzt die Anfangsgeschwindigkeit und den Winkel, sodass die Kugel bei A losfliegt und Punkt Z auf der Flugbahn liegt?
 
Bevor das so weitergeht:
Java:
import java.awt.geom.Point2D;

public class Wurfparabel {
     public static final double G = 9.81;
     private final Point2D start;
     private final double winkel;
     private final double geschwindigkeit;

     public Wurfparabel(Point2D start, double winkel, double geschwindigkeit) {
         this.start = start;
         this.winkel = winkel;
         this.geschwindigkeit = geschwindigkeit;
     }

     public Point2D start() { return start; }
     public double winkel() { return winkel; }
     public double geschwindigkeit() { return geschwindigkeit; }

     public Point2D punkt(double zeit) {
         double x = start.getX() + geschwindigkeit * zeit * Math.cos(winkel);
         return new Point2D.Double(x, y(x));
     }

     public double y(double x) {
         return start.getY() + yRelative(x - start.getX());
     }

     public double yRelative(double x) {
         double cos = Math.cos(winkel);
         return x * Math.tan(winkel) - G * x * x /
             (2 * geschwindigkeit * geschwindigkeit * cos * cos);
     }

     public Wurfparabel treffe(Point2D ziel, double zeit) {
         double y = ziel.getY() - start.getY();
         double x = ziel.getX() - start.getX();
         double w = Math.atan((2 * y + G * zeit * zeit) / (2 * x));
         double v = x / (zeit * Math.cos(w));
         return new Wurfparabel(start, w, v);
     }

     public static Wurfparabel treffe(Point2D start, Point2D ziel,
                                      double zeit) {
         return new Wurfparabel(start, 0, 0).treffe(ziel, zeit);
     }
}
 
Und hier hat jemand nicht in Physik aufgepasst:
`bewegung_y+=schwerkraft*masse;`
Die Fallgeschwindigkeit ist im freien Fall (also ohne Luftwiderstand) unabhängig von der Masse des Körpers. Grund: Es wirkt zwar aufgrund der höheren Masse eine höhere Anziehungskraft auf den Körper, allerdings wird diese wieder durch die Massenträgheit des Körpers annulliert. Ein Körper mit größerer Masse benötigt eben auch eine größere auf ihn einwirkende Kraft, um ihn in gleichem Maße zu beschleunigen wie einen Körper mit geringerer Masse.
 
Arbeitet hier vielleicht jemand in der Forensischen Ballistik? 😀 (SORRY, den schlechten Wortwitz konnte ich mir jetzt nicht verkneifen 😉)
 
Und hier hat jemand nicht in Physik aufgepasst:
`bewegung_y+=schwerkraft*masse;`
Die Fallgeschwindigkeit ist im freien Fall (also ohne Luftwiderstand) unabhängig von der Masse des Körpers. Grund: Es wirkt zwar aufgrund der höheren Masse eine höhere Anziehungskraft auf den Körper, allerdings wird diese wieder durch die Massenträgheit des Körpers annulliert. Ein Körper mit größerer Masse benötigt eben auch eine größere auf ihn einwirkende Kraft, um ihn in gleichem Maße zu beschleunigen wie einen Körper mit geringerer Masse.
Also was das angeht habe ich keine Ahnung und die Kugel fliegt ne Kurve so wie ich mir das vorstelle also ist mir das relativ egal 😛
 
Ich habe nochmal ne Frage: Wieso brauche ich die Zeit in der Klasse Wurfparabel? Geht das nicht unabhängig davon?
 
Du wolltest fragen: "Wieso benötige ich in der Methode, die zu einem Startpunkt und einem Zielpunkt die Wurfparabel berechnet, auch noch die Zeit als zusätzlichen Parameter?"
Antwort: Weil es unendlich viele mögliche Parabeln gibt zwischen Start und Ende. Du kannst einen Ball von einem Startpunkt auf einen Zielpunkt entweder sehr direkt mit sehr flachem Winkel und sehr hoher Geschwindigkeit werfen, oder du wirst ihn in gaaaanz hohem Bogen nahzu senkrecht nach oben mit sehr hoher Geschwindigkeit, so dass er irgendwann in ein paar Tagen den Zielpunkt erreicht. Und alles zwischen diesen Extremen ist auch möglich.
 
Und irgendwie funktioniert das nicht: Egal was ich eingebe, ich kriege NIE einen Winkel der größer als 2 oder kleiner als -2 ist, das kann doch irgendwie nicht sein oder?
 
In welche Richtung ist denn 0°? oben/unten/links/rechts?
Ich habe jetzt folgende Punkte getestet:
Start: 900|200
Ziel: 70 | 420
Der Winkel müsste als ungefähr -70 oder so was in die Richtung sein (Wenn 0° im Kreis nach oben ist).
Wenn ich das aber mit Zeit = 10 mache für
Java:
Wurfparabel parabel = new Wurfparabel(new Point2D.Double(900,200), 0, 0).treffe(new Point2D.Double(70, 420), 10);
        double winkel = Math.toDegrees(parabel.winkel);
bekomme ich für den winkel -0.27096785933509615 raus.
 
Benutze atan2, denn atan alleine kann nicht zwischen allen vier Quadranten unterscheiden.
Also ändere:
`double w = Math.atan((2 * y + G * zeit * zeit) / (2 * x));`
zu:
`double w = Math.atan2(2 * y + G * zeit * zeit, 2 * x);`
 
Joa, der Winkel ist schon mal sinnvoller allerdings habe ich jetzt das Problem, wenn ich das ganze mit der Klasse die ich ganz am Anfang gezeigt habe benutze, da nicht der Punkt getroffen wird den ich haben will.
Falls es jetzt mal jemand selbst testen will schicke ich nochmal beide Klassen so, wie ich sie gerade habe:
Java:
package TEST;

import java.awt.geom.Point2D;

public class Wurfparabel {
     public static final double G = 9.81;
     private final Point2D start;
     private final double winkel;
     private final double geschwindigkeit;

     public Wurfparabel(Point2D start, double winkel, double geschwindigkeit) {
         this.start = start;
         this.winkel = winkel;
         this.geschwindigkeit = geschwindigkeit;
     }

     public Point2D start() { return start; }
     public double winkel() { return winkel; }
     public double geschwindigkeit() { return geschwindigkeit; }

     public Point2D punkt(double zeit) {
         double x = start.getX() + geschwindigkeit * zeit * Math.cos(winkel);
         return new Point2D.Double(x, y(x));
     }

     public double y(double x) {
         return start.getY() + yRelative(x - start.getX());
     }

     public double yRelative(double x) {
         double cos = Math.cos(winkel);
         return x * Math.tan(winkel) - G * x * x /
             (2 * geschwindigkeit * geschwindigkeit * cos * cos);
     }

     public Wurfparabel treffe(Point2D ziel, double zeit) {
         double y = ziel.getY() - start.getY();
         double x = ziel.getX() - start.getX();
         double w =  Math.atan2(2 * y + G * zeit * zeit, 2 * x);
         double v = x / (zeit * Math.cos(w));
         return new Wurfparabel(start, w, v);
     }

     public static Wurfparabel treffe(Point2D start, Point2D ziel,
                                      double zeit) {
         return new Wurfparabel(start, 0, 0).treffe(ziel, zeit);
     }
   
     public static void main(String[] args) {
        treffe(new Point2D.Double(900,200), new Point2D.Double(70, 420), 10);
        Wurfparabel parabel = new Wurfparabel(new Point2D.Double(900,200), 0, 0).treffe(new Point2D.Double(70, 420), 1);
        double winkel = Math.toDegrees(parabel.winkel);
        winkel += 90;
        double s = 10;
        double sx=Math.sin(Math.toRadians(winkel))*s;
        double sy=-Math.cos(Math.toRadians(winkel))*s;
        System.out.println(sx +  " " + sy);
        new Kanonenkugel(900, 200, sx, sy, 0.1, 1);
      
    }
}
Java:
package TEST;

import java.util.Timer;
import java.util.TimerTask;

public class Kanonenkugel {
  
    private Timer timer;
    private double position_x;  //aktuelle position (x;y)
    private double position_y;
    private double bewegung_x; //speed in x bzw. y richtung
    private double bewegung_y;
    private double schwerkraft;  //fallbeschleunigung
    private int size;

    public Kanonenkugel(int start_x, int start_y, double wurf_x, double wurf_y, double schwerkraft, int size) {
        this.position_x=start_x;
        this.position_y=start_y;
        this.bewegung_x=wurf_x;
        this.bewegung_y=wurf_y;
        this.schwerkraft=schwerkraft;
        this.size = size;
      
        timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                nextStep();
            }
        }, 0, 1);  
    }
  
    public void nextStep() {
        bewegung_y+=schwerkraft;
        position_x+=bewegung_x;
        position_y+=bewegung_y;
        System.out.println(getX() + " | " + getY());
        if(getX() > 1100 || getX() < 0) {
            System.exit(0);
        }
    }
  
    public int getX() {
        return (int)position_x;
    }
  
    public int getY() {
        return (int)position_y;
    }
  
    public int getSize() {
        return size;
    }

}
 
Zuletzt bearbeitet:
Du integrierst auch über viel zu große Zeitintervalle von einer Sekunde linear. Es gilt ja:
`a = dv/dt`, somit: `dv = a * dt` und:
`v = ds/dt`, somit: `ds = v * dt`
Du müsstest jetzt also eine numerische Integration über möglichst kleine `dt` vornehmen.
Das Problem ist, dass deine Berechnung quasi eine Simulation per numerischer Integration ist, statt denn eine analytische Gleichung.
 
Praktisch gesehen, ändere:
Java:
bewegung_y+=schwerkraft;
position_x+=bewegung_x;
position_y+=bewegung_y;
zu:
Java:
bewegung_y += schwerkraft * dt;
position_x += bewegung_x * dt;
position_y += bewegung_y * dt;
für möglichst kleine `dt`.

Die analytische Lösung von @mihe7 für den Winkel und die Geschwindigkeit erfordert, dass `dt` gegen Null geht, was bei numerischer Integration aus praktischen Gründen nicht geht. Du wirst also niemals ganz exakt den Zielpunkt treffen, sondern kannst dich diesem nur beliebig gut annähern mit kleinerem `dt`.
 
Also: Deine Simulation, wenn es z.B. ein Echtzeitspiel ist, hat ja eine gewisse Tickrate (bei reiner grafischer Ausgabe auch Framerate genannt). Diese sagt ja quasi, wieviele Frames (bzw. in deinem Fall Simulationsschritte) du pro Sekunde berechnen kannst. Das Zeitintervall zwischen jeweils zwei Schritten nennen wir mal dT (hier mal mit einem großen 'T'). Wenn du z.B. alle 10 Millisekunden einmal einen Simulationsschritt berechnest, wäre `dT = 0.01`, also 10 Millisekunden in Sekunden umgerechnet.
Ein Simulationsschritt wäre bei dir etwa eine Ausführung der run() Methode vom TimerTask.
Was du jetzt machen müsstest, ist, erstmal die Zeit messen, die zwischen jeweils zwei Ausführungen der TimerTask.run() Methode vergehen. Die kann nämlich stark von der gewünschten Zeit abweichen. Z.B. wird der TimerTask garantiert nicht eine Granularität von 1ms. erreichen können. Das Zeitintervall könntest du möglichst genau mit der Differenz von `System.nanoTime() / 1E9f` zwischen jeweils zwei Aufrufen des Timers in Sekunden erreichen.
Wenn du diesen Wert hast, könntest du jetzt noch dein gewünschtes Integrationsintervall`dt` (mit kleinem 't') festlegen. Wenn du willst, dass eine möglichst genaue Simulation vorgenommen wird, kannst du z.B. einfach mal `float dt = 1E-4f` verwenden. Jetzt müsstest du nur noch solange wiederholt deine nextStep() Methode mit eben diesem `dt` als Argument aufrufen, bis die aufkumulierten `dt`s deinem `dT` entsprechen, das heißt also, bis die Simulation die aktuell vergangene Echtzeit eingeholt hat. Das kannst du z.B. mit einer einfachen `do { nextStep(dt); dT -= dt; } while (dT > dt);` Schleife erreichen. Das wäre zwar noch nicht 100%ig korrekt so, aber reicht für den Anfang.
 
Gut, ich wollte schon fragen ob es da nicht irgendwas einfacheres gibt, aber wirklich weiter komme ich da auch nicht
 
Du merkst Dir den Zeitpunkt der Schussabgabe und berechnest in Deinem Timer die Differenz -> damit weißt Du, welche Zeit seit Schussabgabe vergangen ist und das setzt Du in die Methode ein und bekommst den Punkt zurück, an dem sich die Kugel zu diesem Zeitpunkt befindet.
 
Zum Beispiel:
Java:
long start = System.currentTimeMillis();
// im Timer:
double zeit = (System.currentTimeMillis() - start) / 1000.0;
EDIT: nicht long zeit, sondern double zeit...
 
Klappt schonmal ganz gut, jetzt muss ich nur noch die Anfangsgeschwindigkeit anpassen lasse, sodass auch die Reichweite passt
 

Neue Themen


Zurück
Oben