Navegação com move_base no ROS: Planejamento Global e Local

Este artigo explora o pacote move_base do ROS, um componente crucial para a navegação autônoma de robôs. Ele integra algoritmos de planejamento de caminho global e local para guiar o robô do ponto de partida até o destino, adaptando-se a obstáculos.

Esturtura de Dados para Metas

O ROS utiliza a estrutura MoveBaseActionGoal para definir os objetivos de navegação. Os campos mais importantes incluem a posição (x, y, z) e orientação (quaternion x, y, z, w) do alvo.


rosmsg show MoveBaseActionGoal

[move_base_msgs/MoveBaseActionGoal]:
std_msgs/Header header
  uint32 seq
  time stamp
  string frame_id
actionlib_msgs/GoalID goal_id
  time stamp
  string id
move_base_msgs/MoveBaseGoal goal
  geometry_msgs/PoseStamped target_pose
    std_msgs/Header header
      uint32 seq
      time stamp
      string frame_id
    geometry_msgs/Pose pose
      geometry_msgs/Point position
        float64 x
        float64 y
        float64 z
      geometry_msgs/Quaternion orientation
        float64 x
        float64 y
        float64 z
        float64 w

Configuração de Parâmetros

O move_base requer a configuração de diversos parâmetros, como raio do robô, distância de alcance do objetivo, velocidade e custos de operação. Esses parâmetros são geralmente definidos em arquivos YAML, como:

  • base_local_planner_params.yaml
  • costmap_common_params.yaml
  • global_costmap_params.yaml
  • local_costmap_params.yaml

Planejamento de Caminho Global

O planejamento global, realizado por pacotes como navfn, determina a rota geral do robô até o destino. Algoritmos como Dijkstra ou A* são empregados para encontrar o caminho de menor custo no mapa de ocupação (costmap).

Planejamento de Caminho Local

O planejamento local, frequentemente implementado pelo pacote base_local_planner, é responsável por gerar comandos de velocidade (linear e angular) em tempo real para que o robô siga o caminho global e evite obstáculos imediatos. Algoritmos como Trajectory Rollout e Dynamic Window Approach (DWA) são utilizados:

  1. Amostragem de estados de velocidade do robô (dx, dy, dtheta).
  2. Simulação de trajetórias para cada estado amostrado.
  3. Avaliação das trajetórias com base em critérios como colisão e tempo.
  4. Seleção da trajetória ótima.
  5. Repetição do processo.

Simulação com ArbotiX: Definindo Metas Manuais

Para testes, pode-se usar um ambiente simulado com o ArbotiX. Inicie o simulador e o move_base com um mapa (por exemplo, um mapa em branco):


# Iniciar o simulador do robô
roslaunch rbx1_bringup fake_turtlebot.launch

# Iniciar o move_base com um mapa em branco
roslaunch rbx1_nav fake_move_base_blank_map.launch

Visualize o robô e o ambiente em RViz:


# Para versões Fuerte:
# rosrun rviz rviz -d `rospack find rbx1_nav`/nav_fuerte.vcg
# Para versões Indigo/Kinetic:
rosrun rviz rviz -d `rospack find rbx1_nav`/nav.rviz

É possível enviar metas diretamente via tópico /move_base_simple/goal:


# Mover 1 metro para frente
rostopic pub /move_base_simple/goal geometry_msgs/PoseStamped \
'{ header: { frame_id: "base_link" }, pose: { position: { x: 1.0, y: 0, z: 0 }, orientation: { x: 0, y: 0, z: 0, w: 1 } } }'

# Mover de volta à origem
rostopic pub /move_base_simple/goal geometry_msgs/PoseStamped \
'{ header: { frame_id: "map" }, pose: { position: { x: 0, y: 0, z: 0 }, orientation: { x: 0, y: 0, z: 0, w: 1 } } }'

Em RViz, a linha azul rerpesenta o caminho global planejado, e as setas vermelhas indicam o caminho local em tempo real. A opção 2D Nav Goal no RViz permite definir um ponto de destino interativamente.

Simulação com Obstáculos

Para testar o comportamento com obstáculos, utilize um lançamento que inclua um mapa com barreiras:


roslaunch rbx1_nav fake_move_base_obstacle.launch

Ao executar um script de navegação (como o move_base_square.py que faz o robô percorrer um quadrado), observa-se que o move_base ajusta o caminho global para contornar os obstáculos:


#!/usr/bin/env python
import roslib; roslib.load_manifest('rbx1_nav')
import rospy
import actionlib
from actionlib_msgs.msg import *
from geometry_msgs.msg import Pose, Point, Quaternion, Twist
from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal
from tf.transformations import quaternion_from_euler
from visualization_msgs.msg import Marker
from math import radians, pi

class MoveBaseSquare:
    def __init__(self):
        rospy.init_node('nav_test', anonymous=False)
        
        rospy.on_shutdown(self.shutdown)
        
        square_size = rospy.get_param("~square_size", 1.0) 
        
        quaternions = list()
        euler_angles = (pi/2, pi, 3*pi/2, 0)
        
        for angle in euler_angles:
            q_angle = quaternion_from_euler(0, 0, angle, axes='sxyz')
            q = Quaternion(*q_angle)
            quaternions.append(q)
        
        waypoints = list()
        waypoints.append(Pose(Point(square_size, 0.0, 0.0), quaternions[0]))
        waypoints.append(Pose(Point(square_size, square_size, 0.0), quaternions[1]))
        waypoints.append(Pose(Point(0.0, square_size, 0.0), quaternions[2]))
        waypoints.append(Pose(Point(0.0, 0.0, 0.0), quaternions[3]))
        
        self.init_markers()
        
        for waypoint in waypoints:           
            p = Point()
            p = waypoint.position
            self.markers.points.append(p)
            
        self.cmd_vel_pub = rospy.Publisher('cmd_vel', Twist)
        
        self.move_base = actionlib.SimpleActionClient("move_base", MoveBaseAction)
        
        rospy.loginfo("Waiting for move_base action server...")
        self.move_base.wait_for_server(rospy.Duration(60))
        rospy.loginfo("Connected to move base server")
        rospy.loginfo("Starting navigation test")
        
        i = 0
        while i < 4 and not rospy.is_shutdown():
            self.marker_pub.publish(self.markers)
            
            goal = MoveBaseGoal()
            goal.target_pose.header.frame_id = 'map'
            goal.target_pose.header.stamp = rospy.Time.now()
            goal.target_pose.pose = waypoints[i]
            
            self.move(goal)
            
            i += 1
        
    def move(self, goal):
            self.move_base.send_goal(goal)
            finished_within_time = self.move_base.wait_for_result(rospy.Duration(60)) 
            
            if not finished_within_time:
                self.move_base.cancel_goal()
                rospy.loginfo("Timed out achieving goal")
            else:
                state = self.move_base.get_state()
                if state == GoalStatus.SUCCEEDED:
                    rospy.loginfo("Goal succeeded!")
                    
    def init_markers(self):
        marker_scale = 0.2
        marker_lifetime = 0 
        marker_ns = 'waypoints'
        marker_id = 0
        marker_color = {'r': 1.0, 'g': 0.7, 'b': 1.0, 'a': 1.0}
        
        self.marker_pub = rospy.Publisher('waypoint_markers', Marker)
        
        self.markers = Marker()
        self.markers.ns = marker_ns
        self.markers.id = marker_id
        self.markers.type = Marker.SPHERE_LIST
        self.markers.action = Marker.ADD
        self.markers.lifetime = rospy.Duration(marker_lifetime)
        self.markers.scale.x = marker_scale
        self.markers.scale.y = marker_scale
        self.markers.color.r = marker_color['r']
        self.markers.color.g = marker_color['g']
        self.markers.color.b = marker_color['b']
        self.markers.color.a = marker_color['a']
        
        self.markers.header.frame_id = 'map'
        self.markers.header.stamp = rospy.Time.now()
        self.markers.points = list()
 
    def shutdown(self):
        rospy.loginfo("Stopping the robot...")
        self.move_base.cancel_goal()
        rospy.sleep(2)
        self.cmd_vel_pub.publish(Twist())
        rospy.sleep(1)
 
if __name__ == '__main__':
    try:
        MoveBaseSquare()
    except rospy.ROSInterruptException:
        rospy.loginfo("Navigation test finished.")

As áreas sombreadas ao redor dos obstáculos em RViz representam o "buffer" de segurança inflado (definido pelo parâmetro inflation_radius), garantindo que o robô mantenha uma distância segura. É possível reconfigurar dinamicamente os parâmetros do move_base durante a execução para ajustar o comportamento de navegação.

Tags: ROS move_base navegacao planejamento de caminho Robótica

Publicado em 8-12 02:43