Wie ziele ich auf einen Panzerturm mit einem versetzten Lauf?


7

Ich habe ein Panzermodell, das aus mehreren Teilen besteht, einem Körper, einem Turm und dem Lauf der Waffe. Der Turm ist vom Körperursprung versetzt und kann sich um die Y-Achse (nach oben) drehen. Der Lauf ist mit dem Turm verbunden und kann sich um die X-Achse seines Verbindungspunkts mit dem Turm drehen.

Angesichts eines Ziels im 3D-Raum und der Position des Panzers fragte ich mich, wie ich die richtigen Drehungen für die Teile berechnen sollte, damit sich der Lauf so ausrichtet, dass er auf das Ziel schießt.

Die Position des Tanks kann sich nicht ändern, nur die Drehungen des Turmes und des Laufs. Ich würde wissen, wie man das löst, wenn der Lauf mit der z-Achse des Turmes ausgerichtet ist, aber ich habe Schwierigkeiten, dieses Problem zu lösen, wenn Teile voneinander versetzt sind. Schließlich möchte ich dies erweitern, damit mehrere verbundene Teile den Endteil (Brennpunkt) auf ein Ziel richten können. Ich benutze XNA C #.

BEARBEITEN:

Unten ist ein Beispiel für das, was ich meine, ein Turm, der am blauen Punkt mit dem Körper verbunden ist. Der Lauf ist am roten Punkt mit dem Turm verbunden:

Geben Sie hier die Bildbeschreibung ein

Antworten:


5

Thermonologie

Ich nenne Ihr "linker / rechter Drehpunkt des Revolvers" Gelenk "horizontales Gelenk" und Ihren "Auf / Ab-Drehpunkt des Laufs" Gelenk "vertikales Gelenk".

Berechnen Sie mit Vorwärtskinematik (Transformationen, die zu (bisher unbekannter) Ausrichtung / Position führen) die Position und den Upvektor und den Vorwärtsvektor (dieser Vektor zeigt vom horizontalen Gelenk nach vorne).

Inverser Teil Verwenden Sie danach die Position und den Upvektor und den Vorwärtsvektor, um die vertikalen und horizontalen Zielrotationen zu berechnen.

Formeln für den umgekehrten Teil des Falls, wenn sich die horizontale Verbindung direkt auf der vertikalen Verbindung befindet

(Pseudocode - ungetestet)

// input
Vector3f UpVector; // upvector (normalized)
Vector3f ForwardVector; // (normalized)
Vector3f TargetPosition; // (absolute target position
Vector3f FKPosition; // forward kinematic Position of the turret
                     // is the absolute position in worldspace of the turret head

// temp
Vector3f SideVector; (normalized)

Vector3f TargetDiffUnnormalized; // difference to the target
Vector3f TargetDiff; // (normalized)

// output
float HorizontalRotationRawRad;
float VerticalRotationRawRad;

// calculation

// first we calculate the sidevector which is perpendicular to the up and ForwardVector
SideVector = UpVector.Cross(ForwardVector).normalized();

TargetDiffUnnormalized = TargetPosition - FKPosition;
TargetDiff = TargetDiffUnnormalized.normalize();

// we "project" the TargetDiff vector to the Plane which is described by the ForwardVector and SideVector

float ProjectedForward = ForwardVector.dot(TargetDiff)
float ProjectedSide    = SideVector.dot(TargetDiff)

// now we need to normalize the 2d Vector which is described by ProjectedForward and ProjectedSide because the length of it is not 1.0 and we would get wrong results

Vector2f ProjectedHorizontal = new Vector2f(ProjectedForward, ProjectedSide);
ProjectedHorizontal = ProjectedHorizontal.normalized();

// NOTE< here the components _could_ be wrong and the multiplication _could_ be wrong, depending on the direction of rotation and so on >
HorizontalRotationRawRad = acos(ProjectedHorizontal.Y);

