Introdução ao Robot Operating System (ROS)

O Robot Operating System (ROS) é um framework de código aberto para desenvolvimento de software robótico. Apesar do nome, não é um sistema operacional, mas uma coleção de ferramentas, bibliotecas e convenções que simplificam a criação de sistemas robóticos complexos. Desenvolvido pela Open Robotics, o ROS é amplamente usado em robôs industriais, drones, veículos autônomos e pesquisa acadêmica.

Existem duas versões principais:

Exemplo: Iniciando o núcleo do ROS 2
source /opt/ros/humble/setup.bash
ros2 run demo_nodes_cpp talker

Explicação: O comando source configura o ambiente ROS 2, carregando variáveis e bibliotecas. O ros2 run executa o nó talker, que publica mensagens. Isso demonstra a modularidade do ROS, onde nós (programas) comunicam-se via mensagens.
Motivo: Iniciar o ROS 2 e executar um nó é o primeiro passo para entender sua arquitetura distribuída, permitindo comunicação entre componentes.
Resultado: O nó talker começa a publicar mensagens periódicas, visíveis em um terminal com ros2 topic echo.

Características do ROS

O ROS fornece um ecossistema robusto para robótica, com ferramentas para comunicação, visualização e integração de hardware.

🎯 Características principais

1

Modularidade

Divisão em nós (nodes) que executam tarefas específicas, comunicando-se via mensagens.

2

Comunicação Distribuída

Suporte a tópicos, serviços e ações para troca de dados entre processos, mesmo em máquinas diferentes.

3

Integração com Hardware

Drivers para sensores, atuadores e plataformas robóticas (ex.: LIDAR, câmeras).

4

Ferramentas de Visualização

RViz e Gazebo para simulação e depuração de robôs.

Explicação: A modularidade permite dividir um sistema robótico em componentes independentes (ex.: um nó para controle de motores, outro para processamento de sensores). A comunicação distribuída usa o middleware DDS em ROS 2, garantindo robustez em redes.
Motivo: Essas características tornam o ROS flexível, reutilizável e escalável, ideal para projetos complexos.
Resultado: Desenvolvedores podem criar, testar e integrar módulos sem reescrever código, acelerando o desenvolvimento.

Arquitetura do ROS 2

O ROS 2 é construído sobre o Data Distribution Service (DDS), um padrão de comunicação que suporta sistemas distribuídos e tempo real. Sua arquitetura é baseada em nós, tópicos, serviços e ações.

📌 Componentes principais

Listando nós ativos
ros2 node list

Explicação: O comando ros2 node list exibe todos os nós ativos no sistema ROS 2, identificando processos em execução. Cada nó é um programa independente que se comunica via tópicos ou serviços.
Motivo: Entender a arquitetura ajuda a projetar sistemas modulares, onde cada nó tem uma função clara, facilitando manutenção e escalabilidade.
Resultado: Executar o comando retorna uma lista de nós, como /talker ou /listener, se estiverem rodando.

Unidade executável que realiza uma tarefa específica.

Tópico

Canal para mensagens contínuas entre publicadores e assinantes.

Serviço

Comunicação bidirecional para tarefas rápidas.

Instalando o ROS 2 no Ubuntu

O ROS 2 é compatível com Linux (Ubuntu é recomendado), macOS e Windows, mas Ubuntu é o ambiente principal. Aqui, instalamos o ROS 2 Humble Hawksbill no Ubuntu 22.04.

⚙️ Passos de instalação

# Configurar fontes do ROS 2
sudo apt update && sudo apt install curl gnupg lsb-release
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null

# Instalar ROS 2 Humble
sudo apt update
sudo apt install ros-humble-desktop

# Configurar ambiente
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc
source ~/.bashrc

Explicação: Os comandos adicionam o repositório oficial do ROS 2, instalam a versão completa (ros-humble-desktop) com ferramentas como RViz, e configuram o ambiente para carregar o ROS 2 automaticamente. O pacote desktop inclui bibliotecas, ferramentas e exemplos.
Motivo: Configurar o ambiente corretamente é essencial para executar nós e usar ferramentas do ROS 2 sem erros.
Resultado: Após a instalação, comandos como ros2 run e ros2 topic estão disponíveis. Executar ros2 --version confirma a instalação (ex.: "humble").

