Guia de início rápido do ROS (Robot Operating System)
Introdução ao ROS (legendado) da Open Robotics no Vimeo.
O que é ROS?
O Robot Operating System (ROS) é um framework de código aberto amplamente utilizado na pesquisa e indústria de robótica. O ROS fornece uma coleção de bibliotecas e ferramentas para ajudar os desenvolvedores a criar aplicações de robôs. O ROS é projetado para funcionar com várias plataformas robóticas, tornando-o uma ferramenta flexível e poderosa para roboticistas.
Principais funcionalidades do ROS
-
Arquitetura Modular: O ROS tem uma arquitetura modular, permitindo que os desenvolvedores construam sistemas complexos combinando componentes menores e reutilizáveis chamados nodes. Cada node normalmente executa uma função específica, e os nodes se comunicam entre si usando mensagens por meio de topics ou services.
-
Middleware de Comunicação: ROS oferece uma infraestrutura de comunicação robusta que suporta comunicação entre processos e computação distribuída. Isso é alcançado através de um modelo de publicação-assinatura para fluxos de dados (tópicos) e um modelo de solicitação-resposta para chamadas de serviço.
-
Abstração de Hardware: ROS fornece uma camada de abstração sobre o hardware, permitindo que os desenvolvedores escrevam código independente do dispositivo. Isso permite que o mesmo código seja usado com diferentes configurações de hardware, facilitando a integração e a experimentação.
-
Ferramentas e Utilitários: O ROS vem com um conjunto rico de ferramentas e utilitários para visualização, depuração e simulação. Por exemplo, o RViz é usado para visualizar dados de sensores e informações de estado do robô, enquanto o Gazebo fornece um ambiente de simulação poderoso para testar algoritmos e projetos de robôs.
-
Ecosistema Extenso: O ecossistema ROS é vasto e está em constante crescimento, com inúmeros pacotes disponíveis para diferentes aplicações robóticas, incluindo navegação, manipulação, percepção e muito mais. A comunidade contribui ativamente para o desenvolvimento e manutenção desses pacotes.
Evolução das Versões do ROS
Desde o seu desenvolvimento em 2007, o ROS evoluiu através de múltiplas versões, cada uma introduzindo novos recursos e melhorias para atender às crescentes necessidades da comunidade robótica. O desenvolvimento do ROS pode ser categorizado em duas séries principais: ROS 1 e ROS 2. Este guia se concentra na versão de Suporte de Longo Prazo (LTS) do ROS 1, conhecida como ROS Noetic Ninjemys, o código também deve funcionar com versões anteriores.
ROS 1 vs. ROS 2
Embora o ROS 1 forneça uma base sólida para o desenvolvimento robótico, o ROS 2 aborda suas deficiências oferecendo:
- Desempenho em Tempo Real: Suporte aprimorado para sistemas em tempo real e comportamento determinístico.
- Segurança: Funcionalidades de segurança melhoradas para uma operação segura e fiável em vários ambientes.
- Escalabilidade: Melhor suporte para sistemas multi-robôs e implementações em larga escala.
- Suporte Multiplataforma: Compatibilidade expandida com vários sistemas operativos além do Linux, incluindo Windows e macOS.
- Comunicação Flexível: Uso de DDS para uma comunicação entre processos mais flexível e eficiente.
Mensagens e Tópicos ROS
No ROS, a comunicação entre os nós é facilitada por meio de mensagens e tópicos. Uma mensagem é uma estrutura de dados que define as informações trocadas entre os nós, enquanto um tópico é um canal nomeado sobre o qual as mensagens são enviadas e recebidas. Os nós podem publicar mensagens em um tópico ou assinar mensagens de um tópico, permitindo que eles se comuniquem entre si. Este modelo de publicação-assinatura permite a comunicação assíncrona e o desacoplamento entre os nós. Cada sensor ou atuador em um sistema robótico normalmente publica dados em um tópico, que pode então ser consumido por outros nós para processamento ou controle. Para o propósito deste guia, vamos nos concentrar em mensagens de Imagem, Profundidade e Nuvem de Pontos e tópicos de câmera.
Configurando o Ultralytics YOLO com ROS
Este guia foi testado usando este ambiente ROS, que é um fork do repositório ROS do ROSbot. Este ambiente inclui o pacote Ultralytics YOLO, um contêiner Docker para fácil configuração, pacotes ROS abrangentes e mundos Gazebo para testes rápidos. Ele foi projetado para funcionar com o Husarion ROSbot 2 PRO. Os exemplos de código fornecidos funcionarão em qualquer ambiente ROS Noetic/Melodic, incluindo simulação e mundo real.
Instalação de Dependências
Além do ambiente ROS, você precisará instalar as seguintes dependências:
-
Pacote ROS Numpy: É necessário para a conversão rápida entre mensagens de imagem ROS e arrays numpy.
pip install ros_numpy
-
Pacote Ultralytics:
pip install ultralytics
Use Ultralytics com ROS sensor_msgs/Image
O sensor_msgs/Image
tipo de mensagem é comumente usado em ROS para representar dados de imagem. Ele contém campos para codificação, altura, largura e dados de pixel, tornando-o adequado para transmitir imagens capturadas por câmeras ou outros sensores. As mensagens de imagem são amplamente utilizadas em aplicações robóticas para tarefas como percepção visual, detecção de objetos, e navegação.
Uso da Imagem Passo a Passo
O trecho de código a seguir demonstra como usar o pacote Ultralytics YOLO com ROS. Neste exemplo, nos inscrevemos em um tópico de câmera, processamos a imagem recebida usando YOLO e publicamos os objetos detectados em novos tópicos para detecção e segmentação.
Primeiro, importe as bibliotecas necessárias e instancie dois modelos: um para segmentação e um para detecção. Inicialize um nó ROS (com o nome ultralytics
) para habilitar a comunicação com o ROS master. Para garantir uma conexão estável, incluímos uma breve pausa, dando ao nó tempo suficiente para estabelecer a conexão antes de prosseguir.
import time
import rospy
from ultralytics import YOLO
detection_model = YOLO("yolo11m.pt")
segmentation_model = YOLO("yolo11m-seg.pt")
rospy.init_node("ultralytics")
time.sleep(1)
Inicializar dois tópicos ROS: um para detecção e um para segmentação. Estes tópicos serão utilizados para publicar as imagens anotadas, tornando-as acessíveis para processamento posterior. A comunicação entre os nós é facilitada através de sensor_msgs/Image
mensagens.
from sensor_msgs.msg import Image
det_image_pub = rospy.Publisher("/ultralytics/detection/image", Image, queue_size=5)
seg_image_pub = rospy.Publisher("/ultralytics/segmentation/image", Image, queue_size=5)
Finalmente, crie um assinante que escute as mensagens no /camera/color/image_raw
tópico e chama uma função de callback para cada nova mensagem. Esta função de callback recebe mensagens do tipo sensor_msgs/Image
, converte-os em um array numpy usando ros_numpy
, processa as imagens com os modelos YOLO previamente instanciados, anota as imagens e, em seguida, as publica de volta nos respectivos tópicos: /ultralytics/detection/image
para deteção e /ultralytics/segmentation/image
para segmentação.
import ros_numpy
def callback(data):
"""Callback function to process image and publish annotated images."""
array = ros_numpy.numpify(data)
if det_image_pub.get_num_connections():
det_result = detection_model(array)
det_annotated = det_result[0].plot(show=False)
det_image_pub.publish(ros_numpy.msgify(Image, det_annotated, encoding="rgb8"))
if seg_image_pub.get_num_connections():
seg_result = segmentation_model(array)
seg_annotated = seg_result[0].plot(show=False)
seg_image_pub.publish(ros_numpy.msgify(Image, seg_annotated, encoding="rgb8"))
rospy.Subscriber("/camera/color/image_raw", Image, callback)
while True:
rospy.spin()
Código completo
import time
import ros_numpy
import rospy
from sensor_msgs.msg import Image
from ultralytics import YOLO
detection_model = YOLO("yolo11m.pt")
segmentation_model = YOLO("yolo11m-seg.pt")
rospy.init_node("ultralytics")
time.sleep(1)
det_image_pub = rospy.Publisher("/ultralytics/detection/image", Image, queue_size=5)
seg_image_pub = rospy.Publisher("/ultralytics/segmentation/image", Image, queue_size=5)
def callback(data):
"""Callback function to process image and publish annotated images."""
array = ros_numpy.numpify(data)
if det_image_pub.get_num_connections():
det_result = detection_model(array)
det_annotated = det_result[0].plot(show=False)
det_image_pub.publish(ros_numpy.msgify(Image, det_annotated, encoding="rgb8"))
if seg_image_pub.get_num_connections():
seg_result = segmentation_model(array)
seg_annotated = seg_result[0].plot(show=False)
seg_image_pub.publish(ros_numpy.msgify(Image, seg_annotated, encoding="rgb8"))
rospy.Subscriber("/camera/color/image_raw", Image, callback)
while True:
rospy.spin()
Depuração
Depurar nós ROS (Robot Operating System) pode ser desafiador devido à natureza distribuída do sistema. Várias ferramentas podem auxiliar neste processo:
rostopic echo <TOPIC-NAME>
: Este comando permite visualizar as mensagens publicadas num tópico específico, ajudando a inspecionar o fluxo de dados.rostopic list
: Utilize este comando para listar todos os tópicos disponíveis no sistema ROS, dando-lhe uma visão geral dos fluxos de dados ativos.rqt_graph
: Esta ferramenta de visualização exibe o gráfico de comunicação entre os nós, fornecendo informações sobre como os nós estão interligados e como interagem.- Para visualizações mais complexas, como representações 3D, você pode usar RViz. O RViz (ROS Visualization) é uma poderosa ferramenta de visualização 3D para ROS. Permite visualizar o estado do seu robô e o seu ambiente em tempo real. Com o RViz, pode visualizar dados de sensores (por exemplo,
sensors_msgs/Image
), estados do modelo do robô e vários outros tipos de informação, tornando mais fácil depurar e entender o comportamento do seu sistema robótico.
Publicar Classes Detetadas com std_msgs/String
As mensagens ROS padrão também incluem std_msgs/String
mensagens. Em muitas aplicações, não é necessário republicar a imagem anotada inteira; em vez disso, apenas as classes presentes na visão do robô são necessárias. O exemplo a seguir demonstra como usar std_msgs/String
mensagens para republicar as classes detectadas no /ultralytics/detection/classes
tópico. Estas mensagens são mais leves e fornecem informações essenciais, tornando-as valiosas para várias aplicações.
Exemplo de Caso de Uso
Considere um robô de armazém equipado com uma câmera e detecção de objetos modelo de detecção. Em vez de enviar grandes imagens anotadas através da rede, o robô pode publicar uma lista de classes detetadas como std_msgs/String
mensagens. Por exemplo, quando o robô detecta objetos como "caixa", "palete" e "empilhadeira", ele publica essas classes no /ultralytics/detection/classes
tópico. Esta informação pode então ser usada por um sistema de monitorização central para rastrear o inventário em tempo real, otimizar o planeamento do percurso do robô para evitar obstáculos ou acionar ações específicas, como apanhar uma caixa detetada. Esta abordagem reduz a largura de banda necessária para a comunicação e concentra-se na transmissão de dados críticos.
Uso Passo a Passo da String
Este exemplo demonstra como usar o pacote Ultralytics YOLO com ROS. Neste exemplo, nós nos inscrevemos em um tópico de câmera, processamos a imagem recebida usando YOLO e publicamos os objetos detectados em um novo tópico. /ultralytics/detection/classes
usando std_msgs/String
mensagens. O ros_numpy
pacote é usado para converter a mensagem ROS Image em um array numpy para processamento com YOLO.
import time
import ros_numpy
import rospy
from sensor_msgs.msg import Image
from std_msgs.msg import String
from ultralytics import YOLO
detection_model = YOLO("yolo11m.pt")
rospy.init_node("ultralytics")
time.sleep(1)
classes_pub = rospy.Publisher("/ultralytics/detection/classes", String, queue_size=5)
def callback(data):
"""Callback function to process image and publish detected classes."""
array = ros_numpy.numpify(data)
if classes_pub.get_num_connections():
det_result = detection_model(array)
classes = det_result[0].boxes.cls.cpu().numpy().astype(int)
names = [det_result[0].names[i] for i in classes]
classes_pub.publish(String(data=str(names)))
rospy.Subscriber("/camera/color/image_raw", Image, callback)
while True:
rospy.spin()
Use Ultralytics com Imagens de Profundidade ROS
Além de imagens RGB, o ROS suporta imagens de profundidade, que fornecem informações sobre a distância dos objetos da câmera. As imagens de profundidade são cruciais para aplicações robóticas, como desvio de obstáculos, mapeamento 3D e localização.
Uma imagem de profundidade é uma imagem onde cada pixel representa a distância da câmera a um objeto. Ao contrário das imagens RGB que capturam cor, as imagens de profundidade capturam informações espaciais, permitindo que os robôs percebam a estrutura 3D de seu ambiente.
Obtenção de Imagens de Profundidade
As imagens de profundidade podem ser obtidas utilizando vários sensores:
- Câmeras Estéreo: Use duas câmeras para calcular a profundidade com base na disparidade da imagem.
- Câmeras Time-of-Flight (ToF): Medem o tempo que a luz leva para retornar de um objeto.
- Sensores de Luz Estruturada: Projetam um padrão e medem sua deformação em superfícies.
Usando YOLO com Imagens de Profundidade
No ROS, as imagens de profundidade são representadas por sensor_msgs/Image
tipo de mensagem, que inclui campos para codificação, altura, largura e dados de pixel. O campo de codificação para imagens de profundidade geralmente usa um formato como "16UC1", indicando um inteiro não assinado de 16 bits por pixel, onde cada valor representa a distância até o objeto. As imagens de profundidade são comumente usadas em conjunto com imagens RGB para fornecer uma visão mais abrangente do ambiente.
Usando YOLO, é possível extrair e combinar informações de imagens RGB e de profundidade. Por exemplo, o YOLO pode detectar objetos em uma imagem RGB, e essa detecção pode ser usada para identificar regiões correspondentes na imagem de profundidade. Isso permite a extração de informações de profundidade precisas para objetos detectados, aprimorando a capacidade do robô de entender seu ambiente em três dimensões.
Câmeras RGB-D
Ao trabalhar com imagens de profundidade, é essencial garantir que as imagens RGB e de profundidade estejam corretamente alinhadas. Câmeras RGB-D, como a série Intel RealSense, fornecem imagens RGB e de profundidade sincronizadas, facilitando a combinação de informações de ambas as fontes. Se estiver usando câmeras RGB e de profundidade separadas, é crucial calibrá-las para garantir um alinhamento preciso.
Utilização Passo a Passo da Profundidade
Neste exemplo, usamos YOLO para segmentar uma imagem e aplicar a máscara extraída para segmentar o objeto na imagem de profundidade. Isso nos permite determinar a distância de cada pixel do objeto de interesse do centro focal da câmera. Ao obter essas informações de distância, podemos calcular a distância entre a câmera e o objeto específico na cena. Comece importando as bibliotecas necessárias, criando um nó ROS e instanciando um modelo de segmentação e um tópico ROS.
import time
import rospy
from std_msgs.msg import String
from ultralytics import YOLO
rospy.init_node("ultralytics")
time.sleep(1)
segmentation_model = YOLO("yolo11m-seg.pt")
classes_pub = rospy.Publisher("/ultralytics/detection/distance", String, queue_size=5)
Em seguida, defina uma função de callback que processa a mensagem de imagem de profundidade recebida. A função aguarda as mensagens de imagem de profundidade e imagem RGB, converte-as em arrays numpy e aplica o modelo de segmentação à imagem RGB. Em seguida, extrai a máscara de segmentação para cada objeto detetado e calcula a distância média do objeto da câmara usando a imagem de profundidade. A maioria dos sensores tem uma distância máxima, conhecida como distância de recorte, além da qual os valores são representados como inf (np.inf
). Antes do processamento, é importante filtrar esses valores nulos e atribuir-lhes um valor de 0
. Finalmente, publica os objetos detetados juntamente com as suas distâncias médias até ao /ultralytics/detection/distance
tópico.
import numpy as np
import ros_numpy
from sensor_msgs.msg import Image
def callback(data):
"""Callback function to process depth image and RGB image."""
image = rospy.wait_for_message("/camera/color/image_raw", Image)
image = ros_numpy.numpify(image)
depth = ros_numpy.numpify(data)
result = segmentation_model(image)
all_objects = []
for index, cls in enumerate(result[0].boxes.cls):
class_index = int(cls.cpu().numpy())
name = result[0].names[class_index]
mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
obj = depth[mask == 1]
obj = obj[~np.isnan(obj)]
avg_distance = np.mean(obj) if len(obj) else np.inf
all_objects.append(f"{name}: {avg_distance:.2f}m")
classes_pub.publish(String(data=str(all_objects)))
rospy.Subscriber("/camera/depth/image_raw", Image, callback)
while True:
rospy.spin()
Código completo
import time
import numpy as np
import ros_numpy
import rospy
from sensor_msgs.msg import Image
from std_msgs.msg import String
from ultralytics import YOLO
rospy.init_node("ultralytics")
time.sleep(1)
segmentation_model = YOLO("yolo11m-seg.pt")
classes_pub = rospy.Publisher("/ultralytics/detection/distance", String, queue_size=5)
def callback(data):
"""Callback function to process depth image and RGB image."""
image = rospy.wait_for_message("/camera/color/image_raw", Image)
image = ros_numpy.numpify(image)
depth = ros_numpy.numpify(data)
result = segmentation_model(image)
all_objects = []
for index, cls in enumerate(result[0].boxes.cls):
class_index = int(cls.cpu().numpy())
name = result[0].names[class_index]
mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
obj = depth[mask == 1]
obj = obj[~np.isnan(obj)]
avg_distance = np.mean(obj) if len(obj) else np.inf
all_objects.append(f"{name}: {avg_distance:.2f}m")
classes_pub.publish(String(data=str(all_objects)))
rospy.Subscriber("/camera/depth/image_raw", Image, callback)
while True:
rospy.spin()
Use Ultralytics com ROS sensor_msgs/PointCloud2
O sensor_msgs/PointCloud2
tipo de mensagem é uma estrutura de dados usada em ROS para representar dados de nuvem de pontos 3D. Este tipo de mensagem é fundamental para aplicações robóticas, permitindo tarefas como mapeamento 3D, reconhecimento de objetos e localização.
Uma nuvem de pontos é uma coleção de pontos de dados definidos dentro de um sistema de coordenadas tridimensional. Esses pontos de dados representam a superfície externa de um objeto ou uma cena, capturados por meio de tecnologias de digitalização 3D. Cada ponto na nuvem tem X
, Y
, e Z
coordenadas, que correspondem à sua posição no espaço, e podem também incluir informações adicionais como cor e intensidade.
Frame de referência
Ao trabalhar com sensor_msgs/PointCloud2
, é essencial considerar o referencial do sensor do qual os dados da nuvem de pontos foram adquiridos. A nuvem de pontos é inicialmente capturada no referencial do sensor. Você pode determinar este referencial ouvindo o /tf_static
tópico. No entanto, dependendo dos requisitos específicos da sua aplicação, poderá ser necessário converter a nuvem de pontos para outro referencial. Esta transformação pode ser alcançada usando o tf2_ros
pacote, que fornece ferramentas para gerenciar quadros de coordenadas e transformar dados entre eles.
Obtenção de Nuvens de Pontos
Nuvens de pontos podem ser obtidas usando vários sensores:
- LIDAR (Detecção de Luz e Alcance): Usa pulsos de laser para medir distâncias até objetos e criar mapas 3D de alta precisão.
- Câmeras de Profundidade: Capturam informações de profundidade para cada pixel, permitindo a reconstrução 3D da cena.
- Câmeras Estéreo: Utilize duas ou mais câmeras para obter informações de profundidade através da triangulação.
- Scanners de Luz Estruturada: Projete um padrão conhecido em uma superfície e meça a deformação para calcular a profundidade.
Usando YOLO com Nuvens de Pontos
Para integrar o YOLO com sensor_msgs/PointCloud2
mensagens type, podemos empregar um método semelhante ao usado para mapas de profundidade. Ao aproveitar as informações de cor incorporadas na nuvem de pontos, podemos extrair uma imagem 2D, realizar a segmentação nesta imagem usando YOLO e, em seguida, aplicar a máscara resultante aos pontos tridimensionais para isolar o objeto 3D de interesse.
Para lidar com nuvens de pontos, recomendamos o uso do Open3D (pip install open3d
), uma biblioteca Python amigável. O Open3D fornece ferramentas robustas para gerenciar estruturas de dados de nuvens de pontos, visualizá-las e executar operações complexas de forma integrada. Esta biblioteca pode simplificar significativamente o processo e melhorar nossa capacidade de manipular e analisar nuvens de pontos em conjunto com a segmentação baseada em YOLO.
Uso Passo a Passo de Nuvens de Pontos
Importe as bibliotecas necessárias e instancie o modelo YOLO para segmentação.
import time
import rospy
from ultralytics import YOLO
rospy.init_node("ultralytics")
time.sleep(1)
segmentation_model = YOLO("yolo11m-seg.pt")
Criar uma função pointcloud2_to_array
, que transforma um sensor_msgs/PointCloud2
mensagem em dois arrays numpy. O sensor_msgs/PointCloud2
mensagens contêm n
pontos com base no(a) width
e height
da imagem adquirida. Por exemplo, um 480 x 640
a imagem terá 307,200
pontos. Cada ponto inclui três coordenadas espaciais (xyz
) e a cor correspondente em RGB
formato. Estes podem ser considerados como dois canais separados de informação.
A função retorna o xyz
coordenadas e RGB
valores no formato da resolução original da câmera (width x height
). A maioria dos sensores tem uma distância máxima, conhecida como distância de recorte, além da qual os valores são representados como inf (np.inf
). Antes do processamento, é importante filtrar esses valores nulos e atribuir-lhes um valor de 0
.
import numpy as np
import ros_numpy
def pointcloud2_to_array(pointcloud2: PointCloud2) -> tuple:
"""
Convert a ROS PointCloud2 message to a numpy array.
Args:
pointcloud2 (PointCloud2): the PointCloud2 message
Returns:
(tuple): tuple containing (xyz, rgb)
"""
pc_array = ros_numpy.point_cloud2.pointcloud2_to_array(pointcloud2)
split = ros_numpy.point_cloud2.split_rgb_field(pc_array)
rgb = np.stack([split["b"], split["g"], split["r"]], axis=2)
xyz = ros_numpy.point_cloud2.get_xyz_points(pc_array, remove_nans=False)
xyz = np.array(xyz).reshape((pointcloud2.height, pointcloud2.width, 3))
nan_rows = np.isnan(xyz).all(axis=2)
xyz[nan_rows] = [0, 0, 0]
rgb[nan_rows] = [0, 0, 0]
return xyz, rgb
Em seguida, inscreva-se no /camera/depth/points
tópico para receber a mensagem da nuvem de pontos e converter o sensor_msgs/PointCloud2
mensagem em arrays numpy contendo as coordenadas XYZ e os valores RGB (usando o pointcloud2_to_array
função). Processe a imagem RGB usando o modelo YOLO para extrair objetos segmentados. Para cada objeto detectado, extraia a máscara de segmentação e aplique-a tanto à imagem RGB quanto às coordenadas XYZ para isolar o objeto no espaço 3D.
O processamento da máscara é direto, pois consiste em valores binários, com 1
indicando a presença do objeto e 0
indicando a ausência. Para aplicar a máscara, basta multiplicar os canais originais pela máscara. Esta operação isola efetivamente o objeto de interesse dentro da imagem. Finalmente, crie um objeto de nuvem de pontos Open3D e visualize o objeto segmentado no espaço 3D com as cores associadas.
import sys
import open3d as o3d
ros_cloud = rospy.wait_for_message("/camera/depth/points", PointCloud2)
xyz, rgb = pointcloud2_to_array(ros_cloud)
result = segmentation_model(rgb)
if not len(result[0].boxes.cls):
print("No objects detected")
sys.exit()
classes = result[0].boxes.cls.cpu().numpy().astype(int)
for index, class_id in enumerate(classes):
mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
mask_expanded = np.stack([mask, mask, mask], axis=2)
obj_rgb = rgb * mask_expanded
obj_xyz = xyz * mask_expanded
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(obj_xyz.reshape((ros_cloud.height * ros_cloud.width, 3)))
pcd.colors = o3d.utility.Vector3dVector(obj_rgb.reshape((ros_cloud.height * ros_cloud.width, 3)) / 255)
o3d.visualization.draw_geometries([pcd])
Código completo
import sys
import time
import numpy as np
import open3d as o3d
import ros_numpy
import rospy
from sensor_msgs.msg import PointCloud2
from ultralytics import YOLO
rospy.init_node("ultralytics")
time.sleep(1)
segmentation_model = YOLO("yolo11m-seg.pt")
def pointcloud2_to_array(pointcloud2: PointCloud2) -> tuple:
"""
Convert a ROS PointCloud2 message to a numpy array.
Args:
pointcloud2 (PointCloud2): the PointCloud2 message
Returns:
(tuple): tuple containing (xyz, rgb)
"""
pc_array = ros_numpy.point_cloud2.pointcloud2_to_array(pointcloud2)
split = ros_numpy.point_cloud2.split_rgb_field(pc_array)
rgb = np.stack([split["b"], split["g"], split["r"]], axis=2)
xyz = ros_numpy.point_cloud2.get_xyz_points(pc_array, remove_nans=False)
xyz = np.array(xyz).reshape((pointcloud2.height, pointcloud2.width, 3))
nan_rows = np.isnan(xyz).all(axis=2)
xyz[nan_rows] = [0, 0, 0]
rgb[nan_rows] = [0, 0, 0]
return xyz, rgb
ros_cloud = rospy.wait_for_message("/camera/depth/points", PointCloud2)
xyz, rgb = pointcloud2_to_array(ros_cloud)
result = segmentation_model(rgb)
if not len(result[0].boxes.cls):
print("No objects detected")
sys.exit()
classes = result[0].boxes.cls.cpu().numpy().astype(int)
for index, class_id in enumerate(classes):
mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
mask_expanded = np.stack([mask, mask, mask], axis=2)
obj_rgb = rgb * mask_expanded
obj_xyz = xyz * mask_expanded
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(obj_xyz.reshape((ros_cloud.height * ros_cloud.width, 3)))
pcd.colors = o3d.utility.Vector3dVector(obj_rgb.reshape((ros_cloud.height * ros_cloud.width, 3)) / 255)
o3d.visualization.draw_geometries([pcd])
FAQ
O que é o Robot Operating System (ROS)?
O Robot Operating System (ROS) é um framework de código aberto comumente usado em robótica para ajudar os desenvolvedores a criar aplicações de robôs robustas. Ele fornece uma coleção de bibliotecas e ferramentas para construir e interagir com sistemas robóticos, permitindo um desenvolvimento mais fácil de aplicações complexas. O ROS suporta a comunicação entre nós usando mensagens sobre tópicos ou serviços.
Como integrar o Ultralytics YOLO com o ROS para detecção de objetos em tempo real?
A integração do Ultralytics YOLO com ROS envolve a configuração de um ambiente ROS e o uso do YOLO para processamento de dados de sensores. Comece instalando as dependências necessárias, como ros_numpy
e Ultralytics YOLO:
pip install ros_numpy ultralytics
Em seguida, crie um nó ROS e subscreva um tópico de imagem para processar os dados recebidos. Aqui está um exemplo mínimo:
import ros_numpy
import rospy
from sensor_msgs.msg import Image
from ultralytics import YOLO
detection_model = YOLO("yolo11m.pt")
rospy.init_node("ultralytics")
det_image_pub = rospy.Publisher("/ultralytics/detection/image", Image, queue_size=5)
def callback(data):
array = ros_numpy.numpify(data)
det_result = detection_model(array)
det_annotated = det_result[0].plot(show=False)
det_image_pub.publish(ros_numpy.msgify(Image, det_annotated, encoding="rgb8"))
rospy.Subscriber("/camera/color/image_raw", Image, callback)
rospy.spin()
O que são tópicos ROS e como são usados no Ultralytics YOLO?
Os tópicos ROS facilitam a comunicação entre nós numa rede ROS, utilizando um modelo de publicação-subscrição. Um tópico é um canal nomeado que os nós usam para enviar e receber mensagens de forma assíncrona. No contexto do Ultralytics YOLO, pode fazer com que um nó subscreva um tópico de imagem, processe as imagens usando YOLO para tarefas como deteção ou segmentação e publique os resultados em novos tópicos.
Por exemplo, subscreva um tópico de câmara e processe a imagem recebida para deteção:
rospy.Subscriber("/camera/color/image_raw", Image, callback)
Por que usar imagens de profundidade com Ultralytics YOLO em ROS?
Imagens de profundidade em ROS, representadas por sensor_msgs/Image
, fornecem a distância dos objetos da câmera, crucial para tarefas como prevenção de obstáculos, mapeamento 3D e localização. Por usando informações de profundidade juntamente com imagens RGB, os robôs podem entender melhor seu ambiente 3D.
Com o YOLO, você pode extrair máscaras de segmentação de imagens RGB e aplicar essas máscaras a imagens de profundidade para obter informações precisas de objetos 3D, melhorando a capacidade do robô de navegar e interagir com o ambiente.
Como posso visualizar nuvens de pontos 3D com YOLO em ROS?
Para visualizar nuvens de pontos 3D em ROS com YOLO:
- Converter
sensor_msgs/PointCloud2
mensagens para arrays numpy. - Use YOLO para segmentar imagens RGB.
- Aplique a máscara de segmentação à nuvem de pontos.
Aqui está um exemplo usando o Open3D para visualização:
import sys
import open3d as o3d
import ros_numpy
import rospy
from sensor_msgs.msg import PointCloud2
from ultralytics import YOLO
rospy.init_node("ultralytics")
segmentation_model = YOLO("yolo11m-seg.pt")
def pointcloud2_to_array(pointcloud2):
pc_array = ros_numpy.point_cloud2.pointcloud2_to_array(pointcloud2)
split = ros_numpy.point_cloud2.split_rgb_field(pc_array)
rgb = np.stack([split["b"], split["g"], split["r"]], axis=2)
xyz = ros_numpy.point_cloud2.get_xyz_points(pc_array, remove_nans=False)
xyz = np.array(xyz).reshape((pointcloud2.height, pointcloud2.width, 3))
return xyz, rgb
ros_cloud = rospy.wait_for_message("/camera/depth/points", PointCloud2)
xyz, rgb = pointcloud2_to_array(ros_cloud)
result = segmentation_model(rgb)
if not len(result[0].boxes.cls):
print("No objects detected")
sys.exit()
classes = result[0].boxes.cls.cpu().numpy().astype(int)
for index, class_id in enumerate(classes):
mask = result[0].masks.data.cpu().numpy()[index, :, :].astype(int)
mask_expanded = np.stack([mask, mask, mask], axis=2)
obj_rgb = rgb * mask_expanded
obj_xyz = xyz * mask_expanded
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(obj_xyz.reshape((-1, 3)))
pcd.colors = o3d.utility.Vector3dVector(obj_rgb.reshape((-1, 3)) / 255)
o3d.visualization.draw_geometries([pcd])
Esta abordagem fornece uma visualização 3D de objetos segmentados, útil para tarefas como navegação e manipulação em aplicações de robótica.