功能介绍
以自定义数据类型为基础,完成一个节点作为服务器,另一个节点接收传送两个数字到服务端,服务端计算后反馈到客户端进行打印输出
1、工作空间
1.1 创建工作空间lee_ws
mkdir -p ~/lee_ws/src
cd ~/lee_ws/src/
catkin_init_workspace
cd ~/lee_ws/
catkin_make
echo "source ~/lee_ws/devel/setup.bash" >> ~/.bashrc
source devel/setup.bash
1.2 创建功能包lee_srv
cd ~/lee_ws/src
catkin_create_pkg lee_srv std_msgs rospy roscpp
cd ~/lee_ws/
catkin_make
source ~/lee_ws/devel/setup.bash
1.3 创建srv文件
路径: /home/lee/lee_ws/src/lee_srv/srv/AddTwoInts.srv
int64 num1
int64 num2
---
int64 sum
1.4 package.xml配置
在package.xml最下面的build_depend
和exec_depend
附近添加即可
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
1.5 CMakeLists.txt配置
在CMakeLists.txt中配置下面相应的代码
# 需要加入 message_generation,必须有 std_msgs
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
# 配置 srv 源文件
add_service_files(
FILES
AddTwoInts.srv
)
# 生成消息时依赖于 std_msgs
generate_messages(
DEPENDENCIES
std_msgs
)
#执行时依赖
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES lee
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)
1.6 编译后的中间文件查看
C++ 需要调用的中间文件(…/工作空间/devel/include/包名/xxx.h)
1.7 总结步骤1.3-1.5
- 1.3建立的自定义srv文件
- 需要通过1.4和1.5两个步骤的配置才能通过catkin_make对1.3的文件进行编译
- 1.6为通过编译自定义文件生成的python、C++和js等头文件,用于相应编程代码的头文件调用
1.8 VScode编辑ros参数配置
快捷键 ctrl + shift + B 调用编译,选择:catkin_make:build这一行,点击小齿轮配置设置,修改.vscode/tasks.json 文件,将下面复制进去即可
这么配置的原因:下一次快捷键 ctrl + shift + B直接进行代码编译
路径:/home/lee/lee_ws/.vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
//代表提示的描述性信息
"label": "catkin_make:debug",
//可以选择shell或者process,如果是shell代码是在shell里面运行一个命令,如果是process代表作为一个进程来运行
"type": "shell",
//这个是我们需要运行的命令
"command": "catkin_make",
"args": [],
"group": {"kind":"build","isDefault":true},
//可选always或者silence,代表是否输出信息
"presentation": {"reveal": "always"},
"problemMatcher": "$msCompile"
}
]
}
1.9 vscode 头文件配置
为了方便代码提示以及避免误抛异常,需要先配置 vscode,将1.3-1.5生成的 head 中间文件路径配置进 c_cpp_properties.json 的 includepath属性,如果不配置vscode会报错代码,但是编译不受影响
路径: /home/lee/lee_ws/.vscode/c_cpp_properties.json
{
"configurations": [
{
"browse": {
"databaseFilename": "",
"limitSymbolsToIncludedHeaders": true
},
"includePath": [
"/opt/ros/noetic/include/**",
"/home/lee/catkin_ws/src/lee/include/**",
"/usr/include/**",
"/home/lee/catkin_ws/devel/include/**" //配置 head 文件的路径
],
"name": "ROS",
"intelliSenseMode": "gcc-x64",
"compilerPath": "/usr/bin/gcc",
"cStandard": "c11",
"cppStandard": "c++17"
}
],
"version": 4
}
2、服务端代码 C++
路径: /home/lee/lee_ws/src/lee_srv/src/lee_srv_add_server.cpp
2.1 代码部分
#include "ros/ros.h"
#include "lee_srv/AddTwoInts.h"
//接收到订阅的消息后,进入回调函数
bool chatterCallback(lee_srv::AddTwoInts::Request& req,
lee_srv::AddTwoInts::Response& resp){
int num1 = req.num1;
int num2 = req.num2;
ROS_INFO("服务器接收到的请求数据为:num1 = %d, num2 = %d",num1, num2);
//逻辑处理
if (num1 < 0 || num2 < 0)
{
ROS_ERROR("提交的数据异常:数据不可以为负数");
return false;
}
//如果没有异常,那么相加并将结果赋值给 resp
resp.sum = num1 + num2;
return true;
}
int main(int argc, char *argv[])
{
//解决在终端上打印中文不会出现乱码,二选一或两个一起选都行
setlocale(LC_CTYPE, "zh_CN.utf8");
setlocale(LC_ALL, "");
//初始化节点:命名(唯一)
//参数1和参数2 为节点传值使用
//参数3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一
ros::init(argc,argv,"lee_srv_add_server");
//实例化节点句柄
ros::NodeHandle nh;
//创建一个ServiceServer,服务名为chatter_srv的topic,注册回调函数chatterCallback
ros::ServiceServer server = nh.advertiseService("chatter_srv",chatterCallback);
ROS_INFO("服务已经启动....");
//设置循环回调函数
ros::spin();
return 0;
}
2.2 CMakeLists.txt部分
在install上面直接添加如下代码即可
add_executable(lee_srv_add_server src/lee_srv_add_server.cpp)
add_dependencies(lee_srv_add_server ${PROJECT_NAME}_gencpp)
target_link_libraries(lee_srv_add_server ${catkin_LIBRARIES})
3、客户端代码 C++
路径: /home/lee/lee_ws/src/lee_srv/src/lee_srv_add_client.cpp
3.1 代码部分
#include "ros/ros.h"
#include "lee_srv/AddTwoInts.h"
int main(int argc, char *argv[])
{
//解决在终端上打印中文不会出现乱码,二选一或两个一起选都行
setlocale(LC_CTYPE, "zh_CN.utf8");
setlocale(LC_ALL, "");
//调用时动态传值,如果通过 launch 的 args 传参,需要传递的参数个数 +3
if (argc != 3)
//if (argc != 5)//launch 传参(0-文件路径 1传入的参数 2传入的参数 3节点名称 4日志路径)
{
ROS_ERROR("请提交两个整数");
return 1;
}
//初始化节点:命名(唯一)
//参数1和参数2 为节点传值使用
//参数3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一
ros::init(argc,argv,"lee_srv_add_client");
//实例化节点句柄
ros::NodeHandle nh;
//创建一个ServiceClient,客户端名为chatter_srv的topic,消息类型为lee_srv::AddTwoInts
ros::ServiceClient client = nh.serviceClient<lee_srv::AddTwoInts>("chatter_srv");
//等待服务启动成功,如服务器没启动则一直在此等待
ros::service::waitForService("chatter_srv");
//组织请求数据
//atoi函数:将字符串转化为int类型
//atol函数:将字符串转化为long类型
//atoll函数:将字符串转化为long long类型
//atof函数:将字符串转化为double类型
lee_srv::AddTwoInts add;
add.request.num1 = atoi(argv[1]);
add.request.num2 = atoi(argv[2]);
//发送请求,返回 bool 值,标记是否成功
bool flag = client.call(add);
//处理响应
if (flag)
{
ROS_INFO("请求成功,响应结果为:%d",(int)add.response.sum);
}
else
{
ROS_ERROR("请求处理失败....");
return 1;
}
return 0;
}
3.2 CMakeLists.txt部分
在install上面直接添加如下代码即可
add_executable(lee_srv_add_client src/lee_srv_add_client.cpp)
add_dependencies(lee_srv_add_client ${PROJECT_NAME}_gencpp)
target_link_libraries(lee_srv_add_client ${catkin_LIBRARIES})
4、代码测试
4.1 启动rosmaster
roscore
4.2 启动 lee_srv_add_server 节点
rosrun lee_srv lee_srv_add_server
4.3 启动 lee_srv_add_client 节点,并带参数
rosrun lee_srv lee_srv_add_client 1 2