
\qquad
前言:在上一篇博客中,我们首先了解了控制器插件的具体使用方法和配置流程,采用多个实例了解了模型插件和世界插件等的具体使用方法,本节博客将继续深入体会插件的功能效用,以两个实例重点介绍系统插件和传感器插件的配置方法,其中传感器插件的配置是重点,与之前的教程一致,可统一学习,最后通过创造API实现动态调整。
目标:给gzclient设计系统插件,将图像存储到/tmp/gazebo_frames目录下方;
创建并编写插件文件:
- 创建插件文件:
cd ~/gazebo_plugin_tutorial gedit system_gui.cc
- 1
- 2
- 编写插件文件:
#include#include #include #include namespace gazebo { class SystemGUI : public SystemPlugin { // Destructor public: virtual ~SystemGUI() { this->connections.clear(); if (this->userCam) this->userCam->EnableSaveFrame(false); this->userCam.reset(); } // 在启动时,Gazebo加载之前,Load和Init会在插件被构造后调用,且二者必须不能阻塞(must not block) public: void Load(int /*_argc*/, char ** /*_argv*/) { this->connections.push_back( event::Events::ConnectPreRender( std::bind(&SystemGUI::Update, this))); } // 只在`Load`后调用一次 private: void Init() { } // 每次PreRender事件都会调用,看`Load`函数 private: void Update() { if (!this->userCam) { // 得到一个激活用户相机的指针 this->userCam = gui::get_active_camera(); // 开启帧的保存 this->userCam->EnableSaveFrame(true); // 指定要保存帧的路径 this->userCam->SetSaveFramePathname("/tmp/gazebo_frames"); } // 得到scene的指针 rendering::ScenePtr scene = rendering::get_scene(); // 等待,直到scene被初始化了 if (!scene || !scene->Initialized()) return; // 通过名字寻找一个特定的图像 if (scene->GetVisual("ground_plane")) std::cout << "Has ground plane visual\n"; } // 申明用户相机的指针 private: rendering::UserCameraPtr userCam; // 申明存储所有事件连接的vector private: std::vector<event::ConnectionPtr> connections; }; // 和模拟器上注册插件 GZ_REGISTER_SYSTEM_PLUGIN(SystemGUI) }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 修改编译规则(底部添加):
add_library(system_gui SHARED system_gui.cc) target_link_libraries(system_gui ${GAZEBO_LIBRARIES})
- 1
- 2
- 编译插件:
cd ~/gazebo_plugin_tutorial/build cmake .. make
- 1
- 2
- 3
gzserver &
gzclient -g libsystem_gui.so
- 在
tmp/gazebo_frames目录下,应该会出现一些照片
- 在同一个终端输入如下代码终止后台运行的程序
fg- 按Ctrl+C终止进程

