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.
Start Gazebo by typing the following command at the command prompt:
gazebo
Insert a simple sphere into the scene by using the upper toolbar.
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.
Set the port for your new server and click Okay.
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
.
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.
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.
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.
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();
}
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.