zoukankan      html  css  js  c++  java
  • ros程序源码分析3--action篇

    客户端程序

    头文件部分:

    1 #include <new_pkg/DoDishesAction.h> // Note: "Action" is appended
    2 #include <actionlib/client/simple_action_client.h>

    此处,actionlibaction编程的库,包含action编程的所有函数、类及命名空间,详细列举见http://docs.ros.org/api/actionlib/html/namespaceactionlib.html

    本代码应用的是简单action建立的两个类SimpleActionClientSimpleActionServer两个类,分别定义于simple_action_client.hsimple_action_server.h中。

    进入ROS安装的目录可以看到

    actionlib下有clientserver两个文件夹,上述两个头文件分别在这两个文件夹中。

    另外一点注意到,本程序开始没有包含ros/ros.h头文件,是因为在上面两个action头文件中都包含了ros.h

    在编译完自定义的DoDishes.action文件后,在以下目录中会生成7个头文件。

    DoDishesAction.h中,包含了另外带Action字样的头文件,

    1 #include <new_pkg/DoDishesActionGoal.h>
    2 #include <new_pkg/DoDishesActionResult.h>
    3 #include <new_pkg/DoDishesActionFeedback.h>

    在其余三个头文件中,又分别包含了另外三个不带Action字样的头文件,比如在DoDishesActionGoal.h中有

    1 #include <new_pkg/DoDishesGoal.h>

    所以只需要包含DoDishesAction.h,其他所有的头文件就都包含了进来。

    主程序

     1 typedef actionlib::SimpleActionClient<new_pkg::DoDishesAction> Client;
     2 int main(int argc, char** argv)
     3 {
     4     ros::init(argc, argv, "do_dishes_client");
     5     Client client("do_dishes", true); // true -> don't need ros::spin()
     6     client.waitForServer();
     7     new_pkg::DoDishesGoal goal;
     8     goal.dishwasher_id=3; //id为3的盘子为目标
     9     client.sendGoal(goal,&doneCb,&activeCb,&feedbackCb);
    10     client.waitForResult(ros::Duration(11.0));
    11     printf("Current State: %s
    ", client.getState().toString().c_str());
    12     return 0;

    在定义对象之初就需要用数据类型对抽象类进行实例化,因为类型名称长所以用typedef另起一个短的名字(第一行)

    SimpleActionClient类的构造函数中

    第一个参数是action名称,第二个参数表示是否使用内置spin函数,如果为真则始终开启消息回调函数,如果为假则需要自定义调用回调函数。构造函数有带NodeHandle参数和不带的类型,不带的就在函数内自动加,更省事。

    客户端要用到的函数:

    client.waitForServer(),客户端等待服务器函数,可以传递一个ros::Duration作为最大等待值,程序进入到这个函数开始挂起等待,服务器就位或者达到等待最大时间退出,前者返回true,后者返回false.

    client.waitForResult(),客户端等待服务器本次结束函数,同上可以传递最大等待值。接收到True意味着本次交互服务器端已完成。

    client.sendGoal(),客户端发送目标函数,本函数一共需要四个参数。第一个必须,另三个可选。

    第一个参数就是本次交互的目标,第二个参数是服务端运行结束时调用的函数,第三个是服务器刚刚收到参数激活时调用的函数,第四个是服务器在进程中不停回传反馈时调用的函数。

    其中三个函数的返回值类型定义如下

    在这出现了boost::function这个东西,它是一个函数对象的容器,尖括号里是函数签名,圆括号外是返回类型,圆括号里是参数类型,符合函数签名的函数可以被存放到boost::function定义的对象中。比如

     1 bool bigger(int i,int j)
     2 {
     3     return (i>j); 
     4 }
     5 int main()
     6 {
     7     boost::function<bool (int,int)> f;
     8     f=&bigger;
     9     f(10 , 9);
    10 }

    main函数中f(10,9)就等同于bigger(10,9).

    所以上面的三个类型也定义了要传递的三个函数的类型。

    本程序中这三个程序如下

     1 void activeCb()
     2 {
     3     ROS_INFO("Goal just went active");
     4 }
     5 void doneCb(const actionlib::SimpleClientGoalState& state,const new_pkg::DoDishesResultConstPtr& result)
     6 {
     7     ROS_INFO("Yay! %u dish(s) are now clean",result->total_dishes_cleaned);
     8     ROS_INFO("Current State: %s",state.toString().c_str());
     9     ros::shutdown();//ROS_INFO也将不能用,不影响printf
    10 }
    11 void feedbackCb(const new_pkg::DoDishesFeedbackConstPtr& feedback)
    12 {
    13     ROS_INFO(" percent_complete : %f ", feedback->percent_complete);
    14 }

    注意里面的Ptr结尾的是指针类型,在索引其成员时用->而不能直接用.运算符。

    当调用ros::shutdown()函数来关闭节点时,会终结所有开放的订阅,发布,服务,调用。

    对结果信息和对结束状态信息的处理在doneCb()函数中,对反馈信息的处理在feedbackCb()函数中,如果不需要处理这些信息,可以在sendGoal()函数参数处缺省。

    注意传递函数的时候在sendGoal中加&.

    服务器端程序

    1 typedef actionlib::SimpleActionServer<new_pkg::DoDishesAction> Server;             
    2 int main(int argc, char** argv)
    3 {
    4     ros::init(argc, argv, "do_dishes_server");
    5     Server server("do_dishes", boost::bind(&execute, _1, &server), false);
    6     server.start();
    7     ros::spin();
    8     return 0;
    9 }

    程序中用到的构造函数原型如下:

    其中typedef boost::function< void(const GoalConstPtr &)>  ExecuteCallback

    最后一个参数auto_start为是否自动启动,若为true则服务器一构建就开始启动,若为false就不自动启动等程序中的server.start()函数来启动。      

    中间execute_callback这个参数也是一个函数容器,接收<void(const GoalConstPtr &)>类型的函数。

    这个函数在server接收到新goal是运行一次,如果在这个执行函数中要用到server本身,就需要代码中这样使用。

    因为构造函数中的这个函数参数只允许有一个形参,要把server本身也传进去使用boost::bind(&execute,_1,&server)这个语句。

    boost::bind()函数功能的简要说明:

    boost::bind(&fun, _2, 6)( 2, 5); // 等同与 fun(5, 6)

    也就是说代码中的boost语句相当于  execute(  ,&server),第一个参数没有值,形成的新函数就相当于只有一个形参。第二个参数怎么传递看自定义的execute第二个形参是怎么使用的。

    execute()函数的定义如下:

     1 void execute(const new_pkg::DoDishesGoalConstPtr& goal, Server* as)
     2 {
     3     new_pkg::DoDishesFeedback feedback;
     4     ROS_INFO("Dishwasher %u is working.",goal->dishwasher_id);
     5 
     6     ros::Rate r(2);
     7     for(int i=1;i<=10;i++)
     8     {
     9         feedback.percent_complete=i*10;
    10         as->publishFeedback(feedback);
    11         r.sleep();
    12     }
    13     ROS_INFO("Dishwasher %u finish working.",goal->dishwasher_id); 
    14     new_pkg::DoDishesResult result;
    15     result.total_dishes_cleaned=1; 
    16     as->setSucceeded(result);
    17 }

    goal的接收和结果及反馈的发布都在这个函数当中。

    发布反馈用publishFeedback()函数,函数重载为两种形式,参数可以是反馈信息本身也可以是其地址。

    发布成功结果用setSucceeded()函数,可以传递两个参数,第一个就是result,第二个是文本信息,关于文本信息在客户端的接收目前我还没有去学习。

    发布失败结果用setAborted(),参数同上。

  • 相关阅读:
    hdu 4777 树状数组+合数分解
    hdu5635 BestCoder Round #74 (div.2)
    hdu 5636 搜索 BestCoder Round #74 (div.2)
    hdu 5637 BestCoder Round #74 (div.2)
    hdu4605 树状数组+离散化+dfs
    hdu4521 线段树+dp
    hdu3340 线段树+多边形
    孜孜不倦,必能求索;风尘仆仆,终有归途。
    增加、删除类文件或者在一个类中增加、删除方法时,是不能够热部署到服务上的。这时候需要停止服务器重新部署后再启动,就不会出现上面的提示了。
    为什么jdk1.8不支持sql.append,该如何解决
  • 原文地址:https://www.cnblogs.com/xuhaoforwards/p/9452510.html
Copyright © 2011-2022 走看看