mkdir ~/velodyne_plugin
cd ~/velodyne_plugin
gedit velodyne_plugin.cc
#ifndef _VELODYNE_PLUGIN_HH_
#define _VELODYNE_PLUGIN_HH_
#include
#include
namespace gazebo
{
/// \brief A plugin to control a Velodyne sensor.
class VelodynePlugin : public ModelPlugin
{
/// \brief Constructor
public: VelodynePlugin() {}
/// \brief The load function is called by Gazebo when the plugin is
/// inserted into simulation
/// \param[in] _model A pointer to the model that this plugin is
/// attached to.
/// \param[in] _sdf A pointer to the plugin's SDF element.
public: virtual void Load(physics::ModelPtr _model, sdf::ElementPtr _sdf)
{
// Just output a message for now
std::cerr << "\nThe velodyne plugin is attach to model[" <<
_model->GetName() << "]\n";
}
};
// Tell Gazebo about this plugin, so that Gazebo can call Load on this plugin.
GZ_REGISTER_MODEL_PLUGIN(VelodynePlugin)
}
#endif
gedit CMakeLists.txt
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
# Find Gazebo
find_package(gazebo REQUIRED)
include_directories(${GAZEBO_INCLUDE_DIRS})
link_directories(${GAZEBO_LIBRARY_DIRS})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GAZEBO_CXX_FLAGS}")
# Build our plugin
add_library(velodyne_plugin SHARED velodyne_plugin.cc)
target_link_libraries(velodyne_plugin ${GAZEBO_LIBRARIES})
- 创建世界文件:
gedit velodyne.world
- 1
- 编写世界文件:
<sdf version="1.5"> <world name="default"> <include> <uri>model://sunuri> include> <include> <uri>model://ground_planeuri> include> <model name="my_velodyne"> <include> <uri>model://velodyne_hdl32uri> include> <plugin name="velodyne_control" >filename="libvelodyne_plugin.so"/> model> world> sdf>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 构造目录并编译文件:
cd ~/gazebo_plugin_tutorial/build/ cmake .. make
- 1
- 2
- 3
- 添加库路径并从build目录中运行gazebo:
cd ~/velodyne_plugin/build cexport LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:~/velodyne_plugin/build cgazebo ../velodyne.world
- 1
- 2
- 3
- 效果展示:
The velodyne plugin is attached to model[my_velodyne]
- 1
- 修改插件源文件
gedit ~/velodyne_plugin/velodyne_plugin.cc
- 1
- 修改
Load函数,代码如下:public: virtual void Load(physics::ModelPtr _model, sdf::ElementPtr _sdf) { // Safety check if (_model->GetJointCount() == 0) { std::cerr << "Invalid joint count, Velodyne plugin not loaded\n"; return; } // Store the model pointer for convenience. this->model = _model; // Get the first joint. We are making an assumption about the model // having one joint that is the rotational joint. this->joint = _model->GetJoints()[0]; // Setup a P-controller, with a gain of 0.1. this->pid = common::PID(0.1, 0, 0); // Apply the P-controller to the joint. this->model->GetJointController()->SetVelocityPID( this->joint->GetScopedName(), this->pid); // Set the joint's target velocity. This target velocity is just // for demonstration purposes. this->model->GetJointController()->SetVelocityTarget( this->joint->GetScopedName(), 10.0); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 将如下私有成员添加到对应类中:
/// \brief Pointer to the model. private: physics::ModelPtr model; /// \brief Pointer to the joint. private: physics::JointPtr joint; /// \brief A PID controller for the joint. private: common::PID pid;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 在世界文件中配置插件,读取自定义SDF参数:
gedit ~/gazebo_plugin_tutorial/velodyne.world
- 1
- 修改标签来包含一个元素:
<plugin name="velodyne_control" filename="libvelodyne_plugin.so"> <velocity>25velocity> plugin>
- 1
- 2
- 3
- 重新修改插件文件中的Load函数的底部,使用 sdf::ElementPtr参数来读取:
// 默认速度为0 double velocity = 0; // 检查是否元素存在,然后读取数值 if (_sdf->HasElement("velocity")) velocity = _sdf->Get<double>("velocity"); // 设置关节的目标速度 this->model->GetJointController()->>SetVelocityTarget(this->joint->GetScopedName(), >velocity);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 效果: 重新编译并运行gazebo,修改,传感器应进行旋转
cd ~/velodyne_plugin/build cmake .. make gazebo --verbose ../velodyne.world
- 1
- 2
- 3
- 4

- 消息传递:
依赖于Gazebo的传输机制,它将涉及创建一个命名的主题,发布者可以在该主题上发送double值。这样插件将接受到这些消息,并正确地设置速度值。对于进程间通信,消息传递是很方便的。- 函数法:
新建一个公共函数来调整速度值。一个新的插件将继承我们当前的插件。子级插件将被实例化(而不是我们当前的插件),通过调用函数,我们可以控制速度。当Gazebo与ROS交互时,这一方法最常用。
gedit ~/gazebo_plugin_tutorial/velodyne_plugin.cc
/// \brief Set the velocity of the Velodyne
/// \param[in] _vel New target velocity
public: void SetVelocity(const double &_vel)
{
// Set the joint's target velocity.
this->model->GetJointController()->SetVelocityTarget(this->joint->GetScopedName(), _vel);
}
/// \brief A node used for transport
private: transport::NodePtr node;
/// \brief A subscriber to a named topic.
private: transport::SubscriberPtr sub;
// 创造节点
this->node = transport::NodePtr(new transport::Node());
#if GAZEBO_MAJOR_VERSION < 8
this->node->Init(this->model->GetWorld()->GetName());
#else
this->node->Init(this->model->GetWorld()->Name());
#endif
// 创造一个主题名
std::string topicName = "~/" + this->model->GetName() + "/vel_cmd";
// 订阅这个主题,并且注册一个回调
this->sub = this->node->Subscribe(topicName, &VelodynePlugin::OnMsg, this);
/// \brief Handle incoming message
/// \param[in] _msg Repurpose a vector3 message. This function will
/// only use the x component.
private: void OnMsg(ConstVector3dPtr &_msg)
{
this->SetVelocity(_msg->x());
}
#include
#include
#ifndef _VELODYNE_PLUGIN_HH_
#define _VELODYNE_PLUGIN_HH_
#include
#include
#include
#include
namespace gazebo
{
/// \brief A plugin to control a Velodyne sensor.
class VelodynePlugin : public ModelPlugin
{
/// \brief Constructor
public: VelodynePlugin() {}
/// \brief The load function is called by Gazebo when the plugin is
/// inserted into simulation
/// \param[in] _model A pointer to the model that this plugin is
/// attached to.
/// \param[in] _sdf A pointer to the plugin's SDF element.
public: virtual void Load(physics::ModelPtr _model, sdf::ElementPtr _sdf)
{
// Safety check
if (_model->GetJointCount() == 0)
{
std::cerr << "Invalid joint count, Velodyne plugin not loaded\n";
return;
}
// Store the model pointer for convenience.
this->model = _model;
// Get the first joint. We are making an assumption about the model
// having one joint that is the rotational joint.
this->joint = _model->GetJoints()[0];
// Setup a P-controller, with a gain of 0.1.
this->pid = common::PID(0.1, 0, 0);
// Apply the P-controller to the joint.
this->model->GetJointController()->SetVelocityPID(
this->joint->GetScopedName(), this->pid);
// Default to zero velocity
double velocity = 0;
// Check that the velocity element exists, then read the value
if (_sdf->HasElement("velocity"))
velocity = _sdf->Get<double>("velocity");
this->SetVelocity(velocity);
// Create the node
this->node = transport::NodePtr(new transport::Node());
#if GAZEBO_MAJOR_VERSION < 8
this->node->Init(this->model->GetWorld()->GetName());
#else
this->node->Init(this->model->GetWorld()->Name());
#endif
// Create a topic name
std::string topicName = "~/" + this->model->GetName() + "/vel_cmd";
// Subscribe to the topic, and register a callback
this->sub = this->node->Subscribe(topicName,
&VelodynePlugin::OnMsg, this);
}
/// \brief Set the velocity of the Velodyne
/// \param[in] _vel New target velocity
public: void SetVelocity(const double &_vel)
{
// Set the joint's target velocity.
this->model->GetJointController()->SetVelocityTarget(
this->joint->GetScopedName(), _vel);
}
/// \brief Handle incoming message
/// \param[in] _msg Repurpose a vector3 message. This function will
/// only use the x component.
private: void OnMsg(ConstVector3dPtr &_msg)
{
this->SetVelocity(_msg->x());
}
/// \brief A node used for transport
private: transport::NodePtr node;
/// \brief A subscriber to a named topic.
private: transport::SubscriberPtr sub;
/// \brief Pointer to the model.
private: physics::ModelPtr model;
/// \brief Pointer to the joint.
private: physics::JointPtr joint;
/// \brief A PID controller for the joint.
private: common::PID pid;
};
// Tell Gazebo about this plugin, so that Gazebo can call Load on this plugin.
GZ_REGISTER_MODEL_PLUGIN(VelodynePlugin)
}
#endif
gedit ~/velodyne_plugin/vel.cc
#include
#include
#include
// Gazebo's API has changed between major releases. These changes are
// accounted for with #if..#endif blocks in this file.
#if GAZEBO_MAJOR_VERSION < 6
#include
#else
#include
#endif
int main(int _argc, char **_argv)
{
// 将Gazebo加载为客户端
#if GAZEBO_MAJOR_VERSION < 6
gazebo::setupClient(_argc, _argv);
#else
gazebo::client::setup(_argc, _argv);
#endif
// 为了通信,创建我们的节点
gazebo::transport::NodePtr node(new gazebo::transport::Node());
node->Init();
// 发布到velodyne传感器的主题
gazebo::transport::PublisherPtr pub =
node->Advertise<gazebo::msgs::Vector3d>("~/my_velodyne/vel_cmd");
// 等待订阅者连接到发布者
pub->WaitForConnection();
// 创建一个vector3消息
gazebo::msgs::Vector3d msg;
// 设置x方向的速度
#if GAZEBO_MAJOR_VERSION < 6
gazebo::msgs::Set(&msg, gazebo::math::Vector3(std::atof(_argv[1]), 0, 0));
#else
gazebo::msgs::Set(&msg, ignition::math::Vector3d(std::atof(_argv[1]), 0, 0));
#endif
// 发送消息
pub->Publish(msg);
// 确保所有都关闭了
#if GAZEBO_MAJOR_VERSION < 6
gazebo::shutdown();
#else
gazebo::client::shutdown();
#endif
}
# Build the stand-alone test program
add_executable(vel vel.cc)
if (${gazebo_VERSION_MAJOR} LESS 6)
# These two
include(FindBoost)
find_package(Boost ${MIN_BOOST_VERSION} REQUIRED system filesystem regex)
target_link_libraries(vel ${GAZEBO_LIBRARIES} ${Boost_LIBRARIES})
else()
target_link_libraries(vel ${GAZEBO_LIBRARIES})
endif()
cd ~/gazebo_plugin_tutorial/build
cmake ..
make
gazebo --verbose ../velodyne.world
cd ~/gazebo_plugin_tutorial/build/
./vel 2
