ros通信机制整理

ros通信机制整理

ros通信机制分为话题,服务和参数三部分,本文把动作action也列入其中,主要介绍话题topic,服务server和动作action:

话题通信

话题通信相对来说比较简单,就是一个talker和一个listener,前者发布话题,后者订阅相同名称的话题,并绑定一个回调函数,进阶的部分就是自定义消息类型

自定义消息类型

与src并列建立msg文件夹,自定义name.msg,并把数据类型写进去,比如自定义Num.msg:

string first_name
string last_name
uint8 age
uint32 score

写入后分别修改CMakeLists.txt和package.xml,修改内容如下:

  1. find_package中加入message_generation
  2. add_message_files(FILES Num.msg) generate_messages( DEPENDENCIES std_msgs ) 这样才能生成头文件
  3. catkin_package中加入message_runtime
  4. <build_depend>message_generation</build_depend>
  5. <exec_depend>message_runtime</exec_depend>
    catkin_make之后会在workspace的devel/include/项目名称 中找到Num.h
编写节点

在src中自定义sendtest.cpp 和 recvtest.cpp ,分别写入:(这里没有用到自定义msg,可以用)

sendtest.cpp

int main(int argc,char **argv)
{
   ros::init(argc,argv,"sendtest");
   ros::NodeHandle n;
   ros::Publisher chatter_pub=n.advertise<std_msgs::String>("velocity",1000);  
   //定义一个话题名字为velocity,类型为std_msgs::String,名称和类型都可以根据自己的修改 
   ros::Rate loop_rate(10);
   while(ros::ok())
   {
      std_msgs::String msg;
      std::stringstream ss;
      ss<<"1";
      msg.data=ss.str();
      ROS_INFO("%s",msg.data.c_str());
      chatter_pub.publish(msg);  //将类型为std_msgs::String的msg发布出去
      ros::spinOnce();
      //loop_rate.Sleep();
   }
   return 0;
}

recvtest.cpp

 //定义回调函数
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
   ROS_INFO("receive velocity: [%s]",msg->data.c_str());
}

int main(int argc, char **argv)
{
   ros::init(argc,argv,"recvtest");
   ros::NodeHandle n;
   ros::Subscriber sub=n.subscribe("velocity",1000,chatterCallback); 
   //订阅名称为velocity的话题,并绑定名称为chatterCallback的回调函数
   ros::spin();
   return 0;
}

写完后CMakeLists中修改,生成可执行文件:

 add_executable(${PROJECT_NAME}_send src/sendtest.cpp)
 add_executable(${PROJECT_NAME}_recv src/recvtest.cpp)

## Rename C++ executable without prefix

## Add cmake target dependencies of the executable
## same as for the library above
add_dependencies(${PROJECT_NAME}_send ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS} commtest_generate_messages_cpp)
add_dependencies(${PROJECT_NAME}_recv ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS} commtest_generate_messages_cpp)

## Specify libraries to link a library or executable target against
target_link_libraries(${PROJECT_NAME}_send ${catkin_LIBRARIES})
target_link_libraries(${PROJECT_NAME}_recv ${catkin_LIBRARIES})

catkin_make之后rosrun就可以啦!

话题通信

在我看来话题通信比话题通信多了一个应答,不会一直发布一直订阅,占用内存,有应答的时候再去做处理

自定义server

在src并列建立文件夹srv,并建立AddTwoInts.srv,将如下内容写入:

int64 a
int64 b
---
int64 sum

分界线以上为client发的request,以下为server相应的response
同样的修改CMakeLists.txt和package.xml,修改内容如下:

  • find_package中加入message_generation
  • add_service_files( FILES AddTwoInts.srv ) generate_messages( DEPENDENCIES std_msgs )
    这样才能生成头文件
  • catkin_package中加入message_runtime
  • <build_depend>message_generation</build_depend>
  • <exec_depend>message_runtime</exec_depend>

catkin_make之后会在workspace的devel/include/项目名称 中找到三个头文件:

  • AddTwoInts.h
  • AddTwoIntsRequest.h
  • AddTwoIntsResponse.h
编写服务

在src中自定义server.cpp 和 client.cpp,分别写入:
client.cpp

#include"ros/ros.h"
#include"std_msgs/String.h"
#include"srvclient/AddTwoInts.h" //头文件         

int main(int argc, char **argv)
{
   ros::init(argc,argv,"client");
   ros::NodeHandle n;
   ros::ServiceClient client = n.serviceClient<srvclient::AddTwoInts>("add_ints"); 
   //client的类型为srvclient::AddTwoInts 就是工程名称::srv名称 
   //client的名称为add_ints
   srvclient::AddTwoInts add_srv;  //对add_srv的request赋值
   add_srv.request.a = 5;
   add_srv.request.b = 4;
   if(client.call(add_srv)){  //类似回调函数,client.call 
     ROS_INFO("SERVICE SUCCESS RESULT: %d", add_srv.response.sum);
   }
   else{
     ROS_INFO("SERVICE FAIL");
     return 1;
   }
   ros::spin();
   return 0;
}

server.cpp

#include"ros/ros.h"
#include"std_msgs/String.h"
#include"srvclient/AddTwoInts.h"
//add函数的定义,传进来的参数一个是request类型的引用,一个是response类型的引用
bool add(srvclient::AddTwoInts::Request &req,
         srvclient::AddTwoInts::Response &res)
{
  res.sum = req.a + req.b;
  ROS_INFO("a=%d, b=%d a+b=%d", (int)req.a, (int)req.b, (int)res.sum);
  return true;
}

