Olá, ROS!

Agora que criamos um pacote, podemos começar a escrever programas ROS.

Sumário

  1. Um programa simples
  2. Compilando o programa Hello
    1. Declarando dependências
    2. Declarando um executável
    3. Construindo área de trabalho
    4. Abastecendo setup.bash
  3. Executando o programa hello

Um programa simples

Lista 3.2 mostra uma versão ROS do programa canônico “Hello, world!”. Esse arquivo fonte, nomeado ‘hello.cpp’, pertence a sua pasta de pacotes, próximo ao ‘package.xml’ e ‘CMakeLists.txt’.

⚠️ Alguns tutoriais online sugerem a criação de um diretório scr dentro do seu diretório de pacote para conter arquivos fonte C++. Essa organização adicional pode ser útil, especialmente para pacotes maiores com vários tipos de arquivos, mas não é estritamente necessário.

Lista 3.1

O manifesto (isto é, package.xml) para esse pacote agitr do livro.
1 <?xml version ="1.0"?>
2 <package>
3 <name>agitr</name>
4 <version>0.0.1</version>
5 <description>
6 Examples from A Gen tle I n t r o d u c ti o n t o ROS
7 </description>
8 <maintainer email=" jokane@cse.sc.edu">
9 Jason O' Kane
10 </maintainer>
11 <license>TODO</license>
12 <buildtool_depend>catkin</buildtool_depend>
13 <build_depend>geometry_msgs</build_depend>
14 <run_depend>geometry_msgs</run_depend>
15 <build_depend>turtlesim</build_depend>
16 <run_depend>turtlesim</run_depend>
17 </package>

Nós veremos como compilar e executar esse programa momentaneamente, mas primeiro vamos examinar o próprio código.

  • O cabeçalho de arquivo ros/ros.h inclui declarações das classes padrão ROS. Você vai desejar inclui-lo em todo programa ROS que você escrever.

  • A função ros::init inicializa a biblioteca cliente ROS. Chame essa uma vez no começo do seu programa. * O ultimo parâmetro é uma string contendo o nome default do seu nó.

⏩ Esse nome default pode ser anulado por um arquivo launch (ver página 87) ou por um parâmetro de linha de comando rosrun (ver página 23).

  • O objeto ros::NodeHandle é o principal mecanismo que seu programa vai usar para interagir com o sistema ROS. Criar esse objeto registra o seu programa como um nó com o ROS máster. A técnica mais simples é criar um único objeto NodeHandle para usar durante o seu programa.

Lista 3.2

Um programa trivial ROS chamado hello.cpp.
1 // Essa é uma versão ROS do programa padrão 
2 // "hello, world"
3
4 // Esse cabeçalho define as classes padrão ROS. 
5 #include<ros/ros.h>
6
7 int main (int argc ,char∗∗ argv ) {
8 // Inicializa o sistema ROS.
9 ros::init (argc , argv ,"hello_ ros") ;
10
11 // Estabelece esse programa como o nó ROS.
12 ros :: NodeHandle nh ;
13
14 // Envia a saída como mensagem log.
15 ROS_INFO_STREAM( " Hell o , ␣ROS! " ) ;
16 }

⏩ Internamente, a classe NodeHandle mantém uma contagem de referência e só registra um novo nó com o master quando o primeiro objeto NodeHandle é criado. Da mesma forma, o nó só é desregistrado quando todos objetos do NodeHandle forem destruídos. Esse detalhe tem dois impactos: Primeiro, você pode, se preferir, criar múltiplos objetos NodeHandle, todos os quais se referem ao mesmo nó. Ocasionalmente, haverão situações onde isso fará sentido. Um exemplo dessa tal situação aparece na página 129. Segundo, isso significa que não é possível, usando a interface padrão roscpp , executar múltiplos nós distintos dentro de um único programa.

  • A linha ROS_INFO_STREAM gera uma mensagem informativa. Essa mensagem é enviada a diversos locais diferentes, incluindo a tela do console. Nós veremos mais detalhes sobre esse tipo de mensagem log no capítulo 4.

Compilando o programa Hello

Como você pode compilar e executar esse programa? Isso é tratado pelo sistema de construção do ROS, chamado de catkin. Existem quatro passos.

Declarando dependências

Primeiro, nós precisamos declarar os outros pacotes dos quais o nosso depende. Para os programas em C++, esse passo é necessário primordialmente para assegurar que catkin forneça o compilador C++ com as bandeiras apropriadas para localizar os arquivos e bibliotecas de cabeçalho que ele precisa.

Para listar dependências, edite o CMakeLists.txt no seu diretório de pacotes. A versão default desse arquivo tem essa linha:

find_package(catkin REQUIRED)

Dependências em outros pacotes catkin podem ser adicionadas em uma seção de componentes nessa linha:

find_package(catkin REQUIRED COMPONENTS package-names)

Para o exemplo hello, nós precisamos de uma dependência em um pacote chamado de roscpp, o qual fornece a biblioteca de cliente do ROS em C++. A linha obrigatória find_package, portanto é:

find_package(catkin REQUIRED COMPONENTS roscpp)

Nós devemos também listar dependências no pacote manifesto package.xml, usando os elementos build_depend e run_depend:

package-name

Em nosso exemplo, o programa hello precisa do roscpp tanto na construção quanto na execução, então o manifesto deve conter:

roscpp

Contudo, dependências declaradas no manifesto não são usadas no processo de produção; se você os omite aqui, você provavelmente não verá nehuma mensagem de erro até que você distribua seu pacote a outros que tentam construi-lo sem possuirem os pacotes obrigratórios instalados.

Declarando um executável

Em seguida, nós precisamos adicionar duas linhas ao CMakeLists.txt declarando o executável que gostaríamos de criar. A forma geral é:

add_executable(executable-name source-files)
target_link_libraries(executable-name ${catkin_LIBRARIES})

A primeira linha declara o nome do executável que queremos e uma lista de aquivos fonte que devem ser combinados para formar aquele executável. Se você tem mais de um arquivo fonte, liste-os todos aqui, separados por espaços. A segunda linha fala ao CMake para usar as bandeiras apropriadas de biblioteca ( definidas pelo find_package linha acima) ao conectar esse executável. Se o seu pacote contém mais de um executável, copie e modifique essas duas linhas para cada executável que houver.

Em nosso exemplo, queremos um executável chamado hello, compilado de um único arquivo fonte chamado hello.cpp, então nós adicionaremos essas linhas ao CMakeLists.txt:

add_executable(hello hello.cpp)
target_link_libraries(hello ${catkin_LIBRARIES})

Para referência, Lista 3.3 mostra um pequeno CMakeLists.txt que é suficiente para o nosso exemplo. A versão default do CMakeLists.txt criada pelo catkin_create_pkg contém um pouco de orientação extra para outros poucos propósitos; para muitos programas simples, algo similar a versão simples mostrada aqui é o bastante.

Lista 3.3

The CMakeLists.txt to build hello.cpp.
# Qual versão do CMake é necessária? 
cmake_minimum_required(VERSION 2.8.3)
# Nome desse pacote.
project(agitr)
# Encontre o sistema de construção catkin e qualquer outro pacote o 
# qual nós dependemos.
find_package(catkin REQUIRED COMPONENTS roscpp)
# Declare nosso pacote catkin. 
catkin_package()
# Especifique localização dos arquivos de cabeçalho.
include_directories(include ${catkin_INCLUDE_DIRS})
# Declare o executável junto com seus arquivos fonte. Se
# existem vários executáveis, use várias cópias 
# dessa linha.
add_executable (hellohello.cpp)
# Especifique bibliotecas para qual se contra-liga. Novamente, essa
# linha deve ser copiado para cada executável distinto  
# no pacote.
target_link_libraries(hello${catkin_LIBRARIES})

Construindo área de trabalho

Uma vez que o seu CMakeLists.txt está pronto, você pode construir sua area de trabalho — incluindo compilar todos os executáveis em todos esses pacotes — usando este comando:

catkin_make

Por ser projetado para construir todos os pacotes na sua área de trabalho, esse comando deve ser executado pelo diretório da área de trabalho. Ele irá realizar vários passos de configuração (especialmente na primeira execução) e criar subdiretórios chamados devel and build dentro da sua área de trabalho. Esses dois novos diretórios contêm arquivos relacionados à construção como arquivos criadores automaticamente gerados, código objeto e os próprios executáveis. Se preferir, os subdiretórios devel and build podem ser deletados com segurança quando você terminar de trabalhar no seu pacote.

Se houverem erros de compilação, você os verá aqui. Depois de corrigi-los você pode usar catkin_make novamente para completer o processo de construção.

⚠️ Se você vir erros do catkin_make que o cabeçalho ros/ros.h não pode ser encontrado, ou erros “ referência indefinida” no ros::init ou outras funções ROS, a razão mais provável é que o seu CMakeLists.txt não declara corretamente uma dependência no roscpp.

Abastecendo setup.bash

A etapa final é executar um roteiro chamado setup.bash, o qual é criado pelo catkin_make dentro o subdiretório devel da sua area de trabalho.

source devel/setup.bash

Esse roteiro de geração automatica define várias variáveis ambientais que permitem que o ROS encontre o seu pacote e é um executável recentemente gerado. Isso é análago ao setup.bash global da seção 2.2, mas designado especificamente para a sua área de trabalho. A menos que a estrutura do diretório mude, você só vai precisar fazer isso uma só vez em cada terminal, mesmo que você modifique o código e compile novamente com catkin_make.

Executando o programa hello

Quando todas as etapas de construção estão completas, o seu novo programa ROS está pronto para execução usando rosrun (Seção 2.6), assim como qualquer outro programa ROS. No nosso exemplo, o comando é:

rosrun agitr hello

O programa deve produzir uma saída que se pareça mais ou menos assim:

[ INFO ] [1416432122.659693753]: Hello, ROS!

Não esqueça de iniciar o roscore primeiro: Esse programa é um nó e nós precisam de um máster para rodar corretamente. Por sinal, os números nessa linha de saída representam o tempo — medido em segundos desde 1 de janeiro de 1970 — quando a nossa linha ROS_INFO_STREAM foi executada.

⚠️ Esse rosrun paralelo a alguns outros commandos ROS, pode gerar um erro que se parece com isto:

[rospack] Error: stack/package package-name not found

Duas causas comuns para esse erro são (a) erro de ortografia no nome do pacote e (b) falha em executar o setup.bash para sua área de trabalho.