if( ProjectedHorizontal.X < 0.0f )
{
   HorizontalRotationRawRad *= -1.0f;
}

//-----

Vector3f HorizontalDirection = ForwardVector.scale(ProjectedHorizontal.X) + SideVector.scale(ProjectedHorizontal.Y)

// NOTE< HorizontalDirection should be normalized, check if wanted with assertion to make sure >

VerticalRotationRawRad = acos(HorizontalDirection.dot(TargetDiff));

Bearbeiten 1

Die Mathematik und der Code sowie die Beschreibung für den Fall, dass sich die horizontale Verbindung nicht auf der vertikalen Verbindung befindet (der allgemeinere Fall), folgen hier.

Wenn Sie den 2D-Fall von oben betrachten (sozusagen auf der Ebene des horizontalen Gelenks in der 3D-Welt), stellen Sie fest, dass sich das vertikale Gelenk beim Drehen des horizontalen Gelenks auf einem Kreis dreht.

Geben Sie hier die Bildbeschreibung ein

Jetzt suchen wir den Rotationsversatz, da Sie die Rotation zur HorizontalRotationRawRadVariablen hinzufügen müssen , um die tatsächliche Rotation zu erhalten.

Um dies zu archivieren, müssen Sie den Zielpunkt im 3D-Raum auf die horizontale Gelenkebene projizieren (Sie können dies mit der Berechnung des Schnittpunkts zwischen der Ebene und einem Strahl archivieren, der am Zielpunkt beginnt und in Richtung der Normalen zeigt des Flugzeugs).

Ich nenne diesen Punkt, den Sie als Ergebnis der Kreuzung erhalten PlaneTarget.

Jetzt können Sie die Entfernung von FKPositionbis berechnen PlaneTargetund ich nenne esc .

Für die folgende Berechnung müssen Sie nur einmal beim Gameloading oder bei der Bauzeit des Panzers oder immer dann berechnen, wenn nur der Abstand von der horizontalen Verbindung zur vertikalen Verbindung und der Winkel der beiden (von 90 bis 180 Grad) und die Seite von die vertikale Verbindung.

Um den Versatzwinkel zu berechnen, verwenden wir das Kosinusgesetz:

Gesetz der Kosinusse

Wir kennen c und b und Gamma und suchen Alpha.

nach einigem rechnen ...

c² = a² +b² - 2ab cos(gamma) 
rearanging->
c² = a² + b² - a * 2b cos(gamma)

this is a quadratic equation, solve it with some math or a solver which you wrote to get c

Wir wissen, dass wir die Fälle abfangen müssen, wenn sich das Ziel innerhalb des Kreises befindet (c <b), weil es nicht viel Sinn macht.

Jetzt müssen wir den Winkel Alpha mit etwas mehr Mathematik berechnen ...

a² = b² + c² - 2bc cos(alpha) | + 2bc cos(alpha) | - a²
2bc cos(alpha) = b² + c² - a²

alpha = acos(b² + c² - a² / 2bc)

Jetzt können wir Alpha von / zu VerticalRotationRawRad addieren / subtrahieren, um den richtigen Winkel für das horizontale Gelenk zu erhalten.

Die Berechnung der vertikalen Verbindung ist etwas einfacher. Verwenden Sie den Winkel der horizontalen Verbindung, um den Punkt der vertikalen Verbindung im Weltraum zu drehen.

Dann machen Sie einfach ein Punktprodukt zwischen der normalisierten Richtung des Zylinders und der Differenz zur Zielposition (natürlich hat das Ergebnis kein Vorzeichen, wenn es eines haben sollte, verwenden Sie einfach die horizontale Ebene und schauen Sie, ob die Zielposition darüber liegt oder darunter).


3
Ich denke, das ist übertrieben ...
Gustavo Maciel

7
Nein, das ist es definitiv nicht. Für einen mehrsegmentigen Anker ist dieser Ansatz erforderlich.
Outurnate

