Come calcolo i percorsi per oggetti con accelerazione limitata?


9

Ad esempio, supponiamo che io abbia un'auto e un'auto abbia un raggio di sterzata minimo specifico e voglio guidare quell'auto dal punto a al punto b, ma l'auto non è rivolta verso il punto b. Come calcolo un percorso al punto b? Essere in grado di specificare l'orientamento nel punto b sarebbe anche buono (diciamo che vuoi guidare sul tuo vialetto e poi entrare nel tuo garage - non va molto bene se sei arrivato al tuo vialetto guidando sul prato e sono rivolti lateralmente :)

Un puntatore alla documentazione (o anche solo un nome) sarebbe perfettamente a posto - ho problemi a trovare qualcosa.

Nei miei tentativi, funzionano in casi semplici, ma falliscono miseramente in situazioni come quando il punto b è più vicino a un raggio di sterzata minimo.

Ad esempio, come determineresti un percorso simile a questo (il percorso in grassetto):

Solo un percorso curvo a scopo illustrativo

modifica: Nel mio problema reale, ci sono alcuni semplici vincoli di percorso, ma ho già un algoritmo A * che funziona, ma consente alle cose di fare cambi di rotta istantanei, quindi sembra sciocco vedere un'auto improvvisamente girare di 90 ° in un centesimo quando arrivano a una svolta.


gamedev.stackexchange.com/questions/86881/… ma non sono sicuro di capire la risposta su come impostare lo spazio 3d
xaxxon

"idealmente questo algoritmo sarebbe in grado di gestire il cambio di velocità" Il raggio di sterzata minimo è assolutamente correlato alla velocità con cui cambia o è costante per una sola auto?
DMGregory

Eliminerò quella parte. Per quello che sto facendo, è più "sim city" che "gran tourismo". Capisco perché me lo stai chiedendo e non sono sicuro di cosa stavo pensando quando l'ho aggiunto, poiché capisco che è irrilevante.
xaxxon,

Il diagramma della curva di Bezier mi ha ricordato un po 'di quest'altra risposta, anche relativa alla pianificazione del percorso con un'accelerazione limitata - in quel caso l'accelerazione è stata modellata come un propulsore a razzo direzionale piuttosto che un raggio di sterzata, ma potrebbe comunque innescare alcune idee utili.
DMGregory

Risposte:


7

Non ho ancora elaborato tutte le equazioni per questo, ma ecco alcuni elementi visivi per aiutare a avvolgere la testa attorno al problema. Si riduce a una certa geometria:

Un'auto con cerchi che indicano il raggio di sterzata. ( Icone auto via Kenney )

Da qualsiasi dato punto di partenza e orientamento, possiamo disegnare due cerchi con il nostro raggio di sterzata minimo: uno a sinistra, uno a destra. Questi descrivono i punti nel modo più stretto possibile per iniziare il nostro percorso.

Possiamo fare lo stesso per qualsiasi posizione finale e orientamento desiderati. Questi cerchi descrivono la fine più stretta possibile del nostro percorso.

Ora il problema si riduce a trovare un percorso che unisce uno dei cerchi iniziali a uno dei cerchi finali, baciandoli lungo la sua tangente.

(Questo presuppone che non abbiamo bisogno di individuare gli ostacoli nel mezzo, che non è stato menzionato nella domanda. La risposta di Stormwind spiega come possiamo usare le informazioni del grafico di navigazione per questi tipi di problemi. Una volta che abbiamo la sequenza di nodi per passare, possiamo applicare il metodo seguente per ogni segmento del piano.)

Se, per semplicità, usiamo linee rette, otteniamo qualcosa del genere:

Diagramma che mostra i vari percorsi che un'auto potrebbe percorrere.

Questo ci dà il caso limitante. Una volta trovato un percorso con questo metodo, puoi gonfiare artificialmente uno o entrambi i cerchi di inizio e fine per ottenere un percorso meno diretto ma più fluido, fino al punto in cui i due cerchi si baciano.

Calcolo di questi percorsi

Elaboriamo i casi per una direzione di svolta - diciamo che iniziamo il nostro percorso girando a destra.

Il centro del nostro cerchio di svolta a destra è:

startRightCenter = carStart.position + carStart.right * minRadius

Chiamiamo l'angolo della sezione diritta del nostro percorso (misurato dall'asse x positivo) pathAngle

Se disegniamo un vettore dal rightCenterpunto in cui lasciamo il cerchio di svolta (a quel punto dobbiamo essere rivolti verso pathAngle), allora quel vettore è ...

startOffset = minRadius * (-cos(pathAngle), sin(pathAngle))

Ciò significa che il punto in cui lasciamo il cerchio deve essere ...

departure = startRightCenter + startOffset

Il punto in cui rientriamo in un cerchio di svolta dipende dal fatto che stiamo mirando a terminare con una svolta a sinistra o a destra:

// To end with a right turn:
reentry = endRightCenter + startOffset

// To end with a left turn: (crossover)
reentry = endLeftCenter - startOffset

Ora, se abbiamo fatto bene il nostro lavoro, la linea che unisce departureal reentrydovrebbe essere perpendicolare startOffset:

dot(reentry - departure,  startOffset) = 0

E risolvere questa equazione ci fornirà gli angoli in cui questo è vero. (Uso un plurale qui perché tecnicamente ci sono due di questi angoli, ma uno di questi prevede la guida al contrario che di solito non è ciò che vogliamo)

Sostituiamo la svolta a destra con la svolta a destra come esempio:

dot(endRightCenter + startOffset - startRightCenter - startOffset, startOffset) = 0
dot(endRightCenter - startRightCenter, startOffset) = 0
pathAngle = atan2(endRightCenter - startRightCenter)

Il caso crossover è più complicato - è quello per cui non ho ancora studiato tutta la matematica. Pubblicherò la risposta senza per ora, nel caso in cui ti sia utile mentre elaboro i dettagli rimanenti.

Modifica: destinazione all'interno del raggio di sterzata minimo

Si scopre che questo metodo spesso funziona immediatamente anche quando la destinazione è più vicina della nostra distanza minima di svolta. Almeno una parte di uno dei cerchi di rientro finisce fuori dal raggio di virata, permettendoci di trovare un percorso percorribile a condizione che non ci dispiaccia che diventi un po 'pretzel ...

Dimostrazione di opzioni durante la pianificazione del percorso verso una destinazione vicina.

Se non ci piace il percorso che prendiamo in quel modo (o se uno non è fattibile - non ho controllato tutti i casi in modo esaustivo - forse ce ne sono di impossibili), possiamo sempre guidare dritto o indietro fino a quando non ne otteniamo uno adatto baciare il contatto tra un cerchio iniziale e finale, come illustrato sopra.


È un bel modo semplice di pensarci e le tangenti sui cerchi sono abbastanza facili da lavorare. Finora ho scremato la tua risposta, ma un problema che ogni approccio che ho seguito è se l'obiettivo è all'interno dei cerchi di svolta del punto iniziale.
xaxxon,

La politica più semplice che conosco è quella di invertire fino a quando l'obiettivo è su uno dei tuoi cerchi di svolta, quindi trasformarlo. Con un orientamento di destinazione, invertiresti fino a quando i cerchi di svolta iniziale e finale si baciano da qualche parte. Aggiungerò un diagramma per visualizzare quel caso.
DMGregory

2
Un mese (e diverse distrazioni) dopo ho ottenuto che funzionasse. Calcolo 4 tangenti: le tangenti "esterno" e "interno" (o "incrocio"). Quindi start.left_circle su goal.left_circle, start.left_circle "attraversando" su goal.right_circle (e poi gli altri due cambiano i cerchi). Ecco un percorso "esterno": youtube.com/watch?v=99e5Wm8OKb0 ed ecco un percorso "incrocio": youtube.com/watch?v=iEMt8mBheZU
xaxxon,

1

Questo dipende molto dal resto del modello di dati per la navigazione. Vale a dire. quali dati hai a portata di mano, cosa puoi aggiungere facilmente e come li consumi.

Prendendo uno scenario simile da un sistema di traffico in acqua, e con il presupposto che

  • sei in un ciclo di gioco
  • hai un sistema di percorsi dei nodi
  • le tue auto si comportano come oggetti autonomi che si controllano "dall'interno", usando la propria forza e sterzata
  • le tue auto non si muovono come su binari

