Guia de manejo del d..

Anuncio
Capítulo 1 GUÍA DE MANEJO DEL DRIVER DE LA
TCS
1 Introducción
El presente documento tiene como objetivo describir cómo se maneja el driver que
permite realizar programas en una PDA para controlar a la TCS.
Un driver no es más que un objeto que tiene un conjunto de funciones que permiten
manejar un dispositivo y un conjunto de variables que permiten monitorizar el estado
del mismo, sin necesidad de conocer cómo está hecho. Lo único que se necesita conocer
es el conjunto de funciones del driver que son interfaz con el usuario.
2 Nociones básicas del driver
La clase que maneja la TCS se llama Comm y se encuentra declarada en el fichero
Comm.h y definida en el fichero Comm.cpp. Esta clase necesita el driver del puerto
serie para poder funcionar, que es el medio de comunicación que el PC o la PDA tiene
con la TCS. Este driver se encuentra en los ficheros Serial.h y Serial.cpp.
Para entender el funcionamiento del driver es necesario entender qué es una
comunicación entre el PC/PDA con la TCS. Una comunicación consiste de una
petición de ejecución de comandos a la TCS y de una respuesta por parte de la
TCS a esa petición; es decir, una comunicación implica el trasiego de dos flujos de
datos: uno del PC/PDA a la TCS y otro en sentido contrario. Por ejemplo, una
comunicación podría consistir en la petición a la TCS de la ejecución de dos comandos:
que mueva los motores a una determinada velocidad y que le devuelva todas las
medidas analógicas que tenga en ese momento; como contrapartida la TCS respondería
con los valores de las medidas analógicas pedidas. Se pueden realizar tantas
comunicaciones con la TCS como se quieran, de hecho el ejemplo anterior podría
haberse llevado a cabo con dos comunicaciones en vez de con una, simplemente
enviando un sólo comando en cada una de las comunicaciones. En cualquier caso, en
este esquema de funcionamiento es la PDA la que actúa como maestro; es decir, toma la
1
iniciativa en las comunicaciones, y la TCS actúa como esclavo; es decir, responde a la
PDA cuando esta se lo pide únicamente.
3 Variables y funciones del driver
A continuación se describen las variables del driver que puede utilizar el programador
cuando maneja el driver:
•
interrupcion: vector de cuatro elementos que informa de los datos no
esperados que se reciben en una comunicación con la TCS; es decir, es
información que no se ha pedido a la TCS y que en cambio la TCS decide
mandar para informar al robot de eventos que ocurren y que debe conocer. Estos
eventos se llaman interrupciones, aunque no lo son propiamente, porque surgen
sin esperarse de la misma forma que las interrupciones, lo que lleva a tener que
programar líneas de código que atiendan estos eventos. Cada uno de los
elementos del vector tiene dos posibles estados: 1 si hay interrupción y 0 si no la
hay. Las posibles fuentes de interrupción son:
o interrupcion[1]: información recibida por radiofrecuencia. El
PC/PDA no sabe cuándo puede recibir información por radio. Por ello
cuando la TCS recibe información por radio, se lo comunica a la PDA.
Para la recepción de mensajes por radio se usa el elemento 1 del vector
interrupcion.
o interrupcion[2]: medidas de los ultrasonidos. Los sensores de
ultrasonidos no pueden estar midiendo de continuo ya que el transductor
se desgasta con el uso. Esto lleva a realizar medidas de forma no
discriminada y bajo petición. Dado que un sensor de ultrasonido puede
tardar hasta 20ms en hacer una medida (por lo tanto 8 sensores pueden
tardar hasta 160 ms), no se puede permitir que cada vez que la PDA pida
medidas de ultrasonido tenga que esperar a que se obtenga el dato ya que
estaría perdiendo un tiempo en el cual podría estar haciendo otra cosa y
le podría llevar a que el robot se chocara. Por ello, se ha optado por
gestionar los ultrasonidos como una interrupción; es decir, en una
comunicación con la TCS, la PDA pide que se hagan ciertas medidas de
ultrasonido, pero no espera a que se hagan, sino que sigue realizando
2
comunicaciones para pedir otro tipo de medidas a la TCS o para actuar.
Cuando la TCS ha terminado de hacer las medidas, introduce los
resultados en una comunicación con la PDA sin que ésta sepa a priori en
qué comunicación la TCS le va a enviar las medidas. Para los sensores
de ultrasonido se usa el elemento 2 del vector interrupción.
o interrupcion[3]: medidas de sensores digitales. Este tipo de
sensores miden 0 o 1. Como sólo existen dos posibles estados este tipo
de sensores ofrecen medidas muy estáticas ya que cambian poco en el
tiempo. Para evitar trasiego de información innecesaria en la
comunicación entre el PC/PDA y la TCS, la TCS enviará a la PDA las
medidas de los sensores digitales cuando alguno de estos cambie de
estado. Por ejemplo, este tipo de sensores se suele usar para saber si el
robot se ha chocado; en ese caso la TCS informará al robot sólo cuando
se acabe de chocar o cuando deje de hacerlo. Para los sensores digitales
se usa el elemento 3 del vector interrupcion.
•
compruebaPing: variable de dos estados que indica si al mandar el comando
“ping” (comando que se usa comúnmente en comunicaciones para determinar si
un dispositivo está vivo: ¿estas ahí?) a la TCS, éste ha respondido. A veces se
usa para establecer una comunicación con la TCS, únicamente para saber si
existe algún evento que deba ser recibido; ya que como se comentó en la sección
2 la TCS sólo puede mandar eventos si la PDA a tomado la iniciativa de abrir
una comunicación.
•
RFrespuesta: variable de dos estados que indica si el último mensaje de
radiofrecuencia
espera
una
respuesta
(RFrespuesta==1)
o
no
(RFrespuesta==0).
A continuación se describen las funciones del driver que permiten controlar y establecer
una comunicación entre la PDA y la TCS:
1. inicio(): función que se debe llamar al principio de cualquier comunicación
entre la PDA y la TCS.
2. comunica(): establece la comunicación de la PDA con la TCS, enviando
todas las ordenes a la TCS y recibiendo la respuesta de la misma. En caso de que
surja algún problema durante la comunicación devolverá un 0 y sino un 1.
3
A continuación se describen las funciones interfaz del driver que permiten enviar
ordenes para manejar la TCS:
1. configuracion(número de ADs, número de USs): función que
indica a la TCS cómo se configuran los pines de conexión con el exterior en
función del número de sensores de cada tipo que se le manden en esta función.
2. leerAD(sensor): función que pide la medida de un sensor analógico que
se manda como parámetro. El parámetro sensor va de 1 hasta el número de
sensores analógicos que se hayan configurado.
3. leerADs(): función que pide todas las medidas de los sensores analógicos
que se hayan configurado.
4. pedirUS(sensor): función que pide la medida de un sensor de ultrasonido
que se manda como parámetro. El parámetro sensor va de 1 hasta el número
de sensores de ultrasonidos que se hayan configurado.
5. pedirUSs(): función que pide todas las medidas de los sensores de
ultrasonido que se hayan configurado.
6. activarMotores(Byte de configuración): activa los motores. Si
el byte de configuración es 1 se activa el motor 1, si es 2 se activa el motor 0 y si
es 3 se activan los dos motores. Para que un motor se ponga en funcionamiento
es necesario haberlo activado.
7. periodoMotores(Periodo): indica a la TCS el periodo de la señal
PWM que va a utilizar para controlar los motores. El parámetro tiene que ser un
número que esté comprendido entre 70 y 255. Cuanto mayor sea el valor, mayor
será el periodo.
8. periodoServos(Periodo): indica a la TCS el periodo de la señal PWM
que va a utilizar para controlar los servos. Como los servos se controlan siempre
a 50Hz, el valor del parámetro afecta poco al comportamiento del servo, ya que
es un ajuste fino de la frecuencia entorno a 50Hz. Tiene que ser un número que
esté comprendido entre 0 y 255; por ejemplo 200.
9. DcMotores(Dc motor1, Dc motor0): fija el valor de los anchos del
pulso de la señales PWM que controlan los motores 0 y 1. Variando el ancho del
pulso del PWM se puede variar la velocidad de los motores. El parámetro tiene
que ser un número que esté comprendido entre 0 y el valor del periodo de
4
control de los motores. En concreto el valor en el que se encuentra parado el
motor es cuando coincide con la mitad del periodo de control de los motores
(por ejemplo 100 en caso de que el periodo sea 200); el valor en que el motor
gira a la máxima velocidad en sentido negativo es 0; el valor en el que el motor
gira a la máxima velocidad en sentido positivo es el que coincide con el valor
del periodo de control; y valores intermedios hacen que el motor se mueva a una
velocidad que se puede obtener interpolando entre los valores anteriormente
citados.
10. DcServos(Dc servo1, Dc servo0): fija el valor de los anchos del
pulso de la señales PWM que controlan los servos 0 y 1. Variando el ancho del
pulso se puede variar el ángulo de giro de cada servo. El valor del ancho del
pulso puede variar entre 50 y 240. Un valor de 50 hace que el servo se encuentre
posicionado a -90º, en cambio un valor de 240 hace que el servo se encuentre
posicionado a 90º. Valores intermedios hacen que el servo se posicione en un
ángulo que se puede obtener interpolando entre los valores anteriormente
citados.
11.
Ping(): función que sirve para determinar si la TCS se encuentra en
funcionamiento y que existe comunicación entre la PDA y la TCS.
12. escribeRadio(longitud_mensaje,
mensaje,
espera_respuesta):
función que indica a la TCS que tiene que enviar un mensaje por radio. El
parámetro longitud_mensaje indica el número de bytes que se quieren
transmitir, el parámetro mensaje representa el mensaje a transmitir y el
parámetro espera_respuesta indica si la PDA espera una respuesta de aquel a
quien envía el mensaje.
Funciones para leer las medidas de los sensores:
1. valorADs(): devuelve un vector de 10 elementos (10 bytes) con todas las
medida analógicas que ha devuelto la TCS.
2. valorEncoders(): devuelve un vector de 4 elementos (4 bytes) con las
medidas de los encoders. El primer byte es la parte menos significativa de la
medida del encoder 0, el segundo byte es la parte más significativa del encoder
0, el tercer byte es la medida menos significativa del encoder 1 y el cuarto byte
es la medida más significativa del encoder 1.
5
3. valorUSs(): devuelve un vector de 8 elementos (8 bytes) con todas las
medidas de ultrasonido que se han pedido a la TCS.
4. valorDig(): devuelve un vector de 1 elemento (1 byte) con la medida de los
ocho sensores digitales. Cada bit del elemento se corresponde con la medida de
un sensor.
5. valorRF(nElementos): devuelve un vector de nElementos elementos con el
mensaje que la TCS ha recibido por radio.
4 Instrucciones de manejo del objeto Comm
A continuación se detallan las instrucciones para el manejo del driver desde una
aplicación programada en C++, que será la que se ejecute en un PC o en una PDA.
4.1 Creación de objeto
El primer paso es añadir los siguientes archivos al proyecto donde se va a realizar la
aplicación deseada:
•
Comm.h: Definición de la clase Comm, encargada de la comunicación.
•
Comm.cpp: Código de la clase Comm.
•
Serial.h: Definición de la clase Serial, encargada del manejo del puerto serie.
•
Serial.cpp: Código de la clase Serial.
Una vez añadidos, en el archivo del proyecto donde se vaya a crear el objeto de la clase
Comm se introduce el código correspondiente, habiendo incluido anteriormente la
cabecera Comm.h:
#include “Comm.h”
…
Comm comunicacion;
…
De esta manera se ha creado el objeto comunicacion con el que se podrán establecer
comunicaciones con la TCS.
4.2 Inicialización de la comunicación
Antes de establecer una nueva comunicación lo primero que hay que hacer es llamar a
al método inicio() del objeto Comm:
comunicacion.inicio();
6
Este método tiene como función vaciar el buffer de datos de envío. En caso de que no se
ejecutara, al introducir las órdenes para la TCS, éstas se irían colocando tras las órdenes
solicitadas en la comunicación anterior.
4.3 Introducción de las órdenes a enviar
Una vez que se ha inicializado la comunicación, es necesario introducir las órdenes que
se quieren mandar al microcontrolador. Para ello, tan sólo se tienen que ejecutar las
funciones o métodos de la sección 3 que se encargan de mandar actuaciones o de pedir
medidas.
A la hora de ejecutar dichos métodos se debe tener en cuenta los siguientes aspectos:
•
En la primera comunicación con la TCS se debe configurar, enviando la orden
configuracion que le indica el número de sensores de cada tipo que se van
a utilizar. Si por ejemplo, se quiere usar sólo el sensor de ultrasonidos 3, se debe
configurar el número de ultrasonidos al menos a 3, aunque el 2 y el 1 no se usen.
Lo mismo sucede para los sensores analógicos.
•
En caso de que en la misma comunicación se envíe la orden de
configuracion y además alguna otra orden para pedir la medida de sensores
analógicos o ultrasonidos, siempre se deberá introducir primero la orden de
configuracion, ya que si no, cuando en la TCS vaya a realizar las medidas
no estará configurada de la forma adecuada.
•
En las funciones leerAD(sensor) y pedirUS(sensor), se debe tener en
cuenta que el primer sensor se pide con un 1 y el último con el número de
sensores totales configurados (no como los vectores en C++, que usan los
índices que van de 0 al número de elementos – 1).
•
Si se solicita una medida de ultrasonidos antes de haber recibido la respuesta con
medidas de una petición anterior, ésta se sobrescribe y no se sigue esperando.
•
Si no se han activado nunca los motores, es aconsejable que no se envíe
solamente la orden activarMotores en una comunicación, sino que se
acompañe con las órdenes que configuran el periodo de control y los la
velocidad (duty cycle) de los mismos. De esta manera se evita que al activar los
motores, se pongan en marcha de forma no controlada.
7
•
Cuando se envíen los parámetros de actuación a los servos (fundamentalmente el
ángulo de giro, duty cycle), se deben tener en cuenta los límites de estos para no
dañarlos: dc mínimo 50 (-90º) y dc máximo 240 (90º).
•
Al enviar un mensaje para la radiofrecuencia, se debe tener en cuenta que
mensajes de excesiva longitud (mayor de 30 bytes) pueden presentar problemas
a la hora de ser enviados.
Un ejemplo a la hora de introducir las órdenes:
...
comunicacion.inicio();
//se reinicia la comunicación
comunicacion.configuracion(5,4);
//se
introduce
una
nueva
configuración
en
los sensores, 5 ADs y 4 USs
comunicacion.leerAD(4);
// se solicita el 4º sensor analógico
comunicacion.pedirUSs();
// se pide un barrido de todos los US
comunicacion.periodoMotores(200);
// el periodo de los motores a 200
comunicacion.DcMotores(100,100);
// ambos Dc a 100 (motores parados)
comunicacion.activarMotores(3);
// los dos motores activados
...
4.4 Envío y Recepción
Para enviar el vector con las órdenes para el microcontrolador, se llama al método
comunica. Este método envía las órdenes y no devuelve el control hasta que no recibe
la respuesta y la procesa. En caso de que se produjeran errores durante la comunicación,
esta función devolvería un 0. Por ello, sería recomendable que al método se le llamase
desde dentro de un if, y si fallase se pusiera de manifiesto al usuario:
if(!comunicacion.comunica())
//si devuelve 0 en caso de error
MessageBox(_T("Error en la comunicación"));
Sólo se produce una comunicación si hay órdenes a ejecutar; es decir, que:
comunicacion.inicio();
comunicacion.comunica();
no realiza ninguna comunicación.
4.5 Lectura de las medidas
Existen tres tipos de medidas: las que se solicitan en una comunicación y se reciben en
la propia respuesta (analógicas y encoders); las que se solicitan en una comunicación y,
debido al tiempo que se tarda en realizarlas, no se reciben en la propia respuesta sino en
8
la respuesta de una o varias comunicaciones posteriores (ultrasonidos); y las que se
reciben sin ni siquiera haber sido solicitadas con anterioridad (digitales y
radiofrecuencia).
En el caso de las primeras, no existe problema, pues la aplicación principal tratará de
leerlas sólo cuando las haya solicitado primero. Para los otros dos tipos se deberá
consultar el vector interrupcion de forma periódica en el que se indica, con un 1
(sí) o un 0 (no), si en la última comunicación se ha recibido alguna de estas medidas no
esperadas. Cada elemento del vector se refiere a:
•
interrupcion[1]: si se ha recibido o no un mensaje procedente de la
radio.
•
interrupcion[2]: si se ha recibido la medida o medidas de ultrasonidos
solicitadas anteriormente.
•
interrupcion[3]: si se ha producido un cambio en el estado de los
sensores digitales.
Lo lógico sería que cada vez que se realice una comunicación se comprobara si se ha
recibido un nuevo mensaje de la radiofrecuencia o si ha habido cambios en el estado de
los sensores digitales. Y, en caso de que se hubiese pedido anteriormente los
ultrasonidos, comprobar también si estos se han recibido. Hay que tener en cuenta que
el vector interrupcion pondrá a 1 el elemento correspondiente solamente en el
momento que se recibe dicha respuesta no esperada, volviendo a ponerse a 0 en la
siguiente comunicación.
Una vez que se sabe qué medidas se deben leer, la forma de hacerlo es la siguiente. Se
crea un puntero constante de tipo BYTE, y éste se iguala a la función de petición de
medidas correspondiente (ver sección 3).
Un ejemplo de lectura de medidas, suponiendo que anteriormente se ha pedido un
barrido de los sensores de ultrasonidos:
int elemRadio;
const
BYTE
// indica el número de elementos que devuelve la radio
*vectorADs,
*vectorEncoders,
*vectorRadio,
*vectorUSs,
*digit;
//
definición de variables para el manejo de
valores entregados por el driver
vectorADs
=
comunicacion.valorADs();
//el
valor
de
los
ADs
se
guarda
vectorADs, que será un const BYTE*
vectorEncoders = comunicacion.valorEncoders();
//similar a la función anterior
if(comunicacion.interrupcion[1])
//si 1, se ha recibido mensaje de RF
9
en
vectorRadio = comunicacion.valorRF(&elemRadio);
//en el caso de la radio se pasa un puntero
donde
dejará
el
número
de
elementos
que
tiene el mensaje
if(comunicacion.interrupcion[2])
//si 1, se han recibido los USs
vectorUSs = comunicacion.valorUSs();
//se pasa las medidas a vectorUSs
if(comunicacion.interrupcion[3])
//si 1, ha habido cambios en digitales
digit = comunicacion.valorDig();
//se pasa el estado a digit
...
Además, en el caso de la radio se debe tener otro factor en cuenta. Los envíos recibidos
desde la radiofrecuencia pueden esperar una respuesta de la PDA hacia el que envió el
mensaje. La forma de saberlo es a través de la variable RFrespuesta, que tendrá
valor 1 si espera respuesta y 0 en caso contrario.
if(interrupcion[1]) {
//si 1, se ha recibido mensaje de RF
vectorRadio = comunicacion.valorRF(&elemRadio);
if (RFrespuesta
== 1)
// aquí el código para responder
}
Por otra parte, para saber si la TCS está funcionando y existe comunicación entre la
PDA y la TCS se puede usar la función Ping. En caso de que exista comunicación la
variable compruebaPing se pone a 1 y en caso contrario a 0.
comunicacion.inicio();
//se reinicia la comunicación
comunicacion.Ping();
if (comunicacion.comunica()){
if (comunicacion.compruebaPing)
MessageBox(_T("Comunicación OK. La TCS está viva"),_T("Aviso"),MB_OK);
else
MessageBox(_T("Error de
comunicación"),_T("Error"),MB_OK);
}
4.6 Problemas que pueden surgir
•
Al pedir ultrasonidos y esperar la respuesta el programa deja de responder.
En ocasiones pudiera ocurrir que alguna comunicación fallase, o que la TCS no
entendiese la orden de realizar la lectura de los ultrasonidos. Si esto ocurre,
teniendo la aplicación programada para no continuar hasta recibir la medida,
10
podría quedarse colgado el programa ya que la TCS nunca responderá. La
solución de este problema consiste en reprogramar la aplicación indicando que si
en diez comunicaciones (número orientativo) no se ha recibido la medida se
volviese a pedir.
•
Al crear el objeto Comm salta el mensaje “No se puede abrir el puerto”. Si
no se puede abrir el puerto serie es debido a que hay otro programa usándolo.
Cerrando dicho programa se solucionaría el problema. Si no se puede cerrar o no
hay ningún programa abierto la solución será resetear la PDA (botón de reset
situado en la parte inferior o trasera).
•
No se puede establecer la comunicación. Normalmente esto es debido a que la
última vez que se utilizó la comunicación serie, el puerto se cerró mal, o a que
hay algún problema en la TCS. En tal caso se debe resetear la PDA y la TCS.
4.7 Ejemplo de uso
Suponemos que se tiene un robot con las siguientes características:
•
El robot cuenta con dos motores situados en sus ruedas traseras.
•
La navegación se quiere controlar por medio de tres sensores de ultrasonidos.
•
También tiene el robot dos sensores de infrarrojos (analógicos) situados en un
costado, para poder controlar la distancia a la pared.
•
Además el robot cuenta con tres sensores digitales (lógica negada) que le
advierten en caso de contacto.
•
También se quiere llevar un control de la posición, y esto se realiza por medio de
dos encoders situados en cada una de las ruedas motoras.
En una primera comunicación se deberían pasar los parámetros de configuración de la
plataforma: el número de sensores, tanto de ultrasonidos como analógicos; y la
configuración inicial de los motores, es decir, activarlos pero que se encuentren parados.
Ésta primera comunicación quedaría:
// definición de variables globales
BYTE Dc1=0, Dc2=0;
// Primera comunicación con el robot, donde se activan los motores, se configuran
el número de sensores analógicos y de ultrasonidos.
comunicacion.inicio();
// se reinicia las colas Rx y Tx
comunicacion.configuracion(2,3);
// 3 sensores US y 2 sensores AD
comunicacion.periodoMotores(200);
// se pone un periodo de 200
11
comunicacion.DcMotores(100,100);
// inicialmente parados
comunicacion.activarMotores(3);
// se activan los dos motores
comunicacion.comunica();
// se pasan los parámetros al PIC
En el funcionamiento normal del robot, la función periódica a ejecutar cuando el robot
esté activo podría ser:
// definición de variables locales
const BYTE *Digitales, *USs, *ADs, *Encoders;
comunicacion.inicio();
// se reinician las colas Tx y Rx
comunicacion.pedirUSs();
// se pide el barrido de los tres USs
comunicacion.comunica();
//
se
comunica,
sabiendo
que
los
USs
no
pueden llegar en la propia comunicación
// Se inicia un bucle hasta que se reciben las medidas de los ultrasonidos, o
bien se recibe un cambio en los digitales. En el bucle se está continuamente
pidiendo los encoders y los ADs, para que cuando se salga del mismo estos no
estén desfasados respecto a los USs.
int i = 0;
//
se
establece
un
contador
para
que
no
se
quedara
infinitamente en el bucle si hubiera algún problema en la
comunicación y no se recibiesen nunca los USs
while(comunicacion.interrupcion[3]!=1
&& comunicacion.interrupcion[2]!=1 && i < 10)
{
i++;
comunicacion.inicio();
// se reinician las colas
comunicacion.leerEncoders(); // petición de los Encoders
comunicacion.leerADs();
// petición de los ADs
comunicacion.comunica();
}
if(comunicacion.interrupcion[3] == 1)
// si hubo cambio en digitales
{
Digitales = comunicacion.valorDig(); // se toma el valor de los sensores
digitales
if (Digitales[0] < 15) { // ha habido choque y paro el robot por ejemplo
Dc1 = 100; Dc2 = 100;
}
}
else if(comunicacion.interrupcion[2] == 1)
// si hubo medida de los US
{
USs = comunicacion.valorUSs();
// se guardan las medidas de los USs
ADs = comunicacion.valorADs();
// se guardan las medidas de los ADs
Encoders
=
comunicacion.valorEncoders();//
Encoders
12
se
guardan
las
medidas
de
// lógica de control en función de las medidas. Por ejemplo una sencilla
if (USs[0] < 20 || USs[1] < 20) { // gira en un sentido
Dc1 = 100; Dc2 = 0;
}
else if (USs[1] < 20) { // gira en otro
Dc1 = 0; Dc2 = 100;
}
}
comunicacion.inicio();
comunicacion.DcMotores(Dc1,Dc2);
comunicacion.comunica();
//
se
volvería
a
enlazar
con
el
principio,
bien
siendo
esto
una
función
que
la
volverían a llamar, o dentro de un bucle, etc.
Este es un modelo de comunicación, pero se podrán idear multitud de ellos. Es
necesario darse cuenta de la ineficiencia de este código, ya que precisa de tres
comunicaciones al menos por cada ciclo de control: una para pedir los ultrasonidos, otra
para recoger las medidas y otra para actuar. Si en el control no intervinieran los sensores
de ultrasonidos, el modelo sería muy distinto.
13
Descargar