用小乌龟案例演示“话题通信、动作通信、服务通信以及参数服务器”

乌龟节点操作原理解析

乌龟节点的话题

乌龟节点的操作方式和动态参数调节的方式很像,乌龟节点所在turtlesim功能包的节点将大部分话题通信的订阅端和服务通信的服务端全部封装起来使用命令行的格式对它们进行可视化显示。Turtlesim_node是turtlesim功能包下自带的一个节点,需要在命令行启动,只有该节点启动那么turtlesim功能包下所有节点的话题通信的订阅端和服务通信的服务端才可以启动

turtlesim功能包节点的服务通信的topic:

Service的topic

命名空间

Topic中数据的含义

/clear

全局话题

重新设置命令行背景颜色(RGB三元色)

/reset

全局话题

恢复乌龟节点的默认设置并重新设置命令行背景颜色

/kill

全局话题

结束turtlesim功能包下某个节点的运行

/spawn

全局话题

在原有基础上再生成一个乌龟节点

/turtle_name/set_pen

私有话题

设置“是否显示背景”和“背景的RGB三元色”

Turtle_ name/teleport_absolute

私有话题

是乌龟运行到(x,y)处且乌龟头朝与X轴呈θ角度的方向

Turtle_ name/teleport_relative

私有话题

使乌龟以angle角度再运行linear距离

注意:turtle_name表示自己创建乌龟节点的节点名称,那么turtle_ name/topic_name表示节点专属的私有话题,/topic_name表示大家共有的全局话题。

turtlesim功能包节点的话题通信的topic:

话题通信的topic

Topic命名空间

Topic中数据的含义

Turtle_ name/pose

私有话题

乌龟节点的位姿

Turtle_ name/cmd_vel

私有话题

位姿的控制

注意:乌龟节点的位姿包括“乌龟的运动线速度、运动角速度、乌龟的坐标以及乌龟头的朝向”。

乌龟节点的使用原则

我们从ROS官方说明中可以得知,对于一个topic而言我们不需要全部实例化它的接收方和发布方,我们只需要实例化其中的一个再结合官方封装的GUI即可完成某项功能。

1. 话题通信:需要实例化发布端的topic

官方中提到的subscribed topics表明“官方已经创建了topic为turtle_name/cmd_vel的订阅方”,我们只需要实例化topic为turtle_name/cmd_vel的发布方即可。turtle_name/cmd_vel话题中包含着“我们想要乌龟运动的速度信息”。

订阅/发布该topic的双方完成的功能为:订阅端(官方实现)利用发布端(自己实现)传来的坐标和运动状态信息在GUI界面显示乌龟的运动。

2. 话题通信:需要实例化订阅端的topic

官方中提到的subscribed topics表明“官方已经创建了topic为turtle_name/pose的发布端”,我们只需要实例化topic为turtle_name/pose的订阅端即可。turtle_name/pose话题中包含着“乌龟运动的状态信息”。

订阅/发布该topic的双方完成的功能为:在订阅端处(自己实现)可以实时的收到发布端(官方实现)传来的乌龟位姿信息

3. 服务通信:需要实例化客户端的topic

Service的topic

Topic中数据的含义

/clear

重新设置命令行背景颜色(RGB三元色)

/reset

恢复乌龟节点的默认设置并重新设置命令行背景颜色

/kill

结束turtlesim功能包下某个节点的运行

/spawn

在原有基础上再生成一个乌龟节点

/turtle_name/set_pen

设置“是否显示背景”和“背景的RGB三元色”

Turtle_ name/teleport_absolute

是乌龟运行到(x,y)处且乌龟头朝与X轴呈θ角度的方向

Turtle_ name/teleport_relative

使乌龟以angle角度再运行linear距离

4. 参数服务器中需要上报的参数:

我们可以看到当我们运行turtlesim功能包下官方自带的turtlesim_node节点再GUI界面中创建名为turtle1的小乌龟后,我们可以使用rosparam list查看到:

这三个参数是属于turtle_name节点的私有参数。

乌龟节点使用实操

服务通信

