Vous êtes sur la page 1sur 33

UNIVERSIDAD DE LAS FUERZAS ARMADAS

ESPE EXTENSIÓN LATACUNGA

INGENIERIA ELECTRÓNICA E INSTRUMENTACIÓN


Ingeniería Electrónica e Instrumentación, Sexto, Universidad de las Fuerzas Armadas ESPE-L, Márquez de
Maenza S/N Latacunga, Ecuador.

SISTEMAS DE CONTROL

SEGUIDOR DE LÍNEA CON CONTROL PID

Nombres: ESCUDERO MIGUEL


HERRERA DANNY
RUIZ CRISTIAN

MARZO 2015
Tabla de contenido
TEMA ............................................................................................................................................. 2
Objetivos ....................................................................................................................................... 2
General .................................................................................................................................. 2
Específicos ............................................................................................................................. 2
Resumen ....................................................................................................................................... 2
Abstract ......................................................................................................................................... 3
Marco Teórico ............................................................................................................................... 3
El Sensor Óptico reflexivo con salida a Transistor CNY 70 ........................................ 3

Arduino Mega........................................................................................................................ 3
Control PID ............................................................................................................................ 4
Funcionamiento .................................................................................................................... 8
Conclusiones ......................................................................................................................... 9
Recomendaciones ................................................................................................................. 9
Bibliografía ............................................................................................................................ 9
Anexos ................................................................................................................................. 10

1. TEMA: IMPLEMENTACIÓN DE UN SEGUIDOR DE LINEA CON CONTROL PID.


2. OBJETIVOS:
2.1. General:

Implementar un control PID a un seguidor de Línea usando Arduino para


mejorar su desempeño y para poder visualizar la respuesta del sistema.

2.2. Específicos:
 Diseñar un seguidor de Línea.
 Realizar la respectiva programación en Arduino.
 Visualizar la respuesta del sistema en un ordenador.
 Probar el prototipo agregando perturbaciones.
 Analizar los resultados en base a los conocimientos recibidos.

3. Resumen

El presente proyecto presenta el diseño, construcción y programación en Arduino de


un seguidor de línea el cual tiene 6 sensores digitales, con el propósito de implementar
un control PID ya que es de suma importancia que el seguidor pueda corregir errores y
volver a la línea en caso de una perturbación. Para verificar estas características del
control PID, usamos el programa Processing el cual se conecta con Arduino y permite
visualizar la respuesta en estado estable de nuestro seguidor, haciendo un entorno
amigable e interactivo para el aprendizaje.

2
4. Abstract

This work presents the design, construction and programming Arduino a line follower
which has 6 digital sensors, in order to implement a PID control as it is paramount that
the follower can correct errors and return to the line if a disturbance. To verify these
characteristics of PID control, use the Processing program which connects with
Arduino and displays the steady state response of our tracker, making a friendly and
interactive learning environment.

5. Marco teórico

5.1. El Sensor Óptico reflexivo con salida a Transistor CNY 70.


El CNY70 es un sensor óptico reflexivo que tiene una construcción compacta dónde el
emisor de luz y el receptor se colocan en la misma dirección para detectar la presencia
de un objeto utilizando la reflexión del infrarrojo sobre el objeto. La longitud de onda de
trabajo es 950nm. El detector consiste en un fototransistor.

- Características.
La construcción compacta con distancia de del centro-a-centro de 0.1 ' (pulgadas)
No necesita ningún ambiente especial
Señal de salida alta
El coeficiente de temperatura bajo
Detector provista de filtro óptico
El ratio de corriente de transferencia (CTR) típico es del 5%

5.2. Arduino Mega

El Mega Arduino es una placa electrónica basada en el ATmega1280 (ficha técnica).


Cuenta con 54 pines digitales de entrada / salida (de los cuales 14 se pueden utilizar
como salidas PWM), 16 entradas analógicas, 4 UARTs (puertas seriales), un oscilador
de 16MHz, una conexión USB, un conector de alimentación, una cabecera ICSP, y un
botón de reinicio. Contiene todo lo necesario para apoyar el microcontrolador; basta
con conectarlo a un ordenador con un cable USB o el poder con un adaptador de CA o
la batería a CC para empezar. La Mega es compatible con la mayoría de los escudos
diseñados para el Arduino Duemilanove o Diecimila.

3
5.3. Control PID

Figura 1: diagrama de bloques de un control PID

Un PID es un mecanismo de control por realimentación que calcula la desviación o


error entre un valor medido y el valor que se quiere obtener, para aplicar una acción
correctora que ajuste el proceso. El algoritmo de cálculo del control PID se da en tres
parámetros distintos: el proporcional, el integral, y el derivativo. El valor Proporcional
determina la reacción del error actual. El Integral genera una corrección proporcional a
la integral del error, esto nos asegura que aplicando un esfuerzo de control suficiente,

