---
title: "GAZEBO. Cómo añadir un modelo URDF a una simulación"
description: "1. Introducción En este artículo vas a aprender cómo hacer aparecer (spawn) un modelo URDF en GAZEBO. Básicamente hay dos formas de hacerlo: Usando el servicio /spawn_entity del paquete gazebo_msgs. Usando el nodo spawn_entity.py  del paquete gazebo_ros. En los siguientes apartados vamos a ver como añadir a nuestra simulación un modelo URDF muy simple, consistente […]"
featured_image: "https://robotica-facil-con-ros2.es/wp-content/uploads/2022/08/id3828p-gazebo-spawn-urdf.png"
url: "https://robotica-facil-con-ros2.es/?p=3282"
date_modified: "2026-07-06T07:14:53+00:00"
---

### 1. Introducción

En este artículo vas a aprender cómo hacer aparecer (spawn) un modelo URDF en GAZEBO.

Básicamente hay dos formas de hacerlo:

    - Usando el servicio **/spawn\_entity **del paquete *gazebo\_msgs*.
    - Usando el nodo ***spawn\_entity.py***  del paquete *gazebo\_ros.*

En los siguientes apartados vamos a ver como añadir a nuestra simulación un modelo URDF muy simple, consistente únicamente en una esfera, empleando ambos métodos.

### 2 Mostrar el modelo URDF usando el servicio /spawn\_entity

Este servicio, que forma parte del paquete *gazebo\_msgs*, permite mostrar una entidad en Gazebo. La entidad no tiene por qué ser necesariamente el modelo de un objeto o un robot, pudiendo ser otros elementos de Gazebo, por ejemplo una fuente de luz.

En la siguiente imagen se muestra la información que hay que pasarle al servicio y cómo es su respuesta. 

![gazebo spawn_entity service](http://robotica-facil-con-ros2.es/wp-content/uploads/2022/08/id3828-spawn_entity-service.png)

#### 2.1 Desde la línea de comando

Desde el terminal podemos llamar al servicio /spawn\_entity mediante el comando ros2 service call.

Para ello primero lanzamos Gazebo si no lo hemos hecho:

ros2 launch gazebo_ros gazebo.launch.py

Y a continuación llamamos al servicio:

ros2 service call /spawn_entity &#039;gazebo_msgs/SpawnEntity&#039; &#039;{name: &quot;urdf_ball&quot;, xml: &quot;&lt;?xml version=\&quot;1.0\&quot; ?&gt;&lt;robot name=\&quot;will_be_ignored\&quot;&gt;&lt;link name=\&quot;link\&quot;&gt;&lt;visual&gt;&lt;geometry&gt;&lt;sphere radius=\&quot;0.5\&quot;/&gt;&lt;/geometry&gt;&lt;/visual&gt;&lt;collision&gt;&lt;geometry&gt;&lt;sphere radius=\&quot;0.5\&quot;/&gt;&lt;/geometry&gt;&lt;/collision&gt;&lt;inertial&gt;&lt;mass value=\&quot;1\&quot;/&gt;&lt;inertia ixx=\&quot;1\&quot; ixy=\&quot;0.0\&quot; ixz=\&quot;0.0\&quot; iyy=\&quot;1\&quot; iyz=\&quot;0.0\&quot; izz=\&quot;1\&quot;/&gt;&lt;/inertial&gt;&lt;/link&gt;&lt;/robot&gt;&quot;, robot_namespace: &quot;ball_namespace&quot;, initial_pose: {position: {x: 0.0, y: 0.0, z: 0.5}, orientation: {x: 0.0, y: 0.0, z: 0.0, w: 0.0}}, reference_frame: &quot;world&quot;}&#039;

#### 2.2 Desde un nodo ROS2

El siguiente código muestra un ejemplo de cómo llamar al servicio /spawn\_entity desde un nodo.

from gazebo_msgs.srv import SpawnEntity
import rclpy
from rclpy.node import Node


class ServiceClientAsync(Node):

    def __init__(self):
        super().__init__(&#039;service_client_async&#039;)
        self.client = self.create_client(SpawnEntity, &#039;spawn_entity&#039;)
        while not self.client.wait_for_service(timeout_sec=1.0):
            self.get_logger().info(&#039;service not available, waiting again...&#039;)


    def send_request(self, request_data):
        self.future = self.client.call_async(request_data)
        rclpy.spin_until_future_complete(self, self.future)
        return self.future.result()


def main(args=None):
    rclpy.init(args=args)

    request = SpawnEntity.Request()
    request.name = &quot;urdf_ball&quot;
    request.xml = &quot;&lt;?xml version=\&quot;1.0\&quot; ?&gt;&lt;robot name=\&quot;will_be_ignored\&quot;&gt;&lt;link name=\&quot;link\&quot;&gt;&lt;visual&gt;&lt;geometry&gt;&lt;sphere radius=\&quot;0.5\&quot;/&gt;&lt;/geometry&gt;&lt;/visual&gt;&lt;collision&gt;&lt;geometry&gt;&lt;sphere radius=\&quot;0.5\&quot;/&gt;&lt;/geometry&gt;&lt;/collision&gt;&lt;inertial&gt;&lt;mass value=\&quot;1\&quot;/&gt;&lt;inertia ixx=\&quot;1\&quot; ixy=\&quot;0.0\&quot; ixz=\&quot;0.0\&quot; iyy=\&quot;1\&quot; iyz=\&quot;0.0\&quot; izz=\&quot;1\&quot;/&gt;&lt;/inertial&gt;&lt;/link&gt;&lt;/robot&gt;&quot;
    request.robot_namespace = &quot;ball_namespace&quot;
    request.reference_frame = &quot;world&quot;
    request.initial_pose.position.x = 0.0
    request.initial_pose.position.y = 0.0
    request.initial_pose.position.z = 0.0
    request.initial_pose.orientation.x = 0.0
    request.initial_pose.orientation.y = 0.0
    request.initial_pose.orientation.z = 0.0
    request.initial_pose.orientation.w = 0.0

    service_client = ServiceClientAsync()
    response = service_client.send_request(request)

    service_client.get_logger().info(
        &#039;Result of spawing service: %s , %s&#039; %
        (response.success, response.status_message))

    service_client.destroy_node()
    rclpy.shutdown()


if __name__ == &#039;__main__&#039;:
    main()

### 3 Mostrar el modelo URDF usando el nodo spawn\_entity

Como ya hemos indicado, otra forma de añadir nuestro modelo URDF a una simulación de Gazebo es mediante el nodo ***spawn\_entity.py*** que forma parte del paquete *gazebo\_ros*.

Este nodo admite los argumentos que se indican en la siguiente tabla los cuales, entre otras cosas, permiten definir el origen del código XML que define nuestro modelo (admite tanto URDF como SDF) y su pose inicial.

![Gazebo argumentos de spawn_entity](http://robotica-facil-con-ros2.es/wp-content/uploads/2022/08/Gazebo-spawn_entity-arguments.png)
Argumentos admitidos por el nodo spawn_entity.py

#### 3.1 Desde la línea de comando

A continuación se muestran algunos ejemplos de cómo añadir un modelo a la simulación usando el nodo ***spawn\_entity.py***  con diferentes orígenes desde donde tomar la descripción XML del mismo.

###### Cargando los datos del modelo desde un archivo

ros2 run gazebo_ros spawn_entity.py -entity myentity -x 0 -y 0 -z 0 -R 0 -P 0 -Y 0 -file /path/to/entity/file

###### Cargando los datos del modelo usando la entrada stdin del terminal.

cat /path/to/entity/file | ros2 run gazebo_ros spawn_entity.py -entity new_model -x 0 -y 0 -z 0 -stdin

###### Cargando los datos del modelo desde la base de datos de Gazebo.

ros2 run gazebo_ros spawn_entity.py -entity TheArm -x 0 -y 0 -z 0 -R 0 -P 0 -Y 0 -database simple_arm

###### Cargando desde un topic los datos del modelo.

ros2 run gazebo_ros spawn_entity.py -entity a_ball -topic /robot_description

ros2 topic pub -1 --qos-durability transient_local /robot_description &#039;std_msgs/String&#039; &#039;{data: &quot;&lt;?xml version=\&quot;1.0\&quot; ?&gt;&lt;robot name=\&quot;will_be_ignored\&quot;&gt;&lt;link name=\&quot;link\&quot;&gt;&lt;visual&gt;&lt;geometry&gt;&lt;sphere radius=\&quot;0.5\&quot;/&gt;&lt;/geometry&gt;&lt;/visual&gt;&lt;collision&gt;&lt;geometry&gt;&lt;sphere radius=\&quot;0.5\&quot;/&gt;&lt;/geometry&gt;&lt;/collision&gt;&lt;inertial&gt;&lt;mass value=\&quot;1\&quot;/&gt;&lt;inertia ixx=\&quot;1\&quot; ixy=\&quot;0.0\&quot; ixz=\&quot;0.0\&quot; iyy=\&quot;1\&quot; iyz=\&quot;0.0\&quot; izz=\&quot;1\&quot;/&gt;&lt;/inertial&gt;&lt;/link&gt;&lt;/robot&gt;&quot;}&#039;

#### 3.2 Desde un archivo launch en ROS2

A continuación se muestra un ejemplo de código a incluir en un archivo launch para mostrar un modelo URDF en Gazebo, el cual realiza las siguientes acciones:

    1. Obtiene la ruta a nuestro archivo con la información URDF de nuestro modelo.
    2. Crea un objeto *launch.ros\_action.node* para ejecutar ***robot\_state publisher*** al cual le pasamos mediante  su  parámetro *robot\_description*  el  código  URDF, que obtenemos  ejecutando «xacro < urdf\_file >» mediante el uso de *Command*. El nodo *robot\_state\_publisher* se encarga de resolver la cinemática directa del modelo.
    3. Crea un objeto *launch.ros\_action.node* para ejecutar ***spawn\_entity.py***, que forma parte del paquete *gazebo\_ros*. Este script lee el código URDF que publica el nodo *robot\_state\_publisher* y lo muestra en Gazebo.
    4. Crea un objeto *IncludeLaunchDescription* para incluir la ejecución del archivo *launch* ***gazebo.launch.py***, del paquete *gazebo\_ros*, que lanza la ejecución de Gazebo.

import xacro, os
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import Command, PathJoinSubstitution
from launch_ros.substitutions import FindPackageShare
from launch_ros.actions import Node
from ament_index_python import get_package_prefix, get_package_share_directory


def generate_launch_description():

    # Establece los nombres de nuestro paquete y del archivo con el modelo.
    package_name = &#039;my_package&#039;
    urdf_file_name = &#039;my_model.urdf&#039;


    # DESCOMENTAR si el codigo URDF utiliza archivos mesh donde la ruta al
    # archivo de malla es relativa, indicandose de la forma: 
    # &lt;mesh filename =&quot;package://mi_paquete/..../archivo_mesh&quot; /&gt;
    # --------------------------------------------------------------------------
    #gazebo_model_path = os.path.join(get_package_prefix(package_name), &#039;share&#039;)


    # DESCOMENTAR si el codigo URDF utiliza archivos mesh donde la ruta al
    # archivo de malla es relativa a la ubicaci&oacute;n del archivo setup.py, 
    # indicandose de la forma: &lt;mesh filename =&quot;package://..../archivo_mesh&quot; /&gt;
    # --------------------------------------------------------------------------
    #gazebo_model_path = get_package_share_directory(package_name)


    # DESCOMENTAR si el codigo URDF utiliza archivos mesh donde la ruta al
    # archivo de malla se indica de forma relativa.
    # --------------------------------------------------------------------------
    #if &#039;GAZEBO_MODEL_PATH&#039; in os.environ:
    #    os.environ[&#039;GAZEBO_MODEL_PATH&#039;] += os.pathsep + gazebo_model_path
    #else:
    #    os.environ[&#039;GAZEBO_MODEL_PATH&#039;] =  gazebo_model_path


    # Obtiene la ruta al archivo que contiene la informaci&oacute;n de nuestro modelo.
    urdf_file_path = os.path.join(
        get_package_share_directory(package_name), &#039;urdf&#039;, urdf_file_name)


    # Crea y configura el nodo ROBOT_STATE_PUBLISHER, el cual lee el c&oacute;digo URDF 
    # del modelo y resuelve la cinem&aacute;tica directa del robot a partir de la 
    # informaci&oacute;n que proporciona el nodo JOINT_STATE_PUBLISHER_GUI. 
    # Utiliza Command para ejecutar xacro y obtener el c&oacute;digo URDF.
    node_robot_state_publisher = Node(
        package=&#039;robot_state_publisher&#039;,
        executable=&#039;robot_state_publisher&#039;,
        name=&#039;robot_state_publisher&#039;,
        parameters=[{&#039;robot_description&#039;: Command([&#039;xacro &#039;, urdf_file_path])}],
        arguments=[&#039;--ros-args&#039;, &#039;--log-level&#039;, &#039;error&#039;],
        output=&#039;screen&#039;)
    

    # Crea y configura el nodo SPAWN_ENTITY, que es el que va a cargar y mostrar
    # el modelo en Gazebo.
    node_spawn_entity = Node(
        package=&#039;gazebo_ros&#039;, 
        executable=&#039;spawn_entity.py&#039;,
        arguments=[
            &#039;-entity&#039;, urdf_file_name,
            &#039;-topic&#039;, &#039;robot_description&#039;,
            &#039;-x&#039;, &#039;0.0&#039;,
            &#039;-y&#039;, &#039;0.0&#039;,
            &#039;-z&#039;, &#039;0.0&#039;,
            &#039;-R&#039;, &#039;0.0&#039;,
            &#039;-P&#039;, &#039;0.0&#039;,
            &#039;-Y&#039;, &#039;0.0&#039;],
        output=&#039;screen&#039;)


    # Incluye la ejecuci&oacute;n del archivo launch gazebo.launch.py, que forma parte
    # del paquete gazebo_ros.
    gazebo = IncludeLaunchDescription(
                PythonLaunchDescriptionSource([PathJoinSubstitution(
                    [FindPackageShare(&#039;gazebo_ros&#039;), &#039;launch&#039;, &#039;gazebo.launch.py&#039;])]),
             )


    # Devuelve un objeto LaunchDescription con las acciones a realizar.
    return LaunchDescription([
        gazebo,
        node_robot_state_publisher,
        node_spawn_entity,
    ])

### 4 Ejemplo

Puedes descargar el paquete [py\_gazebo\_spawn\_example](https://github.com/joseecm-github/py_gazebo_spawn_example) de Github que contiene un ejemplo de cómo hacer aparecer un modelo en nuestra simulación en Gazebo usando un archivo* launch* o usando el servicio *spawn\_entity* desde un nodo de ROS2.

Para ello, en el terminal, ve a la carpeta src de tu espacio de trabajo y ejecuta la siguiente instrucción:

git clone https://github.com/joseecm-github/py_gazebo_spawn_example

Y a continuación compila el paquete:

cd ..
colcon build --packages-select py_gazebo_spawn_example
. install/setup.bash

Para hacer aparecer el modelo usando un archivo *launch* ejecuta el siguiente comando:

ros2 launch py_gazebo_spawn_example spawn.launch.py

Para ver como funciona usando el servicio primero cerramos Gazebo pulsando en el terminal CTRL+C y lo volvemos a arrancar ejecutando:

ros2 launch gazebo_ros gazebo.launch.py

Y desde otro terminal lanzamos el nodo que llama al servicio:

ros2 run py_gazebo_spawn_example spawn_urdf