服务通信详解:ROS服务通信,最详细最易懂(从文件构建到原理解析)_超级霸霸强的博客-CSDN博客_ros通信https://blog.csdn.net/weixin_45590473/article/details/121208056

启动官方节点实例化的对象和GUI界面

命令:rosrun turtlesim turtlesim_node

运行结果:

注意:这一步操作必须要有,GUI界面的启动标志着官方节点中的对象被成功创建。

在原有基础上创建新的乌龟节点

#include "ros/ros.h"  
#include "turtlesim/Spawn.h"  
  
int main(int argc,char* argv[])  
{  
    setlocale(LC_ALL,"");  
    ros::init(argc,argv,"gen_turtle");  
    ros::NodeHandle nh;  
    ros::ServiceClient client = nh.serviceClient<turtlesim::Spawn>("/spawn");  
    turtlesim::Spawn SpawnInfo;  
    SpawnInfo.request.name = "turtle2";  
    SpawnInfo.request.theta = 10;  
    SpawnInfo.request.x = 1;  
    SpawnInfo.request.y = 2;  
    client.waitForExistence();  
    bool Flag = client.call(SpawnInfo);  
    if(Flag){  
        ROS_INFO("Successfully create %s\n\t",SpawnInfo.response.name.c_str());  
    }  
    return 0;  
} 

首先spawn为服务通信的topic,这个服务通信的消息类型如下所示:

参数分别由4个,name用于指定新建乌龟节点的名称、(x,y)代表乌龟初始的位置、theta代表初始时乌龟头的朝向。

运行结果:在GUI界面中生成一个名为turtle2的小乌龟。

改变GUI中乌龟的位置(绝对位置)

#include "ros/ros.h"  
#include "turtlesim/TeleportAbsolute.h"  
  
int main(int argc,char* argv[])  
{  
    setlocale(LC_ALL,"");  
    ros::init(argc,argv,"local_change");  
    ros::NodeHandle nh("turtle1");  
    ros::ServiceClient client = nh.serviceClient<turtlesim::TeleportAbsolute>("teleport_absolute");  
    turtlesim::TeleportAbsolute DistanceAbsolute;  
    DistanceAbsolute.request.theta = 10;  
    DistanceAbsolute.request.x = 19;  
    DistanceAbsolute.request.y = 10;  
    client.waitForExistence();  
    bool Flag = client.call(DistanceAbsolute);  
    if(Flag){  
        ROS_INFO("moves successful\n\t");  
    }  
    return 0;  
}

注意:

与乌龟运动位置相关的话题teleport_absolute和teleport_relative均为节点turtle1的私有话题,私有话题的订阅/发布方式与普通话题的订阅发布方式的唯一区别就是:

ros::NodeHandle nh("turtle1");   

给节点句柄加上命名空间即可。

Turtlesim::TeleportAbsolute数据类型包含的参数如下所示:

运行的结果:

小乌龟从初始位置移动至(x,y)=(19,10)并且乌龟头朝向偏离X正版轴10度的方向。

改变GUI中乌龟的位置(偏移位置)

#include "ros/ros.h"  
#include "turtlesim/TeleportRelative.h"  
  
int main(int argc,char* argv[])  
{  
    setlocale(LC_ALL,"");  
    ros::init(argc,argv,"local_change");  
    ros::NodeHandle nh("turtle1");  
    ros::ServiceClient client = nh.serviceClient<turtlesim::TeleportRelative>("teleport_relative");  
    turtlesim::TeleportRelative DistanceRelative;  
    DistanceRelative.request.angular = 10;  
    DistanceRelative.request.linear = 19;  
    client.waitForExistence();  
    bool Flag = client.call(DistanceRelative);  
    if(Flag){  
        ROS_INFO("moves successful\n\t");  
    }  
    return 0;  
} 

杀死某个乌龟节点的运行进程

#include "ros/ros.h"  
#include "turtlesim/Kill.h"  
  
int main(int argc,char* argv[])  
{  
    setlocale(LC_ALL,"");  
    ros::init(argc,argv,"kill_turtle");  
    ros::NodeHandle nh;  
    ros::ServiceClient client = nh.serviceClient<turtlesim::Kill>("/kill");  
    turtlesim::Kill killNode;  
    killNode.request.name = "turtle1";  
    client.waitForExistence();  
    bool Flag = client.call(killNode);  
    if(Flag){  
        ROS_INFO("successful kill node\n\t");  
    }  
    return 0;  
} 

