Como encontrar a posição final de um braço robótico com 3 servos
Um braço robótico é uma estrutura mecânica articulada que pode ser controlada por meio de servos. Cada servo é responsável por controlar o movimento em um eixo específico, como o eixo X, Y ou Z. Para encontrar a posição final do braço robótico nos eixos X e Y, é necessário utilizar uma fórmula que leve em consideração os ângulos de rotação de cada servo.
Fórmula para encontrar a posição final
A fórmula para encontrar a posição final de um braço robótico com 3 servos nos eixos X e Y é baseada na trigonometria. Vamos supor que os ângulos de rotação dos servos sejam representados por θ1, θ2 e θ3, respectivamente.
Portanto, a posição final do braço robótico nos eixos X e Y é aproximadamente (32,07 cm, 31,75 cm).
Considerações finais
A fórmula apresentada permite encontrar a posição final de um braço robótico nos eixos X e Y com base nos ângulos de rotação dos servos e nos comprimentos dos segmentos do braço. É importante lembrar que essa fórmula assume que o braço robótico opera em um plano bidimensional e que não há interferências ou restrições adicionais.
Além disso, é fundamental ter em mente que a precisão da posição final depende da precisão dos ângulos de rotação dos servos e dos comprimentos dos segmentos do braço. Qualquer imprecisão ou erro na medição ou no controle dos servos pode afetar a posição final do braço robótico.
Em resumo, a fórmula apresentada é uma ferramenta útil para calcular a posição final de um braço robótico nos eixos X e Y, mas é necessário ter cuidado ao utilizar e interpretar os resultados.
Sendo os 3 pacotes iniciais, apenas apoio, e o libfreenect-dev realmente a lib necessária.
Hello World da Biblioteca
Neste primeiro projeto, pouca coisa faremos, apenas iremos compilar identificando o device.
#include <stdio.h>
#include <stdlib.h>
#include <libfreenect.h>
freenect_context *f_ctx;
freenect_device *f_dev;
int user_device_number = 0; // Normalmente 0 se você tiver apenas um Kinect
void depth_cb(freenect_device *dev, void *v_depth, uint32_t timestamp) {
// Callback para dados de profundidade - não usado neste exemplo
}
void rgb_cb(freenect_device *dev, void *rgb, uint32_t timestamp) {
// Salva uma imagem RGB capturada pelo Kinect
FILE *image = fopen("output_image.ppm", "wb");
if (image == NULL) {
printf("Erro ao abrir o arquivo para escrita\n");
return;
}
fprintf(image, "P6\n# Kinect RGB test\n640 480\n255\n");
fwrite(rgb, 640*480*3, 1, image);
fclose(image);
printf("Imagem salva como output_image.ppm\n");
// Depois de salvar a imagem, podemos sair do loop principal
freenect_stop_video(dev);
freenect_close_device(dev);
freenect_shutdown(f_ctx);
exit(0);
}
int main() {
if (freenect_init(&f_ctx, NULL) < 0) {
printf("freenect_init() falhou\n");
return 1;
}
if (freenect_open_device(f_ctx, &f_dev, user_device_number) < 0) {
printf("Não foi possível abrir o dispositivo\n");
freenect_shutdown(f_ctx);
return 1;
}
freenect_set_depth_callback(f_dev, depth_cb);
freenect_set_video_callback(f_dev, rgb_cb);
freenect_set_video_mode(f_dev, freenect_find_video_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_VIDEO_RGB));
freenect_start_video(f_dev);
while (freenect_process_events(f_ctx) >= 0) {
// Processa eventos do Kinect até que a captura de imagem seja concluída
}
return 0;
}
Neste exemplo o programa pega o kinect e tira uma foto, salvando na maquina local.
Vamos entender o código.
A freenect_init inicia a api.
A próxima função freenect_open_device, abre o device conforme o número que estiver descrito. Isso permite abrir mais de um kinect na mesma maquina.
As funções freenect_set_depth_callback e freenect_set_video_callback criam funções de callback, para controle, se voce não sabe o que é leia este artigo:
A função freenect_set_video_mode indica os parâmetros de resolução.
Por ultimo a função freenect_start_video, dá inicio ao kinect que aciona o callback quando pronto.
Perceba aqui que o pulo do gato neste makefile, é a inclusão da pasta /usr/lib/x86_64-linux-gnu que é onde a lib se encontra. Bem como a /usr/include/libfreenect que é onde o header se encontra.
Compilando o projeto
Para compilar esse projeto, é necessário apenas rodar o script na pasta src do ubuntu:
make all
Compilando o projeto
Rodando o programa
Agora iremos rodar o programa, isso é a parte mais simples.
Ao rodar, ele captura uma foto, e salva, conforme apresentado.
O objetivo deste artigo é apresentar uma visão geral sobre este protocolo.
Histórico
Criado em 1987 a ISO8583 descreve o intercâmbio entre requisições bancárias.
Funcionamento
Sem entrar muito no detalhe do funcionamento.
Basicamente a imagem abaixo, ilustra o funcionamento do protocolo em linhas gerais.
De forma geral, o PDV ou POS solicita a operadora uma dada solicitação.
Por exemplo:
inicio de pagamento com cartão de crédito.
No corpo da solicitação, irão alguns dados identificadores da solicitação.
A operadora, pode conforme o tipo da solicitação, perguntar algumas coisas, como senha, tipo de cartão entre outras coisas.
Ou mesmo solicitar a visualização de outras.
A esta solicitação (chamamos de ação), que deve ser respondida pelo PDV ou POS.
A operadora pode realizar quantas perguntas forem necessárias para atendimento de suas necessidades.
Ao fim a operadora envia a resposta final, dando por encerrado a operação.
Em linhas bem gerais é assim que funciona o protocolo ISO 8583.
Estaremos entrando em detalhes, mais adiante.
Porem para entendimento geral, é um protocolo mestre/escravo, onde após a solicitação inicial, a operadora passa a solicitar informações, que devem ser respondidas.
Identificadores de tipo de mensagens
O identificador de tipo de mensagem é um campo numérico de 4 dígitos que especifica o tipo da mensagem que deve ser processado.
V: número de versão da ISO 8583 (0 indica que é ISO 8583:1987; 1 indica ISO 8583:1992).
n: Classe da Mensagem conforme a tabela abaixo: | 0 Reserved for ISO use | 1 Authorization | 2 Financial | | 3 File action | 4 Reversal/Chargeback | 5 Reconciliation | | 6 Administration | 7 Fee collection | 8 Network management | | 9 Reserved for ISO use | | |
X: Função da Mensagem conforme tabela: | 0 Request | 1 Request response | 2 Advice | | 3 Advice response | 4 Notification | 5 – 9 Reserved for ISO use |
Y: Origem da Transação | 0 Acquirer | 1 Acquirer repeat | 2 Card issuer | | 3 Card issuer repeat | 4 Other | 5 Other repeat | | 6 –9 Reserved for ISO use | | |
Simulando PDV e Simulando Autorizadora
Eu vasculhando a internet achei o site da neapay, conforme referência.
Baixei um download simulator_ISSUER_ISO8583_host_auth_PRO.
A aplicação trabalha com JAVA, e é uma boa pedida quem deseja testar e aprender mais sobre o protocolo.
Temos também a variável executada, que será utilizada na marcação dos itens executados.
Sessão Crítica
O controle da sessão crítica é feita nas funções IniciaSessaoCritica e TerminaSessaoCritica, conforme fonte abaixo:
int IniciaSessaoCritica()
{
int rc;
int cont = 0;
while ( (rc = pthread_mutex_lock(&mutex))!=0)
{
usleep(100); /*Aguarda um pouco*/
cont++;
printf("Sessao critica não conseguida\n");
if (cont>3) break;
}
return rc;
}
int TerminaSessaoCritica()
{
int rc;
int cont = 0;
while ( (rc = pthread_mutex_unlock(&mutex))!=0)
{
usleep(100); /*Aguarda um pouco*/
cont++;
printf("Sessao critica não liberada\n");
if (cont>3) break;
}
return rc;
}
Ambas as funções controlam a sessão criada mutex, declarada, conforme fonte abaixo:
Para cada sessão critica, deve-se criar uma variável de controle. Como usaremos apenas a fila, como variável de troca de dados, utilizaremos uma única sessão critica.
Criando a thread da recepção
Agora iremos dar inicio a criação da thread da recepção, na qual incluiremos o inicio da thread.
Neste segmento do código, podemos acompanhar que criamos a thread com pthread_create, passando o ponteiro da função que será criada a thread threadRecepcao. Desta forma a função threadRecepcao, não mais seguirá na thread pai. O ultimo parâmetro é utilizado para passagens de argumentos, no nosso caso, não será usado.
Por fim a função da thread
/*thread function definition*/
void* threadRecepcao(void* args)
{
for(int cont = 0;cont<=MAXITEMS;cont++)
{
printf("Criando pacote nro %d\n",cont);
if(IniciaSessaoCritica()==0)
{
int Valor = (rand()% 100);
fila[cont] = Valor; /*Grava Valor na fila*/
printf("Registrou na fila[%d] = %d\n",cont,Valor);
TerminaSessaoCritica();
usleep(1000);
} else {
printf("Falha na Recepcao nro:\n",cont);
}
}
printf("Recepcao terminou atendimento\n");
}
Podemos observar que a threadRecepção requisita a sessão crítica para movimentar a fila, apenas depois alocando valor para ela. Também podemos perceber que após seu uso, o mesmo é descartado.
Criando o Controlador
O controlador, é executado, em seguida, ordenando as informações.
void ordenacao()
{
int i, x;
bool flgordenado = false;
while((!flgordenado)&&(!flgTerminou))
{
//flgordenado = true;
for (i=0; i<=MAXITEMS-1; i++)
{
if ((fila[i]!=0)&&(fila[i+1]!=0))
{
if(fila[i]>fila[i+1])
{
x = fila[i];
fila[i] = fila[i+1];
fila[i+1] = x;
flgordenado = false;
//printf("Ordenando os pacote nro %d\n",i);
}
} else {
//printf("Posicao vazia %d\n",i);
flgordenado = false;
}
}
}
}
/*thread function definition*/
void* threadControlador(void* args)
{
int oldValue, newValue;
int flgOrdenado = false;
printf("Iniciou o controlador\n");
while(!flgTerminou) /*Faz enquanto nao terminar e nao ordenao*/
{
if(IniciaSessaoCritica()==0)
{
ordenacao();
TerminaSessaoCritica();
//usleep(200);
}
}
printf("Terminou ordenação de todos os itens\n");
}
Neste bloco temos duas funções:
A thread em si é a threadControlador, que chama a função de ordenação ordenacao.
A ordenação só para quando for atendida duas condições:
Tiver terminado as demais threads, indicado pelo flag flgTerminou e quando tiver sido totalmente ordenado flgordenado.
Executor
A thread do Executor, pode ser vista conforme apresentado a seguir:
/*thread function definition*/
void* threadExecutor(void* args)
{
int oldValue, newValue;
bool flgExecutado = false;
printf("Iniciou o controlador\n");
int posicao = 0;
//while(!flgExecutado)
while((!flgExecutado)&&(!flgTerminou))
{
//printf("Pesquisando Posicao %d\n",posicao);
//printf("Entrou na sessao critica\n");
if (fila[posicao]==0) /*Fila nao foi preenchida*/
{
printf("posicao vazia %d\n",posicao);
posicao = posicao;
} else
{
executada[posicao] = fila[posicao];
printf("Executou[%d] = %d\n",posicao,fila[posicao]);
if(posicao==MAXITEMS-1)
{
printf("Chegou ao fim\n ");
flgExecutado= true; /*Finaliza executor*/
} else
{
posicao ++;
}
}
//printf("terminou while\n");
}
flgTerminouExecucao = true;
printf("Terminou ordenação de todos os itens\n");
}
Ela irá rodar até que duas condições sejam satisfeitas:
flgExecutado – Controla a execução de todas as tarefas da lista
flgTerminou – Controla o fim de todas as demais threads do sistema
Testando programa
Compilando programa
A compilação em um Raspberry PI ocorreu com sucesso.
Executando o programa
root@raspberrypi:/home/mmm/projetos/Threads-para-Raiz/c# ./threads
Bem vindo ao programa das threads
Este programa faz parte do artigo:
http://maurinsoft.com.br/index.php/2022/07/02/threads-para-raiz-parte-1/
Inicializando vetor
Iniciando Recepcao
Iniciando Controlador
Registrou na fila[0] = 80
Registrou na fila[1] = 11
Registrou na fila[2] = 76
Registrou na fila[3] = 71
Registrou na fila[4] = 27
Registrou na fila[5] = 23
Registrou na fila[6] = 33
Registrou na fila[7] = 68
Registrou na fila[8] = 32
Registrou na fila[9] = 92
Recepcao terminou atendimento
Iniciou o controlador
Iniciando Executor
Status:Iniciou o controlador
Executou[0] = 11
Executou[1] = 23
Executou[2] = 27
Executou[3] = 32
Executou[4] = 33
Executou[5] = 68
Executou[6] = 71
Executou[7] = 76
Executou[8] = 80
Executou[9] = 92
Chegou ao fim
Terminou ordenação de todos os itens
Status:Terminou ordenação de todos os itens
Fila ordenada:11 23 27 32 33 68 71 76 80 92
Fila executada:11 23 27 32 33 68 71 76 80 92
Como a execução ficou um pouco longa, resolvi jogar como código.
Conclusão
Podemos perceber que a execução de threads pode ser facilitada com uso de flags externos, que facilitam a comunicação entre os processos. Pudemos ver tambem, que a programação em threads é de fato, um estado de arte da programação C, pois envolve alem de um algoritmo bem desenvolvido, a elaboração de estratégias pensando em que uma thread não sabe exatamente quando a outra irá cumprir suas atividades.
Desta forma cada thread tem que ser pensada em aguardar e esperar as informações das threads que colaboraram com esta.
Desta forma o controle interno das informações é bem mais complicado.
Pudemos ver por ultimo o uso dos sinalizadores, que visão identificar o uso de uma sessão crítica do sistema. Que nada mais é que um recurso compartilhado.
Espero que tenham gostado do artigo.
Em caso de dúvidas ou sugestões, fico a disposição como sempre.
Neste primeiro artigo, iremos estabelecer um projeto, que iremos desenvolver nos demais artigos.
Objetivo
A intenção deste projeto é apresentar solução em diversos linguagens de controle de threads com compartilhamento de informações entre elas.
Proposta de projeto
Imagine que temos 3 funcionários em um departamento público.
Recepção de Protocolo – Ele recebe os protocolos de serviço dos clientes.
Controlador de Serviço – Ele recebe os protocolos da recepção, colocando em ordem numérica em uma fila de execução. Por controlar e ordenar, sua atividade demora tempo mediano.
Executor de Serviço – Ele pega o serviço, por executar o serviço é o mais demorado de todos.
Tempo de execução
Ao analisarmos o departamento, fizemos a seguinte constatação:
A recepção de protocolo, é o departamento mais rápido.
O Controlador de Serviço tem um tempo médio que é o dobro da recepção.
Executor de Serviço – Demora o dobro do tempo do controlador de serviço.
Agora que temos o projeto, podemos no próximo artigo começar sua implementação.