Servomotores como os SG90 não possuem proteção contra sobrecorrente ou sobrecorrente embutidas, podendo queimar se submetidos, por longos períodos, a altos esforços. Para evitar isso, foi desenvolvido um algoritmo que permite o ajuste do sinal enviado ao servo para manter a corrente abaixo de um valor estipulado. Aqui, esse é apresentado como a biblioteca ServoProtegido.h. Para as demonstrações e testes, serão utilizados:
Estes estarão conectados conforme o esquemático abaixo.
Diagrama do sistema montado para demonstrações.
ATENÇÃO: ESTE CIRCUITO NÃO DEVE SER ENERGIZADO SE O CÓDIGO CARREGADO NA PLACA UNO REALIZAR LEITURAS ANALÓGICAS SEM QUE A FUNÇÃO analogReference(EXTERNAL)
TENHA SIDO EXECUTADA.
Quando montado em uma protoboard, todos os jumpers de alimentação, incluindo os dos servos, devem estar trançados em pares (cada positivo trançado com seu negativo). Estes devem ser mantidos distantes dos jumpers de sinais. Os resistores de potência devem estar distantes um do outro e do ampop. A fonte utilizada para alimentar os servos deve ser distinta da utilizada para alimentar a Uno.
Obs. I: Os resistores devem ser escolhidos para atender, simultaneamente:
O código abaixo não utiliza a biblioteca, somente monitora os sensores.
#include <Servo.h> Servo servo1, servo2, servo3, servo4; //declara os objetos referentes aos servos que serão controlados Servo servos[] = {servo1, servo2, servo3, servo4}; //inicializa um vetor com estes objetos int filtro(int sensor) { static const uint8_t tamFiltro = 128; //define o tamanho do vetor de amostras para cada entrada static const uint8_t entradas[] = {A0, A1, A2, A3, A4, A5}; //inicializa o vetor de entradas analogicas static unsigned medicoes[sizeof(entradas)][tamFiltro]; //declara a matriz com os vetores de amostra unsigned long soma = 0; //inicia soma com 0 static uint8_t i; //declara o iterador for(i = 0; i < tamFiltro - 1; medicoes[sensor][i] = medicoes[sensor][i + 1], i ++) //de 0 a tamFiltro - 2, deslocando cada valor para o endereço anterior a cada iteração, iterar de 1 em 1 soma += medicoes[sensor][i]; //a cada iteração, acumular o valor do respectivo endereço soma += (medicoes[sensor][tamFiltro - 1] = analogRead(entradas[sensor])); //substituir o ultimo valor pela leitura do sensor e acumular esta return soma / tamFiltro; //retornar o valor acumulado divido pelo tamanho do vetor de amostras } void setup() { analogReference(EXTERNAL); //seleciona a tensão no pino AREF como referência para as leituras Serial.begin(115200); //inicia a interface UART const int saidas[] = {11, 10, 9, 6}; //inicializa o vetor com os pinos referentes às saídas de sinal para os servos for(uint8_t i = 0; i < 4; i ++) //de 0 a 3, incrementando 1 a 1 (servos[i]).attach(saidas[i]); //conectar cada saída ao respectivo servo } void loop() { static uint8_t q; //declara o iterador static unsigned valorFiltrado[6]; //declara o vetor com as leituras filtradas for(q = 0; q < 4; q ++) { //de 0 a 3, incrementando 1 a 1 valorFiltrado[q] = map(filtro(q), 0, 1023, 500, 2500); //armazena, em cada posição do vetor, a leitura filtrada e mapeada para a faixa da respectiva saída (servos[q]).writeMicroseconds(valorFiltrado[q]); //define o sinal enviado para o respectivo servo } static unsigned long tempo; //declara a variavel auxiliar para o controle das mensagens valorFiltrado[4] = filtro(4); //armazena cada leitura valorFiltrado[5] = filtro(5); //em uma posição do vetor if(millis() - tempo > 100) { //a cada 100 milisegundos Serial.print(",usBase/2:"); Serial.println(valorFiltrado[0]/2); //registra o sinal enviado para os servos Serial.print(",usGarra/2:"); Serial.println(valorFiltrado[3]/2); //divido por 2, para melhor visualização Serial.print(",sensorBase:"); Serial.println(valorFiltrado[4]); //registra o valor lido Serial.print(",sensorGarra:"); Serial.println(valorFiltrado[5]); //pelos respectivos sensores tempo = millis(); //atualiza a variavel auxiliar } }
Executando-o, tem-se o resultado visto no vídeo abaixo. Como não há ajustes automáticos em prol da redução da corrente consumida, se o operador mantiver os potenciômetros em posições que forcem os servos, a corrente de cada um é mantida em valores que podem o danificar rapidamente.
As classes e estruturas de ServoProtegido, assim como o uso interface de seus métodos, são explicados abaixo. Seus arquivos podem ser baixados de nosso repositório.
A classe EntradaAnalogica
, como o nome sugere, visa abstrair as entradas analógicas do micro. Seus métodos são:
EntradaAnalogica(const uint8_t pino)
: declara um objeto correspondente à entrada pino
e com tamanho padrão para o filtro;EntradaAnalogica(const uint8_t pino, const uint8_t _tamFiltro)
: declara um objeto correspondente à entrada pino
e com tamanho _tamFiltro
para o filtro;~EntradaAnalogica()
: libera o espaço alocado para o filtro;void setPino(const uint8_t pino)
: altera a entrada pela qual as leituras são feitas;uint8_t getPino()
: informa a entrada pela qual as leituras são feitas;unsigned medeComFiltro()
: realiza uma leitura e retorna a média das últimas _tamFiltro
leituras.A classe ServoProtegido
herda todos os membros da Servo
e acrescenta a camada de proteção. Seus métodos são:
ServoProtegido(const uint8_t pinoSensor)
: declara um objeto que abstrai a proteção contra sobrecorrente de um servo e define que a leitura da corrente deste servo será feita pelo pinoSensor
;~ServoProtegido()
: libera o espaço alocado para a EntradaAnalogica
referente à leitura do sensor deste servo;void setLimiteSaida(const bool extremo, const unsigned novoLimite)
: se extremo
for ServoProtegido::inferior
, novoLimite
passa a ser o menor valor que o sinal gerado automaticamente pode assumir. Se for ServoProtegido::superior
, novoLimite
passa a ser o maior valor que o sinal gerado automaticamente pode assumir;unsigned getLimiteSaida(const bool extremo)
: informa o valor limite definido para o extremo extremo
da saída;void setSensorMax(const unsigned novoMax)
: define o valor a partir do qual as leituras do sensor levam ao controle automático do sinal de saída;unsigned getSensorMax()
: informa o valor a partir do qual as leituras do sensor levam ao controle automático do sinal de saída;void setMargemEntradas(const uint8_t novaMargem)
: novaMargem
é utilizado como referência em duas computações, principalmente:
sensorMax - novaMargem
;novaMargem
na oposta à que aumenta o sinal do sensor;uint8_t getMargemEntradas()
: informa o valor definido para a margem das entradas;void setPasso(const uint8_t novoPasso)
: define o valor base para a variação do sinal de saída automático a cada computação do controle. Quanto maior, mais rápido o ajuste é feito, mas maior a probabilidade da posição do servo ser alterada mais do que deveria;uint8_t getPasso()
: informa o valor base para a variação do sinal de saída automático a cada computação do controle;void setIntervaloControle(const unsigned novoIntervalo)
: define o período mínimo entre as computações referentes ao controle do servo;unsigned getIntervaloControle()
: informa o período mínimo entre as computações referentes ao controle do servo;struct DetalhesExecucao controlaServo(const unsigned usManual)
: processa, primeiro, o sinal lido pelo sensor da corrente do servo. A depender do valor desta, processa usManual
e envia este ou o valor automático para o servo. Retorna uma struct
que contém as principais informações pertinentes ao controle:
unsigned ultimaLeitura
: a leitura feita pelo sensor de corrente nesta computação;uint8_t usUsado
: qual sinal foi escolhido pelo controle, DetalhesExecucao::manual
ou DetalhesExecucao::automatico
;unsigned valorUs
: o valor do sinal escolhido;Este código utiliza os principais recursos desta biblioteca.
#include <ServoProtegido.h> Servo servoBraco, servoAntebraco; //declara os objetos referentes aos servos sem proteção ServoProtegido servoGarra(A5), servoBase(A4); //declara os objetos referentes aos servos protegidos void setup() { analogReference(EXTERNAL); //seleciona a tensão no pino AREF como referência para as leituras Serial.begin(115200); //inicia a interface UART servoBraco.attach(10); //conecta cada servoAntebraco.attach(9); //servo à servoGarra.attach(6); //respectiva servoBase.attach(11); //saída } void loop() { static EntradaAnalogica potenciometro0(A0), potenciometro1(A1), //declara os objetos referentes potenciometro2(A2), potenciometro3(A3); //às entradas analógicas static struct DetalhesExecucao detalhes[2]; //declara as estruturas referentes aos resultados do controle dos servos servoBraco.writeMicroseconds(494 + potenciometro0.medeComFiltro()); //envia, para o servo, a leitura do servoAntebraco.writeMicroseconds(988 + potenciometro1.medeComFiltro()); //potenciometro somada a um valor de compensação detalhes[0] = servoGarra.controlaServo(988 + potenciometro2.medeComFiltro()); //passa o sinal que seria enviado pela função de controle, que avalia detalhes[1] = servoBase.controlaServo(988 + potenciometro3.medeComFiltro()); //a possibilidade de o enviar ao servo e registra os resultados do processo static unsigned long tempo; //declara a variavel auxiliar para o controle das mensagens if(millis() - tempo > 100) { Serial.print(",usGarra/2:"); Serial.println(detalhes[0].valorUs/2); //escreve o sinal enviado para os servos Serial.print(",usBase/2:"); Serial.println(detalhes[1].valorUs/2); //divido por 2, para melhor visualização Serial.print(",sensorGarra:"); Serial.println(detalhes[0].ultimaLeitura); //escreve o valor lido Serial.print(",sensorBase:"); Serial.println(detalhes[1].ultimaLeitura); //pelos respectivos sensores tempo = millis(); //atualiza a variavel auxiliar } }
Executando-o, tem-se o resultado visto no vídeo abaixo. A leitura escolhida como máxima do sensor ainda leva a algum aquecimento, mas muito menor.
A biblioteca explorada neste post implementa o básico do controle proposto. Há possibilidade de muita melhoria, tanto em performance, quanto em legibilidade, o que é incentivado. Além disso, há outros recursos de segurança que podem ser implementados, como a proteção contra sobreaquecimento com base em um sensor de temperatura.
Créditos à Lorena pela edição dos vídeos. Obrigado, Lorena!
|
A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.
Tenha a Metodologia Eletrogate dentro da sua Escola! Conheça nosso Programa de Robótica nas Escolas!