< >
Home » ROS2与Gazebo11入门教程 » ROS2与Gazebo11入门教程-克隆仿真

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();
  • 这些命令将会终止系统中运行的所有服务器。

参考:

纠错,疑问,交流: 请进入讨论区点击加入Q群

获取最新文章: 扫一扫右上角的二维码加入“创客智造”公众号


标签: ros2与gazebo11入门教程