====== Relatório TP-1 ====== ===== Introdução ===== Este trabalho consistiu da construção e análise de um robô diferencial utilizando controle proporcional derivativo. O robô foi construído com peças Lego, motores elétricos e sensores //shaft encoders//. O objetivo prático do robô é marcar sua trajetória em uma superfície e realizar três quadrados (de lado 30 cm) consecutivos e três círculos (de raio 30 cm) consecutivos. ===== Montagem do Robô ===== Nosso objetivo foi montar um robô que fosse leve e pequeno, mas que suportaria o peso e tamanho da //HandyBoard//. Optamos também por não utilizar uma roda //Roll-On// pois queríamos que nossa montagem tivesse apenas peças Lego. Como consequência, optamos por utilizar uma roda livre, similar a de carrinhos de supermercado, que se adapta bem à mudanças de direções. ==== Especificações ==== O robô possui 2 motores Pololu (35:1 Metal Gearmotor 15.5Dx30L mm), 2 //shaft encoders// (1 para cada motor) e 1 //HandyBoard// além de 1 marcador (caneta para marcar CDs) e diversas peças Lego. Suas dimensões estão descritas abaixo.
![]() |
Altura: 15,0cm; Comprimento: 18,0cm; Largura: 13,0cm; Eixo traseiro: 9,0cm; Distância entre eixos: 14,0cm; Diâmetro rodas traseiras: 4,5cm; Diâmetro roda dianteira: 2,5cm. |
void fwd(int power, int pgain, int dgain){
int c = 0;
int last_power = power;
motor(1, power);
while(!stop_button()){
sleep(0.1);
power += (encoder0_velocity - encoder1_velocity) * pgain - (power - last_power) * dgain;
if( c < 100 ){
dencoder[c] = (encoder0_velocity - encoder1_velocity);
dpower[c] = power;
velocity[c] = encoder0_velocity;
c++;
}
if( power < 0 )
power = 0;
motor(1, power);
}
}
Escolhemos então 3 valores para os parâmetros //dgain// e //pgain//: 1, 5 e 10. As nove combinações possíveis foram então testadas e resumidas na tabela abaixo.
Os valores da tabela indicam a iteração em que //count// e //power// estabilizaram para 100 e 0, respectivamente. O melhor resultado (destacado em vermelho na tabela) e o segundo melhor resultado (destacado em alaranjado) foram utilizados para gerar gráficos de iterações por //power// e iterações por //counts//.
{{ :cursos:introrobotica:2011-2:grupo07:image06.png?300 }}
Melhor Resultado (//dgain = 5//, //pgain = 1//)
{{ :cursos:introrobotica:2011-2:grupo07:graph5-1.png?500 }}
Segundo melhor Resultado (//dgain = 10//, //pgain = 5//)
{{ :cursos:introrobotica:2011-2:grupo07:graph10-5.png?500 }}
==== Erro de Translação ====
O calculo da distância é feito com base no número de rotações dos motores. Montamos os //shaft encoders// em polias com 6 furos, o que resulta em 12 pulsos em uma rotação completa dessa roda. Como há uma redução de 1/24 na transmissão do robô, então para uma rotação da roda traseira, são necessários 288 pulsos.
Sendo assim sabemos que o robô precisa de 288 pulsos para andar 14,14cm (circunferência da roda traseira). Para outras distâncias, calculamos a proporção desse valor.
Medimos o erro de translação para diferentes potências dos motores em uma trajetória retilínea de 60cm.
{{ :cursos:introrobotica:2011-2:grupo07:image04.png?500 }}
Como podemos perceber pela análise do gráfico, o erro foi mínimo (de aproximadamente -2 mm) para todas as potências. Podemos inferir também que o erro não foi dependente da potência escolhida, como podemos perceber pelos resultados aleatórios do gráfico.
==== Erro de Rotação ====
A rotação é feita girando as rodas traseiras em sentidos opostos. O cálculo do ângulo de rotação é feito com base nas medidas dos //shaft encoders//. Sabemos que são precisos 144 pulsos para robô girar 90º.
Medimos o erro de rotação para diferentes potências do motor para uma rotação de 90º.
{{ :cursos:introrobotica:2011-2:grupo07:image00.png?500 }}
Conforme podemos perceber o erro de rotação cresce linearmente com o aumento de potência. Devido ao desvio provocado pela roda dianteira, na tarefa do quadrado, fazemos uma rotação ligeiramente menor que 90° para corrigir a trajetória.
===== Apresentação =====
As trajetórias (quadrada e circular) foram testadas diversas vezes e apresentadas no dia 5 de setembro ao monitor e professor do curso.
===== Conclusão =====
Neste trabalho desenvolvemos e implementamos um robô diferencial com controle proporcional derivativo. Descrevemos algumas formas de montar o robô e alguns problemas associados a estas montagens. Uma vez o robô pronto, testamos vários parâmetros de controle até que o robô conseguisse realizar trajetórias quadradas e circulares previstas na especificação do problema, com certa precisão.
===== Referências =====
Wikipedia contributors, 'PID controller', Wikipedia, The Free Encyclopedia,
#include "fencdr0.icb"
#include "fencdr1.icb"
#define FD 1
#define BK -1
#define POTENCIA0 40
#define POTENCIA1 40
#define POTENCIA90 50
#define PULSOSVOLTA 298.0 //288
#define DIREITO 0
#define ESQUERDO 1
/* Calcula o número de pulsos necessários medidos pelos shaft encoders para
realizar uma trajetória retilínea em uma certa distancia
Parâmetro:
distancia: distância em centimetros
*/
int CalcPulsos(float distancia);
/*
Faz um movimento retilínio dependente da direção e distância
Parâmetros:
direcao - direção do movimento do robô. 1 = para frente; -1 = para trás;
distancia - distância a ser percorrida em centimetros;
*/
void Anda(int direcao, int pulsos);
/* Monitora e controla as velocidades de cada motor em paralelo com a função Anda
Parâmetro:
direcao: direção do movimento do robô. 1 = para frente; -1 = para trás;
*/
void controle(int direcao);
/* Faz uma curva de 90° com o robô
Parâmetro:
lado: direção do movimento do robô. 0 = direito; 1 = esquerdo;
*/
void Curva90(int lado);
/*
Faz um quadrado completo
Parâmetros:
distancia - tamanho do lado do quadrado em centimetros;
lado - direção do movimento do robô. 0 = direito; 1 = esquerdo;
*/
void Quadrado(float distancia, int lado);
/* Faz um circulo completo de 30cm de raio */
void Circulo();
void main()
{
int modo = 1;
printf(" press stop to change [circle]\n");
while(1){
if(start_button()){
sleep(0.2);
while(start_button());
printf("Running!\n");
if(modo == 1){
Circulo();
Circulo();
Circulo();
printf(" press stop to change [circle]\n");
}
else{
Quadrado(32.0, ESQUERDO);
Quadrado(32.0, ESQUERDO);
Quadrado(32.0, ESQUERDO);
printf(" press stop to change [square]\n");
}
}
if(stop_button()){
sleep(0.2);
while(stop_button());
modo = (modo + 1) % 2;
if(modo == 1){
printf(" press stop to change [circle]\n");
}
else{
printf(" press stop to change [square]\n");
}
}
}
}
int CalcPulsos(float distancia)
{
//14.1371 é o tamanho da circunferência das rodas traseiras
return (int)(distancia/14.1371 * PULSOSVOLTA);
}
void Anda(int direcao, int pulsos)
{
int break0=1, break1=1;
int pid;
sleep(0.5);
encoder0_counts = 0;
encoder1_counts = 0;
motor(0, direcao*POTENCIA0);
motor(1, direcao*POTENCIA1);
pid = start_process(controle(direcao));
while(break0 || break1){
if(encoder0_counts >= pulsos){
off(0);
break0=0;
}
if(encoder1_counts >= pulsos){
kill_process(pid);
off(1);
break1=0;
}
}
}
void controle(int direcao)
{
int pwr = 0;
while(1){
if(encoder0_velocity > encoder1_velocity){
pwr += 1;
}
else if(encoder0_velocity < encoder1_velocity){
pwr -= 1;
}
if(POTENCIA1 + pwr > 100)
motor(0, direcao*(POTENCIA0 - pwr));
else
motor(1, direcao*(POTENCIA1 + pwr));
sleep(0.1);
}
}
void Curva90(int lado)
{
int break0=1, break1=1;
sleep(0.5);
encoder0_counts = 0;
encoder1_counts = 0;
if(lado == ESQUERDO){
motor(0, BK*POTENCIA90);
motor(1, FD*POTENCIA90);
}
else{
motor(0, FD*POTENCIA90);
motor(1, BK*POTENCIA90);
}
while(break0 || break1){
if(encoder0_counts >= (int)PULSOSVOLTA/2-24){
off(0);
break0=0;
}
if(encoder1_counts >= (int)PULSOSVOLTA/2-24){
off(1);
break1=0;
}
}
}
void Quadrado(float distancia, int lado)
{
Anda(FD, CalcPulsos(distancia));
Curva90(lado);
Anda(FD, CalcPulsos(distancia));
Curva90(lado);
Anda(FD, CalcPulsos(distancia));
Curva90(lado);
Anda(FD, CalcPulsos(distancia));
Curva90(lado);
}
void Circulo()
{
int pid;
int pwr0 = 25;
int pwr1 = 40;
sleep(0.5);
encoder0_counts = 0;
encoder1_counts = 0;
//Liga Motores
motor(0, pwr0);
motor(1, pwr1);
while(1){
//Monitora e controla a velocidade de cada motor
if(encoder0_velocity > 10) pwr0--;
else if(encoder0_velocity < 10) pwr0++;
if(encoder1_velocity > 13) pwr1--;
else if(encoder1_velocity < 13) pwr1++;
motor(0, pwr0);
motor(1, pwr1);
//Desliga motores ao terminar o circulo
if(encoder0_counts >= CalcPulsos(178.0) || encoder1_counts >= CalcPulsos(240.0)){
off(0);
off(1);
break;
}
sleep(0.1);
}
}