Criando um Publisher e um Subscriber na ROS 2
Publisher e Subscriber
Agora que você já sabe como criar um novo pacote, vamos aprender como publicar e assinar tópicos para enviar/receber mensagens.
Primeiro, algumas definições:
- Subscriber: nó que recebe mensagens de um tópico específico (assina no tópico).
- Publisher: nó que envia mensagens para um tópico específico (publica no tópico).
Na ROS 2, quando um nó publica uma mensagem em um tópico, todos os nós assinantes nesse tópico a recebem. O gif abaixo ilustra esse processo:
Criando um Publisher
Abra um novo terminal (Ctrl+Alt+T
) e inicie o VS Code no diretório do pacote my_package
:
cd ~/colcon_ws/src/my_package
code .
Crie um arquivo chamado first_node.py
dentro da pasta my_package/
(essa pasta contém os arquivos Python executados como nós ROS):
cd my_package
touch first_node.py
chmod +x *.py
Os comandos acima fazem o seguinte:
cd my_package
: entra na pasta do módulo Python do pacotemy_package
.touch first_node.py
: cria o arquivo do nó.chmod +x *.py
: concede permissão de execução aos arquivos Python (necessário para executá‑los como nós). Lembre-se sempre de usar este comando após criar um novo arquivo Python que será executado como nó ROS.
Abra first_node.py
e cole o código abaixo:
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
class FirstNode(Node):
def __init__(self):
super().__init__('first_node')
self.vel_pub = self.create_publisher(Twist, '/cmd_vel', 10)
self.timer = self.create_timer(0.25, self.control)
def control(self):
msg = Twist()
msg.linear.x = 0.2 # m/s
self.vel_pub.publish(msg)
def main(args=None):
rclpy.init(args=args)
node = FirstNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Explicação do Código
super().__init__('first_node')
: cria um nó chamadofirst_node
.-
self.create_publisher(Twist, '/cmd_vel', 10)
: recebe três argumentos:-
Twist
: tipo da mensagem que será publicada. -
'/cmd_vel'
: nome do tópico que será publicado. -
10
: tamanho da fila de mensagens. Este argumento é opcional e o valor padrão é 10.
-
-
self.create_timer(0.25, self.control)
: recebe dois argumentos:-
0.25
: tempo entre execuções da funçãoself.control
. -
self.control
: função que será executada a cada 0.25 segundos.
-
A função self.control
será chamada a cada 0.25 segundos, e em cada chamada, ela cria uma mensagem do tipo Twist
, altera o valor da velocidade linear para 0.2 e publica a mensagem no tópico cmd_vel
.
Por fim, a função main
faz o seguinte:
-
rclpy.init(args=args)
: inicializa o módulorclpy
(ROS Client Library for Python). -
first_node = FirstNode()
: cria uma instância da classeFirstNode
. -
rclpy.spin(first_node)
: mantém o nó em execução até que ele seja finalizado. -
first_node.destroy_node()
: finaliza o nó. -
rclpy.shutdown()
: finaliza o módulorclpy
.
Para executar o nó, precisamos fazer duas coisas:
- Criar uma entrada em
setup.py
para expor o executável. - Compilar o pacote e atualizar o ambiente.
Configurando o Arquivo setup.py
Abra setup.py
e garanta que a entrada a seguir exista dentro de entry_points -> console_scripts
:
entry_points={
'console_scripts': [
'first_node = my_package.first_node:main', # Adiciona o nó first_node
],
},
Essa linha cria o comando first_node
que executa a função main
em my_package/first_node.py
.
Rodando o Nó
Atenção
Para evitar conflito, não rode o teleop_keyboard
ao mesmo tempo que este publisher no mesmo tópico. Caso rode ambos, as mensagens podem competir.
Compile e rode apenas o pacote my_package
:
cd ~/colcon_ws
colcon build --packages-select my_package
source ~/colcon_ws/install/setup.bash
Dessa vez, enviamos o argumento --packages-select
para o comando colcon build
. Este argumento faz com que o comando compile apenas o pacote my_package
.
Rode o nó com o comando abaixo: IMPORTANTE: Certifique-se de que o simulador está rodando antes de executar o comando abaixo!
ros2 run my_package first_node
Criando um Subscriber
Com o first_node
em execução, o robô publicará comandos no /cmd_vel
. Agora criaremos um nó que assina o tópico /odom
e imprime a posição recebida.
Crie second_node.py
em my_package/
e torne o arquivo executável (como feito para o first_node.py
).
Antes, confirme o tipo do tópico odom
:
ros2 topic info /odom
O tópico /odom
usa mensagens do tipo nav_msgs/msg/Odometry
. Abra second_node.py
e adicione o código:
import rclpy
from rclpy.node import Node
from nav_msgs.msg import Odometry
from rclpy.qos import ReliabilityPolicy, QoSProfile
class SecondNode(Node):
def __init__(self):
super().__init__('second_node')
self.x = 0.0
self.y = 0.0
self.odom_sub = self.create_subscription(
Odometry,
'/odom',
self.odom_callback,
QoSProfile(depth=10, reliability=ReliabilityPolicy.RELIABLE)
)
self.timer = self.create_timer(0.25, self.control)
def odom_callback(self, msg: Odometry):
self.x = msg.pose.pose.position.x
self.y = msg.pose.pose.position.y
def control(self):
print(f'Posição x: {self.x:.3f}')
print(f'Posição y: {self.y:.3f}\n')
def main(args=None):
rclpy.init(args=args)
node = SecondNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Explicação do Código
-
create_subscription(...)
: assina o tópico/odom
recebendo mensagensOdometry
. -
Tipo:
Odometry
- obtido comros2 topic info /odom
. - Tópico:
'/odom'
- nome do tópico assinado. - Callback:
self.odom_callback
- executada a cada mensagem recebida. - QoS:
QoSProfile(depth=10, reliability=ReliabilityPolicy.RELIABLE)
- perfil de qualidade de serviço (opcional). odom_callback
: atualizax
ey
com a posição do robô.control
: imprime periodicamente as posições.
Dica
Você pode substituir print(...)
por self.get_logger().info(...)
para usar o sistema de logs do ROS 2.
Configurando o Arquivo setup.py
Agora precisamos configurar o arquivo setup.py
para que a ROS consiga encontrar o nó e então compilar o pacote novamente. Para isso, abra o arquivo setup.py
e adicione a seguinte linha dentro do dicionário entry_points
na chave console_scripts
:
'second_node = my_package.second_node:main',
Rodando o Nó
Uma vez que o arquivo setup.py
foi configurado, compile o pacote novamente com o mesmo comando que usamos anteriormente e rode o nó first_node.py
com o comando mencionado anteriormente:
cd ~/colcon_ws
colcon build --packages-select my_package
source ~/colcon_ws/install/setup.bash
ros2 run my_package first_node
E agora, em um novo terminal, execute o comando abaixo para rodar o nó second_node.py
:
ros2 run my_package second_node
Você deverá ver as posições x e y do robô sendo impressas a cada 0,25 segundos. Mova o robô e observe as mudanças nos valores.