4
el error de seguimiento se reduce a cero. El Derivativo determina la reacción del
tiempo en el que el error se produce. La suma de estas tres acciones es usada para
ajustar al proceso vía un elemento de control como la posición de una válvula de
control o la energía suministrada a un calentador, por ejemplo. Ajustando estas tres
variables en el algoritmo de control del PID, el controlador puede proveer un control
diseñado para lo que requiera el proceso a realizar. La respuesta del controlador
puede ser descrita en términos de respuesta del control ante un error, el grado el cual
el controlador llega al "set point", y el grado de oscilación del sistema. Nótese que el
uso del PID para control no garantiza control óptimo del sistema o la estabilidad del
mismo. Algunas aplicaciones pueden solo requerir de uno o dos modos de los que
provee este sistema de control. Un controlador PID puede ser llamado también PI, PD,
P o I en la ausencia de las acciones de control respectivas.

- Proporcional

Figura 2: diagrama de un control proporcional

La parte proporcional consiste en el producto entre la señal de error y la constante


proporcional para lograr que el error en estado estacionario se aproxime a cero, pero
en la mayoría de los casos, estos valores solo serán óptimos en una determinada
porción del rango total de control, siendo distintos los valores óptimos para cada
porción del rango. Sin embargo, existe también un valor límite en la constante
proporcional a partir del cual, en algunos casos, el sistema alcanza valores superiores
a los deseados. Este fenómeno se llama sobreoscilación y, por razones de seguridad,
no debe sobrepasar el 30%, aunque es conveniente que la parte proporcional ni
siquiera produzca sobreoscilación. Hay una relación lineal continua entre el valor de la
variable controlada y la posición del elemento final de control (la válvula se mueve al
mismo valor por unidad de desviación). La parte proporcional no considera el tiempo,
por lo tanto, la mejor manera de solucionar el error permanente y hacer que el sistema
contenga alguna componente que tenga en cuenta la variación respecto al tiempo, es
incluyendo y configurando las acciones integral y derivativa.

La fórmula del proporcional está dada por:

5
El error, la banda proporcional y la posición inicial del elemento final de control se
expresan en tanto por uno. Nos indicará la posición que pasará a ocupar el elemento
final de control
Ejemplo: Cambiar la posición de una válvula (elemento final de control)
proporcionalmente a la desviación de la temperatura (variable) respecto al punto de
consigna

- Integral

Figura 3: diagrama de un control proporcional

El modo de control Integral tiene como propósito disminuir y eliminar el error en estado
estacionario, provocado por el modo proporcional. El control integral actúa cuando hay
una desviación entre la variable y el punto de consigna, integrando esta desviación en
el tiempo y sumándola a la acción proporcional. El error es integrado, lo cual tiene la
función de promediarlo o sumarlo por un período determinado; Luego es multiplicado
por una constante Ki. Posteriormente, la respuesta integral es adicionada al modo
Proporcional para formar el control P + I con el propósito de obtener una respuesta
estable del sistema sin error estacionario.
El modo integral presenta un desfasamiento en la respuesta de 90º que sumados a los
180º de la retroalimentación ( negativa ) acercan al proceso a tener un retraso de 270º,
luego entonces solo será necesario que el tiempo muerto contribuya con 90º de
retardo para provocar la oscilación del proceso. <<< la ganancia total del lazo de
control debe ser menor a 1, y así inducir una atenuación en la salida del controlador
para conducir el proceso a estabilidad del mismo. >>> Se caracteriza por el tiempo de
acción integral en minutos por repetición. Es el tiempo en que delante una señal en
escalón, el elemento final de control repite el mismo movimiento correspondiente a la
acción proporcional.
El control integral se utiliza para obviar el inconveniente del offset (desviación
permanente de la variable con respecto al punto de consigna) de la banda
proporcional.

La fórmula del integral está dada por:

6
- Derivativo

La acción derivativa se manifiesta cuando hay un cambio en el valor absoluto del error;
(si el error es constante, solamente actúan los modos proporcional e
integral).El error es la desviación existente entre el punto de medida y el valor
consigna, o "Set Point".La función de la acción derivativa es mantener el error al
mínimo corrigiéndolo proporcionalmente con la misma velocidad que se produce; de
esta manera evita que el error se incremente.
Se deriva con respecto al tiempo y se multiplica por una constante D y luego se suma
a las señales anteriores (P+I). Es importante adaptar la respuesta de control a los
cambios en el sistema ya que una mayor derivativa corresponde a un cambio más
rápido y el controlador puede responder acordemente.

La fórmula del derivativo está dada por:


El control derivativo se caracteriza por el tiempo de acción derivada en minutos de
anticipo. La acción derivada es adecuada cuando hay retraso entre el movimiento de
la válvula de control y su repercusión a la variable controlada.
Cuando el tiempo de acción derivada es grande, hay inestabilidad en el proceso.
Cuando el tiempo de acción derivada es pequeño la variable oscila demasiado con
relación al punto de consigna. Suele ser poco utilizada debido a la sensibilidad al ruido
que manifiesta y a las complicaciones que ello conlleva.
El tiempo óptimo de acción derivativa es el que retorna la variable al punto de
consigna con las mínimas oscilaciones
Ejemplo: Corrige la posición de la válvula (elemento final de control)
proporcionalmente a la velocidad de cambio de la variable controlada.
La acción derivada puede ayudar a disminuir el rebasamiento de la variable durante el
arranque del proceso. Puede emplearse en sistemas con tiempo de retardo
considerables, porque permite una repercusión rápida de la variable después de
presentarse una perturbación en el proceso.

