看到网上的各种方法都是wiki上的内容,先创建一个基类,再写插件类继承基类实现。那么,基于现有类是怎么实现的呢?其实也是一样的原理,只不过在实现的过程中稍微注意一下就可以了。在这里分享一下我写的一个基于现有类的例子,以供跟我一样有该需求的人进行参考。
在看我这篇文章之前,推荐大家先看看wiki版本的插件编写,然后再反过来看看这两者有什么区别。基于手写一个基类的插件是怎么实现的,大家可以翻看一下wiki,或者查询一下古月大神等的博客都可以找到答案。我这里说的是不用创建基类的写法。在这里,我将一个简单的机器人运动控制类做成一个插件的形式为例,再用另外一个包调用这个插件(wiki是在同一个包中进行调用),实现简单的运动等。
一、编写插件
1.1 编写步骤
一般来说,插件的编写步骤分为以下几步:
1、创建一个基类(这个是wiki上的写法,在这里我不用这一步)
2、创建插件类,继承基类(由于我没有创建一个基类,所以这里也不需要继承了)
3、注册插件
4、编译生成动态链接库
5、加入插件到ros
1.2 创建插件类及注册插件
首先在src目录下创建一个插件包,比如我就在自己的~/catkin_ws/src下创建
catkin_create_pkg controller_plugins roscpp pluginlib
ps:这里这个包名最好不要这样命名。大家在命名的时候一定要注意,比如包名、插件名、命名空间、类名一定要是独一无二的。
接着,在include目录下新建插件类的头文件,我这里为move_controller.h,里面添加如下内容:
#ifndef __MOVE_CONTROLLER_H_
#define __MOVE_CONTROLLER_H_
#include
namespace move_controller
{
class MoveController
{
public:
MoveController(){};
~MoveController(){};
void set_line_velocity(const double& line_vel);
void set_angular_velocity(const double& angular_vel);
void print_sth();
private:
double line_velocity;
double angular_velocity;
};
}
#endif
很简单的一个类,这里有一点要注意的就是,插件类的构造参数不能够带参数的。
好了,接下来该写是怎么实现的了。
在插件包的src目录下新建move_controller.cpp文件,并添加如下内容:
#include "controller_plugins/move_controller.h"
//注册插件,这里的参数为:(实现类,基类)
PLUGINLIB_EXPORT_CLASS(move_controller::MoveController, move_controller::MoveController)
namespace move_controller
{
void MoveController::set_line_velocity(const double& line_vel)
{
line_velocity = line_vel;
printf("set line velocity to %f.\n",line_vel);
}
void MoveController::set_angular_velocity(const double& angular_vel)
{
angular_velocity = angular_vel;
printf("set angular velocity to %f.\n",angular_vel);
}
void MoveController::print_sth()
{
printf("line velocity %f, angular velocity %f.\n",line_velocity,angular_velocity);
}
}
看出来了吧,跟wiki上的差别就在参数这里,这里的这些类名一定要注意正确填写。通过该宏,就可以注册插件了。
1.3编译插件动态链接库
修改CMakeLists.txt文件里面的内容:
cmake_minimum_required(VERSION 2.8.3)
project(controller_plugins)
find_package(catkin REQUIRED COMPONENTS
pluginlib
roscpp
)
catkin_package(
INCLUDE_DIRS include
# LIBRARIES controller_plugins
# CATKIN_DEPENDS pluginlib roscpp
# DEPENDS system_lib
)
include_directories(
include
${catkin_INCLUDE_DIRS}
)
## Declare a C++ library
add_library(controller_for_plugin
src/move_controller.cpp
)
这里的动态库名一定要注意写好,我这里只是为了与包名做区别。如果还有其他需求可以在上述内容上做扩展。比如工程需要还可以install
install(TARGETS controller_for_plugin
DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
)
install(DIRECTORY include/${PROJECT_NAME}/
DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
)
install(DIRECTORY plugins
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
)
1.4将插件加入到ros
在插件包目录下建立plugins文件夹并新建controller_for_plugin.xml文件,内容如下:
This is a MoveController plugin
ps:这里文件名、path中都用的是上一步生成的动态库的名称,也就是插件名。class标签的参数分别为插件类与基类。
修改package.xml文件,内容如下:
catkin
pluginlib
roscpp
pluginlib
roscpp
pluginlib
roscpp
我这里只展示了添加的地方,其余内容均是自动生成的。特别注意,export标签第一个是插件包名,后面跟的是插件的xml文件路径。
最终的插件包的目录图如下所示:
到这里,插件的编写就完成了。接下来直接catkin_make就可以编译插件包了。下面继续写如何用另外一个单独的包调用这个插件。
二、调用插件
2.1编写插件调用类
在~/catkin_ws/src目录下创建调用插件的包:
catkin_create_pkg use_plugins cpp pluginlib
在include/use_plugins文件夹下新建path_plan.h头文件,内容如下:
#ifndef __PATH_PLAN_H__
#define __PATH_PLAN_H__
#include
#include
#include
namespace path_ctrl
{
class PathPlan
{
public:
PathPlan();
~PathPlan(){};
void init();
private:
pluginlib::ClassLoader plugin_loader;
boost::shared_ptr move_controller;
double line_velocity;
double angle_velocity;
};
}
#endif
这里,plugin_loader创建一个插件加载对象,用于加载插件,其本来的参数为(含基类包名,插件类的基类全名),在这里我们写为(插件包名,插件类全名),即plugin_loader("controller_plugins","move_controller::MoveController")。
在调用包src目录新建path_plan.cpp文件,并添加内容如下:
#include "use_plugins/path_plan.h"
namespace path_ctrl
{
PathPlan::PathPlan():plugin_loader("controller_plugins","move_controller::MoveController")
{
line_velocity = 1.0;
angle_velocity = 0.5;
};
void PathPlan::init()
{
//根据输入参数(插件类名),通过插件加载对象创建相应的插件实例
move_controller = plugin_loader.createInstance("move_controller::MoveController");
//现在可以调用插件类的函数了
move_controller->set_line_velocity(line_velocity);
move_controller->set_angular_velocity(angle_velocity);
move_controller->print_sth();
}
}
2.2编写执行文件
在调用包src目录下新建main.cpp,简单内容如下:
#include "use_plugins/path_plan.h"
int main(int argc, char *argv[])
{
path_ctrl::PathPlan path_plan;
path_plan.init();
return 0;
}
如果有其他需求可以自己添加上去,这里就不做重复说明了。
2.3修改CMakeLists.txt文件
cmake_minimum_required(VERSION 2.8.3)
project(use_plugins)
#add_compile_options(-std=c++11)
find_package(catkin REQUIRED COMPONENTS
pluginlib
roscpp
controller_plugins
)
catkin_package(
INCLUDE_DIRS include
#LIBRARIES controller_plugins
#CATKIN_DEPENDS pluginlib roscpp
#DEPENDS system_lib
)
#添加头文件目录
include_directories(
include
${catkin_INCLUDE_DIRS}
)
#添加需要链接的库文件目录
link_directories(${CATKIN_DEVEL_PREFIX}/../lib)
#添加可执行文件
add_executable(${PROJECT_NAME}_node src/main.cpp src/path_plan.cpp)
#设置要链接的库文件的名称
target_link_libraries(${PROJECT_NAME}_node
controller_for_plugin
${catkin_LIBRARIES}
)
这里库文件目录如果不是很清楚可以自己去devel目录下查找就知道了。
如果对大括号里面的变量路径感到疑惑,可以使用MESSAGE指令用于打印显示,例如:
MESSAGE(STATUS "This is CATKIN DEVEL PREFIX dir " ${CATKIN_DEVEL_PREFIX})
2.4修改package.xml文件
也就是把插件包包含进去,内容如下,只显示修改之后的内容:
catkin
pluginlib
roscpp
controller_plugins
pluginlib
roscpp
pluginlib
roscpp
controller_plugins
最终调用包的目录结构如下所示:
欧克。下面就可以开始catkin_make编译了。编译成功之后,就可以通过rosrun use_plugins use_plugins_node运行该节点了,效果如下,可以发现调用成功: