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:
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:
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.listdeb 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.
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:
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 .