Projeto para o Trabalho Avaliado 1 – Robôs Móveis: um robô autônomo que explora o ambiente, detecta uma bandeira e se posiciona para capturá-la, usando ROS 2 Humble.
• Estrutura • Visão Geral • Estratégia • Pacotes ROS 2 • Arquitetura & Algoritmos • Requisitos Atendidos • Compilar e Rodar • Contribuição • Licença • Membros
mission-ros2/
├── config/
├── description/ # Modelo Xacro do robô
├── launch/ # Arquivos .launch.py
│ ├── launch_integrado.launch.py #Arquivo de incicialização da simulação e dos pacotes de mapeamento e navegação.
├── models/
├── prm/
│ ├── mission_manager.py # Máquina de estados global
│ └── flag_servo.py # Servo-visão/LiDAR
├── resource/ # Recursos 3D
├── rviz/
├── test/
├── world/
├── images/
├── package.xml # Imagens para o README
├── setup.py
└── README.md| Componente (pasta/arquivo) | Tipo | Responsabilidade principal | Tópicos / recursos ROS 2 usados |
|---|---|---|---|
prm/mission_manager.py |
Nó Python | Máquina de estados global (Explorar → Econtra Bandeira → Flag_servo (aproximação) → Retorno à base). Salva pose inicial, gera metas ao Nav2 para exploração do mapa, chama flag_servo ao encontrar a bandeira, escuta /flag_servo_arrived para voltar à base. |
nav2_msgs/action/FollowWaypoints, geometry_msgs/PoseStamped, tf2_ros, /map, /odom |
scripts/flag_servo.py |
Nó Python | Câmera Segmentada + LiDAR: alinha e aproxima-se da bandeira, publica conclusão. | /robot_cam/colored_map, sensor_msgs/LaserScan, /cmd_vel, /flag_servo_enable, /flag_servo_arrived |
launch/launch_integrado.launch.py |
Launch file | Sobe SLAM Toolbox, Nav2 stack, launch inicializa_simulcao.launch.py e carrega_robo.launch.py. |
ros2 launch |
description/robot.urdf.xacro* |
Modelo | Robô diferencial com câmera, LiDAR e IMU; frames TF corretos. | robot_state_publisher, gazebo_ros_pkgs |
| Categoria | Pacote / ferramenta | Função na solução |
|---|---|---|
| Localização & Mapeamento | slam_toolbox |
SLAM síncrono 2 D; publica /map e TF map ↔ odom |
| Navegação | nav2_bt_navigator, nav2_controller, nav2_planner |
Planejamento global (A*) e controle local (DWB) |
| Visão | image_transport, cv_bridge, sensor_msgs |
Converte frame da câmera do Gazebo → OpenCV para segmentar a bandeira |
| Comunicação | rclpy, tf2_ros |
Nó Python e transformações de quadros |
| Simulação | gazebo_ros, prm_gazebo |
Carrega o mundo base, sensores de câmera e LiDAR |
stateDiagram-v2
direction LR
[*] --> EXPLORANDO : start
EXPLORANDO --> SERVO : bandeira_vista
SERVO --> RETORNO : alinhado
RETORNO --> [*] : missão_concluída
%% Anotações de cada estado
note right of EXPLORANDO
Frontier-based exploration
Nav2 FollowWaypoints
end note
note right of SERVO
Vision + LiDAR leituras
Controle (v,ω) proporcional para alinhamento
end note
note right of RETORNO
Goal = home_pose
Nav2 NavigateToPose
end note
Considerando a proposta apresentada pelo trabalho de permitir uma navegação universal em ambientes desconhecidos, a ideia inicial foi adotar o uso do SLAM (Simultanious Localization and Mapping), de forma a permitir que houvesse uma representação em ocuppancy grid do ambiente e facilitando a posterior navegação do robô pelo ambiente por meio de algorimos de planejamento de trajetória. Para isso, foi utilizado o pacote do Slam_ToolBox, que oferece funcionalidades de mapeamento síncrono/assíncrono com otimização em tempo real.
Durante esta etapa, o principal objetivo é explorar o mapa em busca da bandeira, de forma autônoma e baseando-se em algoritmos de planejamento de trajetória. Para isso, utilizamos uma adaptação nossa do algoritmo Incremental Frontier Exploration implementado no mission_manager.py, cuja implementação é definida abaixo. O resultado é a exploração completa do mapa até encontrar a bandeira.
- A cada atualização do tópico
/map, identificam-se as fronteiras entre regiões mapeadas e não mapeadas. - Aplica-se um filtro de segurança que ignora pontos muito próximos de obstáculos.
- As células são classificadas da seguinte forma:
-1: células ainda não mapeadas0: células livres100: células ocupadas
- Detectar pontos de fronteiras entre áreas conhecidas e desconhecidas.
- Aplicar filtro de segurança baseado na distância a obstáculos.
- Selecionar o ponto de fronteira mais distante do robô, de forma a garantir uma cobertura maior do mapa.
- Enviar esse ponto ao action server: Nav2
FollowWaypoints
Enquanto faz-se a exploração e o mapeamento por meio do algoritmo acima descrito, um callback do tópico da câmera de segmentação extrai a imagem e segmenta a região de interesse no espaço de cores HSV que corresponde à cor da bandeira [(HSV_MIN = (86, 0, 6) HSV_MAX = (94, 255, 255)]. O frame chega em BGR, é convertido para HSV com cv_bridge e recebe threshold. Então. uma heurística de área (> 1750 px) é aplicada para evitar falsos-positivos oriundos de ruídos, garantindo que a exploração pare quando a bandeira for encontrada e estiver suficientemente presente na imagem do robô
Esta parte da estratégia é controlada pelo nó flag_servo, que fica aguarando a publicação de "true" no tópico "/flag_servo_enable' para iniciar a busca da bandeira. O robô então extrai a imagem da bandeira segmentada da mesma forma que na estratégia anterior, calcula o centróide da bandeira na imagem e faz um controle proporcional para manter o alinhamento com a bandeira, enquanto verifica a distância dos feixes frontais do LIDAR.
Resumo do algoritmo:
Enquanto o contorno está presente:
- ω proporcional ao erro do centróide (pixels).
- v decrescente com a média dos 90 ° frontais do LiDAR (range-keeper).
- Quando
|erro| < 10 pxe distância< 0.35 m→ Entra num alinhamento final, para garantir que está completamente alinhado à bandeira. - publica
/flag_servo_arrived.
O primeiro TF map → odom capturado vira home_pose para voltar para a base. Após alinhamento, a máquina de estados envia esse PoseStamped ao Nav2 NavigateToPose; ao receber status SUCCEEDED a missão termina com o robô já na base.
- Matriz
occ_gridproveniente doslam_toolbox. - Máscara fronteira = célula livre com vizinho desconhecido.
- Filtro: distância > 0.2 m de obstáculos (distance transform).
- Conversão para o frame
map; remove duplicatas (< 0.5 m). - Seleciona a mais distante ⇒ envia ao action
FollowWaypoints.
hsv = cv.cvtColor(cv_img, cv.COLOR_BGR2HSV)
mask = cv.inRange(hsv, HSV_MIN, HSV_MAX)
cnts, _ = cv.findContours(mask, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)| Constante | Valor | Função / Observação |
|---|---|---|
v_max |
0.20 m/s | Velocidade linear inicial |
Kp |
0.004 | Ganho proporcional usado em ω (rad/s) |
stop_distance |
0.35 m | Distância alvo medida pelo LiDAR frontal |
dead_zone |
±10 px | Margem de erro aceitável no centróide |
Ciclo de controle a cada 100 ms:
- Segmentação da bandeira – obtém centróide e área do contorno ciano.
- Cálculo de erro angular – diferença horizontal entre centróide e centro da imagem.
- Controle de rotação – ω proporcional ao erro (ganho
Kp). - Controle de avanço –
vreduz‐se gradualmente conforme a média dos 90 ° frontais do LiDAR indica aproximação ao alvo; jamais cai abaixo de 0.06 m/s até parar emstop_distance. - Condição de parada – quando
|erro| < 10 pxe distância< 0.35 m, publica/flag_servo_arrived, sinalizando à máquina de estados que o robô está alinhado e posicionado.
- A primeira transformação
map → odomrecebida é armazenada comohome_pose. - No estado RETORNO, esse
PoseStampedé enviado como meta única ao Nav2 NavigateToPose. - Ao receber o status
SUCCEEDED, o nómission_managerencerra a missão.
Esta seção descreve como os requisitos propostos para o trabalho foram integralmente atendidos ao longo do desenvolvimento do sistema.
- O robô é capaz de navegar autonomamente em ambientes desconhecidos com o uso do SLAM Toolbox, construindo um mapa 2D em tempo real.
- A exploração do ambiente é guiada por uma estratégia de fronteiras (
Frontier-Based Exploration) customizada, presente no nómission_manager.py. - Ao encontrar a bandeira, o robô retorna com sucesso à sua posição inicial utilizando
Nav2 NavigateToPose.
- A bandeira é detectada por visão computacional com base em segmentação de cor HSV.
- O nó
flag_servo.pyrealiza o alinhamento e aproximação da bandeira utilizando:- Controle proporcional de rotação com base na posição da bandeira na imagem.
- Controle de avanço baseado nas leituras frontais do LiDAR.
- Quando a bandeira está centralizada e próxima, o robô sinaliza a conclusão da etapa de aproximação.
- Uma máquina de estados controla todas as etapas da missão:
- Exploração do ambiente.
- Detecção da bandeira.
- Aproximação com servo-visão.
- Retorno automático à base (posição inicial).
- A missão é completamente autônoma, sem necessidade de intervenção manual.
Como melhoria adicional ao projeto, realizamos uma redução proporcional nas dimensões do robô, facilitando sua locomoção em passagens estreitas e otimizando a navegação:
| Parâmetro | Valor Original | Valor Atual |
|---|---|---|
base_width |
0.31 m | 0.155 m |
base_length |
0.42 m | 0.21 m |
wheel_ygap |
0.025 m | 0.0125 m |
wheel_xoff |
0.12 m | 0.06 m |
caster_xoff |
0.14 m | 0.07 m |
camera_joint |
(0.215 0 0.05) | (0.105 0 0.05) |
As demais dimensões (altura, rodas, sensores, IMU) foram mantidas para preservar o comportamento físico e funcional do robô original.
Além disso, o sistema já está preparado para retornar automaticamente à base após a coleta, utilizando a pose salva no início da missão.
# 1. Clonar
cd ~/ros2_ws/src
git clone https://github.com/Vinicius-GN/prm/
# 2. Compilar
cd ~/ros2_ws
source /opt/ros/humble/setup.bash
rosdep install --from-paths src --ignore-src -r -y
colcon build
source install/setup.sh
# 3. Executar simulação
# Terminal A — mundo + robô
ros2 launch prm launch_integrado.launch.py
# Terminal D — Lógica do Servo
source install/setup.sh
ros2 run prm flag_servo
# Terminal C — Nav2 + missão
source install/setup.sh
ros2 run prm mission_manager
#Nota: O launch demora por volta de 20 segundos para inicializar todos os pacotes. Dessa forma, aguarde um tempo antes de rodar os nós da missão.
= Ao rodar o script do mission manager, alguns warnings aparecem. Aguarde alguns segundos até estabilizar e a exploração ser iniciada.
# Qualquer dúvida ou erros encontrados, falar com os membros do grupo.
Sugestões, bug-reports e pull requests são muito bem-vindos!
Abra uma Issue para discutir melhorias ou envie o PR diretamente.
| Nome | Número USP | GitHub |
|---|---|---|
| Vinicius Gustierrez Neves | 14749363 | @Vinicius-GN |
| Giovanna Herculano Tormena | 12674335 | |
| Guilheme Rebecche | 12550107 |



