Clone a simulation


Overview

Gzserver allows you to load an environment, insert your models, and simulate the world. As you probably know, a robot simulator can be an invaluable tool for testing and tuning in advance the code or behaviour that you will run on a real robot. You might want to run multiple simulation episodes to see how different parameters or approaches behave. Cloning a simulation is useful for running different versions of your code in parallel. This tutorial will guide you through the steps required to clone your current simulation.

Clone the simulation using the GUI

  1. Start Gazebo by typing the following command at the command prompt:

    gazebo
    
  2. Insert a simple sphere into the scene by using the upper toolbar.

  3. Clone your current simulation by clicking on File-> Clone world.

    You should see a dialog window similar to the window below.

    The new cloned world will run on a separate server that will have its own Master. This dialog window allows you to specify the port in which the new Master will accept connections from the clients. Note that you should select a free port from the range 1025-65535. Our recommendation is to start with 11346 and keep incrementing this number for every concurrent server that you may have.

  4. Set the port for your new server and click Okay.

  5. At this moment your new server should be running with an exact copy of your current world. If you look in your server log file, you should see a message confirming that the world has been cloned.

    cat ~/.gazebo/server-11345/default.log
    
    Gazebo multi-robot simulator, version 4.0.0
    Copyright (C) 2012-2014 Open Source Robotics Foundation.
    Released under the Apache 2 License.
    http://gazebosim.org
    
    (1409088199 32370140) Cloning world [default]. Contact the server by typing:
      GAZEBO_MASTER_URI=http://localhost:11346 gzclient
    

    Your cloned server will store its log files in a directory named ~/.gazebo/server-<MASTER_PORT>. E.g.: In our example, the log files for the cloned gzserver will be located under /.gazebo/server-11346.

  6. Open a new terminal and connect gzclient to your new server. If you did not use port 11346, be sure to replace "11346" with your port number:

    GAZEBO_MASTER_URI=http://localhost:11346 gzclient
    

    The above code modifies the environment variable GAZEBO_MASTER_URI to point to the cloned server. Otherwise, you would be visualizing your original world.

    Although your two gzclients are visualizing similar worlds, the simulations are running on different servers. Verify this by changing the gravity in one of the simulations to 0.003 (under the world tab, click on physics, and then change the z value of the property gravity). You should only see one of your spheres slowly flying. This proves that after cloning a simulation, each world is independent.

  7. Once the new server is cloned, it's totally detached from the original one, so you will need to kill it manually:

    killall gzserver
    

    Note that the cloned server will be running on the same machine on which the original server is located. Take this into account before cloning a simulation because you may need SSH access to the server machine (if your server is running remotely) in order to kill the new server after a cloning.

Clone the simulation programmatically

It's also possible to clone a simulation programmatically using the Gazebo transport system. As an example, we'll describe the example code shipped with Gazebo in the folder examples/stand_alone/clone_simulation.

Create a new directory named clone_simulation for this tutorial:

mkdir ~/clone_simulation
cd ~/clone_simulation

Download the files CMakeLists.txt and cloner.cc into the previous folder.

wget http://github.com/osrf/gazebo/raw/master/examples/stand_alone/clone_simulation/CMakeLists.txt
wget http://github.com/osrf/gazebo/raw/master/examples/stand_alone/clone_simulation/cloner.cc

Compile the example:

mkdir build
cd build
cmake ..
make

Run the example:

./cloner
./cloner

Press [ENTER] to clone the current simulation

The example will show a message telling you that the new server is running and that you should press ENTER to clone the current simulation. Before hitting ENTER, connect a gzclient to the current server, by typing in a new terminal:

gzclient

Spawn a sphere using the upper toolbar.

Go back to the terminal where your cloner is running and press ENTER to trigger the simulation cloning.

Press [ENTER] to clone the current simulation


World cloned. You can connect a client by tiping
  GAZEBO_MASTER_URI=http://localhost:11346 gzclient

Press [ENTER] to exit and kill all the servers.

You should see a confirmation message with the location of the new server and instructions on how to connect a new gzclient to it. Open a new terminal and run gzclient:

GAZEBO_MASTER_URI=http://localhost:11346 gzclient

Verify again that the two simulations are independent by changing the gravity in one of the simulations to 0.003 (as noted above). As before, you should only see one of your spheres moving away.

The source code

Let's take a look at the source code for our example:

/*
 * Copyright (C) 2014 Open Source Robotics Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

#include <cstdlib>
#include <iostream>
#include <memory>
#include <thread>
#include <gazebo/gazebo.hh>
#include <gazebo/msgs/msgs.hh>
#include <gazebo/transport/transport.hh>

/////////////////////////////////////////////////
void OnWorldModify(ConstWorldModifyPtr &_msg)
{
  if (_msg->has_cloned() && _msg->cloned() && _msg->has_cloned_uri())
  {
    std::cout << "World cloned. You can connect a client by typing\n"
              << "\tGAZEBO_MASTER_URI=" << _msg->cloned_uri()
              << " gzclient" << std::endl;
  }
}

/////////////////////////////////////////////////
void RunServer()
{
  // Initialize gazebo server.
  std::unique_ptr<gazebo::Server> server(new gazebo::Server());
  try
  {
    if (!server->ParseArgs(0, NULL))
      return;

    // Initialize the informational logger. This will log warnings, and errors.
    gzLogInit("server-", "gzserver.log");

    server->Run();
    server->Fini();
  }
  catch(gazebo::common::Exception &_e)
  {
    _e.Print();
    server->Fini();
  }
}

/////////////////////////////////////////////////
int main(int _argc, char **_argv)
{
  // Launch a server in a different thread.
  std::thread serverThread(RunServer);

  // Create a node for communication.
  gazebo::transport::NodePtr node(new gazebo::transport::Node());
  node->Init();

  // Publisher to the server control.
  gazebo::transport::PublisherPtr serverControlPub =
    node->Advertise<gazebo::msgs::ServerControl>("/gazebo/server/control");

  // Subscriber to receive world updates (e.g.: a notification after a cloning).
  gazebo::transport::SubscriberPtr worldModSub =
    node->Subscribe("/gazebo/world/modify", &OnWorldModify);

  std::cout << "\nPress [ENTER] to clone the current simulation\n" << std::endl;
  getchar();

  // Clone the server programmatically.
  gazebo::msgs::ServerControl msg;
  msg.set_save_world_name("");
  msg.set_clone(true);
  msg.set_new_port(11346);
  serverControlPub->Publish(msg);

  // Wait for the simulation clone before showing the next message.
  gazebo::common::Time::MSleep(200);

  std::cout << "\nPress [ENTER] to exit and kill all the servers." << std::endl;
  getchar();

  // Make sure to shut everything down.
  std::string cmd = "kill -15 `ps -A | grep -m1 gzserver | awk '{print $1}'`";
  int ret = std::system(cmd.c_str());
  if (ret != 0)
    std::cerr << "kill gzserver returned a non zero value:" << ret << std::endl;

  gazebo::shutdown();
  serverThread.join();
}

The code explained

int main(int _argc, char **_argv)
{
  // Launch a server in a different thread.
  std::thread serverThread(RunServer);

This fragment of the code spawns a new thread and executes a new server.

  gazebo::transport::NodePtr node(new gazebo::transport::Node());
  node->Init();

  // Publisher to the server control.
  gazebo::transport::PublisherPtr serverControlPub =
    node->Advertise<gazebo::msgs::ServerControl>("/gazebo/server/control");

  // Subscriber to receive world updates (e.g.: a notification after a cloning).
  gazebo::transport::SubscriberPtr worldModSub =
    node->Subscribe("/gazebo/world/modify", &OnWorldModify);

  std::cout << "\nPress [ENTER] to clone the current simulation\n" << std::endl;
  getchar();

The simulation cloning is performed via the transport system. First, we have to initialize a transport node that will allow us to use the transport. We need a topic publisher to send a new message with our cloning request. The topic is /gazebo/server/control. In addition, we need a subscriber on the topic /gazebo/world/modify for receiving the result of our clone request.

  gazebo::msgs::ServerControl msg;
  msg.set_save_world_name("");
  msg.set_clone(true);
  msg.set_new_port(11346);
  serverControlPub->Publish(msg);

  // Wait for the simulation clone before showing the next message.
  gazebo::common::Time::MSleep(200);

  std::cout << "\nPress [ENTER] to exit and kill all the servers." << std::endl;
  getchar();

This is the part of the code where we prepare our ServerControl message for our cloning request. The field save_world_name specifies the name of the world that we want to clone. The empty string represents the default world. The field clone is set to true when requesting a clone. The field new_port sets the port that the new server will use for connections. This will be the port that we will use to connect our future gzclient to display the new simulation. Finally, the message is sent by calling the Publish() method with our custom message.

void OnWorldModify(ConstWorldModifyPtr &_msg)
{
  if (_msg->has_cloned() && _msg->cloned() && _msg->has_cloned_uri())
  {
    std::cout << "World cloned. You can connect a client by typing\n"
              << "\tGAZEBO_MASTER_URI=" << _msg->cloned_uri()
              << " gzclient" << std::endl;
  }
}

When the server processes our clone request, it sends us a response contained in a WorldModify message. This is the callback that we registered during the subscription and it will be triggered when the response from the server is received. The field cloned will be true when a new server has been cloned. Also, the field cloned_uri will show us the URI of the new server.

  // Make sure to shut everything down.
  std::string cmd = "kill -15 `ps -A | grep -m1 gzserver | awk '{print $1}'`";
  int ret = std::system(cmd.c_str());
  if (ret != 0)
    std::cerr << "kill gzserver returned a non zero value:" << ret << std::endl;

  gazebo::shutdown();

These commands will terminate all the servers running in our system.