potresti avere qualcosa di simile al di sotto (perdonami per l'aspetto infantile delle foto)

inserisci qui la descrizione dell'immagine

(I quadrati rossi sono i nodi, le linee rosse sono le interconnessioni dei nodi. Supponiamo di aver utilizzato un solutore per l'individuazione di percorsi che ha fornito i nodi 1-9 per attraversare; i nodi 4-9 visti sull'immagine e che si desidera passare attraverso i nodi indicati dalla linea verde , al garage al nodo n. 9; tuttavia non vuoi andare esattamente sulla linea verde, ma resta naturalmente sulla corsia di destra e fai manovre fluide).

Ogni nodo avrebbe metadati che contengono ad esempio un raggio, o più raggi, per vari scopi. Uno di questi è il cerchio blu, che fornisce una guida di mira per le auto .

In qualsiasi occasione , il veicolo deve essere a conoscenza dei prossimi due punti nodo P (successivo) e P (successivo + 1) e delle loro posizioni. Naturalmente, anche la macchina ha una posizione. Un'auto punta sul lato destro tangente del cerchio blu di metadati di P (successivo). Quindi le auto vanno nella direzione opposta, quindi non si scontrano. Puntare sulla tangente significa che l'auto può avvicinarsi al cerchio da qualsiasi direzione e mantenere sempre la destra. Questo è un principio di base approssimativo, che può essere migliorato in molti modi.

P (successivo + 1) è necessario per determinare una distanza: quando l'auto raggiunge P (successivo) o entra in un raggio dei suoi metadati, può regolare l'angolo di sterzata in base alla distanza di P (successivo + 1). Vale a dire. se è vicino, gira molto, se è lontano, gira piccolo. Apparentemente ci devono essere anche altre regole e condizioni al contorno, ad esempio il calcolo di una distanza tra l'auto e una linea di aiuto basata sulle tangenti del lato destro di P (successiva) e P (successiva + 1), e una correzione mediante quella - una volontà di rimanere sulla linea tratteggiata (sopra l'immagine) e tratteggiata (sotto l'immagine).

In ogni caso, quando l'auto passa un nodo , lo dimentica e inizia a guardare i due successivi .

Alla tua domanda Apparentemente, quando raggiunge il nodo 7 (nella figura sopra, visto come nodo 2 nella figura sotto), non può girare abbastanza .

inserisci qui la descrizione dell'immagine

Una possibile soluzione è quella di costruire alcune linee guida e mantenere sempre un obiettivo , quindi far muovere la macchina secondo le proprie impostazioni fisiche (accelerare a una velocità specificata, invertire più lentamente, prendere in considerazione i limiti di velocità dei metadati del nodo, frenare a un dato dato o calcolato G, ecc.). Come detto, la macchina è un oggetto autonomo, auto-descrittivo, autoportante in questo scenario.

Avere le linee di aiuto verdi 1,2,3 . Quando l'auto raggiunge il cerchio magenta , inizia la svolta a destra. A questo punto, puoi già calcolare che non ci riuscirà (conosci la velocità di virata massima e puoi calcolare la curva e puoi vedere che attraverserà entrambe le linee di assistenza 2 e 3). Girare completamente a destra e lasciarlo avanzare (con incrementi di fisica) e rallentarlo quando raggiunge la linea di aiuto 3 (si avvicina - utilizzare le soglie, f (dalla distanza alla linea di assistenza) ecc.). Quando è sulla linea di aiuto 3, vai in modalità inversa , gira lo sterzo in pieno opposto . Lascialo invertire finché non raggiunge la linea di aiuto 4(la linea di connessione tra nodo 1 e 2 - google per "algoritmo punto a lato della linea"). Rallenta, quando la raggiunge, vai di nuovo in modalità di marcia avanti , gira la ruota. Ripeti fino a quando la strada non è libera: a quanto pare questa volta è stato sufficiente 1 manovra in più.

Questa è l'idea generale: durante il ciclo di gioco o durante il controllo del sistema que dell'attività dei giochi:

  • Controllare la posizione, la velocità, l'angolazione della vettura, ecc. Rispetto ai limiti e all'obiettivo attuali ,
  • Se non ancora raggiunto , continua con quello che stavi facendo (lascia che la fisica lo sposti; l'auto ha un numero di giri e una marcia). Inserire un nuovo segno di spunta nel sistema que, ad esempio 0,1 s.
  • Se raggiunto , verifica le nuove condizioni, imposta i dati e inizia . Inserire un nuovo segno di spunta nel sistema que, ad esempio 0,1 s.
  • Completa il ciclo del ciclo: continua, ripeti.

Fornendo dati sufficienti ai nodi e alle auto, ci saranno movimento e continuazione.

Modifica: E aggiungendo: questo naturalmente ha bisogno di una messa a punto. La tua simulazione comportamentale può richiedere diverse linee di aiuto, metadati, cerchie, qualsiasi cosa. Ciò darebbe comunque un'idea di una possibile soluzione.


Mi ci vorrà un po 'per leggere la tua risposta. Ho già il pathfinding generico impostato e funzionante, ma consente agli oggetti di accelerare all'infinito in qualsiasi momento.
xaxxon,

Casualmente, in realtà ho qualcosa di molto vicino a quello che descrivi. La linea viola "in movimento" viene generata proceduralmente completamente da due linee rette: youtube.com/watch?v=EyhBhrkmRiY ma non funziona in situazioni "strette" e la curva non viene utilizzata per l'individuazione del percorso reale.
xaxxon,

0

Ho finito per fare ciò che DMGregory ha suggerito e funziona bene. Ecco alcuni codici rilevanti (anche se non indipendenti) che possono essere utilizzati per calcolare i due stili di tangenti. Sono sicuro che questo codice non è efficiente e probabilmente non è nemmeno corretto in tutte le situazioni, ma finora funziona per me:

bool Circle::outer_tangent_to(const Circle & c2, LineSegment & shared_tangent) const {
    if (this->direction != c2.direction) {
        return false;
    }
    if (this->radius != c2.radius) {
        // how to add it: http://mathworld.wolfram.com/Circle-CircleTangents.html
        // just subtract smaller circle radius from larger circle radius and find path to center
        //  then add back in the rest of the larger circle radius
        throw ApbException("circles with different length radius not supported");
    }

    auto vector_to_c2 = c2.center - this->center;
    glm::vec2 normal_to_c2;
    if (this->direction == Circle::CW) {
        normal_to_c2 = glm::normalize(glm::vec2(-vector_to_c2.y, vector_to_c2.x));
    } else {
        normal_to_c2 = glm::normalize(glm::vec2(vector_to_c2.y, -vector_to_c2.x));
    }

    shared_tangent = LineSegment(this->center + (normal_to_c2 * this->radius),
                                 c2.center + (normal_to_c2 * this->radius));
    return true;
}


bool Circle::inner_tangent_to(const Circle & c2, LineSegment & tangent) const {

    if (this->radius != c2.radius) {
        // http://mathworld.wolfram.com/Circle-CircleTangents.html
        // adding this is non-trivial
        throw ApbException("inner_tangents doesn't support circles with different radiuses");
    }

    if (this->direction == c2.direction) {
        // inner tangents require opposing direction circles
        return false;
    }

    auto vector_to_c2 = c2.center - this->center;
    auto distance_between_circles = glm::length(vector_to_c2);

    if ( distance_between_circles < 2 * this->radius) {
//      throw ApbException("Circles are too close and don't have inner tangents");
        return false;
    } else {
        auto normalized_to_c2 = glm::normalize(vector_to_c2);
        auto distance_to_midpoint = glm::length(vector_to_c2) / 2;
        auto midpoint = this->center + (vector_to_c2 / 2.0f);

        // if hypotenuse is oo then cos_angle = 0 and angle = 90˚
        // if hypotenuse is radius then cos_angle = r/r = 1 and angle = 0
        auto cos_angle = radius / distance_to_midpoint;
        auto angle = acosf(cos_angle);

        // now find the angle between the circles
        auto midpoint_angle = glm::orientedAngle(glm::vec2(1, 0), normalized_to_c2);

        glm::vec2 p1;
        if (this->direction == Circle::CW) {
            p1 = this->center + (glm::vec2{cos(midpoint_angle + angle), sin(midpoint_angle + angle)} * this->radius);
        } else {
            p1 = this->center + (glm::vec2{cos(midpoint_angle - angle), sin(midpoint_angle - angle)} * this->radius);
        }

        auto tangent_to_midpoint = midpoint - p1;
        auto p2 = p1 + (2.0f * tangent_to_midpoint);
        tangent = {p1, p2};

        return true;
    }
};

Ecco due filmati del codice sopra in azione:

Ecco un percorso "esterno": http://youtube.com/watch?v=99e5Wm8OKb0 ed ecco un percorso "incrocio": http://youtube.com/watch?v=iEMt8mBheZU

Se questo codice aiuta ma hai domande su alcune delle parti che non sono mostrate qui, pubblica un commento e dovrei vederlo tra un giorno o due.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.