Tabela de conteúdos

Trabalho Prático 3

Introdução

Neste trabalho, o robô construído foi adaptado para executar três novas tarefas: (1) o lançamento de uma bola leve em direção a uma fonte de luz polarizada; (2) o deslocamento através de um campo conhecido utilizando-se um algoritmo wavefront; (3) simultâneas a essas tarefas, a pausa total das atividades após 60 segundos de execução. Para a execução da tarefa três, em especial, foram utilizadas técnicas de programação paralela, e um novo motor foi acrescentado ao robô para a tarefa um. Os trabalhos foram feitos com sucesso, cumprindo os objetivos propostos, e as maiores dificuldades encontradas foram acerca da correta execução do algoritmo wavefront.

Foto frontal do robô finalizado

Mudanças estruturais

Diversas mudanças estruturais foram feitas no robô, para garantir o comprimento das tarefas. A catapulta, descrita adiante, foi colocada, e a parte responsável por detectar a presença e a cor de blocos foi removida. Diversas alterações estruturais gerais foram feitas no robô, para que ele comportasse a nova estrutura da catapulta.

As rodas do robô também foram substituídas por rodas maiores, que dão mais velocidade.

Tarefas realizadas

Catapulta

Uma vez que o mecanismo de localização da fonte de luz polarizada já foi criado e implementado com sucesso no trabalho passado, bastou criar a catapulta e o algoritmo para fazer o lançamento da bola na direção correta. Para tal, acrescentou-se ao robô um novo motor que, através do uso de três reduções, movimentava uma alavanca que, por sua vez, movimentava o braço da catapulta, preso à estrutura do robô por elásticos em sua parte superior, e através de conexões de lego em sua parte inferior. Tal alavanca, ao continuar seu giro, acabava por soltar o braço da catapulta tensionado, de modo a retornar a sua posição inicial rapidamente, movimento que dava à bola o impulso necessário para chegar à lâmpada polarizada.

Mecanismo da catapulta

Como resultado, obteve-se uma catapulta que conseguia atirar a bola com força e precisão razoáveis, fazendo com que esta alcançasse uma boa distância.

Foto lateral: catapulta desarmadaFoto lateral: catapulta armada

Deslocamento com wavefront

O robô, para esta tarefa, precisava se deslocar por um campo conhecido de maneira autônoma, determinando o caminho mais curto do ponto de partida até o alvo, para alcançá-lo posteriormente. Para a execução da tarefa, utilizou-se o algoritmo wavefront, “comprimento de onda”, que divide o ambiente em um grid, ou seja, mapeia-o em uma matriz, onde cada posição da matriz representa um quadrado do campo de deslocamento. Neste caso, o quadrado representado tinha 30cm de lado. Após isso, atribui-se a cada posição onde existam obstáculos, e nas bordas do campo, valores numéricos extremamente altos. Em seguida, a partir do ponto de chegada, atribuem-se valores crescentes às casas adjacentes a ela, progressivamente, até alcançarem o robô ou algum obstáculo: se existem 3 casas entre o robô e o alvo, elas serão numeradas 1, 2 e 3, respectivamente, e a saída será 0. Se entre a saída e o robô, acima da casa de número 2, existe um caminho livre, cercado de obstáculos, ele também será numerado 3.

O robô, então, move-se sempre para a casa adjacente a ele que possua o menor número possível, até alcançar o alvo. Além disso, o algoritmo trata uma situação importante, em que o robô começa cercado por todos os lados, menos pelas costas, por obstáculos. Nesse caso, o robô irá detectar a situação, girar 180 graus e se movimentar normalmente. Parte do algoritmo aqui explicado compõe o algoritmo de controle deliberativo, disponível na especificação do trabalho. O algoritmo que permite ao robô se deslocar em linha reta e girar 90 graus utilizado foi o mesmo dos trabalhos anteriores.

Desligamento após 60 segundos

Para que esta tarefa fosse executada independente do que o robô estivesse fazendo, surgiu a necessidade de executá-la em paralelo em relação ao restante do algoritmo do robô. Isso foi feito utilizando-se a biblioteca pthreads. Como resultado, a trava dos sessenta segundos foi implementada com sucesso, funcionando independentemente do estado atual do robô.

