Suivi de cible: Quand accélérer et ralentir une tourelle rotative?

24

Disons que j'ai une circulaire mobile targetdéfinie comme:

Vector2 position;
Vector2 velocity;
float radius;

Et un rotatif turret(monté sur un véhicule en mouvement de quelque sorte) défini comme:

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

(Ou quelque chose dans ce sens. Notez que la position et la vitesse des deux sont contrôlées ailleurs - supposez que la vitesse est constante et que la position change en fonction de la vitesse.)

J'essaie d'écrire deux fonctions IA liées pour déterminer, sur un cadre donné:

  • Quelle accélération angulaire (et dans quelle direction) appliquer à l'angle de la tourelle pour maintenir la tourelle dirigée vers la cible?

  • Si la cible est actuellement en vue, peut-elle (n'importe quelle partie dans son rayon) être gardée en vue pendant xquelques secondes, où xest une fraction de seconde? (Alternativement: existe-t-il une autre stratégie pour s'assurer que la cible est bien "verrouillée" et ne vole pas simplement à travers les vues?)

Et je pourrais utiliser de l'aide ...

Andrew Russell
la source
1
Vous pouvez avoir différentes valeurs pour l'accélération et la décélération en rotation - dans le monde réel, l'un est probablement un moteur et l'autre un frein.
e100

Réponses:

19

Vous devez d'abord déterminer la différence d'angle entre la direction de la tourelle et la direction de la cible.

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;

Une fois que vous avez ces quantités, vous pouvez configurer une expression du deuxième degré pour l'angle de la tourelle. Vous devez calculer cela à chaque mise à jour pour vous assurer que vous utilisez toujours les dernières données de positions et de vitesses.

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

Ici, le premier terme (zéro degré) dans l'expression d'accélération fera tourner la tourelle vers la cible. Cependant, il ne s'arrêtera pas dans le temps mais oscillera plutôt d'avant en arrière. Pour l'arrêter, nous avons besoin du deuxième terme d'amortissement (premier degré) qui provoque une vitesse de rotation élevée à opposer à une accélération élevée.

Maintenant, les constantes positives (pas nécessairement les constantes de programme) doivent être déterminées et équilibrées pour que le système se comporte bien. C0est le principal contrôle de la vitesse du système. Une valeur élevée pour C0donnera une vitesse de rotation rapide et une valeur faible donnera une vitesse de rotation faible. La valeur réelle dépend de nombreux facteurs, vous devez donc utiliser essais et erreurs ici. C1contrôle l'amplitude de l'amortissement. Le discriminant de l'équation quadratique nous dit que si C1*C1 - 4*C0 >= 0nous avons un système non oscillant.

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

Vous devriez probablement choisir C1un peu plus grand que cela pour des raisons numériques, mais pas trop grand car il peut devenir très sur-amorti et lent à répondre à la place. Encore une fois, vous devez modifier.

Il est également important de noter que ce code ne calcule que l'accélération angulaire. L'angle et la vitesse angulaire doivent être mis à jour à partir de cela ailleurs, en utilisant et en quelque sorte un intégrateur. D'après la question, je suppose que cela a été couvert.

Enfin, il y a quelque chose à dire sur le retard, car la tourelle sera probablement toujours derrière lors du suivi d'une cible rapide. Un moyen simple de résoudre ce problème consiste à ajouter une prédiction linéaire à la position de la cible, c'est-à-dire à viser toujours légèrement en avant dans la direction avant de la cible.

// 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;
/// ...

Quant à maintenir la tourelle dirigée dans le rayon de la cible pendant un certain temps, cela peut être une exigence difficile à imposer directement à ce type de système. Vous pouvez être certain que ce contrôleur s'efforcera de garder la tourelle dirigée vers la cible (ou plutôt la position prédite) à tout moment. Si le résultat s'avère que vous avez de ne pas être satisfaisante pour modifier les paramètres predictionTime, C0et C1(dans des limites de stabilité).

