1、创建项目,ROS2项目创建过程省略。
2、创建服务通信自定义图片信息
2.1服务通信自定义包创建
2.1.1进入项目的src目录下执行下面语句创建server_demo包
ros2 pkg create --build-type ament_cmake server_demo
2.1.2在server_demo目录下创建srv文件夹,并创建一个Myimage.srv文件,并文件中添加(srv文件首字母必须要大写,而且文件名不可以有特殊符号)
sensor_msgs/Image client_image
---
sensor_msgs/Image server_image
2.2编辑CMakeLists.txt和package.xml文件
2.2.1CMakeLists.txt文件添加以下内容
find_package(sensor_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"srv/Myimage.srv"
DEPENDENCIES sensor_msgs
)
如图所示
2.2.2package.xml文件添加以下内容
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
如图所示
2.3编译服务通信自定义包
3.C++的客户端和服务端创建
3.1创建C++的服务通信的包
ros2 pkg create cpp_service --build-type ament_cmake --dependencies rclcpp std_msgs server_demo --node-name server_cpp
为了方便我直接创建包的时候添加了--node-name server_cpp顺便把server_cpp文件也生成
3.2服务端代码
server_cpp文件的代码如下
#include "rclcpp/rclcpp.hpp"
#include "server_demo/srv/myimage.hpp"
#include "opencv2/opencv.hpp"
#include "cv_bridge/cv_bridge.h"
#include "std_msgs/msg/string.hpp"
using server_demo::srv::Myimage;
using std::placeholders::_1;
using std::placeholders::_2;
using namespace std::chrono_literals;
// 3.定义节点类;
class MinimalService: public rclcpp::Node{
public:
MinimalService():Node("minimal_service"){
// 3-1.创建服务端;
server = this->create_service<Myimage>("image_test",std::bind(&MinimalService::appect, this, _1, _2));
RCLCPP_INFO(this->get_logger(),"image_test 服务端启动完毕,等待请求提交...");
}
private:
rclcpp::Service<Myimage>::SharedPtr server;
// 3-2.处理请求数据并响应结果。
void appect(const Myimage::Request::SharedPtr req,const Myimage::Response::SharedPtr res){
cv_bridge::CvImagePtr cv_ptr = cv_bridge::toCvCopy(req->client_image, sensor_msgs::image_encodings::BGR8); //获取客户端的图片
cv::Mat img1= cv_ptr->image;
cv::Mat img= cv::imread("/home/ubuntu/project/image_publish/src/1.jpg",1);//读取传给客户端的照片
sensor_msgs::msg::Image::SharedPtr msg=cv_bridge::CvImage(std_msgs::msg::Header(), "bgr8", img).toImageMsg();
cv::resize(img1, img1, cv::Size(img.size().width,img.size().height));
cv::imshow("server",img1);
cv::waitKey(25000);
res->server_image = *msg; //发送服务器的图片
cv::destroyAllWindows();
}
};
int main(int argc, char const *argv[])
{
// 2.初始化 ROS2 客户端;
rclcpp::init(argc,argv);
// 4.调用spin函数,并传入节点对象指针;
auto server = std::make_shared<MinimalService>();
rclcpp::spin(server);
// 5.释放资源。
rclcpp::shutdown();
return 0;
}
3.3客户端代码
在cpp_service/src目录下创建一个client_cpp.cpp的文件编写客户端的代码
#include "cv_bridge/cv_bridge.h"
#include "rclcpp/rclcpp.hpp"
#include "server_demo/srv/myimage.hpp"
#include "opencv2/opencv.hpp"
#include "cv_bridge/cv_bridge.h"
using server_demo::srv::Myimage;
using namespace std::chrono_literals;
// 3.定义节点类;
class MinimalClient: public rclcpp::Node{
public:
MinimalClient():Node("minimal_client"){
// 3-1.创建客户端;
client = this->create_client<Myimage>("image_test");
RCLCPP_INFO(this->get_logger(),"客户端创建,等待连接服务端!");
}
// 3-2.等待服务连接;
bool connect_server(){
while (!client->wait_for_service(1s))
{
if (!rclcpp::ok())
{
RCLCPP_INFO(rclcpp::get_logger("rclcpp"),"强制退出!");
return false;
}
RCLCPP_INFO(this->get_logger(),"服务连接中,请稍候...");
}
return true;
}
// 3-3.组织请求数据并发送;
rclcpp::Client<Myimage>::FutureAndRequestId send_request(sensor_msgs::msg::Image image1){
auto request = std::make_shared<Myimage::Request>();
request->client_image = image1;
return client->async_send_request(request);
}
private:
rclcpp::Client<Myimage>::SharedPtr client;
};
int main(int argc, char ** argv)
{
// 2.初始化 ROS2 客户端;
rclcpp::init(argc,argv);
// 4.创建对象指针并调用其功能;
auto client = std::make_shared<MinimalClient>();
bool flag = client->connect_server();
if (!flag)
{
RCLCPP_INFO(rclcpp::get_logger("rclcpp"),"服务连接失败!");
return 0;
}
cv::Mat img= cv::imread("/home/ubuntu/project/image_publish/src/3.jpg",1);
sensor_msgs::msg::Image::SharedPtr img1 = cv_bridge::CvImage(std_msgs::msg::Header(), "bgr8", img).toImageMsg();
auto response = client->send_request(*img1);
// 处理响应
if (rclcpp::spin_until_future_complete(client,response) == rclcpp::FutureReturnCode::SUCCESS)
{
RCLCPP_INFO(client->get_logger(),"请求正常处理");
cv_bridge::CvImagePtr cv_ptr = cv_bridge::toCvCopy(response.get()->server_image, sensor_msgs::image_encodings::BGR8);
cv::Mat s_img= cv_ptr->image;
cv::resize(s_img, s_img, cv::Size(s_img.size().width,s_img.size().height));
cv::imshow("client",s_img);
cv::waitKey(5000);
cv::destroyAllWindows();
} else {
RCLCPP_INFO(client->get_logger(),"请求异常");
}
// 5.释放资源。
rclcpp::shutdown();
return 0;
}
3.4编辑CMakeList.txt
文件添加内容如下框中的地方
cmake_minimum_required(VERSION 3.8)
project(cpp_service)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(server_demo REQUIRED)
find_package(cv_bridge REQUIRED) #cv_brige为图片传输的重要包需要添加
find_package(OpenCV REQUIRED)
add_executable(server_cpp src/server_cpp.cpp)
target_include_directories(server_cpp PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
target_compile_features(server_cpp PUBLIC c_std_99 cxx_std_17) # Require C99 and C++17
ament_target_dependencies(
server_cpp
"rclcpp"
"std_msgs"
"server_demo"
"cv_bridge"
)
add_executable(client_cpp src/client_cpp.cpp)
target_include_directories(client_cpp PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
target_compile_features(client_cpp PUBLIC c_std_99 cxx_std_17) # Require C99 and C++17
ament_target_dependencies(
client_cpp
"rclcpp"
"std_msgs"
"server_demo"
"cv_bridge"
)
target_link_libraries(client_cpp ${OpenCV_LIBS})
target_link_libraries(server_cpp ${OpenCV_LIBS})
install(TARGETS client_cpp
DESTINATION lib/${PROJECT_NAME})
install(TARGETS server_cpp
DESTINATION lib/${PROJECT_NAME})
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# comment the line when a copyright and license is added to all source files
set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# comment the line when this package is in a git repo and when
# a copyright and license is added to all source files
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
3.5测试结果如下
服务端接收的图片
客户端接收的图片
4.python的客户端和服务端创建及调用YOLO模型
python的客户端和服务端的如下并完成调用yolo模型进行识别,调用yolo模型我这里使用的是
ultralytics进行调用,ultralytics安装下载请自行百度
4.1创建python的服务通信包
执行以下语句创建python的包
ros2 pkg create py_server --build-type ament_python --dependencies rclpy std_msgs server_demo
4.2创建客户端和服务端的代码文件
在py_server目录下新建一个client_py.py和server_py.py文件
4.3python服务端代码
server_py.py的代码如下
import rclpy
from rclpy.node import Node
from ultralytics import YOLO
from cv_bridge import CvBridge, CvBridgeError
from sensor_msgs.msg import Image
import numpy as np
from server_demo.srv import Myimage
# 3.定义节点类;
class MinimalService(Node):
def __init__(self):
super().__init__('minimal_service_py')
# 3-1.创建服务端;
self.srv = self.create_service(Myimage,'image_test', self.add_two_ints_callback)
self.get_logger().info("服务端启动!")
# 3-2.处理请求数据并响应结果。
def add_two_ints_callback(self, request, response):
bridge = CvBridge()
img =bridge.imgmsg_to_cv2(request.client_image, "bgr8")
results = model(img)
# 获取框
#boxes = results[0].boxes.xywh.cpu()
# track_ids = results[0].boxes.id.int().cpu().tolist()
# 在帧上展示结果
annotated_frame = results[0].plot()
bridge = CvBridge()
image_message = bridge.cv2_to_imgmsg(annotated_frame,encoding="bgr8")
response.server_image = image_message
return response
def main():
# 2.初始化 ROS2 客户端;
rclpy.init()
global model
model = YOLO('/home/ubuntu/weights/yolov8n.pt') #调用yolo官方模型
# 4.调用spin函数,并传入节点对象;
minimal_service = MinimalService()
rclpy.spin(minimal_service)
# 5.释放资源。
rclpy.shutdown()
if __name__ == '__main__':
main()
4.4python客户端代码
client_py.py的代码如下
import sys
import rclpy
import cv2
from rclpy.node import Node
from cv_bridge import CvBridge, CvBridgeError
from ultralytics import YOLO
from sensor_msgs.msg import Image
import numpy as np
from server_demo.srv import Myimage
# 3.定义节点类;
class MinimalClient(Node):
def __init__(self):
super().__init__('minimal_client_py')
# 3-1.创建客户端;
self.cli = self.create_client(Myimage, 'image_test')
self.req = Myimage.Request()
# 3-2.等待服务连接;
while not self.cli.wait_for_service(timeout_sec=1.0):
self.get_logger().info('服务连接中,请稍候...')
# 3-3.组织请求数据并发送;
def send_request(self):
img = cv2.imread('/home/ubuntu/project/image_publish/src/3.jpg')
bridge = CvBridge()
image_message = bridge.cv2_to_imgmsg(img,encoding="bgr8")
self.req.client_image =image_message
self.future = self.cli.call_async(self.req)
def main():
# 2.初始化 ROS2 客户端;
rclpy.init()
# 4.创建对象并调用其功能;
minimal_client = MinimalClient()
minimal_client.send_request()
# 处理响应
rclpy.spin_until_future_complete(minimal_client,minimal_client.future)
try:
response = minimal_client.future.result()
except Exception as e:
minimal_client.get_logger().info(
'服务请求失败:%r' % (e,))
else:
bridge = CvBridge()
cv_img = bridge.imgmsg_to_cv2(response.server_image, "bgr8")
cv2.imshow("YOLOv8 Tracking", cv_img)
minimal_client.get_logger().info('响应成功')
cv2.waitKey(5000)
cv2.destroyAllWindows()
# 5.释放资源。
rclpy.shutdown()
if __name__ == '__main__':
main()
4.5setup.py文件编辑
setup.py文件内容添加如下
'client_py = py_server.client_py:main',
'server_py = py_server.server_py:main'