2
Diese Lösung würde definitiv für einen Lauf funktionieren, der sich direkt am Drehpunkt des Turmes befindet, aber ich denke immer noch nicht, dass sie für einen versetzten Lauf funktionieren würde. Ich habe der ursprünglichen Frage ein Bild hinzugefügt, um zu zeigen, was ich meine. Ich fange an zu denken, dass ich eine Art IK-Konvergenzalgorithmus anwenden muss, aber es wäre großartig, wenn es eine Lösung gäbe, die dieses Problem nicht mit iterativen Konvergenzmethoden löst. Bitte korrigieren Sie mich, wenn ich mich in Bezug auf das oben Gesagte irre. Ich möchte den Algorithmus so entwerfen, dass er für jedes Tankdesign funktioniert.
Jkh2

2
Ich mag die überarbeitete Antwort, danke, dass
du

0

Ich würde versuchen, es so zu machen:

Vector3 dir = target.pos - tank.pos;
dir.normalise();
RadianAngle turretYaw = acos(dir.x);
RadianAngle barrelPitch = asin(dir.y);

Jetzt löschen Sie einfach die Revolver- und Laufrotationen und wenden diese neuen an. Ich bin mir nicht sicher, ob es funktionieren wird, aber es ist einen Versuch wert!


3
Möchtest du kommentieren, warum -1? Auch wenn die Antwort nicht die richtige ist, ist sie auch nicht irreführend.
Gustavo Maciel

0

Die Auf- / Ab-Drehung des Laufs funktioniert folgendermaßen:

Geben Sie hier die Bildbeschreibung ein

x ist die Entfernung zum Ziel und y ist der Höhenunterschied zum Ziel

Sie müssen die Waffe um Alpha drehen , das atan2 (y, x) ist.

Dies ist in Seitenansicht. In Ihrer 3D-Welt, in der z nach oben zeigt, wäre x sqrt (xx + yy) und y wäre z .

Wenn Sie nicht wissen, was atan2 ist, googeln Sie es. Es ist die hilfreichste Sache überhaupt und C # hat es auch.


Die Links- / Rechtsdrehung des Turms funktioniert folgendermaßen:

Geben Sie hier die Bildbeschreibung ein

Die Kanone Ihres Panzers ist durch einen Vektor v (roter Pfeil) vom Ursprung des Turmes versetzt .

Um Ihr Ziel zu treffen, müssen Sie Ihren Turm per Beta drehen , damit er (der Turm , nicht die Waffe!) Auf eine Stelle zeigt, die um minus v (grüner Pfeil) vom Ziel versetzt ist . Macht Sinn? Der Rest ist wieder nur grundlegende Trigonometrie (an der ich lutsche, also mit Vorsicht verwenden).


Hier ist ein Pseudocode, der davon ausgeht, dass alle Positionen der Panzerkomponente relativ zu den Positionen ihrer Eltern sind (Panzer-> Turm-> Kanone):

// first the turret's rotation:
vec3 absoluteTurretPosition = tank.position + turret.position;

vec3 pointToAimAt = target.position - gun.position;

vec3 delta = pointToAimAt - absoluteTurretPosition;

float desiredRotation = Math.Atan2(delta.x, delta.y);

turret.rotate(desiredRotation);

// now the gun's rotation:
vec3 absoluteGunPosition = absoluteTurretPosition + gun.position;

delta = target.position - absoluteGunPosition;

float x = Math.sqrt(delta.x * delta.x + delta.y * delta.y);

desiredRotation = Math.Atan2(x, delta.z);

gun.rotate(desiredRotation);

-1 Vergessen Sie nicht, dass Sie hässlichen Sonderfallcode für die Rotationsberechnung benötigen, wenn Sie die (überstrapazierte) Atan2-Funktion verwenden , die nicht funktioniert
Quonux
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.