Herramientas de usuario

Herramientas del sitio


guias:siguelineas_pid

Enlace a la vista de comparación

guias:siguelineas_pid [2017/10/06 08:01] – [Medida del error: el sensor] Félix Sánchez-Temblequeguias:siguelineas_pid [2021/04/16 20:41] (actual) – editor externo 127.0.0.1
Línea 9: Línea 9:
 Cuando en cualquier sistema -como nuestra casa- queremos mantener un parámetro bajo control -como la temperatura- necesitamos dos partes separadas y diferenciadas: Cuando en cualquier sistema -como nuestra casa- queremos mantener un parámetro bajo control -como la temperatura- necesitamos dos partes separadas y diferenciadas:
  
-  * Un **sensor** para conocer el valor y el error respecto de un objetivo. +  * Un **sensor**  para conocer el valor y el error respecto de un objetivo. 
-  * Un **actuador** para hacer cambios en el sistema corregir el error.+  * Un **actuador**  capaz de hacer cambios en el sistema para corregir el error.
  
 Además de esas dos partes físicas necesitamos **una estrategia de actuación**. La que vamos a contar aquí es una relativamente simple y eficiente: un controlador PID, pero //first things first//. Además de esas dos partes físicas necesitamos **una estrategia de actuación**. La que vamos a contar aquí es una relativamente simple y eficiente: un controlador PID, pero //first things first//.
Línea 20: Línea 20:
 Para detectar la línea utilizamos **sensores de reflectancia**. Estos sensores emiten luz con un diodo (normalmente infrarrojo) y leen la luz rebotada con un sensor de luz (típicamente un fotodiodo o un fototransistor). Cuando la luz rebota sobre una superficie negra da una lectura diferente que cuando rebota sobre una superficie blanca et voilà. Hay que tener cuidado, a la medida de estos sensores le afecta la distancia a la superficie de rebote y la luz ambiental. Para detectar la línea utilizamos **sensores de reflectancia**. Estos sensores emiten luz con un diodo (normalmente infrarrojo) y leen la luz rebotada con un sensor de luz (típicamente un fotodiodo o un fototransistor). Cuando la luz rebota sobre una superficie negra da una lectura diferente que cuando rebota sobre una superficie blanca et voilà. Hay que tener cuidado, a la medida de estos sensores le afecta la distancia a la superficie de rebote y la luz ambiental.
  
-{{:guias:guias:pid:irsensor.jpg?400|}}+{{:guias:guias:pid:irsensor.jpg?400}}
  
