Braço Robótico + Arduino UNO + Controle XBOX 360 V1.0 - Tutoriais #001
O desenvolvimento do Braço Robótico iniciou-se na quinta fase do curso de Sistemas Eletrônicos, onde na disciplina de Microcontroladores, ministrada pelo Professor Leandro Schwarz, foi apresentado como projeto final. Porém ainda nesse momento o projeto encontrava problemas de driver de corrente para os motores e também de controle, uma vez que estávamos utilizando um aplicativo para smartphone também desenvolvido exclusivamente para o controle do braço.
Na sexta fase, recebi um convite do professor Fernando Pacheco para solucionar esses problemas e apresentá-lo durante a semana de tecnologia do IFSC. Assim iniciou uma nova etapa do projeto, o aperfeiçoamento do que já existia e também uma nova concepção do controle.
Durante a fase de pesquisa encontrei alguns artigos onde se utilizava o controle do XBOX 360 junto à microcontroladores, e foi a partir dessas referencias e também do controle ser muito mais ergonômico e de fácil manipulação do operador, que iniciei a nova fase de desenvolvimento de controle do braco robótico.
Vídeo de Funcionamento
Estrutura Mecânica
Todo o projeto da estrutura mecânica foi retirada da plataforma de compartilhamento opensource Thingverse, foram alterados apenas os ajustes de tamanhos dos cortes para o encaixe dos servos motores.
Link do projeto mecânico: http://www.thingiverse.com/thing:2433
O material escolhido para a estrutura foi o MDF e o corte das peças foi realizado no Lazer para garantir a precisão e acabamento.
Corte a laser da estrutura mecânica |
As peças foram pintadas com spray nas cores pretas e amarelas. A pintura preta por ser uma tinta fosca aderiu melhor ao material.
Pintura da estrutura mecânica |
Na montagem foi necessário alguns parafusos e extensores, mitas vezes esses tiveram que ser adaptados ao que era encontrado no mercado mais próximo.
Lista de parafusos:
40 - M2x15 com porca
20 - M2x12 com porca
6 - M2x20 com porca
5 extensores extavado 50xM4
4 extensores extavado 47xM2
4 extensores extavado 17xM2
Montagem para testes e experimentação |
DICA: A tinta amarela utilizada foi à óleo e deixou o movimento da garra um pouco comprometido. Para amenizar e a garra continuar se movimentando teve-se que ajustar o aperto dos parafusos, porém se perdeu em força que a garra deveria ter.
A mesma garra foi impressa em uma impressora 3D e também foi verificado o resultado de um outro projeto que o material utilizado foi o acrilico e devido à ambos materiais serem bem lisos a garra funciona de uma forma muito mais satisfatória.
Servo Motores
Os servo motores são usados quando se deseja movimentar algo de forma precisa e controlada. No projeto foi necessário a utilização de sete servos motores para desempenhar os movimentos do Braço Robótico para se ter o grau de liberdade dos movimentos que se precisava, cinco servos MG995 da TowerPro e dois micro servos MG90S, também da TowerPro.
Servo motores TowerPro - MG995 e MG90s |
Arduino UNO
O arduino UNO possui como componente principal o ATMEGA 328p, 8bits da família AVR. Possui 32kB de memória Flash, 2kB de memória RAM, e 1kB de EEPROM. Trabalha com um cristal de 16MHz.
Os pinos de 0 a 13, podem ser utilizados como entrada ou saída digitais. Especificamente os pinos 3,5,6,9,10 e 11 podem ser utilizados com saídas PWM de 8 bits.
A comunicação serial é feita nos pinos 0 e 1.
Arduino UNO |
USB Host Shield
No arduino, o USB Host Shield atua como um “servidor” USB, que gerencia a conexão, fornecendo alimentação e permitindo a comunicação entre o arduino e os periféricos USB conectados a porta.
USB Host Shield |
Servo Motor Shield 16 Canais
Neste projeto precisamos de 7 saídas PWM, quantidade maior que o arduino UNO disponibiliza. Assim de maneira simples podemos expandir esse número de saídas com a Servo Motor Shield, usando apenas dois pinos I2C.
A Servo Motor Shield possui resolução de 12 bits e necessita de alimentação externa. Neste projeto utilizei uma fonte de 3,5A.
Servo Motor Shield - I2C to pwm |
Controle XBOX 360 e Receiver Wirelles
Os controles do XBOX 360 utilizam wireless para se comunicar com o console, visando a utilização com computadores a microsoft desenvolveu um receptor wireless para o controle, com USB 2.0.
Durante a fase de pesquisa quando encontrei o receptor em algumas referências de projetos, percebi a perfeita funcionabilidade para controlar o braço robótico, sem falar na ergonomia do controle.
Controle XBOX 360 + Receiver Microsoft |
Programação
Para o funcionamento do código é necessário incluir as bibliotecas:
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
#include <XBOXRECV.h>
#ifdef dobogusinclude
#include <spi4teensy3.h>
#include <SPI.h>
#endif
USB Usb;
XBOXRECV Xbox(&Usb);
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
#define SERVO1MIN 150 //SERVO BASE
#define SERVO1MAX 500 //SERVO BASE
#define SERVO2MIN 150 //SERVO 1 COTOVELO
#define SERVO2MAX 400 //SERVO 1 COTOVELO
#define SERVO3MIN 150
#define SERVO3MAX 400
#define SERVO4MIN 200
#define SERVO4MAX 500
#define SERVO5MIN 150
#define SERVO5MAX 500
#define SERVO6MIN 150
#define SERVO6MAX 500
#define SERVO7MIN 350
#define SERVO7MAX 510
// our servo # counter
uint8_t servo1 = 0; //SERVO BASE
uint8_t servo2 = 1; //SERVO 1 COTOVELO
uint8_t servo3 = 2; //SERVO 2 COTOVELO
uint8_t servo4 = 3;
uint8_t servo5 = 4;
uint8_t servo6 = 5;
uint8_t servo7 = 6;
double angle1 = SERVO1MAX/2;
double angle2 = 350; //angulo do SERVO 1 e servo 2 COTOVELO
double angle4 = SERVO4MAX/2;
double angle5 = SERVO5MAX/2;
double angle6 = SERVO6MAX/2;
double angle7 = SERVO7MAX/2;
void setup() {
pwm.begin();
pwm.setPWMFreq(60); // Analog servos run at ~60 Hz updates
Serial.begin(115200);
#if !defined(__MIPSEL__)
while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
#endif
if (Usb.Init() == -1) {
Serial.print(F("\r\nOSC did not start"));
while (1); //halt
}
Serial.print(F("\r\nXbox Wireless Receiver Library Started"));
}
void loop() {
Usb.Task();
if (Xbox.XboxReceiverConnected) {
for (uint8_t i = 0; i < 4; i++) {
if (Xbox.Xbox360Connected[i]) {
if (Xbox.getButtonPress(L2, i) || Xbox.getButtonPress(R2, i)) {
Serial.print("L2: ");
Serial.print(Xbox.getButtonPress(L2, i));
Serial.print("\tR2: ");
Serial.println(Xbox.getButtonPress(R2, i));
Xbox.setRumbleOn(Xbox.getButtonPress(L2, i), Xbox.getButtonPress(R2, i), i);
}
if (Xbox.getAnalogHat(LeftHatX, i) > 7500 || Xbox.getAnalogHat(LeftHatX, i) < -7500 || Xbox.getAnalogHat(LeftHatY, i) > 7500 || Xbox.getAnalogHat(LeftHatY, i) < -7500 || Xbox.getAnalogHat(RightHatX, i) > 7500 || Xbox.getAnalogHat(RightHatX, i) < -7500 || Xbox.getAnalogHat(RightHatY, i) > 7500 || Xbox.getAnalogHat(RightHatY, i) < -7500) {
if (Xbox.getAnalogHat(LeftHatX, i) > 7500) {
if (Xbox.getAnalogHat(LeftHatX, i) == 32767){
incrementa_base();
}
Serial.print(F("LeftHatX: "));
Serial.print(Xbox.getAnalogHat(LeftHatX, i));
Serial.print("\t");
}
if (Xbox.getAnalogHat(LeftHatX, i) < -7500) {
if (Xbox.getAnalogHat(LeftHatX, i) == -32768){
decrementa_base();
}
Serial.print(F("LeftHatX: "));
Serial.print(Xbox.getAnalogHat(LeftHatX, i));
Serial.print("\t");
}
if (Xbox.getAnalogHat(LeftHatY, i) > 7500) {
if (Xbox.getAnalogHat(LeftHatY, i) == 32767){
incrementa_cotovelo();
}
Serial.print(F("LeftHatY: "));
Serial.print(Xbox.getAnalogHat(LeftHatY, i));
Serial.print("\t");
}
if (Xbox.getAnalogHat(LeftHatY, i) < -7500) {
if (Xbox.getAnalogHat(LeftHatY, i) == -32768){
decrementa_cotovelo();
}
Serial.print(F("LeftHatY: "));
Serial.print(Xbox.getAnalogHat(LeftHatY, i));
Serial.print("\t");
}
if (Xbox.getAnalogHat(RightHatX, i) > 7500) {
if (Xbox.getAnalogHat(RightHatX, i) == 32767){
incrementa_servo4();
Serial.print(F("RightHatX: "));
Serial.print(Xbox.getAnalogHat(RightHatX, i));
Serial.print("\t");
}
}
if (Xbox.getAnalogHat(RightHatX, i) < -7500) {
if (Xbox.getAnalogHat(RightHatX, i) == -32768){
decrementa_servo4();
Serial.print(F("RightHatX: "));
Serial.print(Xbox.getAnalogHat(RightHatX, i));
Serial.print("\t");
}
}
if (Xbox.getAnalogHat(RightHatY, i) > 7500) {
if (Xbox.getAnalogHat(RightHatY, i) == 32767){
decrementa_servo5();
Serial.print(F("RightHatY: "));
Serial.print(Xbox.getAnalogHat(RightHatY, i));
Serial.print("\t");
}
}
if (Xbox.getAnalogHat(RightHatY, i) < -7500) {
if (Xbox.getAnalogHat(RightHatY, i) == -32768){
incrementa_servo5();
Serial.print(F("RightHatY: "));
Serial.print(Xbox.getAnalogHat(RightHatY, i));
Serial.print("\t");
}
}
Serial.println();
}
if (Xbox.getButtonClick(UP, i)) {
Xbox.setLedOn(LED1, i);
Serial.println(F("Up"));
}
if (Xbox.getButtonClick(DOWN, i)) {
Xbox.setLedOn(LED4, i);
Serial.println(F("Down"));
}
if (Xbox.getButtonClick(LEFT, i)) {
Xbox.setLedOn(LED3, i);
Serial.println(F("Left"));
}
if (Xbox.getButtonClick(RIGHT, i)) {
Xbox.setLedOn(LED2, i);
Serial.println(F("Right"));
}
if (Xbox.getButtonClick(START, i)) {
Xbox.setLedMode(ALTERNATING, i);
Serial.println(F("Start"));
}
if (Xbox.getButtonClick(BACK, i)) {
Xbox.setLedBlink(ALL, i);
Serial.println(F("Back"));
}
if (Xbox.getButtonClick(L3, i))
Serial.println(F("L3"));
if (Xbox.getButtonClick(R3, i))
Serial.println(F("R3"));
if (Xbox.getButtonClick(L1, i))
Serial.println(F("L1"));
if (Xbox.getButtonClick(R1, i))
Serial.println(F("R1"));
if (Xbox.getButtonClick(XBOX, i)) {
Xbox.setLedMode(ROTATING, i);
Serial.print(F("Xbox (Battery: "));
Serial.print(Xbox.getBatteryLevel(i)); // The battery level in the range 0-3
Serial.println(F(")"));
}
if (Xbox.getButtonClick(SYNC, i)) {
Serial.println(F("Sync"));
Xbox.disconnect(i);
}
if (Xbox.getButtonClick(A, i)){
rotaciona1_mao();
Serial.println(F("A"));
}
if (Xbox.getButtonClick(B, i)){
abre_garra();
Serial.println(F("B"));
}
if (Xbox.getButtonClick(X, i)){
fecha_garra();
Serial.println(F("X"));
}
if (Xbox.getButtonClick(Y, i)){
rotaciona2_mao();
Serial.println(F("Y"));
}
}
}
}
}
void setServoPulse(uint8_t n, double pulse) {
double pulselength;
pulselength = 1000000; // 1,000,000 us per second
pulselength /= 60; // 60 Hz
Serial.print(pulselength); Serial.println(" us per period");
pulselength /= 4096; // 12 bits of resolution
Serial.print(pulselength); Serial.println(" us per bit");
pulse *= 1000;
pulse /= pulselength;
Serial.println(pulse);
pwm.setPWM(n, 0, pulse);
}
void incrementa_base(){
Serial.println(F("Incrementou na Base"));
if(angle1==SERVO1MAX){
}
else
angle1++;
Serial.print("angle: ");
Serial.print(angle1);
Serial.print("\t");
pwm.setPWM(servo1, 0, angle1);
}
void decrementa_base(){
Serial.println(F("DEcrementou na Base"));
if(angle1==SERVO1MIN){
}
else
angle1--;
Serial.print("angle: ");
Serial.print(angle1);
Serial.print("\t");
pwm.setPWM(servo1, 0, angle1);
}
void incrementa_cotovelo(){
Serial.println(F("Incrementou no Cotovelo"));
if(angle2==SERVO2MAX){
}
else
angle2++;
Serial.print("angle: ");
Serial.print(angle2);
Serial.print("\t");
pwm.setPWM(servo2, 0, angle2);
pwm.setPWM(servo3, 0, angle2);
}
void decrementa_cotovelo(){
Serial.println(F("DEcrementou no Cotovelo"));
if(angle2==SERVO2MIN){
}
else
angle2--;
Serial.print("angle: ");
Serial.print(angle2);
Serial.print("\t");
pwm.setPWM(servo2, 0, angle2);
pwm.setPWM(servo3, 0, angle2);
}
void incrementa_servo4(){
Serial.println(F("Incrementou no Servo 4"));
if(angle4==SERVO4MAX){
}
else
angle4++;
Serial.print("angle: ");
Serial.print(angle4);
Serial.print("\t");
pwm.setPWM(servo4, 0, angle4);
}
void decrementa_servo4(){
Serial.println(F("DEcrementou no Servo 4"));
if(angle4==SERVO4MIN){
}
else
angle4--;
Serial.print("angle: ");
Serial.print(angle4);
Serial.print("\t");
pwm.setPWM(servo4, 0, angle4);
}
void incrementa_servo5(){
Serial.println(F("Incrementou no Servo 5"));
if(angle5==SERVO5MAX){
}
else
angle5++;
Serial.print("angle: ");
Serial.print(angle5);
Serial.print("\t");
pwm.setPWM(servo5, 0, angle5);
}
void decrementa_servo5(){
Serial.println(F("DEcrementou no Servo 5"));
if(angle5==SERVO5MIN){
}
else
angle5--;
Serial.print("angle: ");
Serial.print(angle5);
Serial.print("\t");
pwm.setPWM(servo5, 0, angle5);
}
void fecha_garra(){
Serial.print("Fechou garra ");
angle7=SERVO7MAX;
pwm.setPWM(servo7, 0, angle7);
}
void abre_garra(){
Serial.print("abriu garra ");
angle7=SERVO7MIN;
pwm.setPWM(servo7, 0, angle7);
}
void rotaciona1_mao(){
Serial.print("girou mao 1 ");
angle6=SERVO6MIN;
pwm.setPWM(servo6, 0, angle6);
}
void rotaciona2_mao(){
Serial.print("girou mao 1 ");
angle6=SERVO6MAX;
pwm.setPWM(servo6, 0, angle6);
}
Ola, vc conseguiria me fazer o corte da estrutura e me vender?
ResponderExcluirOla, infelizmente não possuo a máquina de corte a laser, mas essa estrutura pode ser encontrada no mercado libre para compra.
ExcluirUm segunda opção seria imprimir na impressora 3D a estrutura do robô.
Olá, você poderia explicar melhor a programação do controle?
ResponderExcluirOlá, posso sim, Qual seria a dúvida na programação? Você possui o hardware para testar, fica mais fácil para compreender o código do controle.
ExcluirBom dia, se eu copiar e colar essa programação no meu arduino ele vai funcionar ?
ResponderExcluirBom dia Leandro, se você estiver utilizando o mesmo hardware que eu a programação teria que funcionar sim.
Excluireu tenho que criar subpastas com as outras programaçoes pra funcionar ?
ResponderExcluirTente entender o que está sendo feito através das bibliotecas padrões de cada dispositivo e então implemente as funções que você irá precisar em sua programação.
ResponderExcluirassim eu estou fazendo um robo de combate, eu vi seu video e vou utilizar os mesmo materiais , tanto que ja comprei todos, é meu primeiro projeto e estou com as duvidas de programação
ResponderExcluirOla Matheus, gostei desse seu projeto e montei um igual para apresentação do meu TCC na faculdade, mas estou tendo alguns problemas com o meu robo, estou utilizando as mesmas peças, placas,etc que o seu, mas os meus servos MG995 não estão respondendo os comandos, ao ligar eles no shild pwm ele começam a girar apenas para um sentindo, e todos os 5 servos MG995 estão assim, ja os MG90S estão funcionando normal, sera que você consegue me ajudar com esse problema? obrigado
ResponderExcluirOla, vou tentar te ajudar como eu sempre faço quando encontro problemas de programação e hardware.
Excluir1. Isole o problema. Faça um programa "teste_de_motor" para acionar um único motor que automaticamente gire o motor para um lado e depois de um tempo gire o motor para o outro lado;
2. Teste individualmente cada motor nesse programa "teste_de_motor";
3. Se todos os motores funcionarem individualmente faça um programa "teste_shield_pwm", onde esse programa será a cópia do programa "teste_de_motor", porém vc vai alternar a porta PWM. Primeiro aciona a PWM1, para esquerda-direita, depois aciona a PWM2, para a esquerda-direita, e assim por diante, até testar todas as portas necessárias para o projeto.
Relate os problemas que for tendo aqui e vou tentando te ajudar. Qualquer coisa pode me mandar um e-mail também. matheus.amartim@gmail.com
Ola, primeiro quero lhe agradecer pela prestatividade, lhe enviei um e-mail relatando todo o caso, mas para não ficar algo vago ou sem explicação aqui no blog vou dar uma breve explicação pra quem um dia vier a ler os comentários. Bom meu problema foi que os servos motores que comprei vieram digitais e não analógicos, são da mesma marca e modelo dos que aqui postado, mas o meu veio com uma versão nova, uma versão digital, então montei um outro tipo de braço robótico que tenho e ele utiliza servos mecânicos, todo o resto do o resto do projeto funcionou perfeitamente, só precisei ajustar a posição dos servos pelo código fonte e funcionou, sendo assim só tenho a agradecer
Excluir