1 安装 jsoncpp 库
- 使用包管理器安装
在Ubuntu中,jsoncpp库可以通过APT包管理器直接安装:
sudo apt-get update
sudo apt-get install libjsoncpp-dev
这个命令会安装jsoncpp库的开发文件(头文件和库文件)
- 检查安装情况
安装完成后,可以检查是否正确安装了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;
}
- 编译代码
确保 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)
- 编译并运行
首先,编译你的代码:
cd ~/catkin_ws
catkin_make
roscore
启动服务端:
source devel/setup.bash
rosrun json_comm json_server
启动客户端:
rosrun json_comm json_client
- 结果
客户端会发送一个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是一种标准格式,方便在不同编程语言或系统中解析和处理。
具体操作示例:
- 客户端发送请求(操作类型为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序列化成字符串并发送给服务端。
- 服务端接收请求并解析:
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对象并根据操作字段执行相应的命令。
- 服务端生成响应并发送:
Json::Value responseRoot;
responseRoot["status"] = "success";
responseRoot["message"] = "Move operation completed";
Json::FastWriter writer;
res.response = writer.write(responseRoot);
服务端在完成操作后,将结果封装为JSON对象并发送回客户端。
- 客户端接收并解析响应:
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;
}
添加第三方库
- 解压文件
首先,解压 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 文件夹。
- 检查依赖项
确保该包的所有依赖项已经安装。你可以使用 rosdep 来自动安装依赖项:
rosdep install usb_cam_develop
- 编译项目
回到ROS工作空间的根目录,然后编译整个工作空间:
cd ~/catkin_ws
catkin_make
或者,如果你使用 catkin build,可以运行:
catkin build
- 更新环境
编译完成后,更新你的工作空间环境,以便ROS可以找到新添加的包:
source devel/setup.bash
- 在项目中使用 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项目中了。