在turtlesim::Kill数据类型中turtlesim::Kill.request.name用于指定杀死目标节点的名称。

运动轨迹属性设置

#include "ros/ros.h"  
#include "turtlesim/SetPen.h"  
  
int main(int argc,char* argv[])  
{  
    setlocale(LC_ALL,"");  
    ros::init(argc,argv,"gen_turtle");  
    ros::NodeHandle nh("turtle1");  
    ros::ServiceClient client = nh.serviceClient<turtlesim::SetPen>("set_pen");  
    turtlesim::SetPen SetPenInfo;  
    SetPenInfo.request.b = 10;  
    SetPenInfo.request.r = 10;  
    SetPenInfo.request.g = 10;  
    SetPenInfo.request.off = 0;  
    SetPenInfo.request.width = 10;  
    client.waitForExistence();  
    bool Flag = client.call(SetPenInfo);  
    if(Flag){  
        ROS_INFO("Successfully set\n\t");  
    }  
    return 0;  
} 

turtlesim::SetPen数据类型如下所示:

r/g/b分别代表RGB三元色所占的比重,取值范围为(0,255),width代表轨迹的线宽,off代表是否显示运动轨迹(0:显示,非0:不显示)。

运行结果如下所示:

话题通信

话题通信详解:ROS话题通信,最详细最易懂(从文件构建到执行原理)_超级霸霸强的博客-CSDN博客_ros话题通信https://blog.csdn.net/weixin_45590473/article/details/121208270

乌龟运动状态的获取

#include"turtlesim/Pose.h"  
#include"ros/ros.h"  
#include "sstream"  
  
void CallBackFunc(const turtlesim::Pose::ConstPtr& obj)  
{  
    std::ostringstream ostr;  
    ostr<<"角速度:"<<obj->angular_velocity<<";线速度:"<<obj->linear_velocity;  
    ostr<<"(x,y,theta):"<<obj->x<<","<<obj->y<<","<<obj->theta<<"\n\t";  
    ROS_INFO("%s",ostr.str().c_str());  
}  
  
int main(int argc,char* argv[])  
{  
    setlocale(LC_ALL,"");  
    ros::init(argc,argv,"turtle_pose");  
    ros::NodeHandle nh("turtle1");  
    ros::Subscriber sub = nh.subscribe<turtlesim::Pose>("pose",10,&CallBackFunc);  
    ros::spin();  
    return 0;  
} 

运行结果如下所示:

乌龟运动的控制 

#include "ros/ros.h"  
#include "geometry_msgs/Twist.h"  
#include "sstream"  
  
int main(int argc,char* argv[])  
{  
    setlocale(LC_ALL,"");  
    ros::init(argc,argv,"keyboard");  
    ros::NodeHandle nh("turtle1");  
    ros::Publisher pub = nh.advertise<geometry_msgs::Twist>("cmd_vel",10);  
    ros::Rate rate(1);  
    while(ros::ok()){  
        geometry_msgs::Twist obj;  
        obj.angular.x = 0;  
        obj.angular.y = 0;  
        obj.angular.z = 90;  
        obj.linear.x = 1;      
        obj.linear.y = 1;  
        obj.linear.z = 0;  
        pub.publish(obj);  
        rate.sleep();  
    }  
    return 0;  
} 

话题turtle1/cmd_vel对应的数据类型如下所示:

因此,我们需要了解geometry_msgs/Twist数据类型:

由于我们的乌龟运行在二维平面上,因此乌龟头的角度由绕Z轴旋转的角度决定(绕Z轴旋转的角度决定了乌龟头在XOY面上的方向),乌龟Z轴方向上移动为0。

参数服务器:GUI背景色控制

参数服务器详解:参数服务器的理论模型+实际操作实例_超级霸霸强的博客-CSDN博客https://blog.csdn.net/weixin_45590473/article/details/121220247

