ROS学习笔记五:创建library

ROS学习笔记五:创建library

很多时候都是调用系统编辑好的library或者别人做好的,自己编辑自己要用的library的时候比较少,不过了解之后,觉得还是很有用处,暂时的麻烦,可以为以后的编程提供很多便利,特别是自己项目要用到的,多几步操作,把一些有用的函数封装起来,以后调用只要加几行代码就可以了。


一.创建一个library

①先创建一个package存放相关文件
cs_create_pkg creating_a_ros_library roscpp std_msgs std_srvs

以下内容说明catkin_simple的好处,已get的忽略


在这里不用catkin_create_pkg创建包,上述指令中的cs的意思是catkin_simple ,可以规范catkin package以及简化CMakeLists文件,很多依赖项在package.xml文件中列出后就不需要在CMakeLists文件中再次列出,例如:
part 1 这是catkin_make package 的CMakeLists文件

cmake_minimum_required(VERSION 2.8.3)
project(example_service)

find_package(catkin REQUIRED COMPONENTS roscpp rospy message_generation std_msgs std_srvs)

 add_service_files(
   FILES
   ExampleServiceMsg.srv
 )

 generate_messages(
   DEPENDENCIES
   std_msgs
 )

catkin_package(

CATKIN_DEPENDS message_runtime

)

#在生成了service messages头文件之后再添加以下的信息
add_executable(example_service src/example_service.cpp)
target_link_libraries(example_service ${catkin_LIBRARIES})
#add_dependencies(example_service example_service_generate_messages_cpp)


add_executable(example_client src/example_client.cpp)
target_link_libraries(example_client ${catkin_LIBRARIES})
#add_dependencies(example_client example_client_generate_messages_cpp)

part 2 这是catkin_simple package 的CMakeLists文件

cmake_minimum_required(VERSION 2.8.3)
project(creating_a_ros_library)

find_package(catkin_simple REQUIRED)

catkin_simple()

cs_add_library(example_ros_library src/example_ros_class.cpp)   


cs_add_executable(ros_library_test_main src/example_ros_class_test_main.cpp)

target_link_libraries(ros_library_test_main example_ros_library)

cs_install()
cs_export()

对比以上两part可以发现用catkin_simple创建package确实比较好用,但是要转变习惯,毕竟习惯用catkin_make创建package。


回归正题
完成创建package后,可以发现生成了src和include文件夹,以及CMakeLists.txt、package.xml和README.md文件,catkin_make package是不会生成readme file的。

②然后借用上一章完成的class练习的代码并做一些修改,将example_ros_class.cpp复制到src文件夹中。
注意去掉example_ros_class.cpp中的main函数部分
如下:

#include "example_ros_class.h" //我们自定义类(class)的头文件

// 不得不通过节点句柄指针进入构造函数再有构造函数去构建subscriber
ExampleRosClass::ExampleRosClass(ros::NodeHandle* nodehandle):nh_(*nodehandle) // 构造函数
{ 
    ROS_INFO("in class constructor of ExampleRosClass");
    initializeSubscribers(); // 需要通过成员协助函数帮助构建subscriber,在构造函数中做初始化的工作
    initializePublishers();
    initializeServices();

    val_to_remember_=0.0; //初始化储存数据的变量的值
}


//以下是一个成员协助函数,帮助构建subscriber
void ExampleRosClass::initializeSubscribers()
{
    ROS_INFO("Initializing Subscribers");
    minimal_subscriber_ = nh_.subscribe("example_class_input_topic", 1, &ExampleRosClass::subscriberCallback,this);  
    //订阅了名称为"example_class_input_topic"的topic
    //&ExampleRosClass::subscriberCallback 是一个指向成员函数“ExampleRosClass”的指针
    // “this”的用意是指向当前实例(ExampleRosClass)
}

//与上相同
void ExampleRosClass::initializeServices()
{
    ROS_INFO("Initializing Services");
    minimal_service_ = nh_.advertiseService("example_minimal_service",
                                                   &ExampleRosClass::serviceCallback,
                                                   this);  

}

