前言:在先前的博客中,我们不仅完成了对机器人模型的建立和仿真,并且创建了机器人的工作空间,即仿真环境的设置,那么想要通过控制机器人传感器来完成对于机器人的控制就需要进一步研究,学会如何使用控制插件,通过编写代码在Gazebo中加载C++库完成对于机器人的实际控制。
- 能让开发者控制几乎所有的Gazebo功能
- 独立的例行程序,易于分享
- 可以插入正在运行的系统或被其移除
- 以编程的方式来更改一个仿真,例如移动模型,对事件进行响应,插入有预先条件的模型
- 想要有一个到Gazebo的快速接口,不必经过传输层(transport layer),例如没有对消息进行序列化和反序列化
- 代码分享需求
sudo apt-get install libgazebo11-dev
mkdir ~/gazebo_plugin_tutorial
cd ~/gazebo_plugin_tutorial
gedit hello_world.cc
#include
// gazebo/gazebo.hh包含了一组核心的基本Gazebo函数,所有的插件都必须在gazebo的命名空间内。
namespace gazebo
{
//每个插件都必须继承自某个插件类型,在这里是WorldPlugin类
class WorldPluginTutorial : public WorldPlugin
{
public: WorldPluginTutorial() : WorldPlugin()
{
printf("Hello World!\n");
}
//强制函数Load,接受被加载SDF文件内的元素和属性。
public: void Load(physics::WorldPtr _world, sdf::ElementPtr _sdf)
{
}
};
// 插件必须使用宏GZ_REGISTER_WORLD_PLUGIN在模拟器中注册。这个宏的唯一参数就是插件的类。
GZ_REGISTER_WORLD_PLUGIN(WorldPluginTutorial)
}
- 创建CMakeLists:
gedit ~/gazebo_plugin_tutorial/CMakeLists.txt
- 编写编译文件:
cmake_minimum_required(VERSION 2.8 FATAL_ERROR) find_package(gazebo REQUIRED) include_directories(${GAZEBO_INCLUDE_DIRS}) link_directories(${GAZEBO_LIBRARY_DIRS}) list(APPEND CMAKE_CXX_FLAGS >"${GAZEBO_CXX_FLAGS}") add_library(hello_world SHARED hello_world.cc) target_link_libraries(hello_world ${GAZEBO_LIBRARIES})
\qquad 【注:从gazebo6的版本开始都需要c++11的flag,对应上述第六行代码】
mkdir ~/gazebo_plugin_tutorial/build
cd ~/gazebo_plugin_tutorial/build
cmake ../
make
~/gazebo_plugin_tutorial/build/libhello_world.so
共享库,在仿真时插入,添加路径的方法如下:export GAZEBO_PLUGIN_PATH=${GAZEBO_PLUGIN_PATH}:~/gazebo_plugin_tutorial/build
【编译完成后,就可以把它附加到SDF文件内的一个世界或一个模型。启动Gazebo会解析SDF文件,并定位插件,加载代码。所以需要指定插件完全路径,或让插件存在于GAZEBO_PLUGIN_PATH环境遍历路径中】
- 创建世界文件:
gedit ~/gazebo_plugin_tutorial/hello.world
- 编写世界文件:
<sdf version="1.4"> <world name="default"> <plugin name="hello_world" filename="libhello_world.so"/> world> sdf>
gzserver
打开世界文件:gzserver ~/gazebo_plugin_tutorial/hello.world --verbose
Gazebo multi-robot simulator, version 9.16.0
Copyright (C) 2012 Open Source Robotics Foundation.
Released under the Apache 2 License.
http://gazebosim.org
[Msg] Waiting for master.
[Msg] Connected to gazebo master @ http://127.0.0.1:11345
[Msg] Publicized address: xxx.xxx.xxx.xxx
[Msg] Loading world file [/home/qqc/gazebo_plugin_tutorial/hello.world]
Hello World!
- 创建插件文件:
cd ~/gazebo_plugin_tutorial gedit model_push.cc
- 编写插件文件:
#include
#include #include #include #include namespace gazebo { class ModelPush : public ModelPlugin { public: void Load(physics::ModelPtr _parent, >sdf::ElementPtr /*_sdf*/) { // Store the pointer to the model this->model = _parent; // Listen to the update event. This event is >broadcast every // simulation iteration. this->updateConnection = >event::Events::ConnectWorldUpdateBegin( std::bind(&ModelPush::OnUpdate, this)); } // Called by the world update start event public: void OnUpdate() { // Apply a small linear velocity to the model. // 每次迭代设置_parent速度(.3,0,0) this->model->SetLinearVel(ignition::math::Vector3d(.3, 0, 0)); } // Pointer to the model private: physics::ModelPtr model; // Pointer to the update event connection private: event::ConnectionPtr updateConnection; }; // Register this plugin with the simulator GZ_REGISTER_MODEL_PLUGIN(ModelPush) }
- 打开原有文件:
gedit ~/gazebo_plugin_tutorial/CMakeLists.txt
- 在文件底部添加如下代码:
add_library(model_push SHARED model_push.cc) target_link_libraries(model_push >${GAZEBO_LIBRARIES})
~/gazebo_plugin_tutorial/build/libmodel_push.so
cd ~/gazebo_plugin_tutorial/build
cmake ../
make
- 打开文件:
cd ~/gazebo_plugin_tutorial gedit model_push.world
- 编写世界文件:
<sdf version="1.4"> <world name="default"> <include> <uri>model://ground_planeuri> include> <include> <uri>model://sunuri> include> <model name="box"> <pose>0 0 0.5 0 0 0pose> <link name="link"> <collision name="collision"> <geometry> <box> <size>1 1 1size> box> geometry> collision> <visual name="visual"> <geometry> <box> <size>1 1 1size> box> geometry> visual> link> <plugin name="model_push" filename="libmodel_push.so"/> model> world> sdf>
export GAZEBO_PLUGIN_PATH=$HOME/gazebo_plugin_tutorial/build:$GAZEBO_PLUGIN_PATH
cd ~/gazebo_plugin_tutorial/
gzserver -u model_push.world
gzclient
- 创建插件文件:
cd ~/gazebo_plugin_tutorial gedit factory.cc
- 编写插件文件:
#include
#include "gazebo/physics/physics.hh" #include "gazebo/common/common.hh" #include "gazebo/gazebo.hh" namespace gazebo { class Factory : public WorldPlugin { public: void Load(physics::WorldPtr _parent, >sdf::ElementPtr /*_sdf*/) { // 方法1: 基于在资源路径内的文件来加载模型,通过调用函数,从文件中插入模型 // Option 1: Insert model from file via function call. // 文件名必须要在环境变量`GAZEBO_MODEL_PATH`中 // The filename must be in the >GAZEBO_MODEL_PATH environment variable. _parent->InsertModelFile("model://box"); // 方法2:通过调用函数,从字符串中插入模型 // Option 2: Insert model from string via function call. // 从字符串插入一个球形模型 // Insert a sphere model from string sdf::SDF sphereSDF; sphereSDF.SetFromString( " \ "); // 自定义模型名称 Demonstrate using a custom model name. sdf::ElementPtr model = sphereSDF.Root()->GetElement("model"); model->GetAttribute("name")->SetFromString("unique_sphere"); _parent->InsertModelSDF(sphereSDF); // 方法3: 通过消息传递,从文件插入模型 // Option 3: Insert model from file via message passing. { // 创造一个新的传输节点 Create a new transport node transport::NodePtr node(new transport::Node()); // 用世界名初始化节点 Initialize the node with the world name node->Init(_parent->Name()); // 在~/factory创建一个发布者publisher Create a publisher on the ~/factory topic transport::PublisherPtr factoryPub = node->Advertise<msgs::Factory>("~/factory"); // 创建消息 Create the message msgs::Factory msg; // 加载模型文件 Model file to load msg.set_sdf_filename("model://cylinder"); // 设置要初始化的模型的位置信息 Pose to initialize the model to msgs::Set(msg.mutable_pose(), ignition::math::Pose3d( ignition::math::Vector3d(1, -2, 0), ignition::math::Quaterniond(0, 0, 0))); // 发送消息 Send the message factoryPub->Publish(msg); } } }; // Register this plugin with the simulator GZ_REGISTER_WORLD_PLUGIN(Factory) }\ \1 0 0 0 0 0 \ \0 0 .5 0 0 0 \\ \\ \\ 0.5 \ \ \\ \\ 0.5
add_library(factory SHARED factory.cc)
target_link_libraries(factory
${GAZEBO_LIBRARIES}
)
cd ~/gazebo_plugin_tutorial/build
cmake ../
make
mkdir ~/gazebo_plugin_tutorial/models
cd ~/gazebo_plugin_tutorial/models
mkdir box cylinder
- 创建盒子模型:
cd box gedit model.sdf
- 编写SDF文件:
<sdf version ='1.6'> <model name ='box'> <pose>1 2 0 0 0 0pose> <link name ='link'> <pose>0 0 .5 0 0 0pose> <collision name ='collision'> <geometry> <box><size>1 1 1size>box> geometry> collision> <visual name ='visual'> <geometry> <box><size>1 1 1size>box> geometry> visual> link> model> sdf>
- 创建配置文件:
gedit model.config
- 编写配置文件:
<model> <name>boxname> <version>1.0version> <sdf >model.sdfsdf> <author> <name>mename> <email>[email protected]email> author> <description> A simple Box. description> model>
- SDF文件:
<sdf version ='1.6'> <model name ='cylinder'> <pose>1 2 0 0 0 0pose> <link name ='link'> <pose>0 0 .5 0 0 0pose> <collision name ='collision'> <geometry> <cylinder><radius>0.5radius><length>1length>cylinder> geometry> collision> <visual name='visual'> <geometry> <cylinder><radius>0.5radius><length>1length>>cylinder> geometry> visual> link> model> sdf>
- Config文件:
<model> <name>cylindername> <version>1.0version> <sdf>model.sdfsdf> <author> <name>mename> <email>[email protected]email> author> <description> A simple cylinder. description> model>
export GAZEBO_MODEL_PATH=$HOME/gazebo_plugin_tutorial/models:$GAZEBO_MODEL_PATH
export GAZEBO_PLUGIN_PATH=$HOME/gazebo_plugin_tutorial/build:$GAZEBO_PLUGIN_PATH
cd ~/gazebo_plugin_tutorial
gedit factory.world
<sdf version="1.4">
<world name="default">
<include>
<uri>model://ground_planeuri>
include>
<include>
<uri>model://sunuri>
include>
<plugin name="factory" filename="libfactory.so"/>
world>
sdf>
gazebo ~/gazebo_plugin_tutorial/factory.world
cd ~/gazebo_plugin_tutorial
gedit world_edit.world
<sdf version ='1.4'>
<world name='default'>
<include>
<uri>model://ground_planeuri>
include>
<include>
<uri>model://sunuri>
include>
<plugin filename="libworld_edit.so" name="world_edit"/>
world>
sdf>
gedit world_edit.cc
#include
#include
#include "gazebo/gazebo.hh"
#include "gazebo/common/Plugin.hh"
#include "gazebo/msgs/msgs.hh"
#include "gazebo/physics/physics.hh"
#include "gazebo/transport/transport.hh"
/// \example examples/plugins/world_edit.cc
/// This example creates a WorldPlugin, initializes the Transport system by
/// creating a new Node, and publishes messages to alter gravity.
namespace gazebo
{
class WorldEdit : public WorldPlugin
{
public: void Load(physics::WorldPtr _parent, sdf::ElementPtr _sdf)
{
// 新建一个运输节点 Create a new transport node
transport::NodePtr node(new transport::Node());
// 用世界名称初始化节点 Initialize the node with the world name
node->Init(_parent->Name());
// 在~/physics主题上创建一个发布者 Create a publisher on the ~/physics topic
transport::PublisherPtr physicsPub = node->Advertise<msgs::Physics>("~/physics");
msgs::Physics physicsMsg;
physicsMsg.set_type(msgs::Physics::ODE);
// 设置步长时间 Set the step time
physicsMsg.set_max_step_size(0.01);
// 改变重力 Change gravity
msgs::Set(physicsMsg.mutable_gravity(), ignition::math::Vector3d(0.01, 0, 0.1));
physicsPub->Publish(physicsMsg);
}
};
// Register this plugin with the simulator
GZ_REGISTER_WORLD_PLUGIN(WorldEdit)
}
add_library(world_edit SHARED world_edit.cc)
target_link_libraries(world_edit ${GAZEBO_LIBRARIES})
cd ~/gazebo_plugin_tutorial/build
cmake ..
make
export GAZEBO_PLUGIN_PATH=${GAZEBO_PLUGIN_PATH}:~/gazebo_plugin_tutorial/build/
cd ~/gazebo_plugin_tutorial
gazebo world_edit.world