Śledzenie celu: kiedy przyspieszać i zwalniać obrotową wieżę?

24

Powiedzmy, że mam ruchomą okólkę targetzdefiniowaną jako:

Vector2 position;
Vector2 velocity;
float radius;

I obrotowy turret(zamontowany na jakimś poruszającym się pojeździe) zdefiniowany jako:

Vector2 position;
Vector2 velocity;
float angle; // radians
float angularVelocity; // radians per second
const float maxAngularVelocity; // radians per second
const float maxAngularAcceleration; // radians per second per second

(Lub coś wzdłuż tych linii. Zauważ, że położenie i prędkość obu są kontrolowane gdzie indziej - załóż, że prędkość jest stała, a zmiany położenia zależą od prędkości.)

Próbuję napisać dwie powiązane funkcje AI, aby określić w danej ramce:

  • Jakie przyspieszenie kątowe (i w jakim kierunku) zastosować do kąta wieży, aby wieża była skierowana na cel?

  • Jeśli cel jest obecnie widoczny, czy może on (dowolna część w jego promieniu) być widoczny przez xkilka sekund, gdzie xjest ułamek sekundy? (Alternatywnie: czy istnieje inna strategia zapewniająca, że ​​cel jest „zablokowany”, a nie tylko przelatuje przez celowniki?)

I przydałaby mi się pomoc ...

Andrew Russell
źródło
1
Możesz mieć różne wartości przyspieszenia i opóźnienia obrotowego - w prawdziwym świecie, jeden jest prawdopodobnie silnikiem, a drugi hamulcem.
e100

Odpowiedzi:

19

Najpierw musisz określić różnicę kąta między kierunkiem kierunku wieży a kierunkiem do celu.

Vector2 turretToTarget = target.position - turret.position;
float desiredAngle = atan2(turretToTarget.y, turretToTarget.x);
float angleDiff = desiredAngle - turret.angle;

// Normalize angle to [-PI,PI] range. This ensures that the turret
// turns the shortest way.
while (angleDiff < -PI) angleDiff += 2*PI;
while (angleDiff >= PI) angleDiff -= 2*PI;

Po uzyskaniu tych wielkości można ustawić wyrażenie drugiego stopnia dla kąta wieży. Musisz to obliczyć przy każdej aktualizacji, aby mieć pewność, że zawsze korzystasz z najnowszych danych pozycji i prędkości.

// Compute angular acceleration.
const float C0 = // Must be determined.
const float C1 = // Must be determined.
float angularAcc = C0 * angleDiff - C1 * turret.angularVelocity;

Tutaj pierwszy wyraz (zero stopni) w wyrażeniu przyspieszenia spowoduje, że wieża zacznie się obracać w kierunku celu. Jednak nie zatrzyma się w czasie, a raczej oscyluje wokół niego. Aby się zatrzymać, potrzebujemy drugiego członu tłumienia (pierwszego stopnia), który powoduje, że wysokiej prędkości obrotu przeciwstawia się duże przyspieszenie.

Teraz stałe dodatnie (niekoniecznie stałe programowe) muszą zostać określone i zrównoważone, aby system zachowywał się dobrze. C0to główna kontrola prędkości systemu. Wysoka wartość dla C0daje dużą prędkość obrotową, a niska wartość daje niską prędkość obrotową. Rzeczywista wartość zależy od wielu czynników, dlatego powinieneś użyć tutaj metody prób i błędów. C1kontroluje wielkość tłumienia. Wyróżnik równania kwadratowego mówi nam, że jeśli C1*C1 - 4*C0 >= 0mamy system non-oscylacyjny.

// New definition.
const float C1 = 2*sqrt(C0); // Stabilizes the system.

Prawdopodobnie powinieneś wybrać C1nieco większy niż ten ze względów liczbowych, ale nie za duży, ponieważ może być bardzo nadmiernie tłumiony i wolno reagować. Ponownie musisz dostosować.

Należy również pamiętać, że ten kod oblicza tylko przyspieszenie kątowe. Kąt i prędkość kątowa muszą zostać zaktualizowane z tego miejsca gdzieś indziej, przy użyciu i pewnego rodzaju integratora. Z pytania zakładam, że zostało to uwzględnione.

Wreszcie jest coś do powiedzenia na temat opóźnień, ponieważ wieża prawdopodobnie zawsze będzie w tyle, gdy śledzi szybki cel. Prostym sposobem na rozwiązanie tego problemu jest dodanie liniowej prognozy do pozycji celu, tj. Zawsze celowanie lekko do przodu w kierunku do przodu celu.

// Improvement of the first lines above.
const float predictionTime = 1; // One second prediction, you need to experiment.
Vector2 turretToTarget = target.position + predictionTime * target.velocity - turret.position;
/// ...

Co do utrzymywania przez pewien czas wieży w promieniu celu, może to być trudny wymóg nałożenia bezpośrednio na tego rodzaju system. Możesz być pewien, że ten kontroler będzie dążył do tego, aby wieżyczka cały czas była wycelowana w cel (a raczej przewidywaną pozycję). Jeśli wynik okaże się nie być zadowalająca trzeba zmodyfikować parametry predictionTime, C0i C1(w ramach stabilnych granicach).