int main(int argc, char **argv)
{
   ros::init(argc,argv,"server");
   ros::NodeHandle n;
   ros::ServiceServer server = n.advertiseService("add_ints", add); 
    //server的类型为srvclient::AddTwoInts 就是工程名称::srv名称 
   //server的名称为add_ints 并绑定add函数
   ROS_INFO("add 2 ints. ");
   ros::spin();
   return 0;
}

修改CMakeLists

add_executable(server src/srv.cpp)
add_executable(client src/client.cpp)

add_dependencies(server srvclient_generate_message_cpp)
add_dependencies(client srvclient_generate_message_cpp)

target_link_libraries(server ${catkin_LIBRARIES})
target_link_libraries(client ${catkin_LIBRARIES})

就可以啦!

动作通信

在我看来,动作通信比服务通信多了反馈,知道过程中的情况
client向server发布目标和取消任务;
server向client发布当前状态,实时反馈和任务执行的最终结果

自定义action

在src并列建立文件夹action 建立readbook.action 写入:

#define the goal  定义目标
uint32 total_pages
---
#define the result 定义结果
bool is_finish
---
#define a feedback message 定义反馈消息
uint32 reading_page

同样修改:

  • find_package中加入actionlib actionlib_msgs
  • add_action_files( FILES readbook.action ) generate_messages( DEPENDENCIES actionlib_msgs )
    这样才能生成头文件
  • catkin_package中加入actionlib和 actionlib_msgs
  • <build_depend>actionlib</build_depend> <build_depend>actionlib_msgs</build_depend>
  • <exec_depend>actionlib</exec_depend> <exec_depend>actionlib_msgs</exec_depend>
    应该就可以了,会生成好几个:
  • readbookAction.h
  • readbookActionFeedback.h
  • readbookActionGoal.h
  • readbookActionResult.h
  • readbookFeedback.h
  • readbookGoal.h
  • readbookResult.h
编写动作

在src中定义read_book_server.cpp和read_book_client.cpp,分别写入:
read_book_client.cpp定义Client客户端,发出action请求:

#include <ros/ros.h>
#include <actionlib/client/simple_action_client.h> //注意头文件
#include "actionlib_test/readbookAction.h" //包含的是readbookAction.h

//完成action后,传入的两个参量,类型分别是state和result
void donecb(const actionlib::SimpleClientGoalState& state,
            const actionlib_test::readbookResultConstPtr& result){
  ROS_INFO("FINISH READING!");
  ros::shutdown();
}
//激活后调用
void activecb(){
  ROS_INFO("Goal is active! Begin to Read!");
}
//收到feedback后调用该回调函数,传入的类型为feedback
void feedbackcb(const actionlib_test::readbookFeedbackConstPtr& feedback){
      ROS_INFO("Reading page: %d", feedback->reading_page);
}

int main(int argc, char** argv)
{
  ros::init(argc, argv, "read_book_client");
  actionlib::SimpleActionClient<actionlib_test::readbookAction> client("read_book",true);
  // 定义了一个客户端,注意其类型
  ROS_INFO("WAIT FOR ACTION SERVER TO START");
  //等待服务器
  client.waitForServer();
  ROS_INFO("Action server started");
  //创建一个action的目标goal,注意类型
  actionlib_test::readbookGoal goal;
  goal.total_pages = 10;
  //发送action的goal给服务器,并设置回调函数,有三个:完成 激活 反馈
  client.sendGoal(goal, &donecb, &activecb, &feedbackcb);

  ros::spin();

  return 0;
}

read_book_server.cpp实现服务器节点,完成action,并反馈

#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include "actionlib_test/readbookAction.h"
//定义excute函数,传入参量一个为goal,一个为server类型的指针,as(action server
void execute(const actionlib_test::readbookGoalConstPtr& goal,
             actionlib::SimpleActionServer<actionlib_test::readbookAction>* as)
{
  ros::Rate r(1);
  actionlib_test::readbookFeedback feedback; //定义反馈
  ROS_INFO("Begin to read %d pages.", goal->total_pages);
  for(int i =0; i < goal->total_pages ; i++){
    feedback.reading_page = i;
    as->publishFeedback(feedback); //由于 ros::Rate r(1)所以以1Hz发布反馈
    r.sleep();
  }
  //action完成后,设置成功
  ROS_INFO("All pages is read.");
  as->setSucceeded();

}

int main(int argc, char** argv)
{
  ros::init(argc, argv, "read_book_server");
  ros::NodeHandle n;
  actionlib::SimpleActionServer<actionlib_test::readbookAction> server(n, "read_book", boost::bind(&execute, _1, &server), false);
  //定义一个server服务器,,绑定excute函数
  server.start();//server开始运行

  ros::spin();

  return 0;
}

修改CMakeLists

add_executable(${PROJECT_NAME}_client src/read_book_client.cpp)
add_executable(${PROJECT_NAME}_server src/read_book_server.cpp)

## Add cmake target dependencies of the executable
## same as for the library above
add_dependencies(${PROJECT_NAME}_client ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(${PROJECT_NAME}_server ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
## Specify libraries to link a library or executable target against
 target_link_libraries(${PROJECT_NAME}_client  ${catkin_LIBRARIES} )
 target_link_libraries(${PROJECT_NAME}_server  ${catkin_LIBRARIES} )

就可以啦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值