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 } } /*********/ } } }