Software do projeto

#include sencdr4.icb
 
#include sencdr1.icb
 
 
 
#define ESQUERDA 1
 
#define FRENTE 0
 
#define DIREITA -1
 
#define ATRAS -2
 
 
 
#define LESTE 0
 
#define NORTE 90
 
#define OESTE 180
 
#define SUL 270
 
 
 
#define X_DIM 7
 
#define Y_DIM 6
 
 
 
#define MAX 255
 
 
 
#define SEN_START 14
 
 
 
#define MOTOR_L 0
 
#define MOTOR_R 2
 
#define MOTOR_C 3
 
#define SENS_DIF 6
 
#define SENS_CDR 2
 
 
 
#define TURN_ANGLE 145
 
 
 
/* Mapa contendo os obstaculos do ambiente */
 
int map[X_DIM][Y_DIM];
 
 
 
/* Pose = posicao (x y) + orientacao */
 
int pose_atual[3];    // posicao (x,y) atual + orientacao (N / S / L / O)
 
int pose_inicial[3];  // posicao inicial
 
int pose_desejada[3]; // posicao final desejada (ignorar a orientacao)
 
 
 
/* Variaveis extras*/
 
int fifo[100][2];
 
int indice = 0;
 
int indice2 = 0;
 
 
 
void catapulta(){
 
    motor(MOTOR_C, 100);
 
    msleep(10000L);
 
    off(MOTOR_C);
 
}
 
 
 
 
 
//void motor(int m, int p) {}
 
//void ao() {}
 
//void msleep(long time) {}
 
 
 
void segue_luz_vertical(){
 
    int light, polarizada, min=1000, value,valuec, count;
 
 
 
    motor(MOTOR_R, -30);
 
    motor(MOTOR_L, 30);
 
    count = 0;
 
    while(count < 300){
 
        value = analog(SENS_DIF);
 
        valuec = analog(SENS_CDR);
 
        if(min > value+valuec){
 
            min = value+valuec;
 
        }
 
        msleep(50L);
 
        count++;
 
        printf("%d %d\n",min,count);
 
    }
 
 
 
    printf("saiiiii\n");
 
    count = 0;
 
    min = min + 10;
 
 
 
    while(1){
 
        value=analog(SENS_DIF);
 
        valuec=analog(SENS_CDR);
 
        printf("%d %d\n",min,value);
 
        if( value+valuec< min){
 
            ao();
 
            msleep(50L);
 
            catapulta();
 
            break;
 
        }
 
 
 
        msleep(50L);
 
    }
 
 
 
    return;
 
}
 
 
 
void segue_luz_horizontal(){
 
    int light, polarizada, min=0, value, valuec, count;
 
 
 
    motor(MOTOR_R, -30);
 
    motor(MOTOR_L, 30);
 
    count = 0;
 
    while(count < 300){
 
        value = analog(SENS_DIF);
 
        valuec = analog(SENS_CDR);
 
        if(min < 1000 + value - valuec){
 
            min = 1000 + value - valuec;
 
        }
 
        msleep(50L);
 
        count++;
 
        printf("%d %d\n",min,count);
 
    }
 
 
 
    printf("saiiiii\n");
 
    count = 0;
 
    min = min - 10;
 
    while(1){
 
        value=analog(SENS_DIF);
 
        valuec = analog(SENS_CDR);
 
        printf("%d %d\n",min,value);
 
        if(1000 + value - valuec > min){
 
            ao();
 
            msleep(50L);
 
            catapulta();
 
            break; 
 
        }
 
        msleep(50L);
 
    }
 
    return;    
 
}
 
 
 
/* Esta funcao deve fazer o seu robo andar 1 unidade de distancia para  
 
 * frente e tambem deve atualizar a variavel pose_atual, de acordo
 
 * com a orientacao atual
 
 */
 
void goAhead() {    
 
    if (pose_atual[2] == LESTE) 
 
      pose_atual[1]++;    
 
    if (pose_atual[2] == NORTE)
 
      pose_atual[0]--;
 
    if (pose_atual[2] == OESTE)
 
      pose_atual[1]--;
 
    if (pose_atual[2] == SUL)
 
      pose_atual[0]++;
 
 
 
    andaPraFrente(320);
 
    //msleep(300L);
 
    off(MOTOR_L);
 
    off(MOTOR_R);
 
}
 
 
 
void giraRobo(int direction){
 
    int i, j;
 
 
 
    int rodou0;
 
    int rodou1;
 
    int app=0;
 
 
 
    int furos;
 
    int speed;
 
    int VMOTOR_0;
 
    int VMOTOR_1;
 
    int inv0;
 
    int inv1;
 
 
 
    inv0 = 0;
 
    inv1 = 0;
 
    rodou0 = 0;
 
    rodou1 = 0;
 
 
 
    furos = 3;
 
    speed = 50;
 
    encoder4_counts = 0;
 
    encoder1_counts = 0;
 
 
 
    VMOTOR_0 = speed;
 
    VMOTOR_1 = speed;
 
 
 
    i = 0;
 
 
 
 
 
    if(direction){
 
        inv1=1;
 
    } else {
 
        inv0=1;
 
    }
 
 
 
    if (inv0) VMOTOR_0 = -speed; 
 
    if (inv1) VMOTOR_1 = -speed;
 
 
 
    while(rodou0 < TURN_ANGLE ){
 
        i++;
 
 
 
        motor(MOTOR_L,VMOTOR_0);
 
        motor(MOTOR_R,VMOTOR_1);
 
 
 
        rodou0 += encoder4_counts;
 
        rodou1 += encoder1_counts;
 
 
 
        if (encoder4_counts < furos){
 
            if(inv0) VMOTOR_0 -= 15;
 
            else VMOTOR_0 += 15;
 
        } else if (encoder4_counts > furos){
 
            if(inv0)VMOTOR_0 += 15;
 
            else VMOTOR_0 -= 15;
 
        }
 
 
 
        if (encoder1_counts < furos){
 
            if(inv1) VMOTOR_1 -= 15;
 
            else VMOTOR_1 += 15;
 
        } else if (encoder1_counts > furos){
 
            if(inv1)VMOTOR_1 += 15;
 
            else VMOTOR_1 -= 15;
 
        }
 
 
 
        encoder4_counts = 0;
 
        encoder1_counts = 0;
 
 
 
        msleep(25L);
 
    }
 
 
 
    off(MOTOR_L);
 
    off(MOTOR_R);
 
    msleep(200L);
 
}
 
 
 
void andaPraFrente(int counts){
 
 
 
 
 
 
 
    int i, j;
 
 
 
    int rodou0;
 
    int rodou1;
 
    int app=0;
 
 
 
    int furos;
 
    int speed;
 
    int VMOTOR_0;
 
    int VMOTOR_1;
 
    int inv0;
 
    int inv1;
 
 
 
    inv0 = 0;
 
    inv1 = 0;
 
    rodou0 = 0;
 
    rodou1 = 0;
 
 
 
    furos = 6;
 
    speed = 80;
 
    encoder4_counts = 0;
 
    encoder1_counts = 0;
 
 
 
    VMOTOR_0 = speed;
 
    VMOTOR_1 = speed;
 
 
 
    if (inv0) VMOTOR_0 = -speed; 
 
    if (inv1) VMOTOR_1 = -speed;
 
 
 
    i = 0;
 
    while(rodou0<counts){
 
        i++;
 
 
 
        rodou0 += encoder4_counts;
 
        rodou1 += encoder1_counts;
 
 
 
        if (encoder4_counts < furos){
 
            if(inv0) VMOTOR_0 -= 4;
 
            else VMOTOR_0 += 4;
 
        } else if (encoder4_counts > furos){
 
            if(inv0)VMOTOR_0 += 4;
 
            else VMOTOR_0 -= 4;
 
        }
 
 
 
        if (encoder1_counts < furos){
 
            if(inv1) VMOTOR_1 -= 4;
 
            else VMOTOR_1 += 4;
 
        } else if (encoder1_counts > furos){
 
            if(inv1)VMOTOR_1 += 4;
 
            else VMOTOR_1 -= 4;
 
        }
 
 
 
        encoder4_counts = 0;
 
        encoder1_counts = 0;
 
 
 
        motor(MOTOR_L,VMOTOR_0);
 
        motor(MOTOR_R,VMOTOR_1);
 
 
 
        msleep(50L);
 
    }
 
    off(MOTOR_L);
 
    off(MOTOR_R);
 
 
 
    msleep(200L);
 
}
 
 
 
