ROS基础学习笔记(一)

    xiaoxiao2022-07-04  160

            该文主要介绍如何创建工作空间、功能包;ROS的三种通信方式:话题编程、服务编程、动作编程;实现ROS中的分布式通信以及主要组件介绍。

    目录

    1 创建工作空间

    1.1 创建工作空间的步骤:

    1.2 创建功能包

    2 ROS三种通信编程方式

    2.1 话题编程

    2.2 服务编程

    2.3 动作编程


    1 创建工作空间

    1.1 创建工作空间的步骤:

    (创建工作空间、编译工作空间、设置环境变量、检查环境变量)

    首先建立一个catkin_ws文件夹,在该文件夹中建立一个src文件夹,可以使用终端命令的方式也可以使用Ubuntu下自带的建立文件夹命令:

    mkdir catkin_ws cd catkin_ws mkdir src cd src catkin_init_workspace//工作空间初始化

    输出上图所示的信息表示初始化成功。然后在catkin_ws文件夹下执行catkin_cmake命令,若输出下图所示的信息则表明编译成功。

    设置环境变量:

    source devel/setup.bash

    检查环境变量是否配置成功:

    echo $ROS_PACKAGE_PATH

    在终端中使用该命令设置的环境变量只能在该终端中有效,如果希望在所有终端中有效,则需要使用以下命令:

    vi ~/.bashrc

    在最下面添加

    source ~/.ros_example/catkin_ws/devel/setup.bash

    输入:wq进行保存,然后使用source ~/.bashrc命令使所有终端生效。

    1.2 创建功能包

    具体格式为命令+功能包名+依赖项

    catkin_create_pkg <package_name> [depend1] [depend2] [depend3]

    例如在src文件下使用

    catkin_create_pkg sunshine std_msgs rospy roscpp

    2 ROS三种通信编程方式

    2.1 话题编程

    具体步骤:1.创建发布者;2.创建订阅者;3.添加编译选项;4.运行可执行程序。

    1.发布者代码:talker.cpp

    /** * 该例程将发布chatter话题,消息类型String */ #include <sstream> #include "ros/ros.h" #include "std_msgs/String.h" int main(int argc, char **argv) { // ROS节点初始化 ros::init(argc, argv, "talker");//输入参数以及节点名称 // 创建节点句柄 ros::NodeHandle n; // 创建一个Publisher,发布名为chatter的topic,消息类型为std_msgs::String ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000); // 设置循环的频率 ros::Rate loop_rate(10); int count = 0; while (ros::ok()) { // 初始化std_msgs::String类型的消息 std_msgs::String msg; std::stringstream ss; ss << "hello world " << count; msg.data = ss.str(); // 发布消息 ROS_INFO("%s", msg.data.c_str()); chatter_pub.publish(msg); // 循环等待回调函数 ros::spinOnce(); // 按照循环频率延时 loop_rate.sleep(); ++count; } return 0; }

    2.订阅者代码:listener.cpp

    /** * 该例程将订阅chatter话题,消息类型String */ #include "ros/ros.h" #include "std_msgs/String.h" // 接收到订阅的消息后,会进入消息回调函数 void chatterCallback(const std_msgs::String::ConstPtr& msg) { // 将接收到的消息打印出来 ROS_INFO("I heard: [%s]", msg->data.c_str()); } int main(int argc, char **argv) { // 初始化ROS节点 ros::init(argc, argv, "listener"); // 创建节点句柄 ros::NodeHandle n; // 创建一个Subscriber,订阅名为chatter的topic,注册回调函数chatterCallback ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback); // 循环等待回调函数 ros::spin(); return 0; }

    3.在CmakeLists.txt中添加

    add_executable(talker src/talker.cpp) target_link_libraries(talker ${catkin_LIBRARIES}) add_executable(listener src/listener.cpp) target_link_libraries(listener ${catkin_LIBRARIES})

    4、执行可执行文件

    rosrun 功能包名 cpp文件

    具体代码事例:

    rosrun learning_communication talker rosrun learning_communication listener

    5、自定义话题消息

    定义msg文件Person.msg

    string name uint8 sex uint8 age uint8 unknown = 0 uint8 male = 1 uint8 female = 2

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

    <build_depend>message_generation</build_depend> <exec_depend>message_runtime</exec_depend>

    (ROS kinect 版本使用exec,indiago版本使用run)

    在CMakeLists.txt中添加编译选项

    find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs message_generation ) catkin_package( # INCLUDE_DIRS include # LIBRARIES learning_communication CATKIN_DEPENDS roscpp rospy std_msgs message_runtime # DEPENDS system_lib ) add_message_files(FILES Person.msg) generate_messages(DEPENDENCIES std_msgs)

    2.2 服务编程

    具体步骤:1.创建服务器;2.创建客户端;3.添加编译选项;4.运行可执行程序。

    自定义服务请求与应答:

    int64 a int64 b --- int64 sum

    与话题编程一样,同样需要在package.xml中添加依赖在CMakeLists.txt中添加包,唯一的区别是添加服务文件

    add_service_files(FILES AddTwoInts.srv)

    server.cpp

    /** * AddTwoInts Server */ #include "ros/ros.h" #include "learning_communication/AddTwoInts.h" // service回调函数,输入参数req,输出参数res;回调函数是真正实现服务功能的部分 bool add(learning_communication::AddTwoInts::Request &req, learning_communication::AddTwoInts::Response &res) { // 将输入参数中的请求数据相加,结果放到应答变量中 res.sum = req.a + req.b; ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b); ROS_INFO("sending back response: [%ld]", (long int)res.sum); return true; } int main(int argc, char **argv) { // ROS节点初始化 ros::init(argc, argv, "add_two_ints_server"); // 创建节点句柄 ros::NodeHandle n; // 创建一个名为add_two_ints的server,注册回调函数add() ros::ServiceServer service = n.advertiseService("add_two_ints", add); // 循环等待回调函数 ROS_INFO("Ready to add two ints."); ros::spin(); return 0; }

    client.cpp

    /** * AddTwoInts Client */ #include <cstdlib> #include "ros/ros.h" #include "learning_communication/AddTwoInts.h" int main(int argc, char **argv) { // ROS节点初始化 ros::init(argc, argv, "add_two_ints_client"); // 从终端命令行获取两个加数 if (argc != 3) { ROS_INFO("usage: add_two_ints_client X Y"); return 1; } // 创建节点句柄 ros::NodeHandle n; // 创建一个client,请求add_two_int service // service消息类型是learning_communication::AddTwoInts ros::ServiceClient client = n.serviceClient<learning_communication::AddTwoInts>("add_two_ints"); // 创建learning_communication::AddTwoInts类型的service消息 learning_communication::AddTwoInts srv; srv.request.a = atoll(argv[1]); srv.request.b = atoll(argv[2]); // 发布service请求,等待加法运算的应答结果 if (client.call(srv)) { ROS_INFO("Sum: %ld", (long int)srv.response.sum); } else { ROS_ERROR("Failed to call service add_two_ints"); return 1; } return 0; }

    编译功能包:

    add_executable(server src/server.cpp) target_link_libraries(server ${catkin_LIBRARIES}) add_dependencies(server ${PROJECT_NAME}_gencpp)

    add_executable(client src/client.cpp) target_link_libraries(client ${catkin_LIBRARIES}) add_dependencies(client ${PROJECT_NAME}_gencpp)

    运行server和client

    首先保证ros启动使用roscore命令;

    运行server节点

    rosrun learning_communication server

    运行server节点

    rosrun learning_communication client 4 6

    2.3 动作编程

    定义action文件;在Package.xml中添加功能包依赖,在CmakeLists.txt中添加编译选项。

    DoDishes.action

    #定义目标信息 uint32 dishwasher_id --- #定义结果消息 uint32 total_dishes_cleaned --- #定义周期反馈的消息 float32 percent_complete

    CmakeLists.txt

    find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs message_generation actionlib_msgs actionlib ) add_action_files(DIRECTORY action FILES DoDishes.action) generate_messages(DEPENDENCIES std_msgs actionlib_msgs) add_executable(DoDishes_client src/DoDishes_client.cpp) target_link_libraries(DoDishes_client ${catkin_LIBRARIES}) add_dependencies(DoDishes_client ${PROJECT_NAME}_EXPORTED_TARGETS) add_executable(DoDishes_server src/DoDishes_server.cpp) target_link_libraries(DoDishes_server ${catkin_LIBRARIES}) add_dependencies(DoDishes_server ${PROJECT_NAME}_EXPORTED_TARGETS)

    Package.xml

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

    如何实现一个动作服务器的具体步骤:

    初始化ROS节点;创建动作服务器实例;启动服务器,等待动作请求,在回调函数中完成动作服务功能的处理,反馈进度信息;

    动作完成,发送结束信息。

    编程实现

    #include <ros/ros.h> #include <actionlib/server/simple_action_server.h> #include "learning_communication/DoDishesAction.h" typedef actionlib::SimpleActionServer<learning_communication::DoDishesAction> Server; // 收到action的goal后调用该回调函数 void execute(const learning_communication::DoDishesGoalConstPtr& goal, Server* as) { ros::Rate r(1); learning_communication::DoDishesFeedback feedback; ROS_INFO("Dishwasher %d is working.", goal->dishwasher_id); // 假设洗盘子的进度,并且按照1hz的频率发布进度feedback for(int i=1; i<=10; i++) { feedback.percent_complete = i * 10; as->publishFeedback(feedback); r.sleep(); } // 当action完成后,向客户端返回结果 ROS_INFO("Dishwasher %d finish working.", goal->dishwasher_id); as->setSucceeded(); } int main(int argc, char** argv) { ros::init(argc, argv, "do_dishes_server"); ros::NodeHandle n; // 定义一个服务器 Server server(n, "do_dishes", boost::bind(&execute, _1, &server), false); // 服务器开始运行 server.start(); ros::spin(); return 0; }

    如何实现一个动作客户端:

    初始化ROS节点;创建动作客户端实例;链接动作服务端,发送动作目标;根据不同类型的服务端反馈处理回调函数。

    #include <actionlib/client/simple_action_client.h> #include "learning_communication/DoDishesAction.h" typedef actionlib::SimpleActionClient<learning_communication::DoDishesAction> Client; // 当action完成后会调用该回调函数一次 void doneCb(const actionlib::SimpleClientGoalState& state, const learning_communication::DoDishesResultConstPtr& result) { ROS_INFO("Yay! The dishes are now clean"); ros::shutdown(); } // 当action激活后会调用该回调函数一次 void activeCb() { ROS_INFO("Goal just went active"); } // 收到feedback后调用该回调函数 void feedbackCb(const learning_communication::DoDishesFeedbackConstPtr& feedback) { ROS_INFO(" percent_complete : %f ", feedback->percent_complete); } int main(int argc, char** argv) { ros::init(argc, argv, "do_dishes_client"); // 定义一个客户端 Client client("do_dishes", true); // 等待服务器端 ROS_INFO("Waiting for action server to start."); client.waitForServer(); ROS_INFO("Action server started, sending goal."); // 创建一个action的goal learning_communication::DoDishesGoal goal; goal.dishwasher_id = 1; // 发送action的goal给服务器端,并且设置回调函数 client.sendGoal(goal, &doneCb, &activeCb, &feedbackCb); ros::spin(); return 0; }

     


    最新回复(0)