1
$\begingroup$

I am trying to create node for an "infinite" model/entity spawner for my ROS2 Jazzy + Gazebo Harmonic application. And I am lookinf for Gazebo Harmonic's equivalent to Gazebo11's spawn_entity service.

I am aware the create_entity.py of gazebo11 has been replaced with the ros_gz_sim create node for spawning entities into a Gazebo environment from a launch file. But I am looking for an gz-sim9 alternative for the gazebo11 spawner node which was created like this:

import sys
import rclpy
from gazebo_msgs.srv import SpawnEntity

def request_spawn(xml: str):
   rclpy.init()
   node = rclpy.create_node('spawn_entity')
   client = node.create_client(SpawnEntity, 'spawn_entity')
   if not client.service_is_ready():
       client.wait_for_service()
   request = SpawnEntity.Request()
   request.xml = xml
   future = client.call_async(request)
   rclpy.spin_until_future_complete(node, future)

I found ros_gz_interfaces.srv has a SpawnEntity and a DeleteEntity srv. And would like to use it similar to this:

#!/usr/bin/env python3
import os
import rclpy

from rclpy.node import Node
from std_msgs.msg import Float64
from geometry_msgs.msg import Pose
from ros_gz_interfaces.srv import SpawnEntity

class ModelSpawner(Node):
    """
    Node which spawns models
    """
    _l_spawn: float = 0.0
    _spawn_frame: str = "box_conveyor"
    _min_dist: float = 1.5
    _sdf_path: str 
    _origin: Pose = Pose()

    def __init__(self, sdf_path: os.path, origin: Pose) -> None:
        super().__init__("gz_spawner_node")
        self._sdf_path = sdf_path
        self._origin = origin
        self._enc_sub = self.create_subscription(
            Float64,
            "/io/belt_pos",
            self.update_box_pos
        )
        self._client = self.create_client(SpawnEntity, "/spawn_entity")         # ???
        if not self._client.service_is_ready():
            self._client.wait_for_service()

    def update_box_pos(self, msg: Float64) -> None:
        if (msg.data - self._l_spawn < self._min_dist):
            return
        request = SpawnEntity.Request()
        request.entity_factory.name = "box"
        request.entity_factory.sdf = self._sdf_path
        request.entity_factory.pose = self._origin
        request.entity_factory.relative_to = self._spawn_frame
        
        future = self._client.call_async(request)
        rclpy.spin_until_future_complete(self, future)

        self._l_spawn = msg.data

Does gz-sim9 (Gazebo Harmonic) have a callable service like spawn_entity in gazebo11? If yes, which one and how is it used?


Solution

@JRTG's answer was definitely helpful. Writing a GZ Node instead of a ROS Node is way more efficient (if no ROS control is needed). The linked example can easily be used as template. The gz services needed for spawning and despawning are:

  • /world/<world_name>/create
  • /world/<world_name>/remove

Something I bumped into was getting gz-transport working with Python, so maybe people can benefit from my findings: To install gz-transport the following steps as in the Gazebo guide:

sudo sh -c 'echo "deb http://packages.osrfoundation.org/gazebo/ubuntu-stable `lsb_release -cs` main" > /etc/apt/sources.list.d/gazebo-stable.list'
wget https://packages.osrfoundation.org/gazebo.key -O - | sudo apt-key add -
sudo apt-get update
sudo apt-get install libgz-transport<#>-dev

With <#> being the version number you need in my case 14. This won't get you the Python libs. To install the Python libs add:

sudo apt-get install python3-gz-transport14

This will give you access to gz.transport and gz.msgs11 in Python3. Working test code:

#!/usr/bin/env python3
import os
import random

from gz.transport14 import Node
from gz.msgs11.entity_factory_pb2 import EntityFactory
from gz.msgs11.boolean_pb2 import Boolean
from bffp_executables.orientation import euler_to_quaternion 

SDF_PATH = <model_path>
WORLD_NAME = <world_name>

def gz_create_entity(world_name: str, sdf_path: os.path, *,
                name: str = '', relative_to: str = '',
                x: float = 0.0, y: float = 0.0, z: float = 0.0, 
                yaw: float = 0.0, pitch: float = 0.0, roll: float = 0.0) -> None:
    ''' Spawns model into Gazebo world'''
    node = Node()
    service = f"/world/{world_name}/create"
    request = EntityFactory()
    request.sdf_filename = sdf_path
    request.name = name
    request.relative_to = relative_to
    request.pose.position.x = x
    request.pose.position.y = y
    request.pose.position.z = z
    qx, qy, qz, qw = euler_to_quaternion(yaw, pitch, roll)
    request.pose.orientation.w = qw
    request.pose.orientation.x = qx
    request.pose.orientation.y = qy
    request.pose.orientation.z = qz
    response = node.request(service, request, EntityFactory, Boolean, 100)
    if not response:
        raise TimeoutError(f"Call failed: {response.data}")

if __name__ == "__main__":
    gz_create_entity(WORLD_NAME, SDF_PATH, name=f"model_{random.random()}")
$\endgroup$

1 Answer 1

1
$\begingroup$

You can spawn objects with a service request to the create service.

E.g. from the command line:

gz service -s /world/world_name/create \
--reqtype gz.msgs.EntityFactory \
--reptype gz.msgs.Boolean \
--timeout 300 \
--req 'sdf: '\
'"<?xml version=\"1.0\" ?>'\
'<sdf version=\"1.6\">'\
'<include>'\
'<pose degrees=\"true\">1.0 0.1 0 0 0 90</pose>'\
'<name>Object1</name>'\
'<uri>sdf/object.sdf</uri>'\
'</include>'\
'</sdf>" '\
'allow_renaming: true'

Obviously, replace world_name by the name of your world, and <uri> to point to the sdf of the object you want to spawn.

It would take me too long to look up the equivalent python syntax, but based on the example code in your question, it should be straightforward:

  • Create and populate an EntityFactory message,
  • Create a client for the create service of your world,
  • Make the service request to spawn the object.

EDIT: I just realized your example code uses a ROS 2 node and ROS 2 messages, rather than a Gazebo node and gz_transport messages. This is possible, but will need the ros_gz_bridge. It might be more straightforward to use a gazebo node directly, see e.g. this example code.]

The create service is provided by the gz-sim-user-commands-system, so this plugin must be loaded. For more documentation, see here.

If you also need removal of objects, you can use the remove service which is also provided by the the gz-sim-user-commands-system.

Alternatively, have a look at the waste_bin system plugin that I wrote for this purpose.

$\endgroup$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.