Simulaciones - IvanCS-Chenfu/Gazebo GitHub Wiki

Una vez creado el URDF del robot, necesitaremos un archivo el cual inserte el robot en Gazebo usando el servicio /spawn_entity

Espinas_bot

En el archivo sim_box_bot.cpp

Librerías

#include <rclcpp/rclcpp.hpp>

Importa la API principal de ROS 2 en C++. Permite crear nodos, imprimir logs, usar servicios, etc.

#include <gazebo_msgs/srv/spawn_entity.hpp>

Este es el tipo de mensaje de servicio que se usa para insertar modelos en Gazebo. Es parte del paquete gazebo_msgs.

Servicio: /spawn_entity

Tipo: gazebo_msgs::srv::SpawnEntity

#include <ament_index_cpp/get_package_share_directory.hpp>

Esta utilidad de ROS 2 permite obtener la ruta absoluta del paquete my_box_robot para acceder al archivo espinas_bot.xacro.

#include <fstream>

Librería estándar de C++ para leer archivos como el URDF.

Clase

class BoxSpawner : public rclcpp::Node {

Definimos una clase que hereda de rclcpp::Node. Al instanciarse, este nodo:

Se conecta al servicio /spawn_entity

Lee el archivo URDF

Envía una petición para insertar el robot

BoxSpawner() : Node("box_spawner") {

Inicializa el nodo con el nombre box_spawner. Esto aparecerá en ROS 2 como nombre de nodo.

auto client = this->create_client<gazebo_msgs::srv::SpawnEntity>("/spawn_entity");

Crea un cliente para el servicio /spawn_entity (disponible si usas libgazebo_ros_factory.so en el mundo).

while (!client->wait_for_service(std::chrono::seconds(1))) {
    RCLCPP_INFO(this->get_logger(), "Esperando el servicio /spawn_entity...");
}

Este bucle espera a que el servicio esté disponible. Sin esto, podrías fallar si Gazebo no ha terminado de inicializarse.

auto request = std::make_shared<gazebo_msgs::srv::SpawnEntity::Request>();

Creamos una instancia del mensaje de tipo SpawnEntity::Request.

request->name = "espinas_bot";  // aqui

Nombre del modelo dentro de Gazebo. No es el nombre del archivo, es cómo se verá en el entorno simulado.

request->xml = read_urdf();

Cargamos el contenido del archivo .xacro (convertido a string). Este es el modelo que Gazebo insertará.

request->robot_namespace = "/espinas_bot";  // aqui

Este es el namespace de todos los topics del robot. Si usas esto, los topics serán como:

/espinas_bot/cmd_vel

/espinas_bot/joint_states

Esto evita colisiones si tienes múltiples robots en el mismo entorno.

request->initial_pose.position.x = 0.0;
request->initial_pose.position.y = 0.0;
request->initial_pose.position.z = 0.15;

Define la posición del robot en el mundo (x, y, z). z = 0.15 eleva el robot ligeramente sobre el suelo.

auto result = client->async_send_request(request);

Envía la petición de forma asíncrona al servicio de Gazebo.

if (rclcpp::spin_until_future_complete(this->get_node_base_interface(), result) ==
    rclcpp::FutureReturnCode::SUCCESS)

Esto hace que el nodo se quede bloqueado hasta que reciba una respuesta del servicio. Si el resultado es exitoso

Leer URDF

std::string read_urdf() {
    std::string urdf_path = ament_index_cpp::get_package_share_directory("my_box_robot") + "/urdf/espinas_bot.xacro";

Obtiene la ruta absoluta del archivo espinas_bot.xacro dentro del paquete my_box_robot.

    std::ifstream urdf_file(urdf_path);
    std::stringstream buffer;
    buffer << urdf_file.rdbuf();
    return buffer.str();
}

Lee todo el archivo y lo convierte en un string para enviarlo como request->xml.

Main

int main(int argc, char **argv) {
    rclcpp::init(argc, argv);
    rclcpp::spin(std::make_shared<BoxSpawner>());
    rclcpp::shutdown();
    return 0;
}

Inicializa ROS 2

Lanza el nodo BoxSpawner

Espera a que termine (spin)

Apaga ROS 2 correctamente

En el archivo CMAkeLists.txt

Para compilar este archivo necesitas:

add_executable(sim_box_bot src/sim_box_bot.cpp)

ament_target_dependencies(sim_box_bot
  rclcpp
  gazebo_msgs
  ament_index_cpp
)

En el archivo package.xml

Para compilar este archivo necesitas:

<depend>rclcpp</depend>
<depend>gazebo_msgs</depend>
<depend>ament_index_cpp</depend>

Lanzamiento del Nodo

from launch import LaunchDescription
from launch.actions import ExecuteProcess
from launch_ros.actions import Node
import os

Importa lo necesario de launch para ejecutar procesos

Importa Node de launch_ros.actions, que permite lanzar nodos ROS 2 directamente.

os se usa para construir rutas dinámicamente

def generate_launch_description():

Función principal que devuelve un LaunchDescription, es decir, la lista de cosas que se lanzarán con ros2 launch.

    world_path = os.path.join(
        os.getenv('HOME'),
        'ros', 'Gazebo_ws', 'src', 'my_box_robot', 'worlds', 'empty.world'
    )

Construye la ruta absoluta al archivo empty.world usando la variable de entorno HOME.

    return LaunchDescription([
        ExecuteProcess(
            cmd=[
                'gazebo',
                '--verbose',
                world_path,
                '-s', 'libgazebo_ros_factory.so'
            ],
            output='screen'
        )
    ])

Gazebo en modo detallado (--verbose)

El mundo empty.world

Con el plugin libgazebo_ros_factory.so (para usar /spawn_entity)

Node(
    package='my_box_robot',
    executable='sim_box_bot',
    name='spawn_bot_node',
    output='screen'
)

Este bloque lanza el ejecutable sim_box_bot del paquete my_box_robot.

package: el nombre del paquete

executable: el nombre del ejecutable compilado (debe coincidir con el de CMakeLists.txt)

name: el nombre del nodo que aparecerá en ros2 node list

output='screen': imprime los logs en pantalla

En el archivo CMAkeLists.txt

add_executable(sim_box_bot src/sim_box_bot.cpp)

ament_target_dependencies(sim_box_bot
  rclcpp
  gazebo_msgs
  ament_index_cpp
)

install(TARGETS sim_box_bot
  DESTINATION lib/${PROJECT_NAME}
)

Compila el nodo

Declara sus dependencias

Instala el ejecutable para que ros2 launch lo encuentre

install(DIRECTORY
  launch
  DESTINATION share/${PROJECT_NAME}/
)

Así se copia tu carpeta launch/ al entorno de instalación de ROS.

En el archivo package.xml

Asegúrate de tener:

<depend>rclcpp</depend>
<depend>gazebo_msgs</depend>
<depend>ament_index_cpp</depend>
⚠️ **GitHub.com Fallback** ⚠️