# Curso ROS
[TOC]
* ROS Noetic será o último ROS1 e terá suporte até 2025
* Ubuntu 20.04
* ROS Melodic só terá suporte até 2023
* Ubuntu 18.04
* Obs: Melodic só tem suporte oficial ao python 2
## ROS Setup
* `source /opt/ros/melodic/setup.bash`
* Para fazer automatico toda vez que abrir o terminal tem que colocar no arquivo `source /opt/ros/melodic/setup.bash` na ultima linha do arquivo ~/.bashrc
* Se tiver mais de uma versão instalada tem que mudar no ~/.bashrc para a versão desejada ou só tacar `source /opt/ros/melodic/setup.bash` para mudar pra versão desejada
* Comando para adicionar ao ~/.bashrc : `echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc`
* Obs: substitua melodic pela versão desejada ( noetic, melodic ou kinect)
## ROS Instalando pacotes
* `sudo apt install ros-version-nomeDoPacote`
## ROS Workspace (Catkin)
* Ambiente de desenvolvimento de códigos ROS
* Passos para criar
* Fazer uma pasta com "nomeDoWorkSpace_ws"
* Ir para a pasta e fazer uma pasta "src" (source folder)
* Dentro de "nomeDoWorkSpace_ws" usar o comando `catkin_make`
* Todo código que vamos escrever vai ficar em src
* Mudar o código pode exigir que vc digite catkin_make denovo
* Para preparar o workspace para funcionamento vc tbm deve ir na pasta devel criada no catkin_make e fazer o comando `source setup.bash`. Se não quisermos ficar fazendo isso toda vez que abrirmos o terminal para trabalhar nessa workspace podemos adicionar naquele arquivo ~/.bashrc
* Para isso abra o ~/.bashrc e escreva `~nomeDoWorkSpace_ws/devel/setup.bash` na ultima linha
* O comando seria: `echo "source ~/nomeDoWorkSpace_ws/devel/setup.bash" >> ~/.bashrc`
## ROS master
* Primeiro passo para o uso do ROS
* Comando: `roscore`
* Nomear e registrar serviços para todos os subprogramas (nós)
## ROS Packages (pkg)
* Blocos reutilizáveis, similar a uma biblioteca
* Unidade indepente
* Para fazer uma
* `catkin_create_pkg nomeDoPacote dependencias`
* as dependencias são tipo as bibliotecas ROS que tu precisa um análogo seria `import numpy` no python ou `#import <iostream>` no C++
* Comum usarmos `roscpp` ou `rospy` para adicionarmos respectivamente compatibilidade com C++ e python
* Também é comum std_msgs para troca de comunicações ROS entre tópicos e nós
* Todo pacote gerado tem esses arquivos por padrão
* 
* Depois de fazer um pkg é necessário dar um `catkin_make` para gerar todos os arquivos e executáveis necessários
## ROS Nodes (Nós)
Blocos de código que executam determinada tarefa dentro de um pacote.
* Ex: Suponha um pacote responsável por lidar com a câmera
* O código capaz de captar os dados da câmera seria um nó
* A parte de processamento de imagem seria outro nó
* Qualquer outra porra que eu queira fazer com a câmera seria outro nó
* Definir quais nós pertencem a quais pacotes é subjetivo e depende muito da aplicação
* Nós se comunicam entre si através de Comunicações ROS (Tópicos + Mensagens)
* um nó pode se comunicar inclusive com um nó de outro pacote
* Se um nó crasha isso não crasha o código todo
* Nós podem ter liguagem diferente
* Da pra rodar apenas um nó de mesmo nome, pra rodar outra instancia do mesmo nó se tem que adicionar um prefixo
* Podemos rodar um nó especifico de um pacote utilizando o comando:
* `rosrun nomeDoPacote nomeDoExecutavelDoNo`
* Perceba que se o código for em python é o nome dado para o arquivo .py agora se for me C++ é o nome dado ao executável através do primeiro argumento da funcao `add_executable()` no arquivo CMakeLists.txt
### Fazendo um node (nó)
#### Se for em python
> Sua vida fica mais fácil
* No seu pakage crie uma pasta scripts
* Lá dentro crie um arquivo do tipo .py
* Comando: `touch nomeDoNo.py`
* Transforme em um executável
* Comando: `chmod +x nomeDoNo.py`
* Escreva algo dentro
* Aqui vc daria import das dependencias do pkg que vai usar nesse nó
* Como estamos em um nó em python vc faria `import rospy` por exemplo
* `rospy.init_node("nomeDoNo")` inicializa o nó
* Se passar um segundo parâmetro `rospy.init_node("nomeDoNo", anonymous=True)` o nome do nó passa ser diferenciado e agora vc pode inicializar 2 nós com nomes supostamente iguais
* `rospy.is_shutdown()` false equanto o código está rodando
* `rospy.loginfo` é um print
* `rospy.spleep(x)` faz o código esperar x segundos
* `rospy.Rate(x)` seguido de `rate.sleep()` esse segundo dentro de um loop, temos:
* `rospy.Rate(x)` estabelecendo uma frequencia x Hz
* `rate.sleep()` forçando o loop seguir a frequência pré-estabelecida
* Para executar não se esqueça que precisa ter iniciado o ROS master em outro terminal
* Comando: `roscore`
* Exemplo básico printando hello a cada 1/10 segundos:

#### Se for em C++
> Tenha fé, vai funcionar
* No src do seu pakage crie um arquivo do tipo .cpp
* Comando: `touch nomeDoNo.cpp`
* Escreva algo dentro
* Aqui vc daria import das dependencias do pkg que vai usar nesse nó
* Como estamos em um nó em C++ vc faria #include <ros/ros.h> por exemplo
* `ros::init(argc, argv, "nomeDoNo")` cria o nó
* Se passar um segundo parâmetro `ros::init(argc, argv, "nomeDoNo",ros::init_options::AnonymousName)` o nome do nó passa ser diferenciado e agora vc pode inicializar 2 nós com nomes supostamente iguais
* `ros::NodeHandle` auxilia na inicialização do nó
* `ros::ok()` true enquanto o nó estiver executando
* `ROS_INFO()` como se fosse um printf
* `ros::Duration(x).sleep()`:
* `.Duration()` lida com estabelecer tempos e durações
* `.sleep()` faz esperar o valor estabelecido pelo duration
* `ros::Rate rate(x)` seguido de `rate.sleep()` esse segundo dentro de um loop, temos:
* `ros::Rate rate(x)` estabelecendo uma frequencia x Hz
* `rate.sleep()` forçando o loop seguir a frequência pré-estabelecida
* Falta compilar esse carinha, para isso vá em CMakeLists.txt do seu pakage, lá vai ter uma parte comentada explicando como fazer mas vou simplificar aqui
* Para criar o executável
* Comando: `add_executable(nomeDoExecutavel src/nomeDoNo.cpp)`
* Para adicionar as bibliotecas
* Comando: `target_link_libraries(nomeDoExecutavel ${catkin_LIBRARIES})`
* Depois disso é necessário voltar para o inicio do seu workspace ( diretório raiz Ex: `cd nomeDoWorkspace_ws`) e executar o `catkin_make` novamente, resumindo:
* `cd nomeDoWorkspace_ws`
* `catkin_make`
* Se tudo der certo vc pode rodar o seu executável que vai estar na pasta devel/lib/NomeDoSeuPacote do seu workspace (Ex: `cd nomeDoWorkspace_ws/devel/lib/nomeDoPacote`) e rodá-lo, resumindo:
* `cd nomeDoWorkspace_ws/devel/lib/nomeDoPacote`
* `./nomeDoExecutavel`
* Para executar não se esqueça que precisa ter iniciado o ROS master em outro terminal
* Comando: `roscore`
* Toda vez que vc editar o .cpp vc vai ter que fazer o processo do `catkin_make` novamente pq C++ é compilável e vc vai precisar refazer o seu executável
* Exemplo básico printando hello a cada 1/10 segundos:

### Debugando um node (nó)
Existem algumas funcões úteis para isso:
* Executar um nó específico:
* `rosrun nomeDoPacote nomeDoExecutavelDoNo`
* Ver a lista dos nós em execução:
* `rosnode list`
* Ter informações sobre um nó em execução:
* `rosnode info nomeDoNo`
* Para um nó em execução:
* CTRL+C ou
* `rosnode kill nomeDoPacote`
* Verifica se um nó esta executando e printa o tempo de resposta
* `rosnode ping nomeDoNo`
* Interface gráfica para visualização de nós e tópicos ativos
* `rosrun rqt_graph rqt_graph`
Outras funções úteis de debug podem ser encontradas [aqui](http://wiki.ros.org/rosnode)
## ROS Messages (Mensagens ROS)
Mensagens ROS são maneiras de estruturar dados de tal for que qualquer nó consiga traduzir completamente a informação contida nesse dado. O pacote `std_msgs` contém alguns tipos de mensagem comuns.
* Ex: Quero trasmitir Pose (x,y e ângulo)
* Dentro do pacote`std_msgs` temos uma subdivisão chamada `geometry_msgs`. Dentro dela temos a `Pose.msg` que consiste de uma mensagem do tipo `geometry_msgs/Point.msg` juntamente com uma mensagem do tipo `geometry_msgs/Quaternion.msg` (onde quaternion tem a ver com orientação angular). Uma mensagem do tipo ponto são 3 variáveis float64 correspondentes aos 3 eixos cartesianos já uma mensagem do tipo quaternion são 4 variavéis correspondes as rotações angulares possíveis;
* Perceba que as mensagens ROS são uma abstração de dados mais comuns que já estamos acostumados a lidar
* Essa estrutura de dados é feita de tal forma que um nó escrito em C++ e outro em python podem se comunicar através de um tópico utilizando bibliotecas que traduzam essas mensagens ROS.
* Da mesma forma, devido essa abstração das mensagens ROS podemos ter aparelhos diferentes com nós do ROS se comunicando pelas mensagens ROS.
* Note que quando se trate de serviços (listados mais abaixo) há um sistema de request/response ou seja a mensagem contém essas 2 partes se diferindo um pouco de mensagens entre nós
### Criando tipos de mensagens
É mais prático fazer um pacote que inclua todas as mensagens que deseja criar:
* Dentro do pacote criado não será necessário as pastas include e src então elas podem ser removidas
* Edite`package.xml` e escreva:
* `<build_depend>message_generation</build_depend>`
* `<exec_depend>message_runtime</exec_depend>`
* Ex:
* 
* Agora em `CMakeLists.txt` edite:
* Em `find_package()` adicione `message_genaration` dentro do parenteses
* Ex:
* 
* Mais abaixo há o seguinte treixo comentado:
* 
* Descomente o trecho:
* 
* Mais abaixo há o seguinte treixo comentado:
* 
* Descomente a linha que começa com "CATKIN_DEPENDS" e adicione`message_runtime` ao final, como na imagem:
* 
* Agora crie uma pasta `msg` onde ficarão os tipos de mensagem criadas:
* Para cada mensagem criada crie um arquivo (de preferência com nome em Cammel Case iniciado de letra maiúscula) do tipo .msg
* No arquivo basta listar os tipos básicos da sua mensagem e os nomes dessas variáveis, estes tipos podem ser encontrados [nesse site no tópico 2](http://wiki.ros.org/msg)
* Ex:
* 
* Cada mensagem criada precisa-se adicionar seu nome em `CMakeLists.txt` em `add_message_files()` que inicialmente estava comentada
* Ex:
* 
### Usando mensagens ROS criadas
Primeiro vá no pacote que deseja adicionar o pacote de mensagens criado e faça os seugintes paços:
* Adicione o pacote em `package.xml`
* `<depend>NomeDoPacoteDeMensasgens</depend>`
* Ex:
* 
* Adicione o pacote em `CMakeLists.txt`
* `find_package(NomeDoPacoteDeMensasgens)`
* Ex:
* 
* Basta incluir semelhante a inclusão de mensagens padrão mas com o nome do pacote e da sua mensagem e usar!
## ROS Topic (Tópico)
Um intermediário para armazenamento de dados estruturados como mensagens ROS - explicado abaixo - onde nós podem publicar dados (publishers) e ler dados (subscribers), similar a um buffer.
* Um tópico está associado ao tipo de mensagem ROS que está sendo trocado
* A troca de mensagens é anônima, o tópico não sabe quem publicou ou quem acessa suas mensagens
* A comunicação de um tópico para um mesmo nó só pode ser leitura (subscriber node) ou de publicação (publisher node) e não ambos ao mesmo tempo. No entanto um tópico pode receber de um nó e ser lido por outros nós.
### Criando Tópicos
### Publisher
Nó que enviará dados para o tópico
#### Python
* `rospy.Publisher("/nomeDoTopico",tipoDeMensagem,queue_size=tamanhoDaFilaDeProcessamento)`
* Cria o tópico
* Note que é necessário importar o tipo de mensagem no código
* `from std_msgs.msg import tipoDeMensagem`
* A fila de processamento serve como se fosse uma quantidade de mensagens que podem ser armazenadas antes de o valor ser atualizado, quanto menor o número mais rápido o tópico vai ser atualizado porém mais difícil pode ser para os nós lerem esses dados. Normalmente 10 é um bom número.
* Ex:
* 
* Note que no While estamos publicando no nosso tópico a mensagem do tipo String no nosso tópico criado numa frequencia determinada pelo rate.
* O comando `rostopic list` lista todos os tópicos em execução
* O comando `rostopic echo nomeDoTopico` printa o tópico escolhido na tela
#### C++
* `ros::Publisher pub = nh.advertise<std_msgs::tipoDaMensagem>("/nomeDoTopico",tamanhoDaFilaDeProcessamento)`
* Cria o tópico
* Note que é necessário importar o tipo de mensagem no código
* `#include <std_msgs/String.h>`
* A fila de processamento serve como se fosse uma quantidade de mensagens que podem ser armazenadas antes de o valor ser atualizado, quanto menor o número mais rápido o tópico vai ser atualizado porém mais difícil pode ser para os nós lerem esses dados. Normalmente 10 é um bom número.
* Não se esqueça de adicionar o executável desse novo nó no CMakeLists.txt como mostrado no tópico de criações de nó acima e executar `catkin_make` na raiz do seu workspace.
* Ex:
* 
* Note que no While estamos publicando no nosso tópico a mensagem do tipo String no nosso tópico criado numa frequencia determinada pelo rate.
* O comando `rostopic list` lista todos os tópicos em execução
* O comando `rostopic echo nomeDoTopico` printa o tópico escolhido na tela
### Subscriber
Nó que receberá dados do tópico
#### Python
* `rospy.Subscriber("/nomeDoTopico",tipoDeMensagem,nomeDaFuncaoDeCallback)`
* A função callback vai ser executada sempre que recebemos dado novo, tem como parâmetro a mensagem ROS que vai ser recebida
* No fim colocar `rospy.spin()` para permitir que o callback seja chamado novamente assim que uma mensagem nova chegar
* Se quiser se aprofundar mais no conceito do que o spin faz sinta-se à vontade nesse [link](https://answers.ros.org/question/257361/what-is-the-actual-meaning-of-rosspin/)
* Ex:
* 
* Note que um nó do tipo subscriber só vai executar caso tenha uma nova mensagem, para isso é necessário que o tópico associado esteja recebendo algum dado
#### C++
* `ros::Subscriber sub = nh.subscribe("/nomeDoTopico",tamanhoDaFilaDeProcessamento,nomeDaFuncaoDeCallback)`
* A função callback vai ser executada sempre que recebemos dado novo, tem como parâmetro a mensagem ROS que vai ser recebida
* No fim colocar `ros::spin()` para permitir que o callback seja chamado novamente assim que uma mensagem nova chegar
* Se quiser se aprofundar mais no conceito do que o spin faz sinta-se à vontade nesse [link](https://answers.ros.org/question/257361/what-is-the-actual-meaning-of-rosspin/)
* Não se esqueça de adicionar o executável desse novo nó no CMakeLists.txt como mostrado no tópico de criações de nó acima e executar `catkin_make` na raiz do seu workspace.
* Ex:
* 
* Note que um nó do tipo subscriber só vai executar caso tenha uma nova mensagem, para isso é necessário que o tópico associado esteja recebendo algum dado
### Debugando um topic (tópico)
Existem algumas funcões úteis para isso:
* Ver a lista dos tópicoss em execução:
* `rostopic list`
* Ter informações sobre um tópico em execução:
* `rostopic info nomeDoTopico`
* Publica num tópico existente
* `rostopic nomeDoTopico std_msgs/TipoDeMensagem Mensagem`
* Se eu quiser publicar numa frequência pré-definida em Hz usar os parâmetros
* `-r frequencia`
* Ex:
* 
* Resulta em:
* 
* Interface gráfica para visualização de nós e tópicos ativos
* `rosrun rqt_graph rqt_graph`
Outras funções úteis de debug podem ser encontradas [aqui](http://wiki.ros.org/rosnode)
## ROS Service (Serviços)
Diferentemente dos tópicos os serviços são como se fossem pedidos de ação de outros nós para continudade do funcionamento do nó atual. Alguns nós precisam de determinadas informações ao mesmo tempo que executam suas ações, ou precisam de um feeback sobre oque fizeram com o dado enviado. Dessa forma é feito um request de determinada ação e o serviço da uma resposta(response) sobre a mesma, é um feedback-loop como em um sistema de redes cliente-servidor, nada mais é executado nesse nó enquanto não houver uma resposta do request solicitado.
* Deve ser usado para ações rápidas como para ativar/desativar atuadores.
* Apenas um serviço pode estar ativo por vez.
* Para diferenciar quando um nó precisa de um serviço ou de um tópico podemos pensar nas seguintes frases:
* "Preciso dessa informação agora pra terminar um negócio aqui"
* Isso é a cara de um serviço que eu preciso de algo rápido e nesse instante para continuar funcionando
* "Faz isso e me avisa se funcionou"
* Isso também é a cara de um serviço pois no serviço eu tenho comunicação biderecional, podendendo ter um feedback sobre o meu request
### Criando um Serviço
#### Servidor
##### Python
* `rospy.Service(/nomeDoServico,tipoDaMensagemDoServico,nomeDaFuncaoCallback)`
* Cria o Serviço
* Não esquecer do `rospy.spin()` no final para manter o serviço vivo até que seja excluído
* Se quiser se aprofundar mais no conceito do que o spin faz sinta-se à vontade nesse [link](https://answers.ros.org/question/257361/what-is-the-actual-meaning-of-rosspin/)
* A função callback vai ser executada assim que o serviço for executado (síncrono)
* O comando `rosservice list` lista todos os tópicos em execução
* O comando `rosservice call` faz um request para o serviço
* Ex:
* 
##### C++
* `ros::ServiceServer server = nh.advertiseService(/nomeDoServico,nomeDaFuncaoCallback)`
* Cria o Serviço
* Não esquecer do `ros::spin()` no final para manter o serviço vivo até que seja excluído
* Se quiser se aprofundar mais no conceito do que o spin faz sinta-se à vontade nesse [link](https://answers.ros.org/question/257361/what-is-the-actual-meaning-of-rosspin/)
* A função callback vai ser executada assim que o serviço for executado (síncrono)
* A função callback aqui é um pouco diferente:
* Ela sempre returna bool indicando sucesso ou falha no request
* 2 parâmetros passados por referência, request e response
* Não se esqueça de adicionar o executável desse novo nó de serviço no CMakeLists.txt como mostrado no tópico de criações de nó acima e executar `catkin_make` na raiz do seu workspace.
* O comando `rosservice list` lista todos os tópicos em execução
* O comando `rosservice call` faz um request para o serviço
* Ex:
* 
#### Cliente
##### Python
* `rospy.wait_for_service(/nomeDoServico)`
* Espera serviço ser executado
* Se o cliente falhar em receber o response do serviço será disparado uma exceção e precisamos tratá-la com um bloco try/except
* `try:`
` client = rospy.ServerProxy(nomeDoServico,tipoDaMensagemDoServico)`
`except rospy.ServiceException as e:`
` rospy.logwarn(Mensagem de Erro)`
* Ex:
* 
##### C++
* `ros::ServiceClient client = nh.serviceClient<tipoDaMensagemDoServico>(/nomeDoServico)`
* Cria o client
* Se o cliente falhar em receber o response do serviço será disparado uma exceção e precisamos tratá-la com um bloco if/else:
* `if (client.call(service)){`
` //do something`
`}`
`else{`
` ROS_WARN(Mensagem de Erro)`
`}`
* Não se esqueça de adicionar o executável desse novo nó de serviço no CMakeLists.txt como mostrado no tópico de criações de nó acima e executar `catkin_make` na raiz do seu workspace.
* O comando `rosservice list` lista todos os tópicos em execução
* O comando `rosservice call` faz um request para o serviço
* Ex:
* 
### Debugando um serviço
Existem algumas funcões úteis para isso:
* Ver os comandos úteis para serviços
* `rosservice -h`
* Ver a lista dos serviços em execução:
* `rosservice list`
* Ter informações sobre um serviço em execução:
* `rosservice info nomeDoServico`
* Faz uma requisição para um serviço existente
* `rosservice call nomeDoServico`
* Depois do nome aperte tab para completar o comando
## Comandos úteis ROS
### Cmd
* `roscore`
* inicializa o ROS master
* `rosnode -h`
* lista funções disponíveis para rosnode
* `rostopic -h`
* lista funções disponíveis para rostopic
* `rosservice -h`
* lista funções disponíveis para rosservice
* `rosnode list`
* lista todos os nós ativos
* `catkin_make`
* Atualiza e prepara o pacote para o funcionamento. Necessita da pasta src e estar em um workspace ( NomeDoWorkspace_ws) para funcionar
* `catkin_pkg_create nomeDoPacote dependencias`
* Faz um pacote
* `rosrun nomeDoPacote nomeDoNo`
* executa rapidamente um nó de um pacote
* `rosnode kill nomeDoPacote`
* Encerra o pacote roos que está sendo executado no momento. Papel de Ctrl+C
* `rosnode ping nomeDoNo`
* Verificar se um nó está na rede e conectado e printa o tempo de resposta
* `rosnode info nomeDoNo`
* Para obter informações sobre comandos ROS
* `rosrun rqt_graph rqt_graph`
* Interface gráfica para visualização de Nós ativos
* `rostopic list`
* lista todos os tópicos ativos
* `rostopic echo nomeDoTopico`
* printa o tópico escolhido na tela
### Python
* `rospy.init_node("nomeDoNo")`
* inicializa o nó
* `rospy.is_shutdown()`
* false enquanto o nó estiver executando
* `rospy.loginfo`
* print
* `rospy.spleep(x)`
* faz o código esperar x segundos
* `rospy.Rate(x)` seguido de `rate.sleep()` esse segundo dentro de um loop, temos:
* `rospy.Rate(x)` estabelecendo uma frequencia x Hz
* `rate.sleep()` forçando o loop seguir a frequência pré-estabelecida
* `rospy.Publisher("/nomeDoTopico",tipoDeMensagem,queue_size=tamanhoDaFilaDeProcessamento)`
* Cria o tópico
* `rospy.Subscriber("/nomeDoTopico",tipoDeMensagem,nomeDaFuncaoDeCallback)`
* Lê de um tópico e executa a função callback a cada nova mensagem recebida
### C++
* `ros::init(argc, argv, "nomeDoNo")`
* cria o nó
* `ros::NodeHandle`
* auxilia na inicialização do nó
* `ros::ok()`
* true enquanto o nó estiver executando
* `ROS_INFO()`
* printf
* `ros::Duration(x).sleep()`
* `.Duration()` lida com estabelecer tempos e durações
* `.sleep()` faz esperar o valor estabelecido pelo duration
* `ros::Rate rate(x)` seguido de `rate.sleep()` esse segundo dentro de um loop, temos:
* `ros::Rate rate(x)` estabelecendo uma frequencia x Hz
* `rate.sleep()` forçando o loop seguir a frequência pré-estabelecida
* `ros::Publisher pub = nh.advertise<std_msgs::tipoDaMensagem>("/nomeDoTopico",tamanhoDaFilaDeProcessamento)`
* Cria o tópico
* `ros::Subscriber sub = nh.subscribe("/nomeDoTopico",tamanhoDaFilaDeProcessamento,nomeDaFuncaoDeCallback)`
* Lê de um tópico e executa a função callback a cada nova mensagem recebida
###### tags: `Minervabots - Wiki`