Verificando a instalação
ros2 --version

Nós e Comunicação no ROS 2

Nós são os blocos fundamentais do ROS, cada um executando uma tarefa específica. Eles se comunicam via tópicos (publicador-assinante), serviços (requisição-resposta) ou ações (tarefas prolongadas).

📌 Criando um nó simples

# simple_node.py
import rclpy
from rclpy.node import Node

class SimpleNode(Node):
    def __init__(self):
        super().__init__('simple_node')
        self.get_logger().info('Nó simples iniciado!')

def main():
    rclpy.init()
    node = SimpleNode()
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()

Explicação: O script cria um nó chamado simple_node usando a biblioteca rclpy (ROS Client Library para Python). O método rclpy.spin mantém o nó ativo, e get_logger exibe uma mensagem no terminal. O ROS 2 usa o DDS para gerenciar comunicação entre nós.
Motivo: Criar nós é o primeiro passo para construir sistemas robóticos modulares, permitindo que cada componente seja desenvolvido e testado separadamente.
Resultado: Executar ros2 run my_package simple_node (após configurar um pacote ROS) inicia o nó, que imprime "Nó simples iniciado!" no terminal. O nó aparece em ros2 node list.

Executando o nó
ros2 run my_package simple_node

Publicadores e Assinantes

A comunicação via tópicos é o modelo mais comum no ROS, usando o padrão publicador-assinante. Um nó publicador envia mensagens a um tópico, e nós assinantes recebem essas mensagens.

📊 Exemplo: Publicador e Assinante

# talker.py
import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class Talker(Node):
    def __init__(self):
        super().__init__('talker')
        self.publisher = self.create_publisher(String, 'chatter', 10)
        self.timer = self.create_timer(1.0, self.timer_callback)

    def timer_callback(self):
        msg = String()
        msg.data = 'Olá, ROS 2!'
        self.publisher.publish(msg)
        self.get_logger().info('Publicando: "%s"' % msg.data)

def main():
    rclpy.init()
    node = Talker()
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()
# listener.py
import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class Listener(Node):
    def __init__(self):
        super().__init__('listener')
        self.subscription = self.create_subscription(
            String, 'chatter', self.listener_callback, 10)

    def listener_callback(self, msg):
        self.get_logger().info('Recebido: "%s"' % msg.data)

def main():
    rclpy.init()
    node = Listener()
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()

Explicação: O talker.py cria um nó que publica mensagens do tipo String no tópico chatter a cada segundo. O listener.py cria um nó que se inscreve no mesmo tópico, recebendo e exibindo as mensagens. O ROS 2 gerencia a comunicação via DDS.
Motivo: O modelo publicador-assinante é ideal para comunicação contínua, como dados de sensores ou comandos de controle.
Resultado: Executar ros2 run my_package talker e ros2 run my_package listener em terminais separados faz o listener exibir "Recebido: Olá, ROS 2!" a cada segundo.

Monitorando o tópico
ros2 topic echo /chatter

Exemplos Práticos com ROS 2

A seguir, exemplos práticos demonstram como usar o ROS 2 para tarefas robóticas comuns, incluindo simulações com Gazebo e CoppeliaSim.

📊 Exemplo 1: Publicando dados de sensor

# sensor_publisher.py
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Range
import random

class SensorPublisher(Node):
    def __init__(self):
        super().__init__('sensor_publisher')
        self.publisher = self.create_publisher(Range, 'sensor_range', 10)
        self.timer = self.create_timer(0.5, self.timer_callback)

    def timer_callback(self):
        msg = Range()
        msg.header.frame_id = 'sensor_frame'
        msg.range = random.uniform(0.1, 5.0)
        self.publisher.publish(msg)
        self.get_logger().info(f'Distância: {msg.range:.2f} m')

def main():
    rclpy.init()
    node = SensorPublisher()
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()