/* Esta funcao deve fazer o seu robo girar 90 graus para a esquerda e
 
 * tambem deve atualizar a variavel pose_atual 
 
 */
 
void turnLeft() {
 
    pose_atual[2] += 90;
 
    if (pose_atual[2] == 360)
 
      pose_atual[2]= 0;
 
 
 
    giraRobo(0);
 
    //msleep(300L);
 
    off(MOTOR_L);
 
    off(MOTOR_R);
 
}
 
 
 
/* Esta funcao deve fazer o seu robo girar 90 graus para a direita e
 
 * tambem deve atualizar a varivael pose_atual 
 
 */
 
void turnRight() {
 
    pose_atual[2] -= 90;
 
    if (pose_atual[2] == -90)
 
      pose_atual[2]= 270;
 
 
 
    giraRobo(1);
 
    //msleep(300L);
 
    off(MOTOR_L);
 
    off(MOTOR_R);
 
}
 
 
 
void push(int x, int y, int valor) {
 
    if (x >= 0 && x < X_DIM && y >=0 && y < Y_DIM && map[x][y] == 0) {
 
        //printf("fifo[%d] <= [%d %d]\n", indice, x, y);
 
        map[x][y] = valor;
 
        fifo[indice][0] = x;
 
        fifo[indice][1] = y;
 
        indice++;
 
    }
 
}
 
 
 
void pop(int *x, int *y) {
 
    *x = fifo[indice2][0];
 
    *y = fifo[indice2][1];
 
    indice2++;
 
    //printf("fifo[%d] => [%d %d]\n", indice2, *x, *y);
 
}
 
 
 
 
 
/* Esta funcao deve calcular o mapa de distancias usando o algoritmo
 
 * wavefront.
 
 * A variavel map deve conter as distancias entre o ponto final e inicial
 
 * ao final da sua execucao
 
 */
 
void calculaWaveFront() {
 
    int x,y;
 
    int i,j;
 
    int stop = 0;
 
 
 
    for (i = 0; i < X_DIM; i++) {
 
        map[i][0] = MAX;
 
        map[i][Y_DIM - 1] = MAX;
 
    }
 
    for (j = 0; j < Y_DIM; j++) {
 
        map[0][j] = MAX;
 
        map[X_DIM- 1][j] = MAX;
 
    }
 
 
 
    push(pose_desejada[0], pose_desejada[1], 1);
 
    x = pose_desejada[0], y = pose_desejada[1];
 
    while (!stop) {
 
        pop(&x , &y);
 
        push(x, y - 1, map[x][y] + 1);
 
        push(x, y + 1, map[x][y] + 1);
 
        push(x + 1, y, map[x][y] + 1);
 
        push(x - 1, y, map[x][y] + 1);   
 
        if (x == pose_inicial[0] && y == pose_inicial[1])
 
          stop = 1;
 
    }
 
 
 
    for (j = 0; j < Y_DIM; j++)
 
      for (i = 0; i < X_DIM; i++)
 
        if (map[i][j] == 0) map[i][j] = MAX;   
 
}
 
 
 
/* Esta funcao deve retornar a direcao que o robo deve seguir para sair
 
 * do quadrante atual para chegar ate o proximo quadrante, obedecendo
 
 * o mapa criado pelo algoritmo wavefront
 
 */
 
int calculaDirecao() {
 
    int x, y, xa, ya, xl, yl, xr, yr,yb,xb;
 
 
 
    x = pose_atual[0];
 
    y = pose_atual[1];
 
    xa = x;    ya = y;
 
    xr = x;    yr = y;
 
    xl = x;    yl = y;
 
    xb = x;    yb = y;
 
 
 
    if (pose_atual[2] == SUL) {
 
        xa = x + 1;
 
        xb = x - 1;
 
        yl = y + 1;
 
        yr = y - 1;
 
    }
 
    if (pose_atual[2] == LESTE) {
 
        ya = y + 1;
 
        yb = y - 1;
 
        xl = x - 1;
 
        xr = x + 1;
 
    }
 
    if (pose_atual[2] == NORTE) {
 
        xa = x - 1;
 
        xb = x + 1;
 
        yl = y - 1;
 
        yr = y + 1;
 
    }
 
    if (pose_atual[2] == OESTE) {
 
        ya = y - 1;
 
        yb = y + 1;
 
        xl = x + 1;
 
        xr = x - 1;
 
    }
 
 
 
 
 
    if ((xa > 0 && xa < (X_DIM - 1) && ya > 0 && ya < (Y_DIM - 1)) && map[xa][ya] < map[x][y])
 
      return FRENTE;
 
    if ((xl > 0 && xl < (X_DIM - 1) && yl > 0 && yl < (Y_DIM - 1)) && map[xl][yl] < map[x][y])
 
      return ESQUERDA;
 
    if ((xr > 0 && xr < (X_DIM - 1) && yr > 0 && yr < (Y_DIM - 1)) && map[xr][yr] < map[x][y])
 
      return DIREITA;
 
    if ((xb > 0 && xb < (X_DIM - 1) && yb > 0 && yb < (Y_DIM - 1)) && map[xb][yb] < map[x][y])
 
      return ATRAS;
 
}
 
 
 
/* Imprime o mapa na tela. util para realizar a depuracao (so funciona no PC).
 
 */
 
void printMap() {
 
    int i, j;
 
    for (j = 0; j < Y_DIM; j++) {
 
        for (i = 0; i < X_DIM; i++) {
 
            printf("%3d ", map[i][j]);       
 
        }
 
        printf("\n");
 
    }
 
}
 
 
 
/* Le um valor do knob variando de 0 ate intervalo*/
 
int getKnob(char texto[], int intervalo) {
 
    int valor;
 
    while (!start_button()) {
 
        valor = ((intervalo - 2) * knob()) / 255;
 
        valor += 1;
 
        printf("%s %d\n", texto, valor);
 
        msleep(100L);
 
    }
 
    while (start_button());
 
    return valor;
 
}
 
 
 
int getKnobString(char texto[][], int intervalo) {
 
    int valor;
 
    while (!start_button()) {
 
        valor = ((intervalo - 1) * knob()) / 255;        
 
        printf("%s\n", texto[valor]);
 
        msleep(100L);
 
    }
 
    while (start_button());
 
    return valor;
 
}
 
 
 
void escolhePosicao() {
 
    int posicao;
 
    pose_inicial[0] = getKnob("X inicial: ", (X_DIM - 1));
 
    pose_inicial[1] = getKnob("Y inicial: ", (Y_DIM - 1));
 
    pose_inicial[2] = getKnob("Direcao inicial LNOS: ", 5);
 
    posicao = pose_inicial[2];
 
 
 
    switch(posicao){
 
        case 1:
 
          pose_inicial[2] = LESTE;
 
        break;
 
        case 2:
 
          pose_inicial[2] = NORTE;
 
        break;
 
        case 3: 
 
          pose_inicial[2] = OESTE;
 
        break;
 
        case 4:
 
          pose_inicial[2] = SUL;
 
        break;
 
    }
 
 
 
 
 
    pose_atual[0] = pose_inicial[0];
 
    pose_atual[1] = pose_inicial[1];
 
    pose_atual[2] = pose_inicial[2];
 
    pose_desejada[0] = getKnob("X desejado: ",(X_DIM-1));
 
    pose_desejada[1] = getKnob("Y desejado: ",(Y_DIM-1));
 
}
 
 
 
void escolheMapa() {
 
    int x, y;
 
    int n, i;
 
    n = getKnob("Numero de obstaculos: ", 20);
 
    for (i = 0; i < n; i++) {
 
        x = getKnob("X obstaculo: ",(X_DIM-1));
 
        y = getKnob("Y obstaculo: ",(Y_DIM-1));
 
        map[x][y] = MAX;
 
    }
 
}
 
 
 
