ros c++ server/client

1 安装 jsoncpp 库

  1. 使用包管理器安装
    在Ubuntu中,jsoncpp库可以通过APT包管理器直接安装:
sudo apt-get update
sudo apt-get install libjsoncpp-dev

这个命令会安装jsoncpp库的开发文件(头文件和库文件)

  1. 检查安装情况
    安装完成后,可以检查是否正确安装了jsoncpp:
dpkg -L libjsoncpp-dev

这个命令会列出jsoncpp库的文件路径,特别是头文件的路径(通常位于 /usr/include/jsoncpp/)和库文件路径(如 /usr/lib/)
在这里插入图片描述

2 初始化工作空间

cd 文档/
mkdir catkin_ws
cd catkin_ws/
mkdir src
cd src/
catkin_init_workspace
cd ..
catkin_make
catkin_make install

创建功能包

cd src/
catkin_create_pkg service_json_comm roscpp std_msgs
cd..
catkin_make

3 定义服务文件

创建 srv目录

mkdir -p ~/文档/catkin_ws/src/service_json_comm/srv

创建ser_json.srv文件

echo "string request \n ---\n string response" > ~/文档/catkin_ws/src/service_json_comm/srv/Service_Json.srv

echo 命令 创建一个名为Service_Json.srv的文件,并将string request \n —\n string response写入文件中。如果文件已经存在,则内容会被覆盖

定义服务,request 是一个 string,response 也是一个 string

修改CMakeLists.txt

cmake_minimum_required(VERSION 3.0.2)
project(service_json_comm)

## Compile as C++11, supported in ROS Kinetic and newer
# add_compile_options(-std=c++11)

## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
  roscpp
  std_msgs
  message_generation
)

## System dependencies are found with CMake's conventions
# find_package(Boost REQUIRED COMPONENTS system)


## Uncomment this if the package has a setup.py. This macro ensures
## modules and global scripts declared therein get installed
## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
# catkin_python_setup()

################################################
## Declare ROS messages, services and actions ##
################################################

## To declare and build messages, services or actions from within this
## package, follow these steps:
## * Let MSG_DEP_SET be the set of packages whose message types you use in
##   your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
## * In the file package.xml:
##   * add a build_depend tag for "message_generation"
##   * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET
##   * If MSG_DEP_SET isn't empty the following dependency has been pulled in
##     but can be declared for certainty nonetheless:
##     * add a exec_depend tag for "message_runtime"
## * In this file (CMakeLists.txt):
##   * add "message_generation" and every package in MSG_DEP_SET to
##     find_package(catkin REQUIRED COMPONENTS ...)
##   * add "message_runtime" and every package in MSG_DEP_SET to
##     catkin_package(CATKIN_DEPENDS ...)
##   * uncomment the add_*_files sections below as needed
##     and list every .msg/.srv/.action file to be processed
##   * uncomment the generate_messages entry below
##   * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)

## Generate messages in the 'msg' folder
# add_message_files(
#   FILES
#   Message1.msg
#   Message2.msg
# )

## Generate services in the 'srv' folder
 add_service_files(
   FILES
   Service_Json.srv
#   Service1.srv
#   Service2.srv
 )

## Generate actions in the 'action' folder
# add_action_files(
#   FILES
#   Action1.action
#   Action2.action
# )

## Generate added messages and services with any dependencies listed here
 generate_messages(
   DEPENDENCIES
   std_msgs
 )

################################################
## Declare ROS dynamic reconfigure parameters ##
################################################

## To declare and build dynamic reconfigure parameters within this
## package, follow these steps:
## * In the file package.xml:
##   * add a build_depend and a exec_depend tag for "dynamic_reconfigure"
## * In this file (CMakeLists.txt):
##   * add "dynamic_reconfigure" to
##     find_package(catkin REQUIRED COMPONENTS ...)
##   * uncomment the "generate_dynamic_reconfigure_options" section below
##     and list every .cfg file to be processed

## Generate dynamic reconfigure parameters in the 'cfg' folder
# generate_dynamic_reconfigure_options(
#   cfg/DynReconf1.cfg
#   cfg/DynReconf2.cfg
# )

