Uso prático de ROS no Raspberry Pi - Parte 2

Boa tarde, queridos leitores de Habr! Este é o segundo artigo de uma série de artigos sobre o uso prático de ROS no Raspberry Pi. No primeiro artigo da série, descrevi a instalação dos componentes ROS necessários e a configuração do ambiente de trabalho para o trabalho.
Na segunda parte da série, começaremos o uso prático dos recursos do ROS na plataforma Raspberry Pi. Especificamente, neste artigo, vou falar sobre o uso da placa de câmera Raspberry Pi no Raspberry Pi em conjunto com o ROS para resolver problemas de visão computacional. Quem está interessado, por favor, sob gato.


Camera RPi Camera Board


Para o trabalho, precisaremos de uma câmera Raspberry Pi Camera Board:
imagem

Essa câmera se conecta diretamente à GPU por meio de um conector CSi na placa, que permite gravar e codificar a imagem da câmera sem usar o tempo do processador. Um cabo ZIF é usado para conectar a câmera. O conector do cabo na placa está localizado entre as portas Ethernet e HDMI:

imagem

Instalação da biblioteca


Então, vamos prosseguir com a instalação das bibliotecas necessárias. Primeiro, instale o OpenCV:

$ sudo apt-get install libopencv-dev

Para usar a câmera Raspberry Pi, precisaremos da biblioteca raspicam. Faça o download do arquivo aqui .
Em seguida, instale a biblioteca:

$ tar xvzf raspicamxx.tgz
$ cd raspicamxx
$ mkdir build
$ cd build
$ cmake ..
$ make
$ sudo make install
$ sudo ldconfig

Você precisa habilitar o suporte à câmera no Raspbian através do programa raspi-config:

$ sudo raspi-config

Selecione a opção 5 - Habilite a câmera, salve a seleção e reinicie o sistema.

Introdução ao ROS


Por conveniência, crie uma nova oficina de catkin para nossos pacotes:

$ mkdir -p ~/driverobot_ws/src
$ cd ~/driverobot_ws/src
$ catkin_init_workspace
$ cd ~/driverobot_ws
$ catkin_make

Crie um novo pacote ROS:

$ cd src/
$ catkin_create_pkg raspi_cam_ros image_transport cv_bridge roscpp std_msgs sensor_msgs compressed_image_transport opencv2

A especificação do comando catkin_create_pkg é a seguinte: catkin_create_pkg <package_name> [depend1] [depend2],
onde é possível especificar quaisquer dependências na biblioteca - as bibliotecas que o pacote utilizará.
Este comando cria a estrutura do projeto ROS: um diretório src vazio para scripts de nó e arquivos de configuração CMakeLists.txt e package.xml.
Encontrei um script simples que recebe quadros da câmera e os publica usando o "editor" (editor), e o adaptei para o ROS Indigo. Você pode baixá-lo aqui .
Para usá-lo, você precisa instalar o driver da câmera Raspberry Pi UV4L:

$ curl http://www.linux-projects.org/listing/uv4l_repo/lrkey.asc | sudo apt-key add -

Adicione a seguinte linha ao arquivo /etc/apt/sources.list

deb http://www.linux-projects.org/listing/uv4l_repo/raspbian/ wheezy main

atualize os pacotes e instale:

$ sudo apt-get update
$ sudo apt-get install uv4l uv4l-raspicam

Para baixar o driver, em cada inicialização do sistema, também instalamos um pacote opcional:

$ sudo apt-get install uv4l-raspicam-extras

Vamos para a edição do pacote. Cole as linhas daqui em seu arquivo CMakeLists.txt .
As linhas mais importantes no arquivo CMakeLists.txt:

link_directories(/usr/lib/uv4l/uv4lext/armv6l/)
target_link_libraries(capture ${catkin_LIBRARIES} uv4lext)

Assim, adicionamos um link ao driver uv4l, necessário para compilar o pacote.
No final do arquivo, definimos linhas especiais para o nosso nó:

add_executable(capture src/capturer.cpp)
target_link_libraries(capture ${catkin_LIBRARIES} uv4lext)

A linha add_executable cria um binário para inicialização e o target_link_lbraries vincula bibliotecas adicionais para o binário de captura.
Cole as linhas ausentes daqui no arquivo package.xml para que fique assim:

