创建工作空间 -> 创建功能包 -> 创建源代码(python/c++) -> 配置编译规则 -> 编译运行
workspace
)src : 代码空间(Source Space)
build : 编译空间(Build Space) [中间文件]
devel : 开发空间(Development Space) [最终编译生成的可执行文件, 环境变量]
install : 安装空间(Install Space)
检测环境变量
laniakea@laniakea-virtual-machine >>> echo $ROS_PACKAGE_PATH /home/laniakea/learn_GY0/catkin_ws/src:/opt/ros/melodic/share
观察 catkin_ws
下devel
中的lib
文件夹
放置编译后的可执行文件
.sh
和 .bash
为环境变量的设置脚本
src
下放置功能包
catkin_create_pkg <package_name> [depend1] [depend2] [depend3]
depend-n
: 功能包依赖的选项
std_msgs
: ROS 中标准定义的消息接口
std_srvs
: ROS 中标准定义的服务接口
当看到一个文件夹下有 CMakeLists.txt
和 package.xml
时, 则说明其必是一个功能包
<build_depend>message_generation</build_depend> <!-- 编译依赖 --> <exec_depend>message_runtime</exec_depend> <!-- 运行依赖 -->
string_publisher.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, "string_publisher"); // 创建节点句柄 ros::NodeHandle n; // 创建一个Publisher,发布名为chatter的topic,消息类型为std_msgs::String // "chatter" 话题名 // 缓冲区大小 // 通过句柄创建 Publisher ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000); // 设置循环的频率 // 10Hz 一秒发10次 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); // 按照循环频率延时 loop_rate.sleep(); // 100ms // 如果上面的的程序执行了10ms 则其仅休眠90ms ++count; } return 0; }
string_subscriber.cpp
/** * 该例程将订阅chatter话题,消息类型String */ #include "ros/ros.h" #include "std_msgs/String.h" // 接收到订阅的消息后,会进入消息回调函数 -> 进行数据处理 // const 常指针 // std_msgs::String 消息类型 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, "string_subscriber"); // 创建节点句柄 ros::NodeHandle n; // 创建一个Subscriber,订阅名为chatter的topic,注册回调函数chatterCallback // Subscriber的缓冲区, 和 Publisher的不一定要一样 // 因为不知道 Publisher 的发送频率, 要去一直查询, 所以需要 回调函数 ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback); // 循环等待回调函数 ros::spin(); return 0; }
CMakeLists.txt
add_executable(string_publisher src/string_publisher.cpp) add_executable(string_subscriber src/string_subscriber.cpp) target_link_libraries(string_publisher ${catkin_LIBRARIES}) target_link_libraries(string_subscriber ${catkin_LIBRARIES})
tree . ├── CMakeLists.txt ├── include │ └── learning_communication ├── msg # 存放消息定义文件 │ └── PersonMsg.msg ├── package.xml └── src ├── string_publisher.cpp └── string_subscriber.cpp 4 directories, 5 files
PersonMsg.msg
string name
uint8 age
uint8 sex
uint8 unknown = 0
uint8 male = 1
uint8 female = 2
在package.xml
中添加
<build_depend>message_generation</build_depend> <exec_depend>message_runtime</exec_depend>
在 CMakeLists.txt
中添加
... # 查找依赖的包 find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs std_srvs message_ generation # 动态产生消息功能包 ) ... # 动态产生头文件 add_message_files(FILES PersonMsg.msg) generate_messages(DEPENDENCIES std_msgs) ... catkin_package( # INCLUDE_DIRS include # LIBRARIES learning_communication CATKIN_DEPENDS roscpp rospy std_msgs std_srvs message_runtime # DEPENDS system_lib )
会在devel
中生成 PersonMsg.h
头文件
person_publisher.cpp
/** * 该例程将订阅/person_info话题,自定义消息类型learning_communication::PersonMsg */ #include <ros/ros.h> #include "learning_communication/PersonMsg.h" // 接收到订阅的消息后,会进入消息回调函数 void personInfoCallback(const learning_communication::PersonMsg::ConstPtr& msg) { // 将接收到的消息打印出来 ROS_INFO("Subcribe Person Info: name:%s age:%d sex:%d", msg->name.c_str(), msg->age, msg->sex); } int main(int argc, char **argv) { // 初始化ROS节点 ros::init(argc, argv, "person_subscriber"); // 创建节点句柄 ros::NodeHandle n; // 创建一个Subscriber,订阅名为/person_info的topic,注册回调函数personInfoCallback ros::Subscriber person_info_sub = n.subscribe("/person_info", 10, personInfoCallback); // 循环等待回调函数 ros::spin(); return 0; }
person_subscriber.cpp
/** * 该例程将订阅/person_info话题,自定义消息类型learning_communication::PersonMsg */ #include <ros/ros.h> #include "learning_communication/PersonMsg.h" // 接收到订阅的消息后,会进入消息回调函数 void personInfoCallback(const learning_communication::PersonMsg::ConstPtr& msg) { // 将接收到的消息打印出来 ROS_INFO("Subcribe Person Info: name:%s age:%d sex:%d", msg->name.c_str(), msg->age, msg->sex); } int main(int argc, char **argv) { // 初始化ROS节点 ros::init(argc, argv, "person_subscriber"); // 创建节点句柄 ros::NodeHandle n; // 创建一个Subscriber,订阅名为/person_info的topic,注册回调函数personInfoCallback ros::Subscriber person_info_sub = n.subscribe("/person_info", 10, personInfoCallback); // 循环等待回调函数 ros::spin(); return 0; }
在 CMakeLists.txt
中添加
... add_executable(person_publisher src/person_publisher.cpp) add_executable(person_subscriber src/person_subscriber.cpp) ... # 动态产生头文件的路径 add_dependencies(person_publisher ${PROJECT_NAME}_gencpp) add_dependencies(person_subscriber ${PROJECT_NAME}_gencpp) ... target_link_libraries(person_publisher ${catkin_LIBRARIES}) target_link_libraries(person_subscriber ${catkin_LIBRARIES})
$rossrv show std_srvs/SetBool bool data # request 发送 --- # 区分 request 和 response 的内容 # response 反馈 bool success string message
string_client.cpp
/** * 该例程将请求print_string服务,std_srvs::SetBool */ #include "ros/ros.h" #include "std_srvs/SetBool.h" // ROS中标准服务接口定义 int main(int argc, char **argv) { // ROS节点初始化 ros::init(argc, argv, "string_client"); // 创建节点句柄 ros::NodeHandle n; // 创建一个client,service消息类型是std_srvs::SetBool ros::ServiceClient client = n.serviceClient<std_srvs::SetBool>("print_string"); // 创建std_srvs::SetBool类型的service消息 // 封装客户端的service消息 std_srvs::SetBool srv; srv.request.data = true; // 发布service请求,等待应答结果 // client.call(srv) 阻塞函数, 一直阻塞直到 service 反馈 response if (client.call(srv)) { ROS_INFO("Response : [%s] %s", srv.response.success?"True":"False", srv.response.message.c_str()); } else { ROS_ERROR("Failed to call service print_string"); return 1; } return 0; }
string_server.cpp
/** * 该例程将提供print_string服务,std_srvs::SetBool */ #include "ros/ros.h" #include "std_srvs/SetBool.h" // service回调函数,输入参数req,输出参数res bool print(std_srvs::SetBool::Request &req, std_srvs::SetBool::Response &res) { // 打印字符串 if(req.data) { ROS_INFO("Hello ROS!"); res.success = true; res.message = "Print Successully"; } else { res.success = false; res.message = "Print Failed"; } return true; } int main(int argc, char **argv) { // ROS节点初始化 ros::init(argc, argv, "string_server"); // 创建节点句柄 ros::NodeHandle n;response // 创建一个 名为print_string的server,注册回调函数print() // 一旦有服务进入 (有节点发送服务请求),则进入 print 回调函数中 ros::ServiceServer service = n.advertiseService("print_string", print); // 循环等待回调函数 ROS_INFO("Ready to print hello string."); ros::spin(); return 0; }
在 CMakeLists.txt
中添加
... add_executable(string_client src/string_client.cpp) add_executable(string_server src/string_server.cpp) ... target_link_libraries(string_client ${catkin_LIBRARIES}) target_link_libraries(string_server ${catkin_LIBRARIES})
在learning_communication
文件夹下创建srv
文件夹
自定义文件PersonSrv.srv
string name uint8 age uint8 sex uint8 unknown = 0 uint8 male = 1 uint8 female = 2 --- string result
person_client1
/** * 该例程将请求/show_person服务,服务数据类型learning_communication::PersonSrv */ #include <ros/ros.h> #include "learning_communication/PersonSrv.h" int main(int argc, char** argv) { // 初始化ROS节点 ros::init(argc, argv, "person_client"); // 创建节点句柄 ros::NodeHandle node; // 发现/spawn服务后,创建一个服务客户端,连接名为/spawn的service ros::service::waitForService("/show_person"); ros::ServiceClient person_client = node.serviceClient<learning_communication::PersonSrv>("/show_person"); // 初始化learning_communication::Person的请求数据 learning_communication::PersonSrv srv; srv.request.name = "Tom"; srv.request.age = 20; // srv 文件中 ---以上会生成request命名空间, 以下会生成response命名空间 srv.request.sex = learning_communication::PersonSrv::Request::male; // 请求服务调用 ROS_INFO("Call service to show person[name:%s, age:%d, sex:%d]", srv.request.name.c_str(), srv.request.age, srv.request.sex); // call(srv) 发送服务请求 // 等待服务端 response 的结果 person_client.call(srv); // 显示服务调用结果 ROS_INFO("Show person result : %s", srv.response.result.c_str()); return 0; };
person_server1
/** * 该例程将执行/show_person服务,服务数据类型learning_communication::PersonSrv */ #include <ros/ros.h> #include "learning_communication/PersonSrv.h" // service回调函数,输入参数req,输出参数res bool personCallback(learning_communication::PersonSrv::Request &req, learning_communication::PersonSrv::Response &res) { // 显示请求数据 ROS_INFO("Person: name:%s age:%d sex:%d", req.name.c_str(), req.age, req.sex); // 设置反馈数据 res.result = "OK"; return true; } int main(int argc, char **argv) { // ROS节点初始化 ros::init(argc, argv, "person_server"); // 创建节点句柄 ros::NodeHandle n; // 创建一个名为/show_person的server,注册回调函数personCallback ros::ServiceServer person_service = n.advertiseService("/show_person", personCallback); // 循环等待回调函数 ROS_INFO("Ready to show person informtion."); ros::spin(); return 0; }
add_service_files( FILES PersonSrv.srv ) ... add_dependencies(person_client1 ${PROJECT_NAME}_gencpp) add_dependencies(person_server1 ${PROJECT_NAME}_gencpp) ... target_link_libraries(person_client1 ${catkin_LIBRARIES}) target_link_libraries(person_server1 ${catkin_LIBRARIES})