###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if your package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES service_json_comm
#  CATKIN_DEPENDS roscpp std_msgs
#  DEPENDS system_lib
   CATKIN_DEPENDS message_runtime roscpp std_msgs
)

###########
## Build ##
###########

## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
# include
  ${catkin_INCLUDE_DIRS}
)

## Declare a C++ library
# add_library(${PROJECT_NAME}
#   src/${PROJECT_NAME}/service_json_comm.cpp
# )

## Add cmake target dependencies of the library
## as an example, code may need to be generated before libraries
## either from message generation or dynamic reconfigure
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
# add_executable(${PROJECT_NAME}_node src/service_json_comm_node.cpp)

## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
## target back to the shorter version for ease of user use
## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")

## Add cmake target dependencies of the executable
## same as for the library above
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Specify libraries to link a library or executable target against
# target_link_libraries(${PROJECT_NAME}_node
#   ${catkin_LIBRARIES}
# )

#############
## Install ##
#############

# all install targets should use catkin DESTINATION variables
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html

## Mark executable scripts (Python etc.) for installation
## in contrast to setup.py, you can choose the destination
# catkin_install_python(PROGRAMS
#   scripts/my_python_script
#   DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

## Mark executables for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html
# install(TARGETS ${PROJECT_NAME}_node
#   RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

## Mark libraries for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html
# install(TARGETS ${PROJECT_NAME}
#   ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
#   LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
#   RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
# )

## Mark cpp header files for installation
# install(DIRECTORY include/${PROJECT_NAME}/
#   DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
#   FILES_MATCHING PATTERN "*.h"
#   PATTERN ".svn" EXCLUDE
# )

## Mark other files for installation (e.g. launch and bag files, etc.)
# install(FILES
#   # myfile1
#   # myfile2
#   DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# )

#############
## Testing ##
#############

## Add gtest based cpp test target and link libraries
# catkin_add_gtest(${PROJECT_NAME}-test test/test_service_json_comm.cpp)
# if(TARGET ${PROJECT_NAME}-test)
#   target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
# endif()

## Add folders to be run by python nosetests
# catkin_add_nosetests(test)

修改package.xml

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

4 添加服务节点

在 src 目录下创建 server_json.cpp 文件:

#include "ros/ros.h"
#include "json_comm/JsonService.h"
#include <jsoncpp/json/json.h>

bool handleJsonRequest(json_comm::JsonService::Request &req,
                       json_comm::JsonService::Response &res) {
    // 解析JSON请求字符串
    Json::Value root;
    Json::Reader reader;

    if (!reader.parse(req.request, root)) {
        res.response = "{\"error\": \"Failed to parse JSON\"}";
        return true;
    }

    // 简单的处理:返回一个新的JSON响应
    Json::Value responseRoot;
    responseRoot["status"] = "success";
    responseRoot["received_data"] = root;

    Json::FastWriter writer;
    res.response = writer.write(responseRoot);

    ROS_INFO("Request: %s", req.request.c_str());
    ROS_INFO("Response: %s", res.response.c_str());

    return true;
}

int main(int argc, char **argv) {
    ros::init(argc, argv, "json_server");
    ros::NodeHandle n;

    // 创建服务
    ros::ServiceServer service = n.advertiseService("json_service", handleJsonRequest);
    ROS_INFO("JSON server is ready.");

    ros::spin();

    return 0;
}

Client.cpp

#include "ros/ros.h"
#include "json_comm/JsonService.h"
#include <jsoncpp/json/json.h>

int main(int argc, char **argv) {
    ros::init(argc, argv, "json_client");

    ros::NodeHandle n;
    ros::ServiceClient client = n.serviceClient<json_comm::JsonService>("json_service");

    json_comm::JsonService srv;

    // 构建一个简单的JSON请求
    Json::Value root;
    root["data"] = "Hello, JSON!";
    Json::FastWriter writer;
    srv.request.request = writer.write(root);

    if (client.call(srv)) {
        ROS_INFO("Response: %s", srv.response.response.c_str());
    } else {
        ROS_ERROR("Failed to call service json_service");
        return 1;
    }

    return 0;
}
  1. 编译代码
    确保 CMakeLists.txt 文件包含以下内容来编译你的节点:
add_executable(json_server src/json_server.cpp)
add_dependencies(json_server ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(json_server ${catkin_LIBRARIES} jsoncpp)

add_executable(json_client src/json_client.cpp)
add_dependencies(json_client ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(json_client ${catkin_LIBRARIES} jsoncpp)
  1. 编译并运行
    首先,编译你的代码:
cd ~/catkin_ws
catkin_make
roscore

启动服务端:

source devel/setup.bash
rosrun json_comm json_server

启动客户端:

rosrun json_comm json_client
  1. 结果
    客户端会发送一个JSON格式的请求到服务端,服务端解析这个请求并返回一个新的JSON格式的响应。

操作流程:
消息打包:

在客户端,所有的键值对(如参数、指令、数据等)都被打包进一个JSON对象中,随后将其序列化为字符串。

例如,假设你要传递多个参数,如操作类型和数据内容:

{
  "operation": "move",
  "data": {
    "x": 10,
    "y": 20,
    "z": 30
  }
}

这个JSON对象通过jsoncpp或其他库序列化为一个string类型的消息,并发送到服务端。

服务端接收并反序列化:

服务端接收到这个string消息后,会将其反序列化为JSON对象。

然后,根据消息中的不同字段(例如 “operation” 字段)执行相应的操作,并获取到数据(如 “data” 字段下的内容)。

服务端在执行操作后,可以构造另一个JSON对象作为响应,序列化为string,并将其发送回客户端。

客户端解析响应:

客户端接收到服务端返回的string消息后,同样会将其反序列化为JSON对象,从而获取服务端的返回结果。
这样设计的好处:
灵活性:使用JSON字符串传递消息,不需要为每一种请求或响应定义特定的消息类型,所有的参数和数据都可以用键值对封装起来。
可扩展性:以后如果需要添加新字段或指令,不需要更改消息类型,只需修改JSON的键值对结构即可。
可读性:JSON格式可读性强,方便调试和日志记录。
跨平台性:JSON是一种标准格式,方便在不同编程语言或系统中解析和处理。
具体操作示例:

  1. 客户端发送请求(操作类型为move):
Json::Value root;
root["operation"] = "move";
root["data"]["x"] = 10;
root["data"]["y"] = 20;
root["data"]["z"] = 30;

Json::FastWriter writer;
srv.request.request = writer.write(root);

客户端将这个JSON序列化成字符串并发送给服务端。

  1. 服务端接收请求并解析:
Json::Value root;
Json::Reader reader;

if (reader.parse(req.request, root)) {
    std::string operation = root["operation"].asString();
    
    if (operation == "move") {
        int x = root["data"]["x"].asInt();
        int y = root["data"]["y"].asInt();
        int z = root["data"]["z"].asInt();
        
        // 执行移动操作...
    }
}

服务端将请求反序列化为JSON对象并根据操作字段执行相应的命令。

  1. 服务端生成响应并发送:
Json::Value responseRoot;
responseRoot["status"] = "success";
responseRoot["message"] = "Move operation completed";

Json::FastWriter writer;
res.response = writer.write(responseRoot);

服务端在完成操作后,将结果封装为JSON对象并发送回客户端。

  1. 客户端接收并解析响应:
Json::Value responseRoot;
Json::Reader reader;

if (reader.parse(srv.response.response, responseRoot)) {
    std::string status = responseRoot["status"].asString();
    std::string message = responseRoot["message"].asString();
    
    // 处理响应结果...
}

客户端解析服务端的响应,获取执行结果或状态信息。

结论:
这种通过JSON封装消息的方法非常适合需要灵活传递复杂数据结构的场景。尤其在ROS系统中,避免了定义多个复杂的消息类型,增强了系统的可维护性和扩展性。

举例子:
传递相机内参

add_executable(camera_server src/camera_server.cpp)
target_link_libraries(camera_server ${catkin_LIBRARIES} jsoncpp)

add_executable(camera_client src/camera_client.cpp)
target_link_libraries(camera_client ${catkin_LIBRARIES} jsoncpp)

service

#include "ros/ros.h"
#include "camera_comm/JsonService.h"
#include <jsoncpp/json/json.h>
#include <iostream>

// 处理服务请求
bool handleCameraRequest(camera_comm::JsonService::Request &req,
                         camera_comm::JsonService::Response &res) {
    // 解析JSON请求字符串
    Json::Value root;
    Json::Reader reader;
    
    if (!reader.parse(req.request, root)) {
        res.response = "{\"error\": \"Failed to parse JSON\"}";
        return true;
    }

    // 解析相机内参
    Json::Value camera_params = root["camera_parameters"];
    double fx = camera_params["fx"].asDouble();
    double fy = camera_params["fy"].asDouble();
    double cx = camera_params["cx"].asDouble();
    double cy = camera_params["cy"].asDouble();

    ROS_INFO("Received Camera Intrinsics:");
    ROS_INFO("fx: %f, fy: %f, cx: %f, cy: %f", fx, fy, cx, cy);

    // 构建响应
    Json::Value responseRoot;
    responseRoot["status"] = "success";
    responseRoot["message"] = "Camera parameters updated successfully";
    
    Json::StreamWriterBuilder writer;
    res.response = Json::writeString(writer, responseRoot);

    return true;
}

int main(int argc, char **argv) {
    ros::init(argc, argv, "camera_server");
    ros::NodeHandle n;

    // 创建服务
    ros::ServiceServer service = n.advertiseService("camera_service", handleCameraRequest);
    ROS_INFO("Camera Server is ready.");

    ros::spin();
    return 0;
}

Client

#include "ros/ros.h"
#include "camera_comm/JsonService.h"
#include <jsoncpp/json/json.h>
#include <iostream>

int main(int argc, char **argv) {
    ros::init(argc, argv, "camera_client");
    ros::NodeHandle n;

    // 创建服务客户端
    ros::ServiceClient client = n.serviceClient<camera_comm::JsonService>("camera_service");
    camera_comm::JsonService srv;

    // 创建包含相机内参的JSON请求
    Json::Value root;
    root["camera_parameters"]["fx"] = 525.0;
    root["camera_parameters"]["fy"] = 525.0;
    root["camera_parameters"]["cx"] = 319.5;
    root["camera_parameters"]["cy"] = 239.5;

    // 序列化为字符串
    Json::StreamWriterBuilder writer;
    srv.request.request = Json::writeString(writer, root);

    // 调用服务
    if (client.call(srv)) {
        ROS_INFO("Response: %s", srv.response.response.c_str());
    } else {
        ROS_ERROR("Failed to call service camera_service");
        return 1;
    }

    return 0;
}

添加第三方库

  1. 解压文件
    首先,解压 usb_cam_develop.zip 文件到你的ROS工作空间中。

将 usb_cam_develop.zip 移动到你的ROS工作空间的 src 目录:

mv usb_cam_develop.zip ~/catkin_ws/src/

进入 src 目录:

cd ~/catkin_ws/src

解压缩文件:

unzip usb_cam_develop.zip

解压后,你应该能够在 src 目录中看到 usb_cam_develop 文件夹。

  1. 检查依赖项
    确保该包的所有依赖项已经安装。你可以使用 rosdep 来自动安装依赖项:
rosdep install usb_cam_develop
  1. 编译项目
    回到ROS工作空间的根目录,然后编译整个工作空间:
cd ~/catkin_ws
catkin_make

或者,如果你使用 catkin build,可以运行:

catkin build
  1. 更新环境
    编译完成后,更新你的工作空间环境,以便ROS可以找到新添加的包:
source devel/setup.bash
  1. 在项目中使用 usb_cam_develop
    现在你可以按照需求在你的ROS项目中使用 usb_cam_develop 包。确保在 CMakeLists.txt 和 package.xml 中正确引用它。

在 CMakeLists.txt 中添加:

find_package(catkin REQUIRED COMPONENTS
  roscpp
  usb_cam_develop
)

在 package.xml 中添加:

<depend>usb_cam_develop</depend>

这样,usb_cam_develop 就成功集成到你的ROS项目中了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值