Explicação: O nó simula um sensor de distância, publicando mensagens do tipo Range no tópico sensor_range a cada 0.5 segundos. O campo range contém um valor aleatório entre 0.1 e 5.0 metros.
Motivo: Sensores são comuns em robótica, e este exemplo mostra como publicar dados para outros nós processarem.
Resultado: Executar o nó com ros2 run my_package sensor_publisher publica valores de distância, visíveis com ros2 topic echo /sensor_range.

🔄 Exemplo 2: Serviço de controle

# control_service.py
import rclpy
from rclpy.node import Node
from std_srvs.srv import Trigger

class ControlService(Node):
    def __init__(self):
        super().__init__('control_service')
        self.srv = self.create_service(Trigger, 'toggle_robot', self.toggle_callback)

    def toggle_callback(self, request, response):
        response.success = True
        response.message = 'Robô ligado!' if random.choice([True, False]) else 'Robô desligado!'
        self.get_logger().info(response.message)
        return response

def main():
    rclpy.init()
    node = ControlService()
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()

Explicação: O nó cria um serviço chamado toggle_robot que simula ligar/desligar um robô, retornando uma mensagem de sucesso. O serviço usa o tipo Trigger, que não requer dados de entrada.
Motivo: Serviços são úteis para comandos pontuais, como ativar/desativar componentes.
Resultado: Executar ros2 run my_package control_service inicia o serviço. Chamar ros2 service call /toggle_robot std_srvs/srv/Trigger retorna a resposta (ex.: "Robô ligado!").

🚗 Exemplo 3: Simulação de Robô Diferencial no Gazebo

Este exemplo simula um robô diferencial com LIDAR no Gazebo, controlado por comandos de velocidade via ROS 2 e visualizado no RViz.

Passos:

Explicação: O arquivo URDF define um robô diferencial com duas rodas e um LIDAR, usando plugins do Gazebo para movimentação (libgazebo_ros_diff_drive.so) e sensores (libgazebo_ros_laser.so). O arquivo de lançamento inicia o Gazebo, publica o modelo do robô, e conecta tópicos ROS 2 com o Gazebo via ros_gz_bridge. O comando ros2 topic pub envia velocidades lineares, fazendo o robô se mover. O RViz visualiza os dados do LIDAR no tópico /mybot/laser_scan.
Motivo: O Gazebo é amplamente usado para simulações realistas com física (gravidade, colisões) e sensores, permitindo testar algoritmos de navegação e percepção em um ambiente virtual antes de usar hardware real. A integração com ROS 2 facilita o desenvolvimento modular.[](https://docs.ros.org/en/humble/Tutorials/Advanced/Simulators/Gazebo/Gazebo.html)
Resultado: O Gazebo exibe o robô em um mundo vazio, movendo-se conforme os comandos de velocidade. O RViz mostra os dados do LIDAR como pontos em 2D, permitindo verificar a percepção do ambiente. O tópico /mybot/odom publica odometria, útil para navegação.

🤖 Exemplo 4: Simulação de Robô Diferencial no CoppeliaSim

Este exemplo simula um robô diferencial com sensor de proximidade no CoppeliaSim, controlado via Python e integrado com ROS 2.

Passos:

Explicação: O CoppeliaSim usa um script Lua anexado ao robô para gerenciar a comunicação ROS 2, publicando odometria no tópico /mybot/odom e dados do sensor de proximidade em /mybot/proximity, enquanto lê comandos de velocidade de /mybot/cmd_vel. O script Python publica comandos de velocidade constantes. O CoppeliaSim oferece cinco motores físicos (MuJoCo, Bullet, ODE, Vortex, Newton), permitindo simulações precisas de dinâmica e sensores. A cena é configurada com um robô Pioneer_p3dx, que inclui motores e um sensor de proximidade.
Motivo: O CoppeliaSim é versátil para simulações complexas, com suporte a múltiplos motores físicos e APIs (Python, Lua, C++), sendo ideal para educação e pesquisa. Sua integração com ROS 2 permite testar algoritmos em cenários customizados.[](https://pmc.ncbi.nlm.nih.gov/articles/PMC9415907/)
Resultado: O robô se move linearmente no CoppeliaSim, e o sensor de proximidade publica distâncias quando detecta objetos. No RViz, os dados de /mybot/proximity são visualizados como valores de distância, e /mybot/odom mostra a trajetória do robô.

ROS 1 vs ROS 2

O ROS 2 foi projetado para superar as limitações do ROS 1, especialmente em sistemas distribuídos e aplicações críticas.

Critério ROS 1 ROS 2
Middleware ROS Master (centralizado) DDS (distribuído)
Suporte a Tempo Real Limitado Robusto (DDS com QoS)
Plataformas Principalmente Linux Linux, Windows, macOS, embarcados
Comunicação Tópicos, Serviços Tópicos, Serviços, Ações
Licença BSD Apache 2.0

Explicação: O ROS 1 usa um mestre centralizado, tornando-o menos robusto para redes distribuídas. O ROS 2 elimina o mestre, usando DDS para comunicação descentralizada e suporte a qualidade de serviço (QoS).
Motivo: O ROS 2 é mais adequado para aplicações modernas, como robôs autônomos e IoT, onde confiabilidade e tempo real são críticos.
Resultado: Projetos novos devem adotar o ROS 2, enquanto o ROS 1 é mantido para sistemas legados.

Projeto Final: Simulador de Robô Móvel

Crie um sistema ROS 2 que simula um robô móvel, com um nó publicando comandos de velocidade e outro processando a posição do robô.

📋 Passos do projeto

# velocity_publisher.py
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist

class VelocityPublisher(Node):
    def __init__(self):
        super().__init__('velocity_publisher')
        self.publisher = self.create_publisher(Twist, 'cmd_vel', 10)
        self.timer = self.create_timer(1.0, self.timer_callback)

    def timer_callback(self):
        msg = Twist()
        msg.linear.x = 0.5  # Velocidade linear (m/s)
        msg.angular.z = 0.2  # Velocidade angular (rad/s)
        self.publisher.publish(msg)
        self.get_logger().info(f'Publicando velocidade: linear={msg.linear.x}, angular={msg.angular.z}')

def main():
    rclpy.init()
    node = VelocityPublisher()
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()
# position_calculator.py
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist, Pose

class PositionCalculator(Node):
    def __init__(self):
        super().__init__('position_calculator')
        self.subscription = self.create_subscription(Twist, 'cmd_vel', self.velocity_callback, 10)
        self.publisher = self.create_publisher(Pose, 'robot_pose', 10)
        self.x = 0.0
        self.y = 0.0
        self.theta = 0.0
        self.timer = self.create_timer(0.1, self.update_pose)

    def velocity_callback(self, msg):
        self.linear_vel = msg.linear.x
        self.angular_vel = msg.angular.z

    def update_pose(self):
        dt = 0.1
        self.x += self.linear_vel * dt * self.theta
        self.y += self.linear_vel * dt * self.theta
        self.theta += self.angular_vel * dt
        pose = Pose()
        pose.position.x = self.x
        pose.position.y = self.y
        pose.orientation.z = self.theta
        self.publisher.publish(pose)
        self.get_logger().info(f'Posição: x={self.x:.2f}, y={self.y:.2f}, theta={self.theta:.2f}')

def main():
    rclpy.init()
    node = PositionCalculator()
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()

Explicação: O velocity_publisher publica comandos de velocidade (Twist) no tópico cmd_vel. O position_calculator recebe esses comandos, calcula a posição do robô (x, y, theta) e publica no tópico robot_pose. A integração numérica simples atualiza a pose a cada 0.1s.
Motivo: Este projeto simula um robô móvel, um caso comum em robótica, ensinando comunicação entre nós e processamento de dados.
Resultado: Executar ambos os nós (após configurar o pacote) mostra a posição sendo atualizada no terminal. Usar ros2 topic echo /robot_pose exibe as mensagens de pose, e RViz pode visualizar o movimento.

Quiz Interativo

Teste seus conhecimentos sobre ROS: