entorno de simulación de carreras de coches: rars

Transcription

entorno de simulación de carreras de coches: rars
ENTORNO DE SIMULACIÓN DE CARRERAS DE COCHES: RARS
Antonio Aldea Gómez, Cristina Romero Barbado
Departamento de Ingeniería Telemática
Universidad Carlos III de Madrid
Madrid, España
{100033997, 100039768}@alumnos.uc3m.es
ABSTRACT
En este paper se estudia el uso de un sistema experto para
la conducción de automóviles por medio del simulador
RARS, así como el desarrollo de un coche de ejemplo.
en cuenta la información suministrada por el usuario y la
información almacenada en al base de conocimiento, otro de los
componentes característicos de un sistema experto El usuario
accede y establece comunicación, es decir un diálogo, con el
sistema experto a través del interface de usuario.
Categoría
Experimentación.
2. DETERMINACIÓN DE LOS
OBJETIVOS
1. INTRODUCCIÓN
Este proyecto está planteado desde el punto de vista de un
sistema experto. Utilizando el simulador gráfico RARS,
hemos desarrollado un coche de ejemplo que en base a un
conocimiento es capaz de recorrer distintos escenarios.
Un sistema experto es una aplicación informática que
procura capturar conocimiento y luego utilizarlo para
emular el mecanismo de raciocinio de un experto humano,
para la resolución de problemas concretos. Esto se logra
automatizando su procedimiento de toma de decisiones. Es
decir, los creadores de sistemas de este tipo analizan no
sólo lo que un experto sabe, sino además la manera en que
resuelve problemas, con el fin de tratar de replicar ese
proceso artificialmente. Un sistema experto está constituido
por distintos componentes, relacionados entre sí de acuerdo
al siguiente esquema:
Base de
inferencia
Para enseñar al coche, hemos estudiado los casos posibles
en los que podría encontrarse (curvas, rectas, etc.), y las
mejores respuestas que podría tener.
El lenguaje de programación que hemos utilizado para
diseñar nuestro prototipo es C++.
Para poder simular el comportamiento de nuestro coche
hemos utilizado el simulador gráfico RARS, un simulador
de código libre, que nos permite diseñar el comportamiento
de nuestro coche de manera relativamente sencilla.
RARS nos propone un modelo físico que describe el
comportamiento del vehículo, y nosotros hemos
implementado la lógica de operación.
Conocimiento
Motor de
El fin de este proyecto es ilustrar cómo funciona un sistema
experto. Para ello hemos desarrollado un coche al que le
hemos dotado de unos conocimientos que le permiten
comportarse de una forma u otra según en la situación en la
que se encuentre dentro de un circuito de carreras.
Interfaz
USUARIO
De usuario
Base de
Hechos
Figura 1-1. Esquema Sistema Experto
El motor de inferencia es el elemento principal, siendo el
componente encargado de la simulación del razonamiento. Por
consiguiente, es el elemento que obtiene un resultado o
conclusión a partir de un proceso de deducción en el que se tiene
Toda la parte de representación gráfica venía facilitada por
el simulador, por lo que nuestro trabajo se ha centrado en
enseñarle reglas a nuestro coche para que corriera de forma
eficaz y competitiva.
3. SIMULADORES DE COCHES DE
CARRERAS EN LA ACTUALIDAD
Aunque los primeros simuladores gráficos aparecieron en
la segunda mitad del siglo XX, fue en la década de los 80
cuando experimentaron su mayor desarrollo. Actualmente
existen gran cantidad y variedad de simuladores que
atienden a diferentes propósitos: simuladores de vuelo
(Microsoft Flight Simulator, X-Plane, etc.), de trenes
(Trainz, BVE, etc.), de la vida (Sims, Spore, etc.), de
juegos (golf, fútbol, coches, tenis, etc.). En el caso que nos
ocupa, nos centraremos en el estudio de simuladores de
juego, y en concreto en simuladores de automovilismo.
Pasamos a describir los principales simuladores gráficos
disponibles hoy en día.
Vamos Automotive Simulator
http://vamos.sourceforge.net/
Vamos es un simulador de carreras que pone especial
énfasis en el modelado físico. Otra de sus características
principales es que tiene un buen diseño en C++. Además,
incluye una aplicación de conducción 3D en tiempo real
que permite experimentar con la simulación.
Su principal propósito es comportarse como banco de
pruebas para ciertas librerías, luego realmente no está
pensado como aplicación de usuario final. Por eso hemos
descartado el uso de Vamos para desarrollar nuestro
trabajo.
Aunque la gran capacidad gráfica y el modelado de los
coches que permite TORCS nos hizo inclinarnos por su
elección como simulador a utilizar, tuvimos que desechar la
idea ya que no conseguimos solucionar los problemas
surgidos con ciertas librerías al intentar instalar su
software. Además, como este simulador funciona
solamente bajo Linux, esto también nos generó bastantes
complicaciones a la hora de intentar configurar la tarjeta
gráfica para que soporte esa aceleración gráfica.
RACER
http://xracer.sourceforge.net/
Racer es un proyecto de simulación de coches de
plataforma cruzada para uso no comercial. Utiliza los
modelos físicos de los coches profesionales para conseguir
aumentar el realismo. Los coches y los circuitos pueden ser
creados de manera relativamente sencilla. El simulador
presta especial atención al aspecto físico, intentando crear
coches cuyo comportamiento se ajuste fielmente a la
realidad, es por ello que también desechamos esta opción
en favor de RARS, que ofrece un buen compromiso entre
realismo y simplicidad.
T1 Car Racing Simulation
http://t1-crs.sourceforge.net/
T1 Car Racing Simulation es un juego de automovilismo en
3D de código libre diseñado para ser utilizado tanto en
plataformas Linux como Windows usando la capa de la
emulación de Cygwin. T1 Car Racing Simulation ha sido
ideado para ser un juego divertido, no para ser un diseño
excepcional en su modelado físico. Por eso también hemos
descartado este simulador, porque queríamos que nuestro
trabajo fuera probado en un entorno lo más realista posible.
TORCS
http://www.racer.nl/
TORCS (The Open Racing Car Simulator), es un simulador
en 3D de carreras de coches que permite competir en
carreras contra oponentes simulados por ordenador. El
interés principal es desarrollar conductores inteligentes,
robots", que compitan en el juego. En TORCS hay 42
coches diferentes, 30 circuitos y más de 50 oponentes
contra los que competir. Las características del simulador
incluyen un modelo simple de daños, colisiones,
propiedades de las ruedas y los neumáticos (presión,
rugosidad, pinchazos...), aerodinámica ("ground effect",
spoilers...), etc. Se trata de un simulador con grandes
capacidades gráficas. Los robots se desarrollan en C/C++
utilizando una API proporcionada por el simulador.
4. EL SIMULADOR RARS: ROBOT AUTO
RACING SIMULATOR
4.1 Descripción de RARS.
El “Robot Auto Racing Simulator” (RARS) fue
originalmente diseñado y desarrollado por Mitchell E.
Timin en 1995. Esta es la descripción que Timin incluyó en
el anuncio original:
“El Robot Auto Racing Simulation (RARS) es un simulador
de carreras de coches en el que los coches son conducidos
por robots. Su propósito es doble: servir como vehículo
para el desarrollo de la Inteligencia Artificial y permitir
que desarrolladores de software puedan competir.
Es un software libre.”
Desde el momento de su creación el simulador ha estado
sujeto a continuo desarrollo.
RARS consiste en una simulación física del
comportamiento de un coche de carreras en un circuito.
Se dispone de una serie de variables de entrada comunes
propias del circuito (situación actual del coche en la pista
con todas sus variables: longitud del tramo, radio del
tramo…), que cada coche recibe, las interpreta de alguna
manera y según el algoritmo utilizado proporciona unas
variables de salida propias del coche (velocidad y
dirección).
Los resultados de estas primeras carreras dieron como
vencedores a los siguientes participantes:
Randy Saint - Ramdu (Texas)
Jussi Pajala - WappuCar (Finlandia, Wappu = Mayday)
4.2 Historia de RARS.
Mike Inman - Indretti (Florida)
La Historia de RARS (Robot Auto Racing Simulator)
comienza en 1995 gracias a Mitchell Timin cuando el 30 de
Abril se celebró un meeting en el que los participantes
enviaron sus robots a una dirección de Internet
programados en ANSI, C o C++. Se utilizó la versión de
RARS 0.50.
Safai Ma - Burns (Canadá)
Los coches que producían errores de compilación (Borland
3.1) o daban errores producidos por otros participantes eran
descalificados. Por esta razón, a pesar de haber una fecha
límite para participar, todos los robots estaban listos con
muchos días de antelación.
Los concursantes optaban a tres premios:
- El primer premio era el software "Street Wizard 5.0", que
era un buscador de mapas y direcciones.
- El segundo premio era un libro de Mark Watson's
McGraw-Hill titulado "C++ Power Paradigms", en el cual
se habla de algoritmos genéticos, redes neuronales, etc.
Bill Benedict - Rusty (E.E.U.U., del Rose-Hulman Tech)
Grant Reeve - Grant1 (Nueva Zelanda)
Oscar Gustavsson- OscCar2 (Suecia)
Tristrom Cooke - Bingo (Australia)
Patrick Tierney - Heath (Australia)
En los últimos años los coches han mejorado mucho
gracias a la utilización de algoritmos genéticos, redes
neuronales, autoaprendizaje, etc.
4.3 Modelo Físico.
El comportamiento del coche viene determinado por el
valor de ciertas variables físicas. Vamos a explicar este
modelo físico apoyándonos en la siguiente figura.
- El tercer premio era otro libro de Mark Watson's
Springer-Verlag llamado "Common LISP Modules.
Artificial Intelligence in the Era of Neural Networks and
Chaos Theory".
La competición se realizó en 6 pistas y se corría dos veces
en cada pista. Se elegía un orden de salida aleatorio para la
primera carrera y en la segunda se invertía el orden de
salida. Las pistas eran las siguientes:
ANEW.TRK 8 vueltas
OVAL2.TRK 20 vueltas
V03.TRK 15 vueltas
STEF2.TRK 15 vueltas
SPEED2.TRK 10 vueltas
ZANDVORT.TRK 8 vueltas
El número máximo de coches era 16. En caso de haber
más, se recurrían a sesiones eliminatorias obteniendo así
los 16 finalistas.
Figura 4-3. Modelo Físico
V Vector de velocidad del coche.
No se permitía que los códigos de los robots accedieran a
zonas de memoria reservada, ni realizaran llamadas al
sistema indebidas. En caso contrario eran descalificados.
P Vector de apuntamiento del coche. Es un vector unitario
que indica la dirección a la que apunta el coche.
Los robots tenían que estar en código fuente o en un
fichero objeto y no podían disponer de más de 25K de
memoria RAM. Se debía ejecutar en no más de 3
milisegundos con el fin de no ralentizar la carrera y que
pareciera lo más real posible.
W Vector de velocidad de los neumáticos con respecto a la
pista. Ha de cumplir:
ά Ángulo de separación de los vectores anteriores.
W = -P *vc,
siendo vc la velocidad que ha de llevar el coche.
L
Vector de derrapaje. Se divide en componente
tangencial (Lt) y componente normal (Ln).
F Fuerza de tracción, esto es, vector de fuerza que empuja
el coche. Su dirección es opuesta a la de L. Se divide en
componente tangencial (Lt) y componente normal (Ln).
4.4 La estructura ‘situation’
El esquema básico de funcionamiento del robot conductor
consiste en determinar lo que está ocurriendo en cada
momento y calcular las salidas de control, o bien elegirlas
de entre varios valores posibles, tal y como se muestra en el
siguiente esquema:
ROBOT
OUTPUT
INPUT
Velocidad: vc
Situación: s
Ángulo: alpha
Figura 4.4.1. Sistema del robot
vc Es la velocidad que se le imprime al coche a partir de la
situación en la que se encuentre y en la que se va a
encontrar, teniendo en cuenta si nos encontramos en una
curva, si esta es cerrada, si estamos en una recta, si el tramo
siguiente es una curva, si estamos adelantando, etc.
alpha Indica el grado de inclinación del coche, marcado
por el que forman la velocidad y la dirección que lleva el
coche, como quedo explicado anteriormente. Al igual que
con vc, hay que tener en cuenta multitud de parámetros,
sobre todo en las curvas, donde según el algoritmo el coche
tendrá un comportamiento muy diferente, puesto que en las
rectas este factor no influye en gran medida, salvo a la hora
de adelantar o realizar algún giro brusco.
Para comprender mejor el funcionamiento del robot es
necesario explicar los datos de entrada de los que
disponemos para, observando sus valores, conocer la
situación actual del vehículo. Estos datos son variables de
la estructura “situation”, las cuales son accesibles por todos
los coches a través de la librería “cars.h”. Para su mejor
comprensión nos apoyaremos en la siguiente figura:
Figura 4.4.2. Variables del robot
s.to_rgt Distancia al borde derecho del circuito.
s.to_lft Distancia al borde izquierdo del circuito.
s.cur_rad Radio del segmento del circuito en el que nos
encontramos. Un radio nulo representa una recta.
s.nxt_rad Radio del siguiente segmento del circuito, que
será una curva.
s.cur_len Longitud del segmento del circuito. Si se trata
de una recta la unidad de medida son los pies, mientras que
si se trata de una curva son los radianes.
s.nxt_len Longitud del siguiente tramo en curva.
s.to_end Distancia que existe desde el punto en que se
encuentra el vehículo en este instante hasta el siguiente
segmento del circuito. Es la distancia que queda por
recorrer del segmento actual.
s.v Velocidad que tiene el coche en cada momento. Es
medida en pies por segundo (feet per second, fps).
s.vn Componente de la velocidad normal a la dirección del
circuito en fps. Si es positiva indica que el coche se mueve
hacia la derecha. Si es negativa, indica que el coche se
mueve hacia la izquierda.
4.5 Programa de Control.
Nuestro robot se ha basado en el código básico de ejemplo
del Tutorial 4, que está disponible en el siguiente enlace
http://rars.sourceforge.net/help.html
El algoritmo que utiliza es muy básico. Simplemente se
preocupa de mantener el coche junto al lateral de la
calzada. Para ello utiliza la variable “lane” que hace que en
las rectas se quede en la situación en la que se encuentra, y
en las curvas se pegue al lado interno de la calzada.
Nosotros hemos modificado ligeramente este algoritmo,
como pasaremos a explicar a continuación, pero a grandes
rasgos, lo que hacemos es estudiar en qué situación está el
coche y en que situación pasará a estar en el siguiente
instante, y así determinar de qué velocidad y ángulo
conviene dotar al vehículo para que tenga una trazada
limpia y rápida.
En cuanto a la velocidad, el algoritmo utilizado también es
muy sencillo, simplemente acelera en las rectas hasta llegar
a una curva, donde calcula la velocidad de entrada en curva
y lo mantiene a lo largo de esta. Nosotros también hemos
mejorado este aspecto haciendo que la velocidad en curva
vaya variando gradualmente según el coche va
abandonando esta.
El tercer aspecto, y quizás el más importante a la hora de
trazar una curva, es el ángulo “alpha” de inclinación del
coche respecto a su dirección. Para ello tiene en cuenta la
posición en la que esta, y en la que debería estar, así saca el
ángulo con el que girar el coche. A éste ángulo se le añade
una componente “bias” que le hará aumentar en el caso de
las curvas, para así poder trazar mejor. El problema de este
algoritmo es que sólo tiene en cuenta la curva en la que está
para modificar este valor. Nosotros, en cambio, hemos
realizado un algoritmo basado en un árbol de profundidad
3, es decir miramos la curva en la que estamos y las dos
siguientes y en función de esto modificamos el ángulo de
una forma u otra.
El último aspecto a tener en cuenta es como se afrontan las
colisiones. Este algoritmo trata de evitarlas bien, frenando
si no tiene otra opción, o tratando de adelantar al coche que
ha detectado por donde tenga espacio en la pista, pero no
tiene en cuenta si la siguiente curva va a ser a un lado u
otro. Puesto que siempre es conveniente coger el lado
bueno de la pista, será conveniente adelantar por un lado u
otro según convenga. Éste aspecto si lo tenemos en cuenta
en nuestro código.
4.6 Funcionamiento del Simulador.
Cuando ejecutamos el programa, la primera pantalla que
nos muestra la siguiente interfaz gráfica:
En ella podemos configurar diversas opciones de la carrera
tales como el circuito en el que deseamos correr, los coches
que queramos que corran, el número de vueltas o el tipo de
visualización del circuito (normal, zoom, etc.)
Una vez hayamos seleccionado el circuito en el que
queremos correr y los coches que van a participar en la
carrera, así como el resto de opciones, pulsaremos el botón
“Start Race” que dará paso a la siguiente pantalla, que
muestra la carrera y otras opciones. En la siguiente figura
podemos apreciar cómo es esta pantalla:
Figura 4.6.2. Carrera en el simulador
En esta pantalla podemos volver a cambiar el tipo de vista
del circuito, utilizando la barra de menú. Las opciones que
nos permite son “Classic”, “OpenGL - FullScreen”,
“OpenGL - Window”, “Telemetry” y “Zoom”.
Para que la carrera dé comienzo, tendremos que pulsar una
de las tres teclas que indican las distintas velocidades, S, D
o F, o sus correspondientes opciones de la barra de menú.
Para visualizar la trazada de un determinado coche,
navegaremos con las teclas “Av Pág” y “Re Pág”, que nos
desplazarán sobre los distintos vehículos, hasta elegir el
deseado.
En la parte inferior de la pantalla se van mostrando varias
variables relevantes en la carrera como las velocidades de
cada coche, la posición que ocupan, los daños que tiene
cada coche, el carburante así como el tiempo al coche que
va a la cabeza.
Figura 4.6.1. Configuración del simulador
5. NUESTRO ROBOT
•
Nuestro algoritmo:
Nuestra propuesta para el desarrollo del robot ha sido en sí
misma una toma de decisiones continua sobre diferentes
variables que se nos fueron presentando durante su
implementación. A continuación detallamos alguna de
ellas.
En primer lugar, con respecto al algoritmo utilizado,
sopesamos los costes y conclusiones que obtendríamos
dirigiéndonos por cada uno de los distintos métodos que
planea cada uno de los robots, que vienen como ejemplo en
la página oficial. Tras descartar tentativas como las de
precómputo del circuito, a través de algoritmos basados en
búsqueda A* o de entrenamiento de redes neuronales (por
el tiempo de que disponíamos para realizar la práctica), nos
decantamos por un sistema experto que decidiese en
concordancia con las variables de entrada actuales, sin
tener en cuenta ningún otro parámetro pasado o futuro.
Nuestro robot intenta imitar lo más fielmente posible la
ejecución de un ser humano a los mandos de un coche, de
esta manera no es difícil imaginar las actuaciones que
llevara a cabo en cada situación:
•
En las curvas, que es el lugar donde realmente se
ganan o se pierden segundos, el coche tiende a
abrirse antes de tomarla, y tras haberla tomado,
para salir de la misma con la mayor velocidad
posible (como ya sabemos suele ser mas efectivo
salir con la máxima aceleración de la curva, que
entrar a esta con mayor velocidad). Las distintas
variables y trazadas de curvas dependerán de otros
factores, tales como que divisa nuestro conductor
en el horizonte(es decir, que viene después otro
par de enlazadas, una larga recta una curva suave
a favor...). Dicho horizonte no se encuentra
formado más que por una jerarquía de tres niveles,
proporcionadas por la estructura de situación que
recibimos como parámetro de entrada a nuestro
código. Cada uno de estos niveles, representa el
tramo de la carretera en que nos encontramos
actualmente, así como los dos tramos siguientes,
que serán tomados como referencia para las
decisiones de conducción (no es lo mismo si
después viene una recta donde tenemos que
empezar a acelerar, que si viene otra curva donde
tenemos que intentar seguir la trazado lo mas
pegados posible a los pianos).
Mientras que en las rectas el comportamiento,
como se puede prever, será mucho mas
dependiente de factores externos (como coches
con los que podamos colisionar o obtener un buen
posicionamiento para mejorar el ángulo alpha de
trazada en la siguiente curva), puesto que las
actuaciones, no serán mas que imprimir la máxima
aceleración posible a nuestro vehiculo con el fin
de aumentar la velocidad todo lo posible, hasta el
punto de frenada siempre que nos permita afrontar
con garantías el siguiente tramo.
Aspectos secundarios que hemos tenido en cuenta a la hora
del desarrollo de nuestro código son:
•
•
Los adelantamientos: Este aspecto viene incluido
en la porción que intenta evitar choques con otros
coches, puesto que, a la vez que nos planteamos
adelantar (la mayoría de veces en recta) por el
interior o exterior, según las circunstancias de
carrera, procuramos no interferir con la trazada de
ningún otro coche, pues seria mayor el perjuicio
en cuestión de daños para nuestro coche que la
ganancia de conseguir una posición mas elevada.
El repostaje: Como comentábamos en el punto
anterior, los daños producidos durante los diversos
lances de carrera, perjudican el rendimiento del
coche de tal manera que, llegado determinado
punto, las prestaciones se resienten sobremanera.
Dadas estas circunstancias, cuando nuestro daño
en el vehículo supera un cierto umbral a partir del
cual su eficiencia no es aceptable, entramos a
boxes para reparar el vehículo. Aunque también
puede darse el caso inverso, que entremos a boxes
para repostar y aprovechemos para reparar los
daños. El cálculo de la carga de gasolina se realiza
al inicio de la carrera, estimando a través del
consumo medio del coche y el número de vueltas
a realizar.
Por último, una consideración final que se debe tener en
cuenta, es la aleatoriedad de las carreras, es decir, el
simulador es real hasta el punto que el mismo coche
corriendo solo sobre un mismo circuito no realiza, en
sucesivas iteraciones, los mismos tiempos. Como sucede en
el mundo real, llegándose a obtener diferencias de hasta
varios segundos en distintas vueltas. Si a lo anteriormente
comentado, le sumamos el comportamiento del resto de
participantes en la carrera, obtenemos que hasta el coche
mas rápido puede acabar fuera de pista debido a los
encontronazos de otros participantes. Por otro lado, el
hecho de intentar evitar cualquier colisión proveniente de
otro participante, reduce de manera más que significativa la
velocidad del coche, por lo que realizar un sobreajuste al
algoritmo de detección de colisiones provocaría un
detrimento del rendimiento del vehículo, cuando otros
participantes se encontraran en los alrededores. Por tanto,
hay que realizar una ponderación entre los tiempos
obtenidos y el ajuste de parámetros del sistema de
anticolisión, obteniendo el compromiso más óptimo para
las circunstancias de la carrera.
Vías de desarrollo:
Básicamente, como resulta natural, los coches que obtienen
un mejor resultado, son aquellos que conocen de antemano
el circuito y llevan a cabo un precómputo del mismo para
obtener la trayectoria óptima. Como es lógico, este
planteamiento no sirve de nada para un sistema experto de,
por ejemplo, conducción automática. En este caso nuestro
planteamiento sería el único acertado, aunque no hay que
confundir conceptos pues nuestro vehículo realiza una
conducción adecuada a los circuitos, y en la vida real,
prima mucho más mantenerse dentro de la calzada que
mejorar la velocidad. Pero, una vez salvadas estas
distancias, quizás, un sistema como el nuestro que
procesara las circunstancias actuales sin conocimiento de
propiedades futuras fuera la solución.
Como propuesta, una mejora al planteamiento para este
tipo de sistema, podría ser un entrenamiento “on-line”; es
decir, basarse de tramos ya superados para afrontar los
siguientes dado que, como sabemos, en la mayoría de los
casos esto nos ayudara en el futuro. Este aspecto, puede ser
desarrollado tanto en circuitos donde se realizarán varias
vueltas superando en diversas ocasiones la misma curva,
como en trazados no cíclicos. Sirva como ejemplo, que si
todas las curvas durante las diez ultimas millas han sido
tomadas con un grado alpha de inclinación muy bajo
(curvas muy suaves), probablemente nos encontramos en
un tramo del estilo autopista, y esto nos permita, tomarnos
ciertas licencias a la hora de afrontas los siguientes tramos.
Por tanto, proponemos un sistema de almacenaje y
tratamiento de la información pasada, para su procesado y
mejora del sistema de manera gradual al devenir de la
carrera.
la versión adecuada para ejecutar el proyecto, etc., no
hemos podido desarrollar un gran robot.
Podríamos haber realizado una implementación más
compleja, utilizando otro tipo de algoritmo, como pueden
ser los que calculan un camino óptimo previo, utilizando
técnicas de aprendizaje por realimentación o redes
neuronales. También podríamos haber diseñado, como
otros coches que trae el proyecto, robots basados en
conocimiento, que usan la información previa que tienen de
cada circuito. Todos estos mecanismos requieren un gran
trabajo y esfuerzo, que por razones temporales no hemos
podido realizar.
Aún así, estamos contentos con nuestro trabajo, ya que
nuestro robot consigue aumentar significativamente las
prestaciones respecto al robot de ejemplo, e incluso es
capaz de superar a algunos de los robots que vienen
implementados.
También queremos mencionar que diseñar un robot de
estas características es bastante complejo, ya que hay que
tener en cuenta multitud de variables físicas que influyen
en la trayectoria del coche. Pero también hay que reconocer
que RARS facilita mucho el trabajo con todas las librerías
que incluye, haciendo que el desarrollador solamente tenga
que ocuparse del algoritmo que desea que siga su coche,
además del simulador que incluye, que aunque no sea tan
potente como puede ser por ejemplo el de TORCS, es muy
bueno y representa bastante bien la realidad.
7. REFERENCIAS
Página oficial de RARS:
http://rars.sourceforge.net
Página oficial de Vamos Automotive Simulator
http://vamos.sourceforge.net/
Página oficial de T1 Car Racing Simulation
http://t1-crs.sourceforge.net/
Página oficial de TORCS
http://torcs.sourceforge.net/
6. CONCLUSIONES
Podemos concluir que la realización de este pequeño
estudio nos ha servido para aprender cómo diseñar un
sistema experto. Nos ha resultado muy interesante observar
cómo enseñándole unas cuantas reglas al robot, es capaz de
utilizarlas y desenvolverse con soltura en el circuito.
Debido a la falta de tiempo, ya que empleamos la mayor
parte de él eligiendo el simulador, instalándolo, buscando
Página oficial de RACER
http://www.racer.nl/
Página de la asignatura:
http://www.it.uc3m.es/~jvillena/irc/practicas/propuestas/torcs.htm
Wikipedia:
http://es.wikipedia.org/wiki/Portada
8. ANEXO: CÓDIGO DEL ROBOT
//-------------------------------------------------------------------------//
I N C L U D E
//-------------------------------------------------------------------------#include
#include
#include
#include
<stdlib.h>
<math.h>
"car.h"
"track.h"
//-------------------------------------------------------------------------//
D E F I N E S
//-------------------------------------------------------------------------#define min(x,y) ((x)<(y)?(x):(y))
//
//
//
//
parameters to tinker with:
accelerations are in feet/second per second.
slips are in feet/second
distances are in feet
const double CORN_MYU
= 0.9;
// lateral g's expected when cornering
const double BRAKE_ACCEL = -33.0;
// acceleration when braking on straight
const double BRAKE_SLIP = 6.5;
// tire slip when braking
const double BRK_CRV_ACC = -27.0;
// acceleration when braking in curve
const double BRK_CRV_SLIP = 3.5;
// tire slip for braking in curve
const double MARGIN = 10.0;
// target distance from curve's inner rail
const double MARGENIZQ = 8.0;
//distancia al margen izquierdo
const double MARGENDCH = 8.0;
//distancia al margen derecho
const double MARG2 = MARGIN+10.0;
// target for entering the curve
const double ENT_SLOPE = .33;
// slope of entrance path before the curve
const double STEER_GAIN = 1.0;
// gain of steering servo
const double DAMP_GAIN = 1.2;
// damping of steering servo
const double BIG_SLIP = 8.0;
// affects the byass of steering servo
const double CURVE_END = 4.0;
// when you are near end of curve, widths
const double TOO_FAST = 1.02;
// a ratio to determine if speed is OK in curve
const double DELTA_LANE = 2.5;
// if collision predicted, change leftDistance by this
double tang = 0.0;
int brake = 0;
static int rad_was = 0;
// 0, 1, o -1 para indicar el tipo del segmento anterior
double brakeDistance = 0.0;
//margen para haber cambiado de lado ante una curva
double rightCurveAngle = 0.0;
//ángulo para el cambio de lado ante una curva a derecha.
double leftCurveAngle = 0.0;
//ángulo para el cambio de lado ante una curva a izquierda.
static double leftDistance;
// target distance from left wall, feet
static double leftDistance0;
// value of leftDistance during early part of straightaway
int straightInit = 0;
double rightRadius = 0.0;
double leftRadius = 0.0;
double third = 0.0;
double byass = 0.0;
// added to servo's alpha result when entering curve
//-------------------------------------------------------------------------//
Clase YEAH
//-------------------------------------------------------------------------// devuelve la máxima velocidad de paso por curva, fps
double corner_speed(double radius){
return sqrt(radius * 32.2 * CORN_MYU);
}
// Calcula la distancia critica necesaria para llevar al coche desde la velocidad v0
// a v1 cuando la aceleración de frenado es "a", ft/sec^2. Velocidad en f/s.
// ("a" should be negative)
double CriticalDistance(double v0, double v1, double a) {
double dv;
dv = v1 - v0;
if(dv > 0.0)
return(0.0);
return (v0 + .5 * dv) * dv / a;
}
//Método que calcula el ángulo necesario para trazar una diagonal en una recta
double changeWay(double anchura,double distancia){
return atan(anchura/distancia);
}
// Esta es la función del robot conductor
con_vec YEAH(situation &s){
con_vec result = CON_VEC_EMPTY; // Esta es la estructura que se devuelve
track_desc td;
//Esta estructura muestra la descripción del circuito
double alpha, vc;
// componentes del resultado
double speed;
// velocidad objetivo para la siguiente curva si estamos en recta
double speed_next = 0.0;
// velocidad objetivo para la siguiente curva si estamos en curva
double width;
// ancho del circuito, feet
double to_end;
// distancia al final del segmento actual
int first = 1;
static double leftDistance_inc = 0.0; // ajuste de "leftDistance", para adelantamientos
td=get_track_description(); //obtenemos la descripción del circuito
if(stuck(s.backward, s.v,s.vn, s.to_lft,s.to_rgt, &result.alpha,&result.vc))
return result;
width = s.to_lft + s.to_rgt ;
// Fijamos "leftDistance" durante las curvas para intentar mantener
// una pequeña distancia al lado interior de la curva
// Si estamos en curva
if(s.cur_rad != 0.0){
if(s.to_end > 4/5*s.cur_len){//primer quinto
third = -0.7;//-1.3;//-1.3;//-0.8;
}
else
if(s.to_end > 3/5*s.cur_len){//segundo quinto
third = -0.6;//-0.8;//-0.4;//.2
}
else
if(s.to_end > 2/5*s.cur_len){//tercer quinto
third = -0.4;//-0.8;//-0.4;//.2
}
else
if(s.to_end > 1/5*s.cur_len){//cuarto quinto
third = -0.2;//-0.8;//-0.4;//.2
}
else//quinto quinto
third = -0.1;//0.1;//1.3;//1.5
if(s.to_end > 2/3*s.cur_len){
third=-1.1;
}
else{
if(s.to_end > s.cur_len/3){
third=-0.8;
}
else{
third=-0.2;
}
}
if(s.cur_rad>0){//Si la curva es a izquierdas
leftDistance = 3*MARGIN;
rad_was=1;
}
//Si la curva es a derechas
else{
leftDistance = width - 3*MARGIN;
rad_was=-1;
}
straightInit = 0;
brake=0;
}
//Si estamos en recta
else{
//indica que venimos de una curva: transicion
if(rad_was!=0){
rad_was=0;
brakeDistance=0.2*s.cur_len ;
if(s.nex_rad < 0.0){ //Siguiente curva a la dcha
rightCurveAngle = changeWay((width-s.to_rgt-1.75*MARGIN),(s.to_end-brakeDistance));
leftDistance=1.75*MARGIN+tan(rightCurveAngle)*(s.to_end-brakeDistance) ;
}
else {
if(s.nex_rad > 0.0){ //curva a la izq
leftCurveAngle = changeWay((width-s.to_lft-1.75*MARGIN),(s.to_end-brakeDistance));
leftDistance=width-(1.75*MARGIN+tan(leftCurveAngle)*(s.to_end-brakeDistance)) ;
}
}
}
else{
//estoy y estaba en una recta
brakeDistance=CriticalDistance(s.v, speed, BRAKE_ACCEL-12);
if(s.to_end < brakeDistance){//Tenemos que frenar e iniciar la curva
//curva a la derecha
if (s.nex_rad<0){
if(brake==0){//Inicio de la frenada. Calculo de trayectoria ideal
rightCurveAngle=changeWay(0.75*width-s.to_lft,s.to_end);
brake=1;
}
leftDistance=0.75*width-(1/2*sin(2*rightCurveAngle)*s.to_end);
}
//curva a la izquierda
else if(s.nex_rad>0){
if(brake==0){
leftCurveAngle=changeWay(s.to_lft-0.25*width,s.to_end);
brake=1;
}
leftDistance=0.25*width + 1/2*sin(2*leftCurveAngle)*s.to_end;
}
}
// Si aun no estamos en la frenada
else{
if (s.nex_rad<0){//curva a la derecha a la vista
leftDistance=1.75*MARGIN + (1/2*sin(2*rightCurveAngle)*s.to_end);
}
if(s.nex_rad>0){
leftDistance=width-1.75*MARGIN-(1/2*sin(2*leftCurveAngle)*s.to_end);
}
}
}
}
//
//
//
//
//
//
//
//
//
//
//
set the byass:
Bias is an additive term in the steering servo, so that the servo
doesn't have to "hunt" much for the correct alpha value. It is an
estimate of the alpha value that would be found by the servo if there
was plenty of settling time. It is zero for straightaways.
Also, for convenience, we call the corner_speed() function here. On
the straightaway, we call it to find out the correct speed for the
corner ahead, using s.nex_rad for the radius. In the curve we of
course use the radius of the curve we are in. But also, we call it
for the next segment, to find out our target speed for the end of
the current segment, which we call speed_next.
if(s.cur_rad == 0.0) {
if(byass != 0.0){
byass= byass*0.7;
if (s.to_end <0.6*s.cur_len){
byass= byass*0.4;
}
if (s.to_end <0.4*s.cur_len){
byass = 0.0;
}
}
if(s.nex_rad > 0.0)
speed = corner_speed(s.nex_rad + MARGIN);
else if(s.nex_rad < 0.0)
speed = corner_speed(-s.nex_rad + MARGIN);
else
speed = 250.0; // This should not execute, for a normal track file
}
else{
// we are in a curve:
if(s.nex_rad == 0.0)
speed_next = 250.0;
else
speed_next = corner_speed(fabs(s.nex_rad) + MARGIN);
speed = corner_speed(fabs(s.cur_rad) + MARGIN + fabs(leftDistance_inc));
speed = speed + 0.2*third*speed;
byass =((s.v*s.v/(speed*speed)) * atan(BIG_SLIP/speed))+(1-(s.to_end/s.cur_len))*curveExit(s);
if(s.cur_rad < 0.0)
// byass must be negative for right turn
byass = -byass;
}
// set alpha: (This line is the complete steering servo.)
/* if (s.cur_rad < 0.0)
alpha = STEER_GAIN * (-s.to_rgt + leftDistance)/width - DAMP_GAIN * s.vn/s.v + byass;
else*/
alpha = STEER_GAIN * (s.to_lft - leftDistance)/width - DAMP_GAIN * s.vn/s.v + byass;
// set vc:
if(s.cur_rad == 0.0){
// If we are on a straightaway,
// if we are far from the end,
brakeDistance=CriticalDistance(s.v, speed, BRAKE_ACCEL);
//brakeDistance=0.25*s.cur_len *brakeDistance;
if(s.to_end > CriticalDistance(s.v, speed, BRAKE_ACCEL))
vc = s.v + 80.0;
// pedal to the metal!
else{
// otherwise, adjust speed for the coming turn:
if(s.v > TOO_FAST * speed)
// if we're a little too fast,
vc = s.v - BRAKE_SLIP;
// brake hard.
else if(s.v < speed/TOO_FAST)
// if we're a little too slow,
vc = 6 * speed;
// 1.1
accelerate hard.
else
// if we are very close to speed,
vc = 3*(s.v + speed);
// approach the speed gently.
}
}
else{
// This is when we are in a curve: (seek correct speed)
// calculate distance to end of curve:
if(s.cur_rad > 0.0)
to_end = s.to_end * (s.cur_rad + MARGIN);
else
to_end = -s.to_end * (s.cur_rad - MARGIN);
// compute required braking distance and compare:
// This is to slow us down for then next curve, if necessary:
if(to_end <= CriticalDistance(s.v, speed_next, BRK_CRV_ACC))
vc = s.v - BRK_CRV_SLIP;
// but if there is a straight, or a faster curve next, then
// we may want to accelerate:
else if(to_end/width < CURVE_END && speed_next > speed)
vc = (s.v + speed_next)/cos(alpha);
else
// normally, just calculate vc to maintain speed in corner
vc = (s.v + speed)/cos(alpha);
}
////////////COLISIONES /////////////////////
//El estudio de comportamiento ante colisión nos indica de manera empírica que las actuaciones
//llevadas a cabo con el fin de evitar las colisiones con un numero de participantes igual o superior
//a 4 producen un deterioro en el rendimiento
//del coche mayor que el beneficio en daños evitados para nuestro robot por lo que se desaconseja su
//uso en este tipo de situaciones.
//
//
//
//
Código de adelantamientos y anticolisión:
Este código primero intenta predecir una colisión. Si no se prevé ninguna
no hace nada. La predicción de colisión es aproximada, y se basa en
extrapolación lineal.
// Si se prevé una colisión, cambiamos la variable leftDistance_inc que a su vez
// modifica alpha. Intentamos adelantar siempre tomando el interior de la curva actual
// o de la siguiente en caso de no poder o de que no nos encontremos en una curva,
// siempre que encontremos hueco por ese lado. Si no podemos adelantar por el interior,
// nos quedaremos detrás, de forma que podamos empezar a acelerar antes a la salida.
// Esto es así salvo que nos encontremos en una recta y aun nos quede suficiente
// espacio para pasar al otro coche, en cuyo caso, aceleraremos y le pasaremos por el
// "lado malo". Además, si estamos en una curva y no podemos obtener el interior, pero
// a continuación viene una curva hacia el lado opuesto, intentaremos obtener el interior
// en la segunda curva, para lo que nos abriremos ligeramente en la primera.
// Cuando se deja de prever colisión leftDistance_inc se vuelve gradualmente a cero.
// Si vamos a colisionar inmediatamente con un coche que va mucho mas lento que nosotros,
// frenamos fuerte y tratamos de esquivarlo, para evitar grandes daños en el coche,
// tratando de evitar en todo momento que nos salgamos de la pista.
double x, y, vx, vy, dot, vsqr, c_time, y_close, x_close;
int kount;
kount = 0;
for(int i=0;i<3;i++){
if (s.nearby[i].who<16){ // si tenemos un coche cerca
y=s.nearby[i].rel_y;
// obtenemos la distancia en y (centro a centro)
x=s.nearby[i].rel_x;
// obtenemos la distancia a la derecha (entre centros)
vx=s.nearby[i].rel_xdot;
// obtenemos la distancia relativa en x
vy=s.nearby[i].rel_ydot;
// obtenemos la velocidad relativa en y: -: Aproximándose
//
+: Alejándose
// Si los coches se están acercando, el producto escalar de las posiciones
// relativas y los vectores de velocidad será negativo.
dot = x * vx + y * vy;
// producto escalar de los vectores
if(dot > -0.1)
// si no se aproxima no hacemos nada
continue;
vsqr = vx*vx + vy*vy;
// calculamos la velocidad relativa cuadrada
c_time = -dot / vsqr;
// Tiempo de la aproximación mas cercana
if(c_time > 3.0)
// Si es superior a 3 sec, lo ignoramos
continue;
x_close = x + c_time * vx;
y_close = y + c_time * vy;
// coordenada x de la máxima aproximación
// coordenada y de la máxima aproximación
// Comprobamos si habrá colisión antes de la máxima aproximación
// y en ese caso reduce c_time y recalcula x_close e y_close:
if(x_close * x < 0.0 && y < 1.1 * CARLEN){
c_time = (fabs(x) - CARWID) / fabs(vx);
x_close = x + c_time * vx;
// x coord at closest approach
y_close = y + c_time * vy;
// y coord at closest approach
}
// Comprobamos si chocarán
if(fabs(x_close) > 2 * CARWID || fabs(y_close) > 1.25 * CARLEN)
continue;
// Predecimos una colisión
++kount;
// Contamos el numero de coches con los que podemos colisionar
if(kount > 1){
// Si hay mas de un coche con el que colisionar
vc = s.v - BRK_CRV_SLIP;
//frenamos
}
//Si estamos a punto de colisionar y va a menos de la cuarta
//parte de nuestra velocidad
if(c_time<0.75 && fabs(vy)>0.75*s.v){
vc = s.v - BRK_CRV_SLIP;//frenamos e iniciamos maniobra evasiva
if(x>0){ //Esta a nuestra derecha
//elusiva a la izqda si cabemos. Si no, a la derecha brusco
if(s.to_lft>CARWID/2)
leftDistance_inc -= DELTA_LANE;
else
leftDistance_inc += 1.2*DELTA_LANE;
}
else{ //Esta a nuestra izquierda
//elusiva a la derecha si cabemos. Si no, a la derecha brusco
if (s.to_rgt>CARWID/2)
leftDistance_inc += DELTA_LANE;
else
leftDistance_inc -= 1.2*DELTA_LANE;
}
}
else{
//Estamos en curva a la izquierda
if(s.cur_rad > 0.0){
// si el coche rival esta a tu izqda (en el interior)
if(x_close < 0.0){
//Siguiente:curva a la dcha
if (s.nex_rad<0.0 && s.to_rgt>MARGIN/2){
//nos abrimos para tomar el interior de la siguiente
leftDistance_inc += DELTA_LANE/2;
}
//Si lo siguiente es curva no es a la derecha, evitamos el choque
else{
leftDistance_inc -= DELTA_LANE/2;
vc=vc*0.9; //Acoplamiento
}
}
//si estamos mas al interior
else{
//Si podemos, nos pegamos al interior...y pasamos si cabemos
if(s.to_lft > MARGIN/2) {
leftDistance_inc -= DELTA_LANE;
if(s.to_lft+x_close>2*CARWID)
vc=vc*1.05;
}
}
}
//Curva a la derecha
else if(s.cur_rad < 0.0) {
// si el coche rival esta a tu derecha (en el interior)
if(x_close > 0.0){
//Siguiente:curva a la izqda
if (s.nex_rad>0.0 && s.to_lft>MARGIN/2){
//nos abrimos para tomar el interior de la siguiente
leftDistance_inc -= DELTA_LANE/2;
}
else{
leftDistance_inc -= DELTA_LANE/2;
vc=vc*0.9; //Acoplamiento
}
}
else{
//Si podemos, nos pegamos al interior...y pasamos si cabemos
if(s.to_rgt > MARGIN/2){
leftDistance_inc += DELTA_LANE;
if(s.to_rgt-x_close>2*CARWID)
vc=vc*1.05;
}
}
}
//estamos en una recta
else{
//intentamos ganar el lado interior de la frenada
//curva a derecha y cabemos
if(s.nex_rad<0 && s.to_rgt-x>2*CARWID && s.to_rgt>MARGIN/2){
//tomamos el interior
leftDistance_inc += DELTA_LANE;
//y si ya esta a nuestra izquierda, aceleramos
vc=1.1*vc;
}
//curva a izquierda y cabemos
else if(s.nex_rad>0 && s.to_lft+x>2*CARWID && s.to_lft>MARGIN/2){
//tomamos el interior y aceleramos
leftDistance_inc -= DELTA_LANE;
vc=1.1*vc;
}
//no cabemos x el interior
else{
//si no estamos en la frenada
if(s.to_end>brakeDistance){
//si le tenemos a la izquierda, le pasamos x la dcha, si cabemos
if(x < 0.0 && s.to_rgt>MARGIN/2){
leftDistance_inc += DELTA_LANE;
vc=vc*1.1;
}
//si le tenemos a la derecha, le pasamos x la izqda, si cabemos
else if(x > 0.0 && s.to_lft>MARGIN/2){
leftDistance_inc -= DELTA_LANE;
vc=vc*1.1;
}
else vc=vc*0.9;
}
}
}
}
}
}
// Reducimos gradualmente leftDistance_inc si no se prevee collision
if(!kount)
if(leftDistance_inc > .1)
leftDistance_inc -= .5*DELTA_LANE;
else if(leftDistance_inc < -.001)
leftDistance_inc += .5*DELTA_LANE;
////////////COLISIONES /////////////////////
// Al inicio de la carrera, mantenemos la trayectoria, y hacemos el calculo de fuel
if(s.starting){
leftDistance = leftDistance0 = s.to_lft;
//calculamos la cantidad de fuel optima para afrontar la carrera. Se consumen en
//torno a 1.5 galones/milla
result.fuel_amount = min(td.length/5280*s.laps_to_go*1.5,MAX_FUEL);
}
// leftDistance_inc representa un ajuste de la variable leftDistance. Se realiza
// modificando alpha
result.vc = vc;
result.alpha = alpha - STEER_GAIN * leftDistance_inc / width;
//
//
//
//
Vamos al Pit: si no tenemos suficiente fuel para completar una vuelta
o el coche esta demasiado dañado
Fuel: Calculamos el minimo fuel para llegar
Damage: Reparar todo
if(s.fuel<td.length/5280*1.7 || s.damage>24000 ){
result.request_pit
= 1;
result.repair_amount = s.damage;
if (s.fuel<td.length/5280*1.7*s.laps_to_go){
result.fuel_amount = min(td.length/5280*s.laps_to_go*1.8,MAX_FUEL)-s.fuel;
}
}
return result;
}
//Función que influye en el ángulo de toma de la curva alpha por medio de la adición de un termino de
//inclinación a nuestro coche a través de la componente byass desde donde es llamada. Se basa en un
//estudio de la situación actual y futura de la carretera.
double curveExit(situation &s)
{
double abrir = -0.50;
double cerrar = -0.20;
if(s.nex_rad ==0)//recta
{
if(s.after_rad ==0)//recta
{
return abrir; // (IZQ o DCH) RECTA RECTA // si estoy en una curva y los dos tramos siguientes
son rectas
}
else //estoy en curva y lo siguiente es una curva
{
if(s.after_rad >0) ////estoy en curva y lo siguiente es una recta y luego curva a izq
{
if(s.cur_rad <0)
return abrir;// DCH RECTA IZQ
return abrir; // IZQ RECTA IZQ
}
else //curva derch
{
if(s.cur_rad <0)
return abrir; // DCH RECTA DCHA
return abrir; // IZQ RECTA DCHA
}
}
/**********/
}
else // lo siguiente es una curva
{
if(s.nex_rad >0)//curva izq
{
/********/
if(s.after_rad == 0) //recta
{
if(s.cur_rad <0)
return abrir; // DCHA IZQ RECTA
return cerrar; // IZQ IZQ RECTA
}
else
{
if(s.after_rad >0)//curva izq
{
if(s.cur_rad <0)
return abrir; // DCHA IZQ IZQ
return cerrar; // IZQ IZQ IZQ
}
else //curva derch
{
if(s.cur_rad <0)
return abrir; // DCHA IZQ DCHA
return cerrar; // IZQ IZQ DCHA
}
}
/*********/
}
else //curva derch
{
/*********/
if(s.after_rad ==0)//recta
{
if(s.cur_rad <0)
return cerrar; // DCHA DCHA RECTA
return abrir; // IZQ DCHA RECTA
}
else
{
if(s.after_rad >0)//curva izq
{
if(s.cur_rad <0)
return cerrar; // DCHA DCHA IZQ
return abrir; // IZQ DCHA IZQ
}
else //curva derch
{
if(s.cur_rad <0)
return cerrar; // DCHA DCHA DCHA
return abrir; //IZQ DCHA IZQ
}
}
/*********/
}
} }

Documents pareils