<?xml version="1.0"?>                                                                                            
<package>                                                                                                        
  <name>raspi_cam_ros</name>                                                                                      
  <version>0.0.0</version>                                                                                       
  <description>The raspi_cam_ros package</description>                                                            
                                                                                                                 
  <maintainer email="pi@todo.todo">pi</maintainer>                                                               
                                                                                                                 
  <license>TODO</license>                                                                                        
  <buildtool_depend>catkin</buildtool_depend>                                                                    
  <build_depend>cv_bridge</build_depend>                                                                         
  <build_depend>image_transport</build_depend>                                                                   
  <build_depend>roscpp</build_depend>                                                                            
  <build_depend>std_msgs</build_depend>                                                                          
  <build_depend>sensor_msgs</build_depend>                                                                       
  <build_depend>opencv2</build_depend>                                                                           
  <build_depend>compressed_image_transport</build_depend>                                                        
  <run_depend>cv_bridge</run_depend>                                                                             
  <run_depend>image_transport</run_depend>                                                                       
  <run_depend>roscpp</run_depend>                                                                                
  <run_depend>std_msgs</run_depend>                                                                              
  <run_depend>sensor_msgs</run_depend>                                                                           
  <run_depend>opencv2</run_depend>                                                                               
  <run_depend>compressed_image_transport</run_depend>                                                                                                                                                                    
</package>

Nas primeiras linhas, os parâmetros básicos do nó são definidos - nome, versão, descrição, informações sobre o autor. As linhas build_depend determinam as dependências da biblioteca necessárias para compilar o pacote, e as linhas run_depend determinam as dependências necessárias para executar o código no pacote.
Crie um arquivo capturer.cpp dentro da pasta src e cole as linhas a partir daqui . Aqui no método main (), o nó é inicializado e iniciado em um loop:

ros::init(argc, argv,"raspi_cam_ros");
ros::NodeHandle n;
UsbCamNode a(n);
a.spin();

Toda a lógica do script é que obtemos a imagem da câmera usando o OpenCV, envolvemos em uma mensagem para ROS no método fillImage e a publicamos no tópico. Aqui, o pacote image_transport é usado para criar uma imagem de "editor".
Ative o driver uv4l executando o comando:

$ uv4l --driver raspicam --auto-video_nr --width 640 --height 480 --nopreview

o que criará o streaming MJPEG.
Compile nosso nó ROS:

$ roscore
$ cd ~/driverobot_ws
$ catkin_make

Verifique o valor da variável ROS_PACKAGE_PATH:

echo $ROS_PACKAGE_PATH
/opt/ros/indigo/share:/opt/ros/indigo/stacks

O valor da variável ROS_PACKAGE_PATH deve incluir o caminho para o nosso espaço de trabalho. Adicione o espaço da nossa oficina ao caminho:

$ source devel/setup.bash

Agora, executando o comando echo $ ROS_PACKAGE_PATH novamente, devemos ver uma saída semelhante:

/home/youruser/catkin_ws/src:/opt/ros/indigo/share:/opt/ros/indigo/stacks

, em que / home / <nome_do_usuário> / catkin_ws / src é o caminho para nosso espaço de trabalho. Isso significa que o ROS pode "ver" nossos nós criados em catkin_ws e podemos executá-los através do rosrun.
Execute nosso nó ROS:

$ rosrun raspi_cam_ros capture

Execute o programa gráfico rqt_image_view para exibir o fluxo de vídeo do tópico:

$ rosrun rqt_image_view rqt_image_view

Selecione o tópico image_raw na janela rqt_image_view.

imagem

Ao iniciar o nó, o erro "Gtk-WARNING **: não pode abrir a tela: -1" pode ocorrer ao trabalhar via ssh ou "GdkGLExt-WARNING **: o sistema de janelas não suporta o OpenGL". ao executar no modo de área de trabalho remota VNC. A solução é conectar-se ao Raspberry Pi via SSH com encaminhamento X11:

$ ssh -X pi@<host_pi>

Você pode descobrir com que frequência as postagens são postadas no tópico usando o comando rostopic:

rostopic hz image_raw

Este comando calcula a frequência de recebimento de mensagens por tópico a cada segundo e a exibe no console.
Para o modelo B +, eu tive uma conclusão como esta:

average rate: 7.905
              min: 0.075s max: 0.249s std dev: 0.02756s 

Como você pode ver, a frequência de postagem é de 8 Hz.
Também verifiquei a frequência de publicação de imagens da câmera no modelo RPi 2. Aqui os resultados foram muitas vezes melhores:

average rate: 30.005
              min: 0.024s max: 0.043s std dev: 0.00272s 

As mensagens já são publicadas com uma frequência de 30 Hz, o que é um aumento muito bom na velocidade em comparação com o modelo B +.

Controle de robô baseado em visual com OpenCV e ROS


Agora, escreveremos um pequeno pacote ROS para usar a visão computacional em um robô com o Raspberry Pi, que executará o algoritmo de reconhecimento (no nosso caso, orientação visual usando o método a seguir) e publicaremos o valor do deslocamento necessário do robô no tópico. Por outro lado, o nó de controle de movimento do robô se inscreverá neste tópico e enviará comandos de controle de movimento ao Arduino.
Agora vamos adicionar um "editor" ao script capturer.cpp, que publicará o valor do turno. Primeiro, ative a definição do tipo de mensagem para o valor do turno - std_msgs / Int16.

