ROS基础

可紊 提交于 2021-01-14 06:13:47

话题编程

实现发布者talker.cpp

 1 #include <sstream>
 2 #include "ros/ros.h"
 3 #include "std_msgs/String.h"
 4 
 5 int main(int argc, char **argv)
 6 {
 7     //ROS节点初始化
 8     ros::init(argc, argv, "talker");
 9     
10     //创建节点句柄
11     ros::NodeHandle n;
12 
13     //创建一个publisher,发布名为chatter的topic,消息类型为std_msgs::String
14     ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
15 
16     //设置循环的频率
17     ros::Rate loop_rate(10);
18 
19     int count = 0;
20     while (ros::ok())
21     {
22         //初始化std_msgs::String类型的消息
23         std_msgs::String msg;
24         std::stringstream ss;
25         ss << "hello world" << count;
26         msg.data = ss.str();
27 
28         //发布消息
29         ROS_INFO("%s", msg.data.c_str());
30         chatter_pub.publish(msg);//发布
31 
32         //循环等待回调函数
33         ros::spinOnce();
34         
35         //按照循环频率延时
36         loop_rate.sleep();
37         ++count;
38     }
39     return 0;
40 }

 

实现订阅者listener.cpp

 1 #include "ros/ros.h"
 2 #include "std_msgs/String.h"
 3 
 4 //接收到订阅的消息后,会进入消息回调函数
 5 void chatterCallback(const std_msgs::String::ConstPtr& msg)
 6 {
 7     //将接收到的消息打印出来
 8     ROS_INFO("I heard: [%s]", msg->data.c_str());
 9 }
10 
11 int main(int argc, char **argv)
12 {
13     //初始化ROS节点
14     ros::init(argc, argv, "listener");
15     
16     //创建节点句柄
17     ros::NodeHandle n;
18 
19     //创建一个Subscriber, 订阅名为chatter的topic,注册回调函数chatterCallback
20     ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
21     
22     //循环等待回调函数
23     ros::spin();
24 
25     return 0;
26 }

 

设置需要编译的代码和生成的可执行文件;设置链接库;设置依赖。CMakeLists.txt

 1 cmake_minimum_required(VERSION 2.8.3)
 2 project(learning_communication)
 3 
 4 find_package(catkin REQUIRED COMPONENTS
 5   roscpp
 6   rospy
 7   std_msgs
 8 )
 9 
10 catkin_package(
11 #  INCLUDE_DIRS include
12 #  LIBRARIES learning_communication
13 #  CATKIN_DEPENDS roscpp rospy std_msgs
14 #  DEPENDS system_lib
15 )
16 
17 include_directories(
18 # include
19   ${catkin_INCLUDE_DIRS}
20 )
21 
22 add_executable(talker src/talker.cpp)
23 
24 add_executable(listener src/listener.cpp)
25 
26 target_link_libraries(talker
27    ${catkin_LIBRARIES}
28 )
29 
30 target_link_libraries(listener
31    ${catkin_LIBRARIES}
32 )

 

listener.cpp,talker.cpp,CMakeLists.txt三部分。

 

发布

 

订阅

 

1.在msg/Person.msg中定义msg文件

string name
uint8 sex
uint8 age

uint8 unknown = 0
uint8 male = 1
uint8 female = 2

2.在package.xml中添加功能包依赖

<build_depend>message_generation</build_depend>

<exec_depend>message_runtime</exec_depend>

将package.xml中的注释去掉即可。

3.在CMakeLists.txt添加编译选项(与消息有关)

find_package(...message_generation)
catkin_package(CATKIN_DEPENDS roscpp rospy std_msgs message_runtime)
add_message_files( FILES Person.msg)

generate_messages(DEPENDENCIES std_msgs)

 

编译后,查看自定义消息是否成功

 

服务编程

1.在srv/AddTwoInts.srv中自定义服务请求与应答

int64 a
int64 b
---
int64 sum

2.在package.xml中添加功能包依赖(同上)

3.在CMakeLists.txt添加编译选项(与服务有关)

add_service_files( FILES AddTwoInts.srv) 部分内容已在消息编程中添加。

 

实现服务器server.cpp

 1 #include "ros/ros.h"
 2 #include ""learning_communication/AddTwoInts.h"
 3 
 4 //service回调函数,输入参数req,输出参数res
 5 bool add(learning_communication::AddTwoInts::Request &req,
 6        learning_communication::AddTwoInts::Response &res)
 7 {
 8     //将输入参数中的请求数据相加,结果放到应答变量中
 9     res.sum = req.a + req.b;  //req
10     ROS_INFO("request: x=%d, y=%d", (long int)req.a, (long int)req.b);
11     ROS_INFO("sending back response: [%d]", (long int)res.sum);//res
12 
13     return true;
14 }
15 
16 int main(int argc, char **argv)
17 {
18     // ROS节点初始化
19     ros::init(argc, argv, "add_two_ints_server");
20     
21     //创建节点句柄
22     ros::NodeHandle n;
23 
24     //创建一个名为add_two_ints的server,注册回调函数add()
25     ros::ServiceServer service = n.advertiseSever("add_two_ints", add);//add_two_ints服务名
26     
27     //循环等待回调函数
28     ROS_INFO("Ready to add ints.");
29     ros::spin();
30 
31     return 0;
32 }

实现客户端client.cpp

 1 #include <cstlib>
 2 #include "ros/ros.h"
 3 #include "learning_communication/AddTwoInts.h"
 4 
 5 int main(int argc, char **argv)
 6 {
 7     //ROS节点初始化
 8     ros::init(argc, argv, "add_two_ints_client");
 9 
10     //从终端命令行获取两个加数
11     if (argc != 3)
12     {
13         ROS_INFO("usage: add_two_ints_client X Y");
14         return 1;
15     }
16 
17     //创建节点句柄
18     ros::NodeHandle n;
19 
20     //创建一个client,请求add_two_int service
21     //service消息类型是learning_communication::AddTwoInts
22     ros::ServiceClient client = n.serviceClient<learning_communication::AddTwoInts>("add_two_ints");
23 
24     //创建learning_communication::AddTwoInts类型的service消息
25     learning_communication::AddTwoInts srv;
26     srv.request.a = atoll(argv[1]);
27     srv.request.b = atoll(argv[2]);
28 
29     //发布service请求,等待加法运算的应答结果
30     if (client.call(srv))
31     {
32         ROS_INFO("Sum: %d", (long int)srv.response.sum);
33     }
34     else
35     {
36         ROS_INFO("Failed to call service add_two_ints");
37         return 1;
38     }
39 
40     return 0;
41 }

 

启动Server节点

等待

 

客户端发布服务请求

 

服务端接收到请求,完成加法,将结果发给客户端。

 

动作编程

1.在action/DoDishes.action中自定义动作消息

#定义目标信息
uint32 dishwasher_id
#Specify which dishwasher we want to use
---
#定义结果信息
uint32 total_dishes_cleaned
---
#定义周期反馈的消息
float32 percent_complete

2.在package.xml中添加功能包依赖

<build_depend>actionlib</build_depend>
<build_depend>actionlib_msgs</build_depend>
<exec_depend>actionlib</exec_depend>
<exec_depend>actionlib_msgs</exec_depend>

3.在CMakeLists.txt添加编译选项

find_package(catkin REQUIRED actionlib_msgs actionlib)
add_action_files(DIRECTORY action FILES DoDishes.action)
generate_messages(DEPENDENCIES actionlib_msgs)

在原编译选项中添加,不应完全复制进CMakeLists.txt中。

 

实现动作服务器DoDishes_server.cpp

 1 #include <ros/ros.h>
 2 #include <actionlib/server/simple_action_server.h>
 3 #include "learning_communication/DoDishesAction.h"
 4 
 5 typedef actionlib::SimpleActionServer<learning_communication::DoDishesAction> Server;
 6 
 7 //收到action的goal后调用该回归函数
 8 void execute(const learning_communication::DoDishesGoalConstPtr& goal, Server* as)
 9 {
10     ros::Rate r(1);
11     learning_communication::DoDishesFeedback feedback;
12 
13     ROS_INFO("Disheswasher %d is working.", goal->dishwasher_id);
14 
15     //假设洗盘子的进度,并且按照1HZ的频率发布进度feedback
16     for (int i = 1; i <= 10; i++)
17     {
18         feedback.percent_complete = i * 10;
19         as->publishFeedback(feedback);
20         r.sleep();
21     }
22 
23     //当action完成后,向客户端返回结果
24     ROS_INFO("Dishwasher %d finish working.", goal->dishwasher_id);
25     as->setSucceeded();
26 }
27 
28 int main(int argc, char** argv)
29 {
30     ros::init(argc, argv, "do_dishes_server");
31     ros::NodeHandle n;
32 
33     //定义一个服务器
34     Server server(n, "do_dishes", boost::bind(&execute, _1, &server), false);
35 
36     //服务器开始运行
37     server.start();
38 
39     ros::spin();
40     return 0;
41 }

 

实现动作客户端DoDishes_client.cpp

 1 #include <actionlib/client/simple_action_client.h>
 2 #include "learning_communication/DoDishesAction.h"
 3 
 4 typedef actionlib::SimpleActionClient<learning_communication::DoDishesAction> Client;
 5 
 6 //当action完成后会调用该回调函数一次
 7 void doneCb(const actionlib::SimpleClientGoalState& state, const learning_communication::DoDishesResultConstPtr& result)
 8 {
 9     ROS_INFO("Yay! The dishes are now clean");
10     ros::shutdown();
11 }
12 
13 //当action激活后会调用该回调函数一次
14 void activeCb()
15 {
16     ROS_INFO("Goal just went active");
17 }
18 
19 //收到feedback后调用该回调函数
20 void feedbackCb(const learning_communication::DoDishesFeedbackConstPtr& feedback)
21 {
22     ROS_INFO(" percent_complete : %f ", feedback->percent_complete);
23 }
24 
25 int main(int argc, char** argv)
26 {
27     
28     ros::init(argc, argv, "do_dishes_client");
29     
30     //定义一个客户端
31     Client client("do_dishes", true);
32     
33     //等待服务器端
34     ROS_INFO("Waiting for action server to start.");
35     client.waitForServer();
36     ROS_INFO("Action server started, sending goal.");
37 
38     //创建一个action的goal
39     learning_communication::DoDishesGoal goal;
40     goal.dishwasher_id = 1;
41     
42     //发送action的goal给服务器端,并且设置回调函数
43     client.sendGoal(goal, &doneCb, &activeCb, &feedbackCb);
44 
45     ros::spin();
46     
47     return 0;
48 }

运行过程中

运行结束

 

TF坐标变换

roslaunch turtle_tf turtle_tf_demo.launch

往另一个乌龟运动。

 

rosrun turtlesim turtle_teleop_key

控制一只乌龟运动,另一只乌龟追。

rosrun tf view_frames

监听5秒,生成PDF文件。

实时监听输出坐标变换信息

(可以看出只有平移变换)

 

TF坐标变换编程

先建一个功能包

应该在catkin_learning/src/下输入命令,上图有错误。

TF广播器turtle_tf_broadcaster.cpp

 1 #include <ros/ros.h>
 2 #include <tf/transform_broadcaster.h>
 3 #include <turtlesim/Pose.h>
 4 
 5 std::string turtle_name;
 6 
 7 void poseCallback(const turtlesim::PoseConstPtr& msg)
 8 {
 9     // tf广播器
10     static tf::TransformBroadcaster br;
11 
12     // 根据乌龟当前的位姿,设置相对于世界坐标系的坐标变换
13     tf::Transform transform;
14     transform.setOrigin( tf::Vector3(msg->x, msg->y, 0.0) );
15     tf::Quaternion q;
16     q.setRPY(0, 0, msg->theta);//z轴转
17     transform.setRotation(q);
18 
19     // 发布坐标变换
20     br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "world", turtle_name));
21 }
22 
23 int main(int argc, char** argv)
24 {
25     // 初始化节点
26     ros::init(argc, argv, "my_tf_broadcaster");
27     if (argc != 2)
28     {
29         ROS_ERROR("need turtle name as argument"); 
30         return -1;
31     };
32     turtle_name = argv[1];
33 
34     // 订阅乌龟的pose信息
35     ros::NodeHandle node;
36     ros::Subscriber sub = node.subscribe(turtle_name+"/pose", 10, &poseCallback);
37 
38     ros::spin();
39 
40     return 0;
41 };

 1 #include <ros/ros.h>
 2 #include <tf/transform_listener.h>
 3 #include <geometry_msgs/Twist.h>
 4 #include <turtlesim/Spawn.h>
 5 
 6 int main(int argc, char** argv)
 7 {
 8     // 初始化节点
 9     ros::init(argc, argv, "my_tf_listener");
10 
11     ros::NodeHandle node;
12 
13     // 通过服务调用,产生第二只乌龟turtle2
14     ros::service::waitForService("spawn");
15     ros::ServiceClient add_turtle =
16     node.serviceClient<turtlesim::Spawn>("spawn");
17     turtlesim::Spawn srv;
18     add_turtle.call(srv);
19 
20     // 定义turtle2的速度控制发布器
21     ros::Publisher turtle_vel = node.advertise<geometry_msgs::Twist>("turtle2/cmd_vel", 10);
22 
23     // tf监听器
24     tf::TransformListener listener;
25 
26     ros::Rate rate(10.0);
27     while (node.ok())
28     {
29         tf::StampedTransform transform;
30         try
31         {
32             // 查找turtle2与turtle1的坐标变换
33             listener.waitForTransform("/turtle2", "/turtle1", ros::Time(0), ros::Duration(3.0));//等待时间
34             listener.lookupTransform("/turtle2", "/turtle1", ros::Time(0), transform);//变换关系放在transform
35         }
36         catch (tf::TransformException &ex) 
37         {
38             ROS_ERROR("%s",ex.what());
39             ros::Duration(1.0).sleep();
40             continue;
41         }
42 
43         // 根据turtle1和turtle2之间的坐标变换,计算turtle2需要运动的线速度和角速度
44         // 并发布速度控制指令,使turtle2向turtle1移动
45         geometry_msgs::Twist vel_msg;
46         vel_msg.angular.z = 4.0 * atan2(transform.getOrigin().y(),
47                                         transform.getOrigin().x());
48         vel_msg.linear.x = 0.5 * sqrt(pow(transform.getOrigin().x(), 2) +
49                                       pow(transform.getOrigin().y(), 2));
50         turtle_vel.publish(vel_msg);
51 
52         rate.sleep();
53     }
54     return 0;
55 };

修改CMakeLists.txt

除此之外,在find_package中添加tf,turtlesim,std_msgs。

添加start_demo_with_listener.launch

 1  <launch>
 2     <!-- 海龟仿真器 -->
 3     <node pkg="turtlesim" type="turtlesim_node" name="sim"/>
 4 
 5     <!-- 键盘控制 -->
 6     <node pkg="turtlesim" type="turtle_teleop_key" name="teleop" output="screen"/>
 7 
 8     <!-- 两只海龟的tf广播,发布坐标关系 -->
 9     <node pkg="learning_tf" type="turtle_tf_broadcaster" args="/turtle1" name="turtle1_tf_broadcaster" />
10     <node pkg="learning_tf" type="turtle_tf_broadcaster" args="/turtle2" name="turtle2_tf_broadcaster" />
11 
12     <!-- 监听tf广播,并且控制turtle2移动 -->
13     <node pkg="learning_tf" type="turtle_tf_listener" name="listener" />
14 
15  </launch>

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!