Staffan E
la source
Je ne suis pas qualifié pour dire si c'est vrai ou non, mais cela ressemble à des trucs intelligents! J'ai résolu ces types de problèmes dans le passé en prédisant en avant l'effet de l'accélération pour déterminer quand accélérer et quand «appliquer les pauses». Est-ce à dire que je l'ai mal fait?
Iain
L'atan2 rend cette méthode difficile à adapter à un système prédictif car les paramètres x et y de atan2 deviennent dépendants de t.
Skizz
C'est exactement la solution à laquelle je faisais allusion dans ma réponse ci-dessous. Excellent détail et présentation!
drxzcl
@Iain: Non, il n'y a pas de bien et de mal ici. Bien que je suppose que votre méthode aurait deux états discrets: accélérer / décélérer, cette méthode est inspirée d'un régulateur de la théorie du contrôle, mettant à l'échelle l'accélération pour obtenir une réponse rapide tout en réduisant le dépassement et les oscillations.
Staffan E
1
Comme pour les autres commentaires, cela fonctionnera pour une cible fixe mais sera probablement inacceptable pour toute cible en mouvement. Les termes C0 et C1 sont des éléments de ressort amortis traditionnels, où C0 représente la résistance du ressort (généralement appelé k) et C1 est le facteur d'amortissement (généralement appelé «B» ou «c»). Alors oui, vous pouvez minimiser l'oscillation en augmentant l'amortissement, mais le problème est que cela n'essaie pas d'anticiper où sera la cible , il est donc condamné à être en retard sur l'objectif souhaité.
dash-tom-bang
3

Ce que vous avez ici est un problème de contrôle de base . La tourelle est le système, l'accélération est le contrôle et le capteur mesure la position / vitesse. Il existe de nombreuses façons d'aborder ces problèmes, car c'est un problème très bien étudié en ingénierie.

Key se retrouve avec un système stable, c'est-à-dire un système qui ne génère pas d'oscillations. Cela se fait généralement en ajoutant de l'amortissement. La page wikipedia devrait vous aider à démarrer.

drxzcl
la source
2

Tout d'abord, calculez le vecteur de la tourelle à la cible. Comparez ensuite cela avec le vecteur actuel de la tourelle. Utilisez ensuite la différence entre les deux pour déterminer l'accélération angulaire et la vitesse angulaire nécessaires pour que la tourelle tourne pour pointer dans la bonne direction dans un délai donné.

OK, cela semblait simple. Cependant, vous devriez vraiment essayer d'anticiper la position de la cible, car la cible va se déplacer au moment où vous aurez tourné la tourelle. Pour faire ça:-

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

où P est la position et V est la vitesse et l'indice est d pour la destination (cible) et s pour la source (tourelle), ce qui donne un vecteur de direction: -

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

où D est un vecteur de direction et Dsd 'est la direction requise au temps t. Maintenant, déterminez la direction de la tourelle en fonction de la position actuelle et de la vitesse et de l'accélération maximales pour un temps donné t: -

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

Ds et Ds 'sont les directions de la source et Rs est la vitesse de rotation. Avec tout cela, vous voulez trouver t pour quand Dsd '== Ds' et donc Rs, la vitesse de rotation requise. N'oubliez pas que tous les P, D et V ont des composantes x et y.

Je n'ai pas pris en compte l'accélération ici - cela ajoute beaucoup plus à la complexité. Une fois que vous avez Rs et t, vous pouvez probablement approcher un R parabolique (c'est-à-dire accélérer et décélérer) pour obtenir le même résultat.

Skizz
la source
Cela semble être une bonne réponse pour le calcul de l'interception, mais malheureusement, il y a un grand écart entre les personnes qui peuvent lire ce type de notation mathématique et le transformer en code de programme, et la plupart des personnes qui créent des jeux qui ne connaissent pas déjà la réponse à la question. En d'autres termes, je pense que les développeurs de jeux qui peuvent lire cette notation mathématique peuvent probablement déjà comprendre comment programmer la solution de tir. Cela m'aiderait à comprendre vos formules si vous expliquiez ce que signifiaient vos termes.
Dronz
2

Ce que vous cherchez probablement ici est un contrôleur PID , similaire à la réponse acceptée sur cette question SO

J'avais initialement répondu à cette question en "roulant la mienne" mais cette réponse est nettement plus complète et élégante.

nicolaskruchten
la source
0

La première chose à faire est de calculer l'angle entre le courant et l'objet suivi.
La prochaine étape consiste à vérifier si l'utilisation de la vitesse actuelle du torrent et l'application de l'accélération maximale vers l'arrière (arrêt du torrent) arrêteront le torrent avant ou après l'objet suivi.
Si la réponse est que le torrent s'arrêtera avant l'objet suivi, appliquez l'accélération maximale vers l'avant (augmentation de la vitesse).
Si la réponse est que le torrent s'arrêtera après l'objet suivi, appliquez l'accélération maximale vers l'arrière (arrêt du torrent).
De cette façon, le torrent arrivera toujours le plus rapidement et s'arrêtera au bon point (ou une fraction après).

Dani
la source