Figura 4: diagrama de un control proporcional

7
- Significado de las constantes

P constante de proporcionalidad: se puede ajustar como el valor de la ganancia del


controlador o el porcentaje de banda proporcional. Ejemplo: Cambia la posición de la
válvula proporcionalmente a la desviación de la variable respecto al punto de
consigna. La señal P mueve la válvula siguiendo fielmente los cambios de temperatura
multiplicados por la ganáncia.
I constante de integración: indica la velocidad con la que se repite la acción
proporcional.
D constante de derivación: hace presente la respuesta de la acción proporcional
duplicándola, sin esperar a que el error se duplique. El valor indicado por la constante
de derivación es el lapso de tiempo durante el cual se manifestará la acción
proporcional correspondiente a 2 veces el error y después desaparecerá. Ejemplo:
Mueve la válvula a una velocidad proporcional a la desviación respecto al punto de
consigna. La señal I va sumando las áreas diferentes entre la variable y el punto de
consigna repitiendo la señal proporcional según el tiempo de acción derivada
(minutos/repetición).
Tanto la acción Integral como la acción Derivativa, afectan a la ganancia dinámica del
proceso. La acción integral sirve para reducir el error estacionario, que existiría
siempre si la constante Ki fuera nula. Ejemplo: Corrige la posición de la válvula
proporcionalmente a la velocidad de cambio de la variable controlada. La señal d es la
pendiente (tangente) por la curva descrita por la variable.
La salida de estos tres términos, el proporcional, el integral, y el derivativo son
sumados para calcular la salida del controlador PID. Definiendo y (t) como la salida del
controlador, la forma final del algoritmo del PID es:

6. Funcionamiento

1. Los 6 sensores CNY 70 sensan continuamente la línea negra y envían una trama
de 0 y 1 dependiendo la refractibilidad de cada uno y su posición.

2. Arduino uno compara cada uno de las tramas enviadas y conoce la posición a la
que se encuentra el robot seguidor de linea

3. Se realizar la acción de control PID

4. Los motores giran según el ancho de pulso enviado por el PWM

Para el correcto funcionamiento de un controlador PID que regule un proceso o


sistema se necesita, al menos:

- Un sensor, que determine el estado del sistema (CNY 70).


- Un controlador, que genere la señal que gobierna al actuador.
- Un actuador, que modifique al sistema de manera controlada (resistencia
eléctrica, motor, válvula, bomba, etc).

8
El sensor proporciona una señal digital al controlador, la cual representa el punto
actual en el que se encuentra el proceso o sistema. La señal un valor lógico de 0 o 1.
El controlador lee una señal externa que representa el valor que se desea alcanzar.
Esta señal recibe el nombre de punto de consigna (o punto de referencia), la cual es
de la misma naturaleza y tiene el mismo rango de valores que la señal que
proporciona el sensor.
El controlador resta la señal de punto actual a la señal de punto de consigna,
obteniendo así la señal de error, que determina en cada instante la diferencia que hay
entre el valor deseado (consigna) y el valor medido. La señal de error es utilizada por
cada uno de los 3 componentes del controlador PID. Las 3 señales sumadas,
componen la señal de salida que el controlador va a utilizar para gobernar al actuador.
La señal resultante de la suma de estas tres se llama variable manipulada y no se
aplica directamente sobre el actuador, sino que debe ser transformada para ser
compatible con el actuador utilizado.

Las tres componentes de un controlador PID son: parte Proporcional, acción Integral y
acción Derivativa. El peso de la influencia que cada una de estas partes tiene en la
suma final, viene dado por la constante proporcional, el tiempo integral y el tiempo
derivativo, respectivamente. Se pretenderá lograr que el bucle de control corrija
eficazmente y en el mínimo tiempo posible los efectos de las perturbaciones.

7. Desarrollo
Para la implementación del control PID a un seguidor de línea se utilizó los
siguientes materiales:

 Modulo arduino omega


 Drive de motores
 Cables de proto board
 Micro motores
 Chasis de acrílico
 Rueda loca
 Arreglo de sensores
 Ruedas para los micro motores
 Software de Arduino

Para desarrollar este proyecto primero fue necesario contar con todas las
herramientas de software que permiten lograr una comunicación entre Arduino omega
y el computador, así como lograr procesar la información proveniente de los
componentes físicos.

El módulo Arduino cuenta con un software exclusivo con su propias librerías y


comandos, correspondiente a su entorno de desarrollo, el mismo que puede ser
descargado de forma libre desde la página web oficial de Arduino, cuando se haya
instalado correctamente esta herramienta, se procede a realizar las respectivas
configuraciones y reconocimiento de puertos, debido a que el Arduino OMEGA se
conecta al ordenador a través un cable USB.
Una vez instalado el software de Arduino se procede a abrir el archivo que contiene el
código.

Código de Arduino para el seguidor de línea

9
#include <PID_v1.h>

#include <QTRSensors.h>

#include <DRV8835MotorShield.h>

#define NUM_SENSORS 6

#define TIMEOUT 2500

#define EMITTER_PIN 2

