《ROS2 机器人开发 从入门道实践》 鱼香ROS2——第4章内容

目录

第4章 服务和参数——深入ROS2通信

4.2 用Python服务通信实现人脸检测

4.2.1 自定义服务接口

4.2.2 Python人脸检测

4.2.3人脸检测服务实现

4.2.4人脸检测客户端的实现

4.3 用C++服务通信做一个巡逻海龟

4.3.1 自定义服务接口

4.3.2 服务端代码实现

4.3.3 客户端代码实现

4.4 在python节点中使用参数

4.4.1 参数声明与设置

 4.4.2 订阅参数事件

4.4.3 客户端代码实现参数修改

4.5 在C++节点中使用参数

4.5.1 参数声明与设置

4.5.2 接收参数事件

4.5.3 修改其他节点的参数

4.6 使用launch启动脚本

4.6.1 使用launch启动多个节点

4.6.2 使用launch传递参数

4.6.3 launch使用进阶


第4章 服务和参数——深入ROS2通信

4.2 用Python服务通信实现人脸检测

4.2.1 自定义服务接口

1. 创建接口功能包

终端中输入

ros2 pkg create chapt4_interfaces --dependencies sensor_msgs rosidl_default_generators --license Apache-2.0
  • ros2 pkg create 功能包名称 --dependencies 依赖

 ROS2提供的用于创建功能包的命令行工具

  • sensor_msgs ROS2中的消息定义功能包,定义了各种各样传感器数据的消息类型,比如,LaserScan消息类型用于表示激光雷达扫描得到的数据,包含了扫描角度范围、距离信息等关键数据;Image消息类型可以用来传递摄像头拍摄到的图像相关信息,像图像的宽度、高度、编码格式以及像素数据等。
  • rosidl_default_generators用于生成ROS接口相关的代码,当你定义了自定义的消息类型(.msg文件)、服务类型(.srv文件)或者动作类型(.action 文件)后,需要通过代码生成工具将这些接口定义转换为不同编程语言(如C++、Python等)可用的代码形式,rosidl_default_generators就是承担这样的角色,它能根据你的接口定义自动生成相应的代码,方便在不同语言环境下进行接口的实现和调用。

2. 创建srv消息文件FaceDetector.srv

sensor_msgs/Image image # 人脸图像
---
int16 number
float32 use_time
int32[] top
int32[] right
int32[] bottom
int32[] left

3. 修改CMakeLists.txt,利用cmake将自定义消息文件生成接口

rosidl_generate_interfaces(${PROJECT_NAME}
  "srv/FaceDetector.srv"
  DEPENDENCIES sensor_msgs
)

4. 修改package.xml,声明这个包所属的组

  <member_of_group>rosidl_interface_packages</member_of_group>

<member_of_group> 标签所包裹的内容(即 rosidl_interface_packages)表示当前所涉及的元素(比如某个功能包)隶属于名为 rosidl_interface_packages 的分组,照该分组预先设定好的构建规则、依赖处理方式等来对这个功能包进行相应的构建操作

5. 设置环境再运行ros2 interface show chapt4_interfaces/srv/FaceDetector.srv

4.2.2 Python人脸检测

1. 安装人脸识别包

pip3 install face_recognition -i https://pypi.tuna.tsinghua.edu.cn/simple

2. 在chapt4/chapt4_ws/src路径下创建功能包

ros2 pkg create demo_python_service --build-type ament_python --dependencies rclpy chapt4_interfaces --license Apache-2.0

3. 下载人脸图片

https://github.com/ultralytics/yolov5/raw/master/data/images/zidane.jpg

使用代理

http://github.fishros.org/https://github.com/ultralytics/yolov5/raw/master/data/images/zidane.jpg

4. 修改setup.py设置colcon build自动拷贝文件到build中

    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
        # 添加拷贝图片文件,逗号前是目标路径,逗号后是拷贝文件的路径(以当前setup.py文件所在层)
        ('share/' + package_name + "/resource", ['resource/default.jpg']),
    ],

colcon build必须在工作空间ws路径下执行

5. 主文件

# 用于检测图像中人脸的位置、识别人脸的特征以及进行人脸比对等操作
import face_recognition
import cv2
# 获取功能包share目录的绝对路径
from ament_index_python.packages import get_package_share_directory
# 提供与操作系统交互的各种功能,像文件和目录操作等
import os

def main():
    # 方法1:获取图片绝对路径
    # default_image_path = get_package_share_directory('demo_python_service') + '/resource/default.jpg'
    # 方法2:使用os,后面不能是/resource/default.jpg,不加/
    default_image_path = os.path.join(get_package_share_directory('demo_python_service'), 'resource/default.jpg')
    # 使用cv2加载图片
    image = cv2.imread(default_image_path)
    # 检测人脸,返回人脸坐标值
    face_locations = face_recognition.face_locations(image, number_of_times_to_upsample = 1, model = 'hog')
    # 绘制人脸边框,通过左上角顶点坐标和右下角顶点坐标
    for top,right,bottom,left in face_locations:
        cv2.rectangle(image, (left, top), (right,bottom), (255,0,0), 4)
    # 显示结果
    cv2.imshow('Face detect result', image)
    # 等待用户按键操作,参数0表示程序会一直等待,直到用户按下任意键为止
    cv2.waitKey(0)