-(imágen de https://diyhacking.com/make-line-follower-robot/)+(imágen de [[https://diyhacking.com/make-line-follower-robot/|https://diyhacking.com/make-line-follower-robot/]])
  
-Para conocer la posición de la línea con respecto a nuestro robot no vamos a usar un único sensor, sino **un conjunto de sensores alineados** (a veces aparecen en inglés como array). Leyéndolos todos podemos saber dónde está la línea. Podemos construirlo o comprarlo hecho. Lo primero nos hará aprender más cosas, lo segundo seguramente nos dé un diseño más compacto.+Para conocer la posición de la línea con respecto a nuestro robot no vamos a usar un único sensor, sino **un conjunto de sensores alineados**  (a veces aparecen en inglés como array). Leyéndolos todos podemos saber dónde está la línea. Podemos construirlo o comprarlo hecho. Lo primero nos hará aprender más cosas, lo segundo seguramente nos dé un diseño más compacto.
  
-{{:guias:guias:pid:array_ir.jpg?400|}}+{{:guias:guias:pid:array_ir.jpg?400}}
  
 Con el programa siguiente, que corresponde a un sensor de Pololu QTR-8A (analógico), podemos leer los ocho sensores y sacar la lectura en el monitor serie de Arduino. Con el programa siguiente, que corresponde a un sensor de Pololu QTR-8A (analógico), podemos leer los ocho sensores y sacar la lectura en el monitor serie de Arduino.
Línea 44: Línea 44:
 { {
   qtra.read(IR); // lee los sensores   qtra.read(IR); // lee los sensores
-  +
   for (int i=0; i<8; i++)   for (int i=0; i<8; i++)
   {   {
Línea 50: Línea 50:
     Serial.print(" , ");     Serial.print(" , ");
   }   }
-   + 
-  Serial.println();  +  Serial.println();
 } }
 </code> </code>
  
-{{:guias:guias:pid:centro.jpg?400|}}+{{:guias:guias:pid:centro.jpg}}
  
-{{:guias:guias:pid:derecha.jpg?400|}}+{{:guias:guias:pid:derecha.jpg}}
  
-Una vez comprobado que las conexiones son correctas y que nuestros ocho sensores funcionan y son capaces de detectar si están sobre blanco o sobre negro, vamos a **juntar todo en una única medida** de error.+Una vez comprobado que las conexiones son correctas y que nuestros ocho sensores funcionan y son capaces de detectar si están sobre blanco o sobre negro, vamos a **juntar todo en una única medida**  de error.
  
 ==== Componer el error ==== ==== Componer el error ====
Línea 67: Línea 67:
 Dividiremos los sensores a la izquierda y a la derecha asignándoles valores positivos y negativos. Las medidas de los sensores más alejados del centro los multiplicaremos por valores -'pesos'- mayores, y sumaremos todos para tener ahora Dividiremos los sensores a la izquierda y a la derecha asignándoles valores positivos y negativos. Las medidas de los sensores más alejados del centro los multiplicaremos por valores -'pesos'- mayores, y sumaremos todos para tener ahora
  
-{{:guias:guias:pid:pesos_sensores.jpg?500|}}+{{:guias:guias:pid:pesos_sensores.jpg?500}}
  
 <code> <code>
Línea 74: Línea 74:
  
 Este valor conjunto será negativo cuando nos desviemos a un lado y positivo cuando nos desviemos al otro lado. Los resultados serán mayores al alejarnos de la línea y cuando estemos sobre ella serán cercanos a cero: ¡no hay error! Este valor conjunto será negativo cuando nos desviemos a un lado y positivo cuando nos desviemos al otro lado. Los resultados serán mayores al alejarnos de la línea y cuando estemos sobre ella serán cercanos a cero: ¡no hay error!
 +
 +{{:guias:guias:pid:sensor_compuesto.gif}}
  
 Para los más puristas: esto no da un resultado estrictamente lineal, pero es suficiente, el algoritmo es robusto. De hecho con sensores que dan salida digital también funciona. Para los más puristas: esto no da un resultado estrictamente lineal, pero es suficiente, el algoritmo es robusto. De hecho con sensores que dan salida digital también funciona.
Línea 79: Línea 81:
 ==== Modificar el sistema: el actuador ==== ==== Modificar el sistema: el actuador ====
  
-Nuestro sistema en este caso será un robot móvil que se mueve con motores de forma autónoma. Y no un diseño cualquiera, sino un diseño con dos motores controlados de forma independiente, uno a cada lado del robot, como sucede en [[proyectos:sapoconcho|Sapoconcho]], [[proyectos:raptor|Raptor]] o [[proyectos:escornabot|Escornabot]].+Nuestro sistema en este caso será un robot móvil que se mueve con motores de forma autónoma. Y no un diseño cualquiera, sino un diseño con dos motores controlados de forma independiente, uno a cada lado del robot, como sucede en [[:proyectos:sapoconcho|]], [[:proyectos:raptor|]] o [[:proyectos:escornabot|]].
  
-{{:old:archivo:sapoconcho_kis.jpg?400|}}+{{:old:archivo:sapoconcho_kis.jpg?400}}
  
 Este diseño -en inglés //differential drive//- permite controlar de manera sencilla el robot con varias acciones: Este diseño -en inglés //differential drive//- permite controlar de manera sencilla el robot con varias acciones:
Línea 88: Línea 90:
   * Si una rueda gira hacia delante y otra hacia atrás, el robot girará sobre si mismo.   * Si una rueda gira hacia delante y otra hacia atrás, el robot girará sobre si mismo.
   * **Si las dos ruedas giran hacia delante con diferente velocidad, el robot trazará una curva**.   * **Si las dos ruedas giran hacia delante con diferente velocidad, el robot trazará una curva**.
 +{{:guias:guias:pid:differential-steering-tutorials-42bots2.png?500}}
  
-{{:guias:guias:pid:differential-steering-tutorials-42bots2.png?500|}} +Imágen adaptada de [[http://42bots.com/tutorials/differential-steering-with-continuous-rotation-servos-and-arduino/|http://42bots.com/tutorials/differential-steering-with-continuous-rotation-servos-and-arduino/]]
- +
-Imágen adaptada de http://42bots.com/tutorials/differential-steering-with-continuous-rotation-servos-and-arduino/+
  
-Esta última acción es la que nos interesa, hacer que nuestro robot se mueva siguiendo una línea curva. Para eso vamos a fijar una **velocidad base** para las dos ruedas (mayor o menor para que corra más o menos) y sobre esta vamos a hacer una **corrección** sumando a una y restando a otra para trazar una curva más o menos cerrada con el siguiente pseudocódigo.+Esta última acción es la que nos interesa, hacer que nuestro robot se mueva siguiendo una línea curva. Para eso vamos a fijar una **velocidad base**  para las dos ruedas (mayor o menor para que corra más o menos) y sobre esta vamos a hacer una **corrección**  sumando a una y restando a otra para trazar una curva más o menos cerrada con el siguiente pseudocódigo.
  
 <code> <code>
Línea 100: Línea 101:
 </code> </code>
  
 +{{:guias:guias:pid:velocidad_corregida.png}}
  
-{{:guias:guias:pid:velocidad_corregida.png|}} +Para pasar esto a un sistema real necesitamos un controlador de motores que podemos usar con las [[:guias:control_de_motores|librerías que ya creamos en Bricolabs]]. En el caso de las librerías de movimiento de Sapoconcho la instrucción tiene esta pinta:
- +
-Para pasar esto a un sistema real necesitamos un controlador de motores que podemos usar con las [[guias:control_de_motores|librerías que ya creamos en Bricolabs]]. En el caso de las librerías de movimiento de Sapoconcho la instrucción tiene esta pinta:+
  
 <code> <code>
Línea 123: Línea 123:
 <code> <code>
   p = -4*IR[0] -3*IR[1] -2*IR[2] -IR[3] +IR[4] +2*IR[5] +3*IR[6] +4*IR[7]; // lee el error   p = -4*IR[0] -3*IR[1] -2*IR[2] -IR[3] +IR[4] +2*IR[5] +3*IR[6] +4*IR[7]; // lee el error
-  +
   u = kp * p // calcula la corrección   u = kp * p // calcula la corrección
-  +
   drive(vbase+u,vbase-u,0); // ejecuta la corrección   drive(vbase+u,vbase-u,0); // ejecuta la corrección
 </code> </code>
  
-Estas instrucciones deben ahora repetirse de forma indefinida para corregir constantemente el error, pero Arduino es especialista en hacer eso con el loop() ¿no? En Python para Raspberry o cualquier otro sistema podemos hacer un bucle //while(1)//.+Estas instrucciones deben ahora repetirse de forma indefinida para corregir constantemente el error, pero Arduino es especialista en hacer eso con el loop() ¿no? En Python para Raspberry o cualquier otro sistema podemos hacer un bucle //while(1) o while True//.
  
-Hay una complicación que se nos ha colado sin apenas verla; **ya tenemos dos parámetros que habrá que ajustar**: la velocidad base y el coeficiente kp. Habrá que probar valores por ensayo y error, y no será fácil. Programar un PID es siempre más fácil que ajustarlo o tunearlo. Una velocidad base inicial para probar puede ser alrededor de la mitad de la velocidad máxima del motor, para luego ir subiendo. El valor del coeficiente kp y los próximos que veremos es mejor probar a ajustarlos con saltos grandes -0.001, 0.01, 0.1, 1, 10, 100...- y luego ajustes más finos.+Hay una complicación que se nos ha colado sin apenas verla; **ya tenemos dos parámetros que habrá que ajustar**: la velocidad base y el coeficiente kp. Habrá que probar valores por ensayo y error, y no será fácil. Programar un PID es siempre más fácil que ajustarlo o tunearlo. Una velocidad base inicial para probar puede ser alrededor de la mitad de la velocidad máxima del motor, para luego ir subiendo. El valor del coeficiente kp y los próximos que veremos es mejor probar a ajustarlos con saltos grandes -0.001, 0.01, 0.1, 1, 10, 100- y luego ajustes más finos.
  
 **Advertencia**: según cómo sean los datos de los sensores y los motores y cómo estén situados (a derecha e izquierda) es posible que tengamos el sistema al revés y tengamos que hacer la corrección en sentido contrario. Probaremos por ensayo y error cambiando dónde se suma y dónde se resta la corrección de velocidad. **Advertencia**: según cómo sean los datos de los sensores y los motores y cómo estén situados (a derecha e izquierda) es posible que tengamos el sistema al revés y tengamos que hacer la corrección en sentido contrario. Probaremos por ensayo y error cambiando dónde se suma y dónde se resta la corrección de velocidad.
Línea 138: Línea 138:
   drive(vbase-u,vbase+u,0); // intercambiamos + por -   drive(vbase-u,vbase+u,0); // intercambiamos + por -
 </code> </code>
 +
 ==== Control integral (I) ==== ==== Control integral (I) ====
  
Línea 158: Línea 159:
 </code> </code>
  
-Y tenemos un nuevo parámetro ki, ya van tres, para tunear el sistema. Para este parámetro probaremos de nuevo valores muy diferentes -0.001, 0.01, 0.1, 1, 10, 100...- y luego ajuste fino.+Y tenemos un nuevo parámetro ki, ya van tres, para tunear el sistema. Para este parámetro probaremos de nuevo valores muy diferentes -0.001, 0.01, 0.1, 1, 10, 100- y luego ajuste fino.
  
 Una ventaja añadida de usar corrección integral es que si perdemos la línea por fuera del sensor, el sistema recordará que hay un error en ese sentido y que debe ser corregido. Una ventaja añadida de usar corrección integral es que si perdemos la línea por fuera del sensor, el sistema recordará que hay un error en ese sentido y que debe ser corregido.
Línea 172: Línea 173:
   d=p-p_anterior; // diferencia con el último valor   d=p-p_anterior; // diferencia con el último valor
   p_anterior=p; // guarda el valor para el siguiente ciclo del bucle   p_anterior=p; // guarda el valor para el siguiente ciclo del bucle
-  +
   u=kp*p+ki*i+kd*d;   u=kp*p+ki*i+kd*d;
-  +
   drive(vbase+u,vbase-u,0); // ejecutar la corrección   drive(vbase+u,vbase-u,0); // ejecutar la corrección
 </code> </code>
Línea 193: Línea 194:
 </code> </code>
  
-Aquí puede encontrarse el código completo para un Sapoconcho con 6 sensores: https://github.com/fstdp/sapoconcho/blob/master/examples/pidfollowline/pidfollowline.ino+Aquí puede encontrarse el código completo para un Sapoconcho con 6 sensores: [[https://github.com/felixstdp/sapoconcho/blob/master/examples/pidfollowline/pidfollowline.ino|https://github.com/felixstdp/sapoconcho/blob/master/examples/pidfollowline/pidfollowline.ino]]
  
 Sin embargo, como se dijo más arriba, programarlo es siempre más sencillo que tunearlo ajustando los parámetros. Ojalá pudiese deciros que hay parámetros universales -dependerán del robot y del circuito- o un método universal de ajuste. Pero no es así. Sin embargo, como se dijo más arriba, programarlo es siempre más sencillo que tunearlo ajustando los parámetros. Ojalá pudiese deciros que hay parámetros universales -dependerán del robot y del circuito- o un método universal de ajuste. Pero no es así.
  
-Tampoco son necesarios los tres ajustes siempre, y así tendremos controles P, PI, PD, PID... En sistemas lentos, por ejemplo, el ajuste diferencial no hace falta. +Tampoco son necesarios los tres ajustes siempre, y así tendremos controles P, PI, PD, PID… En sistemas lentos, por ejemplo, el ajuste diferencial no hace falta.
  
 Un posible método de tunning por pasos (que no siempre funciona) es: Un posible método de tunning por pasos (que no siempre funciona) es:
Línea 213: Línea 214:
 Los sistemas PID clásicos se montaban con circuitos de electrónica analógica y tenían algunos problemas que no siempre pueden corregirse con el ajuste de parámetros. Uno de ellos es que cuando el error es muy grande, antes de eliminarse puede acumularse mucho error integral y al llegar a cero nos pasamos de vueltas y seguimos corrigiendo en sentido contrario al error real. Esto en inglés se conoce como overshooting. Los sistemas PID clásicos se montaban con circuitos de electrónica analógica y tenían algunos problemas que no siempre pueden corregirse con el ajuste de parámetros. Uno de ellos es que cuando el error es muy grande, antes de eliminarse puede acumularse mucho error integral y al llegar a cero nos pasamos de vueltas y seguimos corrigiendo en sentido contrario al error real. Esto en inglés se conoce como overshooting.
  
-{{:guias:guias:pid:faq00582-1.jpg?400|}}+{{:guias:guias:pid:faq00582-1.jpg?400}}
  
 Pero nuestro sistema no es analógico, es un programa, y si detecta que estamos estamos haciendo una corrección integral de sentido contrario al error, podemos eliminar este valor poniéndolo de nuevo a cero (en inglés integral windup). Pero nuestro sistema no es analógico, es un programa, y si detecta que estamos estamos haciendo una corrección integral de sentido contrario al error, podemos eliminar este valor poniéndolo de nuevo a cero (en inglés integral windup).
Línea 219: Línea 220:
 <code> <code>
   p = -4*IR[0]-3*IR[1]-2*IR[2]-IR[3]+IR[4]+2*IR[5]+3*IR[6]+4*IR[7];   p = -4*IR[0]-3*IR[1]-2*IR[2]-IR[3]+IR[4]+2*IR[5]+3*IR[6]+4*IR[7];
-  +
   i=i+p;   i=i+p;
   if ((p*i)<0) i=0;  // corrige el overshooting - integral windup   if ((p*i)<0) i=0;  // corrige el overshooting - integral windup
-  +
   d=p-p_anterior;   d=p-p_anterior;
   p_anterior=p;   p_anterior=p;
Línea 229: Línea 230:
 </code> </code>
  
-O en un código más compacto (y menos sencillo)+O en un código más compacto (y menos legible)
  
 <code> <code>
Línea 245: Línea 246:
   drive(vbase+u,vbase-u,0);   drive(vbase+u,vbase-u,0);
 </code> </code>
 +
 +==== Tercera bola extra: velocidad variable ====
 +
 +Este último algoritmo (publicado después de la OSHWDem17) utiliza una velocidad base variable, más rápida en recta, más lenta en curva. Y más parámetros a tunear. La función es no lineal con una velocidad máxima, una mínima y una constante de decaimiento (kv) de una a otra.
 +
 +<code>
 +  p = -7*IR[0]-5*IR[1]-3*IR[2]-IR[3]+IR[4]+3*IR[5]+5*IR[6]+7*IR[7];
 +  i=i+p;
 +  d=p-p_old;
 +  p_old=p;
 +  if ((p*i)<0) i=0;  // integral windup
 +
 +  u=kp*p+ki*i+kd*d;
 +  vbase=vmin+(vmax-vmin)*exp(-kv*abs(kp*p));
 +  drive(vbase+u,vbase-u);
 +</code>
 +
 +Lo puedes encontrar entero aquí [[https://github.com/felixstdp/raptor/blob/master/raptor_nl_pid.ino|https://github.com/felixstdp/raptor/blob/master/raptor_nl_pid.ino]]
  
 ===== Equipo ===== ===== Equipo =====
Línea 254: Línea 273:
   * [[https://github.com/fstdp/raptor/blob/master/raptor_qtr8rc.ino|Siguelíneas PID con QTR-8RC]]   * [[https://github.com/fstdp/raptor/blob/master/raptor_qtr8rc.ino|Siguelíneas PID con QTR-8RC]]
   * [[https://github.com/fstdp/sapoconcho/blob/master/examples/pidfollowline/pidfollowline.ino|Siguelíneas PID con QTR-8A]]   * [[https://github.com/fstdp/sapoconcho/blob/master/examples/pidfollowline/pidfollowline.ino|Siguelíneas PID con QTR-8A]]
 +
  
guias/siguelineas_pid.1507276901.txt.gz · Última modificación: 2021/04/16 20:39 (editor externo)