四、ROS工程应用基础ROS Class
上篇是关于ROS功能包的理解,本质上还是理解工作空间。
下面就索性直接切入正题:关于如何把ROS应用到工程的一个基础。单独的publisher、subscriber、server、client、msg的编写是绕不开的基础,所以先是这些组件编写。但是单独编写它们是几乎没有应用价值的,因为一个完整的工程要自成系统,而不是节点的堆积。
a.组件编写
(1)Publisher/Subscriber
//publisher:节点talker通过发布者chatter_pub发布话题/chatter
ros::init(argc, argv, "talker");
ros::NodeHandle n;//可以去了解一下“句柄”的概念
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);//"<>"内指明发布的消息类型,1000表示队列缓存
ros::Rate loop_rate(10);//10HZ
while (ros::ok())
{
...
chatter_pub.publish(msg);
loop_rate.sleep();//配合上面的10HZ
...
}
/*********************************************
代码中有不少东西是ros的基本操作
留给publisher本身的东西其实就一个定义和使用
ros的基本操作也很好理解:注册节点、句柄;设置频率;
还有就是很重要,但是上面还没有体现的处理回调函数
*********************************************/
//subscriber:节点listener通过订阅者sub订阅话题/chatter
void chatterCallback(const std_msgs::String::ConstPtr& msg) //个人觉得是ros基础组件中最牛的设计,可以去了解一下回调函数
{
ROS_INFO("%s", msg -> data.c_str());
}
...
ros::init(argc, argv, "listener");
ros::NodeHandle n;
ros::Subscriber sub = n.subscrib("chatter", 1000, chatterCallback);
ros::spin(); //处理回调函数
/********************************************
本人专业学校没有教很深的C++甚至C,主要是自己学
会的不多,但是看到这个回调函数后真的觉得很牛
********************************************/
(2)msg
//自定义话题消息
//在功能包下创建msg文件夹,在msg文件夹中创建.msg文件
string name
uint8 sex
uint8 age
uint unknown = 0
uint male = 1
uint female = 2
//编译成功后可以用rosmsg show xxx查看消息类型
(3)Server/Client
//service提供了一种指令型的通讯
//创建服务内容
//创建srv文件夹,添加服务消息文件.srv
//.srv文件分为两个数据区域,上半是请求区域,下半是应答区域,之间用“---”分隔
int64 a
int64 b
---
int64 sum
//创建server
#include "ros/ros.h"
#include "learning_communication/AddTwoInts.h" //根据.srv文件自动生成
bool add(learning_communication::AddTwoInts::Request &req, //这里还说明功能包的名字和普通文件夹的名字是有本质区别的
learning_communication::AddTwoInts::Response &res) //service回调函数
{
res.sum = req.a + req.b;
ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
ROS_INFO("sending back response: [%ld]", (long int)res.sum);
return true; //返回值为bool适合执行一条独立的指令
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "add_two_ints_server");
ros::NodeHandle n;
ros::ServiceServer service = n.advertiseService("add_two_ints", add);
ROS_INFO("Ready to add two ints.");
ros::spin();
return 0;
}
/*******************************
起初了解了subscriber的回调函数功能,
不理解为什么还需要服务这种通讯,
了解了具体实现后就自然而然明白了原因
*******************************/
//创建client
#include <cstdlib>
#include "ros/ros.h"
#include "learning_communication/AddTwoInts.h"
int main(int argc, char **argv)
{
ros::init(argc, argv, "add_two_ints_client");
if (argc != 3)
{
ROS_INFO("usage: add_two_ints_client X Y");
return 1;
}
ros::NodeHandle n;
ros::ServiceClient client = n.serviceClient<learning_communication::AddTwoInts>;
learning_communication::AddTwoInts srv; //服务内容创建
srv.request.a = atoll(argv[1]);
srv.request.b = atoll(argv[2]);
if (client.call(srv))
{
ROS_INFO("Sum: %ld", (long int)srv.response.sum);
}
else
{
ROS_ERROR("Failed to call service add_two_ints");
return 1;
}
return 0;
}
b.类编写
//ROS类编写真的是一种不一样的感受,结构清晰,工程应用性强,而且灵活
//原文:https://blog.csdn.net/Will_Ye/article/details/79559525
#include <math.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <ros/ros.h>
#include <std_msgs/Bool.h>
#include <std_msgs/Float32.h>
#include <std_srvs/Trigger.h>
class ExampleRosClass
{
public:
ExampleRosClass(ros::NodeHandle* nodehandle); //很重要的一步,关乎调用
private:
ros::NodeHandle nh_; //串联一切
ros::Subscriber minimal_subscriber_; //加下划线的意思是提示只能在该类中被调用
ros::ServiceServer minimal_service_;
ros::Publisher minimal_publisher_;
double val_from_subscriber_; //存储订阅获得的数据,便于传递给其他成员函数
double val_to_remember_; //存储变量数据
//----------------------------------------------------------具体的功能函数声明
void initializeSubscribers();
void initializePublishers();
void initializeServices();
void subscriberCallback(const std_msgs::Float32& message_holder); //subscriber回调函数的原型
bool serviceCallback(std_srvs::TriggerRequest& request, std_srvs::TriggerResponse& response);
//----------------------------------------------------------
};
ExampleRosClass::ExampleRosClass(ros::NodeHandle* nodehandle):nh_(*nodehandle)
{
ROS_INFO("in class constructor of ExampleRosClass");
initializeSubscribers();
initializePublishers();
initializeServices();
val_to_remember_ = 0.0;
}
void ExampleRosClass::initializeSubscribers()
{
ROS_INFO("Initializing Subscribers");
minimal_subscriber_ = nh_.subscribe("example_class_input_topic", 1, &ExampleRosClass::subscriberCallback, this);
}
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);
}
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_;
}
bool ExampleRosClass::serviceCallback(std_srvs::TriggerRequest& request, std_srvs::TriggerResponse& response)
{
ROS_INFO("service callback activatied");
response.success = true;
response.message = "here is a response string";
return true;
}
int main(int argc, char** argv)
{
ros::init(argc, argv, "exampleRosClass");
ros::NodeHandle nh;
ROS_INFO("main: instantiating an object of type ExampleRosClass");
ExampleRosClass exampleRosClass(&nh);
ROS_INFO("main:going into spin; let the callbacks do all the work");
ros::spin();
return 0;
}
c.编译
##一般的编译文件示例
##CMakeLists.txt
cmake_minimum_required(VERSION 3.0.2)
project(learning_topic)
find_package(catkin REQUIRED COMPONENTS
geometry_msgs
roscpp
std_msgs
nav_msgs
turtlesim
)
find_package(OpenCV REQUIRED)
# include
${catkin_INCLUDE_DIRS}
)
include_directories(include ${OpenCV_INCLUDE_DIRS})
include_directories (include)
add_executable(velocity_publisher src/cmd_vel_publisher.cpp)
target_link_libraries(velocity_publisher ${catkin_LIBRARIES})
add_executable(velocity_sub src/listener.cpp)
target_link_libraries(velocity_sub ${catkin_LIBRARIES})
add_executable(class src/class.cpp)
target_link_libraries(class ${catkin_LIBRARIES} ${OpenCV_LIBS} )
//package.xml
<buildtool_depend>catkin</buildtool_depend>
<build_depend>geometry_msgs</build_depend>
<build_depend>roscpp</build_depend>
<build_depend>std_msgs</build_depend>
<build_depend>nav_msgs</build_depend>
<build_export_depend>geometry_msgs</build_export_depend>
<build_export_depend>roscpp</build_export_depend>
<build_export_depend>std_msgs</build_export_depend>
<build_export_depend>nav_msgs</build_export_depend>
<exec_depend>geometry_msgs</exec_depend>
<exec_depend>roscpp</exec_depend>
<exec_depend>std_msgs</exec_depend>
<exec_depend>nav_msgs</exec_depend>
版权声明:本文为BAOLI_LONG原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。