6. 注册成为可执行文件,修改setup.py

    entry_points={
        'console_scripts': [
            # 功能包.文件名.函数名
            'learn_face_detect = demo_python_service.learn_face_detect:main',
        ],

4.2.3人脸检测服务实现

import rclpy
from rclpy.node import Node
from chapt4_interfaces.srv import FaceDetector
import face_recognition
import cv2
from ament_index_python.packages import get_package_share_directory
import os
# 用于在ROS图像消息格式和OpenCV图像格式之间进行转换的工具类
from cv_bridge import CvBridge
import time
#把ros格式转换为cv格式
class FaceDetectNode(Node):
    def __init__(self):
        super().__init__('face_detect_node')
        self.bridge = CvBridge()
        # 建了一个服务,服务的类型是之前导入的FaceDetector,服务名称为/face_detect
        self.service_ = self.create_service(FaceDetector, '/face_detect', self.detect_face_callback)
        self.get_logger().info("执行create_service")
        # 把参数作为类属性
        self.number_of_times_to_upsample = 1
        self.model = 'hog'
        self.default_image_path = os.path.join(get_package_share_directory('demo_python_service'), 'resource/default.jpg')
        self.get_logger().info("人脸识别服务启动!")
        
    def detect_face_callback(self, request, response):
        if request.image.data:
            cv_image = self.bridge.imgmsg_to_cv2(request.image)
        else:
            # 使用cv2加载图片
            cv_image = cv2.imread(self.default_image_path)
            self.get_logger().info("传入图片为空,使用默认图片")
        # 计算耗时
        start_time = time.time()
        self.get_logger().info("加载完成图片,开始识别")
        face_locations = face_recognition.face_locations(cv_image, number_of_times_to_upsample = 1, model = 'hog')
        response.use_time = time.time() - start_time
        response.number = len(face_locations)
        for top,right,bottom,left in face_locations:
            response.top.append(top)
            response.right.append(right)
            response.bottom.append(bottom)
            response.left.append(left)
        return response
    
def main(args = None):
    rclpy.init(args = args)
    node = FaceDetectNode()
    rclpy.spin(node)
    rclpy.shutdown()

4.2.4人脸检测客户端的实现

import rclpy
from rclpy.node import Node
from chapt4_interfaces.srv import FaceDetector
import face_recognition
import cv2
from ament_index_python.packages import get_package_share_directory
import os
from cv_bridge import CvBridge
import time

class FaceDetectClientNode(Node):
    def __init__(self):
        super().__init__('face_detect_client_node')
        self.bridge = CvBridge()
        self.default_image_path = os.path.join(get_package_share_directory('demo_python_service'), 'resource/test.jpg')
        self.get_logger().info("人脸识别客户端启动!")
        self.client = self.create_client(FaceDetector,'/face_detect')
        self.image = cv2.imread(self.default_image_path)

    def send_request(self):
        # 1.判断服务端是否在线
        while self.client.wait_for_service(timeout_sec=1.0) is False:
            self.get_logger().info("等待服务端上线!")
        # 2.构造request
        request = FaceDetector.Request()
        request.image = self.bridge.cv2_to_imgmsg(self.image)
        # 3.发送请求并等待处理完成
        future = self.client.call_async(request)# 需要等待服务端处理完成,才会把结果放到future中,否则没有数据
        # # 如果没有处理完成,休眠等待,但会堵塞线程,使当前线程无法接受来自服务端的返回
        # while not future.done():
        #     time.sleep(1.0)
        # 以异步的方式向服务端发送服务请求,返回的future是一个类似于占位符的东西,在服务端处理完请求并返回结果后存放对应的数据
        # 会在不阻塞线程正常运行的情况下,持续等待服务端返回响应,直到future对应的请求有了结果
        rclpy.spin_until_future_complete(self, future)
        response = future.result()# 获取响应
        self.get_logger().info(f"接受到响应,共检测到有{response.number}张人脸,耗时{response.use_time}s")
        # 调用图片显示函数
        self.show_response(response)

    def show_response(self, response):
        for i in range(response.number):
            top = response.top[i]
            right = response.right[i]
            bottom = response.bottom[i]
            left = response.left[i]
            cv2.rectangle(self.image, (left, top), (right,bottom), (255,0,0), 4)
        cv2.imshow('Face detect result', self.image)
        self.get_logger().info("图片已显示")
        cv2.waitKey(0)#也会堵塞,导致spin无法正常运行


def main(args = None):
    rclpy.init(args = args)
    node = FaceDetectClientNode()
    node.send_request()
    rclpy.spin(node)
    rclpy.shutdown()

4.3 用C++服务通信做一个巡逻海龟

服务端提供海龟控制,客户端提供随机目标点。

4.3.1 自定义服务接口

1. srv目录下新建文件Patrol.srv

float32 target_x # 目标x
float32 target_y # 目标y
# 上面的参数类型为chapt4_interfaces::srv::Patrol::Request
---
# 下面的参数类型为chapt4_interfaces::srv::Patrol::Response
int8 SUCCESS = 1 # 表示成功的常量
int8 FAIL = 0    # 表示失败的常量
int8 result      # 处理结果

2. 修改CMakeLists.txt

rosidl_generate_interfaces(${PROJECT_NAME}
  "srv/FaceDetector.srv"
  "srv/Patrol.srv"
  DEPENDENCIES sensor_msgs
)

3.  设置环境再运行ros2 interface show chapt4_interfaces/srv/Patrol.srv

4.3.2 服务端代码实现

1. /yuxiangros2/chapt4/chapt4_ws/src目录下创建功能包

ros2 pkg create demo_cpp_service --build-type ament_cmake --dependencies chapt4_interfaces rclcpp geometry_msgs turtlesim --license Apache-2.0

 2. chapt4/chapt4_ws/src/demo_cpp_service/src新建文件turtle_control.cpp

#include "chapt4_interfaces/srv/patrol.hpp"
#include "geometry_msgs/msg/twist.hpp"
#include "rclcpp/rclcpp.hpp"
#include "turtlesim/msg/pose.hpp"

class TurtleController : public rclcpp::Node {
   public:
    // 直接在构造函数中设置节点名称turtle_controller
    TurtleController() : Node("turtle_controller") {
        // 调用this->create_service函数创建名为patrol的服务,服务类型为
        // chapt4_interfaces::srv::Patrol。当有客户端向该服务发送请求时会执行回调函数
        patrol_s
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值