乌龟节点操作原理解析
乌龟节点的话题
乌龟节点的操作方式和动态参数调节的方式很像,乌龟节点所在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节点的私有参数。
乌龟节点使用实操
服务通信
启动官方节点实例化的对象和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:不显示)。
运行结果如下所示:
话题通信
乌龟运动状态的获取
#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;
}
动作通信
动作客户端的实现
#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,否则会发生线程冲突。