参数服务中参数的修改可以有两种方式:命令行和CPP源文件。但是不管使用的是命令行还是CPP源文件去修改参数服务器中的参数,在最后修改完参数后,都必须调用rosservice call /clear命令去刷新GUI的背景颜色。

命令行修改参数服务器参数如下所示:

注意:修改完参数后,必须调用rosservice call /clear命令去刷新GUI的背景颜色。

CPP源文件修改:

#include "ros/ros.h"  
  
int main(int argc,char* argv[])  
{  
    setlocale(LC_ALL,"");  
    ros::init(argc,argv,"background_color");  
    ros::NodeHandle nh("turtlesim");  
    nh.setParam("background_b",10);  
    nh.setParam("background_r",20);  
    nh.setParam("background_g",10);  
    return 0;  
}  

动作通信

动作通信详解:ROS之Action动作通信详解(分析+源码)_超级霸霸强的博客-CSDN博客https://blog.csdn.net/weixin_45590473/article/details/122525228

动作客户端的实现

#include "turtle_actionlib/ShapeAction.h"  
#include "actionlib/client/simple_action_client.h"  
#include "sstream"  
  
void DoneCallbackFunc(const actionlib::SimpleClientGoalState &state, const turtle_actionlib::ShapeResultConstPtr &result)  
{  
    std::ostringstream ostr;  
    ostr<<"finishing state:"<<state.state_;  
    ostr<<"result:"<<result->apothem<<","<<result->interior_angle<<"\n\t";  
    ROS_INFO("%s",ostr.str().c_str());  
}  
void ActiveCallbackFunc()  
{  
    ROS_INFO("Active successfully\n\t");  
}  
void FeedbackCallbackFunc(const turtle_actionlib::ShapeFeedbackConstPtr &feedback)  
{  
    // NULL  
}  
  
int main(int argc,char* argv[])  
{  
    setlocale(LC_ALL,"");  
    ros::init(argc,argv,"action_client");  
    ros::NodeHandle nh("turtle1");  
    actionlib::SimpleActionClient<turtle_actionlib::ShapeAction> client(nh,"action_topic",true);  
    turtle_actionlib::ShapeGoal goal;  
    goal.edges = 2;  
    goal.radius = 2;  
    client.waitForServer(); // 必须有这一句  
    client.sendGoal(goal,boost::bind(DoneCallbackFunc,_1,_2),boost::bind(ActiveCallbackFunc),boost::bind(FeedbackCallbackFunc,_1));  
    ros::spin();  
    return 0;  
}  

注意:client.waitForServer()必须存在,这保证了server可以接收到来自client的goal(client仅向server发送一次目标请求信息),除此之外,实例化客户端client时spin_thread参数建议置为true这样系统可以自动管理线程,否则需要认为管理线程。

动作服务端的实现

#include "turtle_actionlib/ShapeAction.h"  
#include "actionlib/server/simple_action_server.h"  
#include "sstream"  
  
void GoalCallbackFunc(const turtle_actionlib::ShapeGoalConstPtr& obj,actionlib::SimpleActionServer<turtle_actionlib::ShapeAction>* server)  
{  
    std::ostringstream ostr;  
    turtle_actionlib::ShapeFeedback feedback;  
    turtle_actionlib::ShapeResult result;  
  
    ostr<<obj->edges<<","<<obj->radius<<"\n\t";  
    ROS_INFO("%s",ostr.str().c_str());  
  
    ros::Rate rate(1);  
    while(ros::ok()){  
        server->publishFeedback(feedback);  
        rate.sleep();  
    }  
    result.apothem = 10; result.interior_angle = 180;  
    server->setSucceeded(result);  
}  
  
int main(int argc,char* argv[])  
{  
    setlocale(LC_ALL,"");  
    ros::init(argc,argv,"action_server");  
    ros::NodeHandle nh("turtle1");  
    actionlib::SimpleActionServer<turtle_actionlib::ShapeAction> server(nh,"action_topic",boost::bind(GoalCallbackFunc,_1,&server),false);  
    server.start();  
    ros::spin();  
    return 0;  
}  

注意:server实例化时第三个参数auto_start必须置为false,否则会发生线程冲突。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肥肥胖胖是太阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值