Hoje, veremos como detectar uma bola vermelha com a câmera Raspberry PI e como começar a apontar nosso drone para ela.

Em artigos anteriores, examinamos em detalhes o lançamento de um drone virtual e real autônomo. Está na hora do nosso drone encontrar um objetivo real.
O objetivo será, como mencionamos, uma bola vermelha.
Preparação de framboesa
Escreveremos um programa para o filtro na versão Desktop do Raspbian. Como para configurar o filtro, vamos ao mesmo tempo alterar as configurações e ver o resultado da nossa trapaça.
Em uma unidade flash separada, instalaremos o Raspbian Desktop .
Para trabalhar com a área de trabalho do Raspbian, você precisa de um monitor + teclado + mouse separados, conectados ao Raspberry, o que não é muito conveniente. Mas você pode trabalhar com a área de trabalho Raspberry sem cabeça e pela rede, através do VNC.
Para fazer isso, você precisa:
- Habilitar o VNC no raspi-config
- Configure o ponto de acesso Wi-Fi em framboesa. O foco com o wpa_supplicant na versão para desktop não passa, as instruções sobre como configurar pontos wifi no Raspberry estão aqui .
- Como cliente, você pode usar o realvnc .
Para desenvolver um filtro em Python - coloque OpenCV:
sudo apt-get install python-opencv
Filtro HSV para transmissão de vídeo
Uma maneira simples e rápida de destacar um objeto colorido em uma imagem é transferir a imagem para o espaço de cores HSV e filtrar pelo intervalo desejado de Matiz, Saturação e valor usando cv2.inRange. Tomei este exemplo como base.
A peculiaridade de nossa tarefa é que nossa bola é vermelha e, se observarmos cuidadosamente a escala de tons (HUE), veremos que a cor vermelha está localizada em suas bordas:

Portanto, se você filtrar apenas uma parte do alcance, quando a iluminação mudar, a bola poderá "desaparecer" do filtro, o que afetará negativamente a finalidade do nosso drone. Portanto, além dos parâmetros H, S e V, adicionaremos o sinalizador de inversão de inversão ao nosso filtro - para poder filtrar tudo, exceto a bola, e simplesmente inverter o filtro.
O código para o programa de filtro é mostrado abaixo: Definir o filtro na bola é algo como isto:
O cálculo da posição da bola no espaço
Após receber o filtro, podemos encontrar a bola na imagem do filtro usando a função de destaque de contorno cv2.findContours
.
Em nosso programa, iteraremos sobre todos os contornos encontrados, selecionaremos aqueles com mais de 10x10 pontos e escolheremos o maior deles - essa será a bola desejada. Se vários objetos atingirem a câmera - para orientação, usamos o mais próximo, ou seja, o maior.
Desenhamos as informações úteis do filtro para a imagem resultante: desenhe os contornos encontrados e circule o maior deles, selecione o centro da linha (para onde voar) com as linhas, calcule o diâmetro da bola nos pontos e calcule a distância até ela, o ângulo de guinada. Além disso, no centro da tela, adicionei um “escopo” + e contei a taxa de quadros - para controlar a velocidade do nosso filtro.
O texto completo do programa de filtro é fornecido abaixo: O programa da imagem da câmera considera a distância da bola e os ângulos de guinada e inclinação em que a bola se desvia do centro da imagem da câmera. Esta informação é suficiente para apontarmos o drone na bola. Além disso, estaremos envolvidos no programa de orientação.
Pacote de detecção de bola ROS
A funcionalidade desenvolvida pode ser transferida para o sistema Raspberry sem cabeça com o ROS instalado. Criaremos um pacote ROS baloon que executará as seguintes funções:
- Lançar mavros, fornecendo-nos um controlador de voo
- Execute nosso programa de detecção de bola. No programa, adicionaremos a publicação das coordenadas da bola e as imagens de depuração nos tópicos correspondentes do ROS
- Execute web_video_server - um componente ROS que nos permitirá monitorar nosso filtro de fora usando um navegador
Antes de criar o pacote ROS, adicione a seguinte funcionalidade ao nosso programa de filtro ROS para trabalhar no ambiente ROS:
- Lendo as configurações da câmera. Os parâmetros no ROS são definidos na forma de um arquivo yaml, geralmente obtido durante a calibração da câmera . Estamos interessados apenas nos tamanhos das imagens (largura e altura), mas, para seguir o conceito de ROS, vamos obtê-los no arquivo yaml.
- Obtendo os parâmetros de filtro especificados. Anotaremos os parâmetros de filtro selecionados anteriormente no arquivo de inicialização do nosso pacote ROS.
- Publicação das coordenadas da bola recebidas no tópico ROS
/baloon_detector/twist
- Publique imagens de depuração nos tópicos do ROS
/baloon_detector/image_filter
e /baloon_detector/image_result
Você pode executar o programa para execução usando o python baloon_pose_cv.py
usual python baloon_pose_cv.py
- mas também há uma maneira mais conveniente - de empacotar a funcionalidade necessária no pacote ROS e configurar sua execução automática na inicialização do sistema.
Há muitas lições sobre a criação de pacotes ROS na rede , então vou me limitar à lista de etapas necessárias.
Vamos criar o diretório da área de trabalho do ROS para o nosso pacote e o próprio pacote do ROS:
mkdir -p ~/baloon_ws/src cd ~/baloon_ws/ catkin_make source devel/setup.bash cd src catkin_create_pkg baloon std_msgs rospy
Colocamos no diretório ~ / baloon_ws / src / baloon / src os arquivos necessários para executar nosso pacote:
- baloon_pose_cv.py - programa de detecção de bolas
- baloon_cv.launch - inicia o arquivo para iniciar nosso pacote ROS
- fe130_320_01.yaml - arquivo obtido como resultado da calibração da câmera . Estamos interessados apenas no tamanho da imagem, mas no futuro esse arquivo poderá ser usado para uma análise mais profunda das imagens, restauração de cenas 3D de uma imagem etc.
- baloon.service, roscore.env - esses arquivos são usados para execução automática do pacote ROS
Abaixo estão os textos de cada um dos arquivos:
baloon_pose_cv.py - o programa principal de detecção de bolas baloon_cv.launch - inicia o arquivo para iniciar nosso pacote ROS <launch> <include file="$(find mavros)/launch/px4.launch"> <arg name="fcu_url" value="/dev/ttyAMA0:921600"/> <arg name="gcs_url" value="tcp-l://0.0.0.0:5760"/> </include> <node name="baloon_detector" pkg="baloon" type="baloon_pose_cv.py" output="screen"> <param name="invert" value="1"/> <param name="h1" value="7"/> <param name="h2" value="158"/> <param name="s1" value="0"/> <param name="s2" value="255"/> <param name="v1" value="0"/> <param name="v2" value="255"/> <param name="real_ballon_r" value="0.145"/> <param name="camera_info_url" value="$(find baloon)/src/fe130_320_01.yaml"/> <param name="framerate" value="40"/> </node> <node name="web_video_server" pkg="web_video_server" type="web_video_server" required="false" respawn="true" respawn_delay="5"/> </launch>
fe130_320_01.yaml - arquivo de calibração da câmera image_width: 320 image_height: 240 camera_name: main_camera_optical camera_matrix: rows: 3 cols: 3 data: [251.8636348237197, 0, 161.853506252244, 0, 252.36606604425, 102.0038140308112, 0, 0, 1] distortion_model: plumb_bob distortion_coefficients: rows: 1 cols: 5 data: [-0.4424451138703088, 0.1594038086314775, 0.006694781700363117, 0.00174908936506397, 0] rectification_matrix: rows: 3 cols: 3 data: [1, 0, 0, 0, 1, 0, 0, 0, 1] projection_matrix: rows: 3 cols: 4 data: [174.0442047119141, 0, 163.822732720786, 0, 0, 175.2916412353516, 105.5565883832869, 0, 0, 0, 1, 0]
Após colocar os arquivos no diretório baloon_ws / src / baloon / src, é necessário adicionar o atributo "arquivo executável" (cmod + x) aos arquivos baloon_pose_cv.py e baloon.service. Então, como o primeiro teste, lançamos nosso nó ROS manualmente:
roslaunch baloon baloon_cv.launch
Após iniciar o nó, podemos observar a imagem do nosso filtro e bola através de um navegador da web:

E também obtenha as coordenadas da bola detectada no tópico /baloon_detector/twist
:
pi@raspberry:~ $ rostopic list |grep baloon /baloon_detector/image_filter /baloon_detector/image_result /baloon_detector/twist pi@raspberry:~ $ rostopic echo /baloon_detector/twist header: seq: 1 stamp: secs: 1554730508 nsecs: 603406906 frame_id: "fcu" twist: linear: x: 6.6452559203 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.137081068334 z: -0.165806278939 ---
Resta adicionar a inicialização automática do nosso nó no início do Raspberry.
Para fazer isso, crie o arquivo de descrição do serviço systemd baloon.service, o seguinte conteúdo:
[Unit] Description=BALOON ROS package Requires=roscore.service After=roscore.service [Service] EnvironmentFile=/home/pi/baloon_ws/src/baloon/src/roscore.env ExecStart=/opt/ros/kinetic/bin/roslaunch baloon baloon_cv.launch --wait Restart=on-abort [Install] WantedBy=multi-user.target
O arquivo refere-se à descrição das variáveis de ambiente roscore.env:
ROS_ROOT=/opt/ros/kinetic/share/ros ROS_DISTRO=kinetic ROS_PACKAGE_PATH=/home/pi/baloon_ws/src:/opt/ros/kinetic/share ROS_PORT=11311 ROS_MASTER_URI=http://localhost:11311 CMAKE_PREFIX_PATH=/home/pi/baloon_ws/devel:/opt/ros/kinetic PATH=/opt/ros/kinetic/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin LD_LIBRARY_PATH=/opt/ros/kinetic/lib PYTHONPATH=/home/pi/catkin_ws/devel/lib/python2.7/dist-packages:/opt/ros/kinetic/lib/python2.7/dist-packages ROS_IP=192.168.11.1
A conexão e o início do serviço são realizados usando os comandos:
sudo systemctl enable ~/baloon_ws/src/baloon/src/baloon.service sudo systemctl start baloon
Agora, o nó deve iniciar automaticamente quando o Raspberry é iniciado.
Você pode parar o nó com o sudo systemctl stop baloon
, cancelar autostart - sudo systemctl disable balloon
.
Como resultado de nossas ações, obtivemos um “detector de bola automático” que é iniciado quando o sistema é iniciado no computador de bordo.
Na próxima parte, prosseguiremos para o lançamento final de um drone de retorno.
O código fonte dos programas é carregado no Github .