int waveFront(){
 
    int direcao, i, j;
 
    int stop = 0;
 
 
 
    calculaWaveFront();
 
    //printMap();
 
 
 
 
 
    while (!stop) {
 
        direcao = calculaDirecao();
 
 
 
        if (direcao == ESQUERDA){
 
            printf("esquerda: %d", direcao);
 
            //sleep(1.0);
 
            turnLeft();
 
        }
 
 
 
        else if (direcao == DIREITA){ 
 
            printf("direita: %d", direcao);
 
            //sleep(1.0);
 
            turnRight();
 
        }
 
 
 
        else if (direcao == FRENTE){ 
 
            printf("frente: %d", direcao);
 
            //sleep(1.0);
 
            goAhead();
 
        }
 
 
 
        else if (direcao == ATRAS){
 
            printf("atras: %d", direcao);
 
            //sleep(1.0);
 
            turnRight();
 
            turnRight();
 
        }
 
 
 
        printf("Estou em [%d %d]\n", pose_atual[0], pose_atual[1]);
 
 
 
        /* Verifica se ja chegou no alvo*/
 
        if (pose_atual[0] == pose_desejada[0] && pose_atual[1] == pose_desejada[1]){
 
            stop = 1;
 
            off(MOTOR_L);
 
            off(MOTOR_R);
 
        }
 
    }    
 
    return 0;
 
}
 
 
 
int main() {
 
    int i, j, th, selecao;
 
    char  string[2][40] = {"Executar        Catapulta", "Executar        Wavefront"};
 
    char  string2[2][30] = {"Polarizacao     Horizontal", "Polarizacao     Vertical"};
 
    //  motor(MOTOR_C, 100);
 
    selecao = getKnobString(string, 2);
 
 
 
    if (selecao == 0){
 
        selecao = getKnobString(string2, 2);
 
 
 
        printf("Esperando Luz\n");
 
        //while(!digital(SEN_START));
 
 
 
        if (selecao == 0) {
 
            th = start_process(segue_luz_horizontal());
 
            sleep(120.0);
 
            kill_process(th);
 
        } else {
 
            th = start_process(segue_luz_vertical());
 
            sleep(120.0);
 
            kill_process(th);
 
        }
 
 
 
        return 0;
 
 
 
    } else {
 
 
 
        for(i=0; i<X_DIM; i++){
 
            for(j=0; j<Y_DIM; j++){
 
                map[i][j] = 0;
 
            }
 
        }
 
 
 
        printf("Wavefront\n");
 
        msleep(1000L);
 
 
 
        escolhePosicao();
 
        escolheMapa();
 
 
 
        printf("Aperte start\n");
 
        while(!start_button());
 
 
 
        printf("Comecando em [%d %d], alvo: [%d %d]\n", 
 
               pose_inicial[0], pose_inicial[1],
 
               pose_desejada[0], pose_desejada[1]);
 
 
 
        while(!digital(SEN_START));
 
        printf("aq\n");
 
        th = start_process(waveFront());
 
 
 
 
 
        sleep(60.0);
 
        kill_process(th);
 
        printf("Acabou o tempo\n");
 
        off(MOTOR_L);
 
        off(MOTOR_R);
 
        return 0;
 
    }
 
}

Vídeos do robô em funcionamento

Catapulta em funcionamento

Deslocamento com base no algoritmo wavefront

Conclusão

Neste trabalho o robô previamente construído foi adaptado para a execução de três tarefas: o lançamento de uma bola em direção a uma luz polarizada previamente localizada, o deslocamento autônomo em um ambiente conhecido com obstáculos, e a criação de um mecanismo de trava que para o robô após sessenta segundos de execução. As maiores dificuldades encontradas estavam relacionadas com o algoritmo de deslocamento autônomo wavefront, utilizado neste trabalho. A trava dos sessenta segundos foi implementada com o uso de programação paralela. Todas as tarefas propostas foram executadas com sucesso.

Referências

MARTIN, Fred G. Interactive C User's Guide MIT Media Laboratory. Manual Edition 0.9. 1997.
MARTIN, Fred G. Robotic Explorations: An Introduction to Engineering Through Design. Prentice Hall; ISBN: 0-130-89568-7.
MARTIN, Fred G. The Handy Board Technical Reference. at http://handyboard.com/oldhb/techdocs/hbmanual.pdf
MARTIN, Fred G. The Art of LEGO Design. Journal for Robot Builders, volume 1, number 2. March 15, 1995