Staffan E.
źródło
Nie jestem uprawniony do stwierdzenia, czy to prawda, czy nie, ale brzmi to jak coś sprytnego! Rozwiązałem tego rodzaju problemy w przeszłości, przewidując efekt przyspieszenia, aby ustalić, kiedy należy przyspieszyć, a kiedy „zastosować przerwy”. Czy to znaczy, że źle to zrobiłem?
Iain
Atan2 utrudnia dostosowanie tej metody do układu predykcyjnego, ponieważ parametry x i y dla atan2 stają się zależne od t.
Skizz
To jest dokładnie rozwiązanie, o którym wspomniałem w mojej odpowiedzi poniżej. Doskonałe szczegóły i prezentacja!
drxzcl
@Iain: Nie, tutaj nie ma dobra i zła. Chociaż myślę, że twoja metoda miałaby dwa dyskretne stany: przyspieszanie / zwalnianie, metoda ta jest inspirowana regulatorem z teorii sterowania, skalującym przyspieszenie, aby uzyskać szybką reakcję przy jednoczesnym zmniejszeniu przekroczenia i oscylacji.
Staffan E
1
Podobnie jak w przypadku innych komentarzy, będzie to działać dla stacjonarnego celu, ale prawdopodobnie będzie niedopuszczalne dla jakichkolwiek ruchomych celów. Terminy C0 i C1 są tradycyjnymi tłumionymi sprężynami, gdzie C0 reprezentuje wytrzymałość sprężyny (zwykle nazywaną k), a C1 jest współczynnikiem tłumienia (zwykle nazywanym „B” lub „c”). Tak, więc można zminimalizować oscylację, zwiększając tłumienie, ale problem polega na tym, że nie próbuje przewidzieć, gdzie będzie cel , więc jest skazany na opóźnienie w stosunku do pożądanego celu.
dash-tom-bang
3

To, co masz tutaj, to podstawowy problem kontrolny . Wieża to system, przyspieszenie to kontrola, a czujnik mierzy pozycję / prędkość. Istnieje wiele sposobów rozwiązania tych problemów, ponieważ jest to bardzo dobrze zbadany problem w inżynierii.

Kluczem jest stabilny system, tj. System, który nie generuje oscylacji. Zwykle odbywa się to przez dodanie tłumienia. Strona wikipedia powinna zacząć.

drxzcl
źródło
2

Najpierw obliczyć wektor od wieży do celu. Następnie porównaj to z bieżącym wektorem wieży. Następnie użyj różnicy między nimi, aby obliczyć przyspieszenie kątowe i prędkość kątową wymagane, aby wieżyczka obróciła się w odpowiednim czasie w odpowiednim kierunku.

OK, to wydawało się proste. Jednak naprawdę powinieneś spróbować przewidzieć pozycję celu, ponieważ cel będzie poruszał się przed obróceniem wieży. Aby to zrobić: -

Pd' = Pd + t.Vd
Ps' = Ps + t.Vs

gdzie P to pozycja, a V to prędkość, a indeks dolny to d dla celu (cel) is dla źródła (wieżyczka), co daje wektor kierunku:

Dsd' = Pd' - Ps' = Pd + t.Vd - (Ps + t.Vs) = Pd - Ps + (Vd - Vs).t

gdzie D jest wektorem kierunku, a Dsd 'jest wymaganym kierunkiem w czasie t. Teraz obierz kierunek wieży w oparciu o aktualną pozycję oraz maksymalną prędkość i przyspieszenie dla danego czasu t: -

Ds' = t.Ds.Rs -> this is a vector rotation

Ds i Ds 'są kierunkami źródła, a Rs jest prędkością obrotową. Mając to na uwadze, chcesz znaleźć t, kiedy Dsd '== Ds', a zatem Rs, wymagana prędkość obrotowa. Nie zapominaj, że wszystkie P, D i V mają składowe xiy.

Nie wziąłem tutaj pod uwagę przyspieszenia - to dodaje jeszcze więcej do złożoności. Po uzyskaniu Rs i t prawdopodobnie można by zbliżyć paraboliczne Rs (tj. Przyspieszać i zwalniać), aby uzyskać ten sam wynik.

Skizz
źródło
To wygląda na dobrą odpowiedź do obliczeń przechwytywania, ale niestety istnieje duża luka między ludźmi, którzy potrafią odczytać tego rodzaju zapis matematyczny i przekształcić go w kod programu, a większością ludzi tworzących gry, które jeszcze nie znają odpowiedzi na pytanie. Innymi słowy, myślę, że twórcy gry, którzy potrafią odczytać notację matematyczną, prawdopodobnie już mogą wymyślić, jak zaprogramować ostrzał. Pomoże mi zrozumieć twoje formuły, jeśli wyjaśnisz, co oznaczają twoje warunki.
Dronz
2

Prawdopodobnie szukasz tutaj kontrolera PID , podobnego do odpowiedzi przyjętej na to pytanie SO

Początkowo odpowiedziałem na to pytanie „tocząc własne”, ale ta odpowiedź jest znacznie bardziej kompletna i elegancka.

nicolaskruchten
źródło
0

Pierwszą rzeczą do zrobienia jest obliczenie kąta między strumieniem a śledzonym obiektem.
Następnie sprawdź, czy użycie bieżącej prędkości torrenta i zastosowanie maksymalnego przyspieszenia wstecz (zatrzymanie torrenta) spowoduje, że torrent zatrzyma się przed lub po śledzonym obiekcie.
Jeśli odpowiedź brzmi, że torrent zatrzyma się przed śledzonym obiektem, zastosuj maksymalne przyspieszenie do przodu (zwiększenie prędkości).
Jeśli odpowiedź brzmi, że torrent zatrzyma się za śledzonym obiektem, zastosuj maksymalne przyspieszenie do tyłu (zatrzymując torrent).
W ten sposób torrent będzie zawsze przybywał najszybciej i zatrzyma się we właściwym punkcie (lub ułamek później).

Dani
źródło