四、ROS工程应用基础:ROS Class

  • Post author:
  • Post category:其他




四、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 版权协议,转载请附上原文出处链接和本声明。