#include <std_msgs/Int16.h>

rosserial pega arquivos especiais de mensagens msg e gera código fonte para eles. Este modelo é usado: O código-fonte para mensagens rosseriais padrão é armazenado na pasta package_name, dentro do diretório ros_lib. Em seguida, inicializamos a mensagem para este tipo:
package_name/msg/Foo.msg → package_name::Foo





std_msgs::Int16 shift_msg;

Crie um "editor":

ros::Publisher shift_pub;

e dentro do construtor UsbCamNode, damos uma definição:

shift_pub = nh.advertise<std_msgs/Int16>(“line_shift”, 1);

Aqui, definimos o tipo de mensagens e o nome do tópico para publicação.
Em seguida, adicione a lógica para calcular o valor da mudança de linha usando o OpenCV e publicando-o no tópico line_shift no método take_and_send_image () antes da linha #ifdef OUTPUT_ENABLED:

// Some logic for the calculation of offest
shift_msg.data = offset;
shift_pub.publish(shift_msg);

Eu não tenho um algoritmo de seguimento de linha pronto, então o leitor é livre para escrever aqui sua própria lógica.
De fato, os dados na mensagem são armazenados no campo de dados. A estrutura da mensagem pode ser visualizada usando o comando:

$ rosmsg show std_msgs/Int16

Agora execute o nó:

$ rosrun raspi_cam_ros capturer

Usamos o comando rostopic echo para gerar dados publicados no tópico line_shift:

$ rostopic echo line_shift

Agora adicione um "assinante" ao nó de controle do robô. Habilite a definição do tipo de mensagem:

#include <std_msgs/UInt16.h>

Em seguida, adicionamos uma função de retorno de chamada que é executada quando uma mensagem é recebida do tópico.

void messageCb(const std_msgs::UInt16& message) 
{
  int shift_val = int(message.data);
  
  char* log_msg;
  if(shift_val < 0) log_msg = "Left";
  else if(shift_val > 0 ) log_msg = "Right";
  else log_msg = "Forward";
  
  nh.loginfo(log_msg);
}

A função de retorno de chamada deve ser do tipo void e usar uma referência const do tipo de mensagem como argumento.
Para simplificar, registro uma mensagem sobre a direção do deslocamento da linha. Aqui você pode adicionar sua própria lógica de movimento do robô para o seu cenário.
Crie um assinante para mensagens do tópico line_shift.

ros::Subscriber<std_msgs::UInt16> sub("line_shift", &messageCb);

Aqui, definimos o nome do tópico e um link para a função de retorno de chamada.
Em seguida, vêm os métodos de esboço padrão para rosserial_arduino:

void setup()
{
  nh.initNode();
  nh.subscribe(sub);
  
  Serial.begin(57600);
}

void loop()
{
  nh.spinOnce();
  delay(100);
}

A única diferença é que adicionamos nh.subscribe (sub) para criar a "assinatura" real do nó no tópico.
Um esboço para controlar um robô pode ser baixado aqui .
Truque! No ROS, existem arquivos de inicialização especiais que permitem iniciar nós como processos separados automaticamente com determinados parâmetros. os arquivos de inicialização são criados no formato xml e sua sintaxe permite executar muitos nós de uma só vez. No entanto, o arquivo de inicialização não garante que os nós sejam iniciados na ordem exata especificada.
Você pode criar um arquivo de inicialização para facilitar o início do servidor rosserial_python.

$ cd <catkin_ws>/src
$ catkin_create_pkg rosserial_controller
$ cd src/rosserial_controller
$ vim rosserial_controller.launch

Escrevemos o arquivo de inicialização com o seguinte conteúdo:

<launch>
 <node pkg="rosserial_python" type="serial_node.py" name="arduino_serial">
       <param name="port" value="/dev/ttyACM0"/>       
 </node>
</launch>

Compile e execute:

$ cd ~/<catkin_ws>
$ catkin_make
$ source devel/setup.bash
$ roslaunch rosserial_controller rosserial_controller.launch

Podemos visualizar os valores publicados no tema line_shift usando o utilitário rqt_plot, como é feito no artigo :

$ rqt_plot line_shift

Agora você pode tirar o máximo proveito da câmera Raspberry Pi e da biblioteca OpenCV em seus cenários para obter orientação visual do robô, reconhecimento de objetos, rastreamento e muitos outros. Liberte sua imaginação!
Da próxima vez, falaremos sobre como controlar o robô no modo de teleoperação pressionando as teclas do teclado.

PS. A rede possui folhas de dicas úteis - ROS Cheatsheet. Para a versão ROS Indigo, você pode fazer o download aqui .

Source: https://habr.com/ru/post/pt388927/


All Articles