ROS2与Gazebo11入门教程-克隆仿真
说明:
- 介绍如何克隆当前仿真所需
概述
- Gzserver允许加载一个仿真环境、插入模型并对仿真世界进行仿真。
- 您可能知道,机器人仿真器可以是一个非常有用的工具,可以预先测试和调整要在真实机器人上运行的代码或行为。
- 您可能需要运行多个仿真情景,以观察不同参数或不同方法情景下的机器人行为。
- 克隆仿真对于并行运行您的代码的不同版本非常有用。
- 本节教程将会指导您完成克隆当前仿真所需的完整过程。
使用GUI克隆仿真
- 通过在命令提示符后输入以下命令来启动Gazebo:
gazebo
使用顶部工具栏将一个简单的球体插入到场景中。
通过点击“文件(File)”->“克隆仿真世界(Clone world)”。这样就应该可以看见一个类似于下面的对话框窗口。
新克隆的仿真世界将会在具有其自身主节点的单独服务器上运行。 这个对话框窗口允许您指定端口,新的主节点通过该端口接受来自客户端的连接。请注意,应该在1025-65535端口号范围内选择一个空闲端口。建议端口号从11346开始,并为可能拥有的每个并发服务器增大该端口号数值。
设置新服务器的端口,然后单击“确定(Okay)”按钮。
此时,新服务器应该会运行当前仿真世界的一个完全相同的副本。如果查看服务器日志文件,则应该会看到一条确认已成功克隆了仿真世界的消息。
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
克隆的服务器会将其日志文件存储在名为~/.gazebo/server- <MASTER_PORT>的目录中。例如:在本示例中,克隆的gzserver的日志文件会存放在~/.gazebo/server-11346目录下。
打开一个新终端,然后将gzclient连接到该新服务器。如果使用的端口不是11346,请确保将“11346”替换成正确的端口号:
GAZEBO_MASTER_URI=http://localhost:11346 gzclient
上行代码会修改环境变量GAZEBO_MASTER_URI以指向克隆的服务器。否则,看到的就仍是原来的仿真世界。
尽管这两个gzclient显示的是相同的仿真世界,但是这两个仿真是运行在不同服务器上的。通过将其中一个仿真的重力更改为0.003可以验证这一点(操作方法为:在“世界(world)”选项卡下,单击“物理(physics)”项,然后将属性“gravity”的z值更改为0.003)。这样应该仅会看到一个球体在缓慢地飞行。这证明克隆仿真后,每个仿真世界都是独立的。
- 克隆好新服务器后,它将会与原来的服务器完全分开,因此需要手动将其终止:
killall gzserver
- 请注意,克隆的服务器将会在原来服务器所在的同一台计算机上运行。克隆仿真之前,请考虑到这一点,因为您可能需要对服务器计算机的SSH访问权限(如果您的服务器正在远程运行),以便在克隆后终止新服务器。
以编程方式克隆仿真
也可以使用Gazebo的传输系统以编程方式克隆仿真。作为示例,下面会介绍Gazebo自带的位于examples/stand_alone/clone_simulation目录中的示例代码。
为本节教程创建一个名为clone_simulation的新目录并进入该目录,命令为:
mkdir ~/clone_simulation
cd ~/clone_simulation
- 使用以下命令将CMakeLists.txt和http://cloner.cc文件下载到前面新建的目录中:
wget https://github.com/osrf/gazebo/raw/master/examples/stand_alone/clone_simulation/CMakeLists.txt
wget https://github.com/osrf/gazebo/raw/master/examples/stand_alone/clone_simulation/cloner.cc
- 编译该示例软件包,命令为:
mkdir build
cd build
cmake ..
make
- 运行该示例,命令为:
./cloner
Press [ENTER] to clone the current simulation
- 该示例会显示一条消息,告诉您新服务器正在运行,且您应该按“回车键(Enter)”克隆当前仿真。在按“回车键(Enter)”之前,通过在新的终端中输入以下命令来将gzclient连接到当前服务器:
gzclient
使用顶部工具栏生成一个球体。
返回到正在运行克隆程序的终端中,然后按“回车键(Enter)”触发仿真克隆。
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.
- 这样应该会看到一条确认消息,其中包含新服务器的位置以及有关如何将新gzclient连接到该服务器的说明。 打开一个新终端并运行gzclient,命令为:
GAZEBO_MASTER_URI=http://localhost:11346 gzclient
- 通过将其中一个仿真的重力更改为0.003(如前所述)来再次验证两个仿真是否相互独立。和前面一样,应该只能看到一个球体会飞离地平面。
源代码
- 现在来看下该示例的源代码:
/*
* 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);
- 上面这段代码会生成一个新线程并执行一个新的服务器。
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();
- 仿真克隆是通过传输系统执行的。首先,必须初始化一个传输节点,以允许使用传输系统。需要一个话题发布者节点来发送带有克隆请求的新消息。该话题是/gazebo/server/control话题。另外还需要话题/gazebo/world/modify的一个订阅者节点来接收克隆请求的结果。
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();
- 上面这段代码是为克隆请求准备ServerControl消息的部分代码。 字段save_world_name用于指定要克隆的仿真世界名称,空字符串表示克隆的是默认仿真世界。请求克隆时,字段clone要设置为true。 字段new_port设置新服务器用于连接的端口。该端口将来会用于连接gzclient以显示新的仿真。最后,通过用自定义消息作为参数调用Publish()方法来发布消息。
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;
}
}
- 当服务器处理克隆请求时,它会向我们发送包含在WorldModify消息中的一个响应。这是在订阅期间注册的回调函数,并且当收到来自服务器的响应时将会触发该回调。在完成新服务器的克隆后,字段cloned将会变为true。此外,字段cloned_uri也会显示新服务器的URI。
// 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();
- 这些命令将会终止系统中运行的所有服务器。
参考:
获取最新文章: 扫一扫右上角的二维码加入“创客智造”公众号