//与上相同
void ExampleRosClass::initializePublishers()
{
    ROS_INFO("Initializing Publishers");
    minimal_publisher_ = nh_.advertise<std_msgs::Float32>("example_class_output_topic", 1, true); 
}

//以下内容与之前在构建publishers/subscribers通信时,编写的publishers和subscribers节点相似
//大部分的工作都是在回调函数中完成的
void ExampleRosClass::subscriberCallback(const std_msgs::Float32& message_holder) {

    val_from_subscriber_ = message_holder.data; // 用成员变量储存回调的数据
    ROS_INFO("myCallback activated: received value %f",val_from_subscriber_);
    std_msgs::Float32 output_msg;
    val_to_remember_ += val_from_subscriber_; //储存每次调用的数据
    output_msg.data= val_to_remember_;

    minimal_publisher_.publish(output_msg); //在这里之所以可以使用publishers的功能,是因为在类中publishers也是其他一个成员函数,所以可以被调用
}


//与构建services/clients通信时相似
bool ExampleRosClass::serviceCallback(std_srvs::TriggerRequest& request, std_srvs::TriggerResponse& response) {
    ROS_INFO("service callback activated");
    response.success = true; // Trigger的消息类型是bool和string,对应的数据类型就是success和message
    response.message = "here is a response string";
    return true;
}

③然后到include文件夹中创建一个也叫“creating_a_ros_library”的文件夹,在里面复制上一章中创建的”example_ros_class.h“头文件

④修改“example_ros_class.cpp”文件中包含类的头文件的地址,如下

#include 

要注意这个格式,如果头文件和.cpp是在同一目录下的,就可以省略前面的packageName,若不是,就要加上头文件所在的packageName

⑤在CMakeLists.txt中添加创建新的library的指令

cs_add_library(example_ros_library src/example_ros_class.cpp)   

注意example_ros_library是这个新建的library的名称,生产这个library的源代码是example_ros_class.cpp

⑥这时候就可以编译catkin_make生成新的library了,生成的位置
~/ros_ws/devel/lib,名称为“libexample_ros_library.so”,这是在我们设定的新建library名称前加上了lib,但其实我们如果要调用这个新建的library,并不需要知道这个.so文件的名称,只需要知道它所在的packageName就行了,然后在package.xml中添加相应的依赖,如下:

<build_depend>creating_a_ros_librarybuild_depend>
<run_depend>creating_a_ros_libraryrun_depend>

非常方便。


二.在其他的包中调用新建的library

只要建好library,要调用就很简单了
①同样,用catkin_simple package再创建一个package
cs_create_pkg using_a_ros_library roscpp std_msgs std_srvs creating_a_ros_library
注意上述指令,最后添加了“creating_a_ros_library”,这会使生成的”package.xml”中添加该package为一个依赖项,这是一个小技巧,等同于手动在“package.xml”文件中依赖项

②在src文件夹中添加上一章class练习中的“example_ros_class.cpp”文件
注意只保留main函数部分及头文件声明部分,其他去掉
头文件所在的package是creating_a_ros_library
代码如下:

int main(int argc, char** argv) 
{
    // ROS set-ups:
    ros::init(argc, argv, "exampleRosClass"); //节点名

    ros::NodeHandle nh; // 必须在main函数中创建一个节点句柄给构造函数使用,并且需要把它传递到构造函数中

    ROS_INFO("main: instantiating an object of type ExampleRosClass");
    ExampleRosClass exampleRosClass(&nh);  //实例化一个ExampleRosClass对象并将指针传递给nodehandle以供构造函数使用

    ROS_INFO("main: going into spin; let the callbacks do all the work");
    ros::spin();
    return 0;
} 

③最后,只需要在”CMakeLists.txt文件中添加生成执行文件的指令就可以了,不需要添加新建library的依赖声明,因为我们使用的是catkin_simple package,并且在package.xml文件中已经做出了声明。

cs_add_executable(ros_library_external_test_main src/example_ros_class_test_main.cpp)

其中ros_library_external_test_main是可执行程序名称,后面跟着的是源代码。

然后编译完成后就可以运行测试了
catkin_make
rosrun using_a_ros_library ros_library_external_test_main

至此就完成了调用新建library的操作。

你可能感兴趣的:(ROS,learning)