QTRSensorsRC qtrrc((unsigned char[]) {3,4,5,6,11,12},

NUM_SENSORS,TIMEOUT, EMITTER_PIN);

unsigned int sensorValues[NUM_SENSORS];

DRV8835MotorShield motors;

const int MAX_SPEED = 400;

int KP = 4;

int KD = 6;

int KI=0.0001;

int velcalibrate = 100;

int lastError=0;

//Define Variables we'll be connecting to

double Setpoint, Input, Output;

int inputPin=5, outputPin=2;

//Specify the links and initial tuning parameters

PID myPID(&Input, &Output, &Setpoint,5,2,1, DIRECT);

unsigned long serialTime; //this will help us know when to talk with processing

void setup()

//initialize the serial link with processing

10
Serial.begin(9600);

//initialize the variables we're linked to

Input = analogRead(inputPin);

Setpoint = 100;

//turn the PID on

myPID.SetMode(AUTOMATIC);

delay(500);

int i;

pinMode(13, OUTPUT);

digitalWrite(13, HIGH); // turn on LED to indicate we are in calibration mode

for (i = 0; i < 80; i++) // make the calibration take about 10 seconds

if (i < 20 || i >= 60)

motors.setSpeeds(-100, 100);

else

motors.setSpeeds(100, -100);

qtrrc.calibrate();

// Since our counter runs to 80, the total delay will be

// 80*20 = 1600 ms.

delay(5);

// reads all sensors 10 times at 2.5 ms per six sensors (i.e. ~25 ms per call)

digitalWrite(13, LOW); // turn off LED to indicate we are through with calibration

motors.setSpeeds(0,0);

11
// print the calibration minimum values measured when emitters were on

Serial.begin(9600);

for (i = 0; i < NUM_SENSORS; i++)

Serial.print(qtrrc.calibratedMinimumOn[i]);

Serial.print(' ');

Serial.println();

// print the calibration maximum values measured when emitters were on

for (i = 0; i < NUM_SENSORS; i++)

Serial.print(qtrrc.calibratedMaximumOn[i]);

Serial.print(' ');

Serial.println();

Serial.println();

delay(1000);

/////////////////////////////////////////////////////

unsigned int last_proportional = 0;

long integral;

void loop()

//pid-related code

Input = analogRead(inputPin);

myPID.Compute();

analogWrite(outputPin,Output);

12
/////////////////////////////////////////////////////////////////////////////

unsigned int position = qtrrc.readLine(sensorValues); // leemos posicion de la linea en la


variable position

// print the sensor values as numbers from 0 to 9, where 0 means maximum reflectance
and

// 9 means minimum reflectance, followed by the line position

unsigned char i;

for (i = 0; i < NUM_SENSORS; i++)

Serial.print(sensorValues[i] * 10 / 1001);

Serial.print(' ');

Serial.print(" ");

Serial.println(position);// set point

// Referencia donde seguira la linea, mitad senasores.

int error = position - 2500;

int speedDifference = error / KP + KD * (error - lastError)+KI/(error - lastError);

Serial.print(" ");

Serial.println(error); // variable de entrada

Serial.print(" ");

Serial.println(speedDifference); // variable de salida

lastError = error;

13
int m1Speed = MAX_SPEED + speedDifference;

int m2Speed = MAX_SPEED - speedDifference;

if (m1Speed < 0)

m1Speed = 0;

if (m2Speed < 0)

m2Speed = 0;

if (m1Speed > MAX_SPEED)

m1Speed = MAX_SPEED;

if (m2Speed > MAX_SPEED)

m2Speed = MAX_SPEED;

motors.setSpeeds(m2Speed, m1Speed);

/////////////////////////////////////////////////////////////////////////////

//send-receive with processing if it's time

if(millis()>serialTime)

SerialReceive();

SerialSend();

serialTime+=500;

/********************************************

* Serial Communication functions / helpers

14
********************************************/

union { // This Data structure lets

byte asBytes[24]; // us take the byte array

float asFloat[6]; // sent from processing and

} // easily convert it to a

foo; // float array

void SerialReceive()

// read the bytes sent from Processing

int index=0;

byte Auto_Man = -1;

byte Direct_Reverse = -1;

while(Serial.available()&&index<26)

if(index==0) Auto_Man = Serial.read();

else if(index==1) Direct_Reverse = Serial.read();

else foo.asBytes[index-2] = Serial.read();

index++;

// if the information we got was in the correct format,

// read it into the system

if(index==26 && (Auto_Man==0 || Auto_Man==1)&& (Direct_Reverse==0 ||


Direct_Reverse==1))

15
{

Setpoint=double(foo.asFloat[0]);

//Input=double(foo.asFloat[1]); // * the user has the ability to send the

// value of "Input" in most cases (as

// in this one) this is not needed.

if(Auto_Man==0) // * only change the output if we are in

{ // manual mode. otherwise we'll get an

Output=double(foo.asFloat[2]); // output blip, then the controller will

} // overwrite.

double p, i, d; // * read in and set the controller tunings

p = double(foo.asFloat[3]); //

i = double(foo.asFloat[4]); //

d = double(foo.asFloat[5]); //

myPID.SetTunings(p, i, d); //

if(Auto_Man==0) myPID.SetMode(MANUAL);// * set the controller mode

else myPID.SetMode(AUTOMATIC); //

if(Direct_Reverse==0) myPID.SetControllerDirection(DIRECT);// * set the controller


Direction

else myPID.SetControllerDirection(REVERSE); //

Serial.flush(); // * clear any random data from the serial buffer

void SerialSend()

16
Serial.print("PID ");

Serial.print(Setpoint);

Serial.print(" ");

Serial.print(Input);

Serial.print(" ");

Serial.print(Output);

Serial.print(" ");

Serial.print(myPID.GetKp());

Serial.print(" ");

Serial.print(myPID.GetKi());

Serial.print(" ");

Serial.print(myPID.GetKd());

Serial.print(" ");

if(myPID.GetMode()==AUTOMATIC) Serial.print("Automatic");

else Serial.print("Manual");

Serial.print(" ");

if(myPID.GetDirection()==DIRECT) Serial.println("Direct");

else Serial.println("Reverse");

Inicialmente se establecen todos los valores iniciales de las ganancias para el control PID
con los que se desea trabajar, se puede ir probando con varios valores hasta obtener el
resultado requerido.
Para recibir la entrada analógica, proveniente del sensor, se utiliza el comando
‘analogRead’, aquí se define el pin de entrada analógico por donde ingresa la señal. La
salida que se utiliza en este proyecto es una señal PWM con la cual se controlara los
motores del seguidor de línea. A la salida de esta función se obtiene el parámetro de
control, el cual corresponde a un rango de valores de 0 a 500 que son valores que
monitoreamos con Arduino mediante los sensores.

En el circuito la señal del sensor ingresa a la placa Arduino, y se compara con un setpoint
que puede ser modificado en el programa arduino debido al monitoreo decidimos que
nuestro setpoint iva a ser 500 ya que en 500 el robot iva por la mitad de la línea. A la salida
se obtiene una señal PWM como se mencionó anteriormente.

17
Para encontrar los valores de las ganancias Kp, Ki, Kd se consultaron en tablas ya
realizadas por medio de cálculos para cada situación como se puede ver en la
siguiente tabla.
Entonces para definir estos valores, se puede ver en la tabla que hay fórmulas de
sintonización para controles rápidos, normales y lentos. Esto depende de cada
aplicación aunque no son valores estándar ya que se pueden modificar según los
requerimientos.

Tabla N.1 para un control rápido

Tabla N.2 para un control normal

Tabla N.3 para un control lento

Entonces para definir estos valores, se puede ver en la tabla que hay fórmulas de
sintonización para controles rápidos, normales y lentos. Esto depende de cada
aplicación aunque no son valores estándar ya que se pueden modificar según los
requerimientos.

Para observar la gráfica q produce nuestro sistema contamos con el siguiente código
en el software Prosessing que trabajan en conjunto.

import java.nio.ByteBuffer;
import processing.serial.*;
import controlP5.*;

18
/***********************************************
* User spcification section
**********************************************/
int windowWidth = 900; // set the size of the
int windowHeight = 600; // form

float InScaleMin = 0; // set the Y-Axis Min


float InScaleMax = 1024; // and Max for both
float OutScaleMin = 0; // the top and
float OutScaleMax = 255; // bottom trends

int windowSpan = 30000; // number of mS into the past you want to display
int refreshRate = 100; // how often you want the graph to be reDrawn;

//float displayFactor = 1; //display Time as Milliseconds


//float displayFactor = 1000; //display Time as Seconds
float displayFactor = 60000; //display Time as Minutes

String outputFileName = ""; // if you'd like to output data to


// a file, specify the path here

/***********************************************
* end user spec
**********************************************/

int nextRefresh;
int arrayLength = windowSpan / refreshRate+1;
int[] InputData = new int[arrayLength]; //we might not need them this big, but
int[] SetpointData = new int[arrayLength]; // this is worst case
int[] OutputData = new int[arrayLength];

float inputTop = 25;


float inputHeight = (windowHeight-70)*2/3;
float outputTop = inputHeight+50;
float outputHeight = (windowHeight-70)*1/3;

float ioLeft = 150, ioWidth = windowWidth-ioLeft-50;


float ioRight = ioLeft+ioWidth;
float pointWidth= (ioWidth)/float(arrayLength-1);

int vertCount = 10;

int nPoints = 0;

float Input, Setpoint, Output;

boolean madeContact =false;


boolean justSent = true;

19
Serial myPort;

ControlP5 controlP5;
controlP5.Button AMButton, DRButton;
controlP5.Textlabel AMLabel, AMCurrent, InLabel,
OutLabel, SPLabel, PLabel,
ILabel, DLabel,DRLabel, DRCurrent;
controlP5.Textfield SPField, InField, OutField,
PField, IField, DField;

PrintWriter output;
PFont AxisFont, TitleFont;

void setup()
{
frameRate(30);
size(windowWidth , windowHeight);

println(Serial.list()); // * Initialize Serial


myPort = new Serial(this, Serial.list()[0], 9600); // Communication with
myPort.bufferUntil(10); // the Arduino

controlP5 = new ControlP5(this); // * Initialize the various


SPField= controlP5.addTextfield("Setpoint",10,100,60,20); // Buttons, Labels, and
InField = controlP5.addTextfield("Input",10,150,60,20); // Text Fields we'll be
OutField = controlP5.addTextfield("Output",10,200,60,20); // using
PField = controlP5.addTextfield("Kp (Proportional)",10,275,60,20); //
IField = controlP5.addTextfield("Ki (Integral)",10,325,60,20); //
DField = controlP5.addTextfield("Kd (Derivative)",10,375,60,20); //
AMButton = controlP5.addButton("Toggle_AM",0.0,10,50,60,20); //
AMLabel = controlP5.addTextlabel("AM","Manual",12,72); //
AMCurrent = controlP5.addTextlabel("AMCurrent","Manual",80,65); //
controlP5.addButton("Send_To_Arduino",0.0,10,475,120,20); //
SPLabel=controlP5.addTextlabel("SP","3",80,103); //
InLabel=controlP5.addTextlabel("In","1",80,153); //
OutLabel=controlP5.addTextlabel("Out","2",80,203); //
PLabel=controlP5.addTextlabel("P","4",80,278); //
ILabel=controlP5.addTextlabel("I","5",80,328); //
DLabel=controlP5.addTextlabel("D","6",80,378); //
DRButton = controlP5.addButton("Toggle_DR",0.0,10,425,60,20); //
DRLabel = controlP5.addTextlabel("DR","Direct",12,447); //
DRCurrent = controlP5.addTextlabel("DRCurrent","Direct",80,440); //

//AxisFont = loadFont("axis.vlw");
//TitleFont = loadFont("Titles.vlw");

nextRefresh=millis();
if (outputFileName!="") output = createWriter(outputFileName);
}

void draw()
{

20
background(200);
drawGraph();
drawButtonArea();
}

void drawGraph()
{
//draw Base, gridlines
stroke(0);
fill(230);
rect(ioLeft, inputTop,ioWidth-1 , inputHeight);
rect(ioLeft, outputTop, ioWidth-1, outputHeight);
stroke(210);

//Section Titles
//textFont(TitleFont);
fill(255);
text("PID Input / Setpoint",(int)ioLeft+10,(int)inputTop-5);
text("PID Output",(int)ioLeft+10,(int)outputTop-5);

//GridLines and Titles


//textFont(AxisFont);

//horizontal grid lines


int interval = (int)inputHeight/5;
for(int i=0;i<6;i++)
{
if(i>0&&i<5) line(ioLeft+1,inputTop+i*interval,ioRight-2,inputTop+i*interval);
text(str((InScaleMax-InScaleMin)/5*(float)(5-
i)+InScaleMin),ioRight+5,inputTop+i*interval+4);

}
interval = (int)outputHeight/5;
for(int i=0;i<6;i++)
{
if(i>0&&i<5) line(ioLeft+1,outputTop+i*interval,ioRight-2,outputTop+i*interval);
text(str((OutScaleMax-OutScaleMin)/5*(float)(5-
i)+OutScaleMin),ioRight+5,outputTop+i*interval+4);
}

//vertical grid lines and TimeStamps


int elapsedTime = millis();
interval = (int)ioWidth/vertCount;
int shift = elapsedTime*(int)ioWidth / windowSpan;
shift %=interval;

int iTimeInterval = windowSpan/vertCount;


float firstDisplay = (float)(iTimeInterval*(elapsedTime/iTimeInterval))/displayFactor;
float timeInterval = (float)(iTimeInterval)/displayFactor;
for(int i=0;i<vertCount;i++)

21
{
int x = (int)ioRight-shift-2-i*interval;

line(x,inputTop+1,x,inputTop+inputHeight-1);
line(x,outputTop+1,x,outputTop+outputHeight-1);

float t = firstDisplay-(float)i*timeInterval;
if(t>=0) text(str(t),x,outputTop+outputHeight+10);
}

// add the latest data to the data Arrays. the values need
// to be massaged to get them to graph correctly. they
// need to be scaled to fit where they're going, and
// because 0, 0 is the top left, we need to flip the values.
// this is easier than having the user stand on their head
// to read the graph.
if(millis() > nextRefresh && madeContact)
{
nextRefresh += refreshRate;

for(int i=nPoints-1;i>0;i--)
{
InputData[i]=InputData[i-1];
SetpointData[i]=SetpointData[i-1];
OutputData[i]=OutputData[i-1];
}
if (nPoints < arrayLength) nPoints++;

InputData[0] = int(inputHeight)-int(inputHeight*(Input-InScaleMin)/(InScaleMax-
InScaleMin));
SetpointData[0] =int( inputHeight)-int(inputHeight*(Setpoint-InScaleMin)/(InScaleMax-
InScaleMin));
OutputData[0] = int(outputHeight)-int(outputHeight*(Output-OutScaleMin)/(OutScaleMax-
OutScaleMin));
}
//draw lines for the input, setpoint, and output
strokeWeight(2);
for(int i=0; i<nPoints-2; i++)
{
int X1 = int(ioRight-2-float(i)*pointWidth);
int X2 = int(ioRight-2-float(i+1)*pointWidth);
boolean y1Above, y1Below, y2Above, y2Below;

//DRAW THE INPUT


boolean drawLine=true;
stroke(255,0,0);
int Y1 = InputData[i];
int Y2 = InputData[i+1];

y1Above = (Y1>inputHeight); // if both points are outside

22
y1Below = (Y1<0); // the min or max, don't draw the
y2Above = (Y2>inputHeight); // line. if only one point is
y2Below = (Y2<0); // outside constrain it to the limit,
if(y1Above) // and leave the other one untouched.
{ //
if(y2Above) drawLine=false; //
else if(y2Below) { //
Y1 = (int)inputHeight; //
Y2 = 0; //
} //
else Y1 = (int)inputHeight; //
} //
else if(y1Below) //
{ //
if(y2Below) drawLine=false; //
else if(y2Above) { //
Y1 = 0; //
Y2 = (int)inputHeight; //
} //
else Y1 = 0; //
} //
else //
{ //
if(y2Below) Y2 = 0; //
else if(y2Above) Y2 = (int)inputHeight; //
} //

if(drawLine)
{
line(X1,Y1+inputTop, X2, Y2+inputTop);
}

//DRAW THE SETPOINT


drawLine=true;
stroke(0,255,0);
Y1 = SetpointData[i];
Y2 = SetpointData[i+1];

y1Above = (Y1>(int)inputHeight); // if both points are outside


y1Below = (Y1<0); // the min or max, don't draw the
y2Above = (Y2>(int)inputHeight); // line. if only one point is
y2Below = (Y2<0); // outside constrain it to the limit,
if(y1Above) // and leave the other one untouched.
{ //
if(y2Above) drawLine=false; //
else if(y2Below) { //
Y1 = (int)(inputHeight); //
Y2 = 0; //
} //
else Y1 = (int)(inputHeight); //
} //
else if(y1Below) //

23
{ //
if(y2Below) drawLine=false; //
else if(y2Above) { //
Y1 = 0; //
Y2 = (int)(inputHeight); //
} //
else Y1 = 0; //
} //
else //
{ //
if(y2Below) Y2 = 0; //
else if(y2Above) Y2 = (int)(inputHeight); //
} //

if(drawLine)
{
line(X1, Y1+inputTop, X2, Y2+inputTop);
}

//DRAW THE OUTPUT


drawLine=true;
stroke(0,0,255);
Y1 = OutputData[i];
Y2 = OutputData[i+1];

y1Above = (Y1>outputHeight); // if both points are outside


y1Below = (Y1<0); // the min or max, don't draw the
y2Above = (Y2>outputHeight); // line. if only one point is
y2Below = (Y2<0); // outside constrain it to the limit,
if(y1Above) // and leave the other one untouched.
{ //
if(y2Above) drawLine=false; //
else if(y2Below) { //
Y1 = (int)outputHeight; //
Y2 = 0; //
} //
else Y1 = (int)outputHeight; //
} //
else if(y1Below) //
{ //
if(y2Below) drawLine=false; //
else if(y2Above) { //
Y1 = 0; //
Y2 = (int)outputHeight; //
} //
else Y1 = 0; //
} //
else //
{ //
if(y2Below) Y2 = 0; //
else if(y2Above) Y2 = (int)outputHeight; //
} //

24
if(drawLine)
{
line(X1, outputTop + Y1, X2, outputTop + Y2);
}
}
strokeWeight(1);
}

void drawButtonArea()
{
stroke(0);
fill(100);
rect(0, 0, ioLeft, windowHeight);
}

void Toggle_AM() {
if(AMLabel.valueLabel().getText()=="Manual")
{
AMLabel.setValue("Automatic");
}
else
{
AMLabel.setValue("Manual");
}
}

void Toggle_DR() {
if(DRLabel.valueLabel().getText()=="Direct")
{
DRLabel.setValue("Reverse");
}
else
{
DRLabel.setValue("Direct");
}
}

// Sending Floating point values to the arduino


// is a huge pain. if anyone knows an easier
// way please let know. the way I'm doing it:
// - Take the 6 floats we need to send and
// put them in a 6 member float array.
// - using the java ByteBuffer class, convert
// that array to a 24 member byte array
// - send those bytes to the arduino
void Send_To_Arduino()
{
float[] toSend = new float[6];

toSend[0] = float(SPField.getText());

25
toSend[1] = float(InField.getText());
toSend[2] = float(OutField.getText());
toSend[3] = float(PField.getText());
toSend[4] = float(IField.getText());
toSend[5] = float(DField.getText());
Byte a = (AMLabel.valueLabel().getText()=="Manual")?(byte)0:(byte)1;
Byte d = (DRLabel.valueLabel().getText()=="Direct")?(byte)0:(byte)1;
myPort.write(a);
myPort.write(d);
myPort.write(floatArrayToByteArray(toSend));
justSent=true;
}

byte[] floatArrayToByteArray(float[] input)


{
int len = 4*input.length;
int index=0;
byte[] b = new byte[4];
byte[] out = new byte[len];
ByteBuffer buf = ByteBuffer.wrap(b);
for(int i=0;i<input.length;i++)
{
buf.position(0);
buf.putFloat(input[i]);
for(int j=0;j<4;j++) out[j+i*4]=b[3-j];
}
return out;
}

//take the string the arduino sends us and parse it


void serialEvent(Serial myPort)
{
String read = myPort.readStringUntil(10);
if(outputFileName!="") output.print(str(millis())+ " "+read);
String[] s = split(read, " ");

if (s.length ==9)
{
Setpoint = float(s[1]); // * pull the information
Input = float(s[2]); // we need out of the
Output = float(s[3]); // string and put it
SPLabel.setValue(s[1]); // where it's needed
InLabel.setValue(s[2]); //
OutLabel.setValue(trim(s[3])); //
PLabel.setValue(trim(s[4])); //
ILabel.setValue(trim(s[5])); //
DLabel.setValue(trim(s[6])); //
AMCurrent.setValue(trim(s[7])); //
DRCurrent.setValue(trim(s[8]));
if(justSent) // * if this is the first read

26
{ // since we sent values to
SPField.setText(trim(s[1])); // the arduino, take the
InField.setText(trim(s[2])); // current values and put
OutField.setText(trim(s[3])); // them into the input fields
PField.setText(trim(s[4])); //
IField.setText(trim(s[5])); //
DField.setText(trim(s[6])); //
// mode = trim(s[7]); //
AMLabel.setValue(trim(s[7])); //
//dr = trim(s[8]); //
DRCurrent.setValue(trim(s[8])); //
justSent=false; //
} //

if(!madeContact) madeContact=true;
}
}

Dándonos como resultado la siguiente grafica donde observamos las perturbaciones


de color rojo y azul y nuestro set-point de color verde

Figura 5: grafica de perturbaciones

27
8. Análisis de resultados

Diagrama del robot

Figura 6: diagrama del robot

Figura 7: diagrama del robot

28
Figura 8: diagrama del robot

Para escoger el set-point del sistema procedemos hacer uso de la herramienta de


Arduino denominada Monitor serial la cual nos muestra un valor entre 0 y 5000
dependiendo de la posición de la línea negra, como se muestra a continuación

Figura 9: valor de cero cuando la línea está a la derecha del arreglo de sensores

29
Figura 10: valor de cero cuando la línea está en el medio del arreglo de sensores

Figura 11: valor de cero cuando la línea está a la izquierda del arreglo de sensores

Mediante estos valores creímos conveniente coger un set-point de 2500 para q el


robot siempre trate de ir por la mitad de la línea.

30
El control PID para el control del robot seguidor de línea es difícil mantenerlo estable
debido a que existen diferentes formas de pistas, al momento en que el control se
encuentra sintonizado la variable a controlar sigue inmediatamente al setpoint ante
cualquier cambio, así también no existe un rango de error exagerado entre estas dos
señales, sin embargo bajo condiciones no ideales el sistema oscila pero se intenta
mantener en un rango aceptable, todos los elementos que se usan en el circuito,
tiempos de muestreo o retroalimentación influyen en la respuesta del control PID.

KP KI KD Resultado
0.0001 0.0001 0.0001 Mantiene siempre
una línea recta
5 0.0001 0.0001 No tiene
oscilaciones, tiene
una respuesta lenta
5 5 0.0001 Es muy oscilatorio,
no alcanza e set
point.
10 6 0.0001 Pierde Pista
5 0.0001 1 Aumenta la
velocidad, pierde la
respuesta a
perturbaciones
6 0.001 6 Aumenta velocidad,
aumenta oscilación.
5 0.0001 6 Mantiene la
velocidad, no
presenta perdidas de
pista, buena
respuesta ante las
perturbaciones
Tabla 4 Análisis de resultados variando las constantes.

El control está implementado de forma digital con una placa Arduino OMEGA que
trabaja como un controlador y a la vez como una tarjeta de adquisición de datos, lo
cual permite una amplia aplicación. Con la que podemos ver la respuesta de nuestro
sistema ante las perturbaciones.

9. Conclusiones:
 El control PID es un sistema de control por realimentación.
 Implementar un control PID al seguidor de línea, hace que éste mejore su
funcionamiento “recordando” en donde está la línea en caso de perderla, es
decir, recibir una perturbación.
 La programación en Arduino facilita la implementación del control PID.
 El Software Processing facilita la visualización de la respuesta del sistema.
 El seguidor de línea mejora su desempeño de diferentes mañeras a medida
que cambiamos las constantes del control PID utilizando la interface de
Processing o la de Arduino.

31
10. Recomendaciones:
 Usar un arreglo de sensores que nos permitan obtener una señal digitalizada.
 En la programación, usar variables que hagan referencia al elemento al que
pertenecen.
 Usar micro motores para disminuir el peso y mejorar el torque.
 Verificar el funcionamiento cambiando los valores de las constantes integral,
diferencial y proporcional para escoger el mejor desempeño del seguidor.

11. Bibliografía

 http://platea.pntic.mec.es/~lmarti2/veloraton/sensoroptref.htm
 http://arduino.cc/en/pmwiki.php?n=Main/ArduinoBoardMega
 http://www.dia.uned.es/~fmorilla/MaterialDidactico/El%20controlador%20PI
D.pdf

12. ANEXOS

32
33

Vous aimerez peut-être aussi