准备工作

开始学习代码之前,先看一下代码结构,有助于在高层次上理解整个功能。

OriginBot的手势控制代码库包含多个组件,每个组件都有其特定的职责:

  • CMakeLists.txt:定义了如何构建项目。
  • README.md:提供了项目的概述和使用说明。
  • launch/:存放启动文件,用于启动和配置节点。
  • src/:包含主要的源代码文件。
  • include/:存放头文件,用于定义接口和数据结构。

关键的代码文件有如下几个:

  • gesture_control_node.cpp:这是手势控制节点的主要实现文件。它订阅手势识别消息,并根据消息内容发布机器人的运动控制指令。
  • param_node.cpp:参数节点用于管理配置参数,如手势识别的灵敏度和机器人的运动速度。
  • gesture_control_engine.cpp 和 gesture_control_engine.h:这些文件定义了GestureControlEngine类,它是处理手势识别和生成控制指令的核心组件

函数调用链路

我尝试梳理出OriginBot手势控制功能的函数调用链路。以下是主要的组件和它们的交互方式:

1. main.cpp

  • 程序的入口点,初始化ROS 2并启动所有的节点和执行器。
  • 调用rclcpp::init来初始化ROS 2节点。
  • 创建GestureControlEngine的实例,并将其节点添加到执行器中。
  • 启动执行器,进入spin循环,等待消息和回调。

2. GestureControlEngine

  • 一个单例类,负责管理手势控制的整个流程。
  • FeedSmart方法将接收到的手势消息推送到队列中,并通知处理线程。
  • RunStrategy方法从队列中取出手势消息,并根据手势类型决定机器人的动作。
  • DoRotateDoMove方法根据手势的方向和步长计算出机器人的运动指令。
  • CancelMove方法用于取消当前的运动指令。
  • UpdateRobotPose方法(未在提供的内容中明确提及,但根据命名推测)可能用于更新机器人的运动状态。

3. GestureControlNode

  • 一个ROS 2节点,订阅手势识别消息并将其传递给GestureControlEngine
  • SmartTopicCallback是订阅手势消息的回调函数,它将接收到的消息传递给GestureControlEngineFeedSmart方法。

4. ParametersClass

  • 另一个ROS 2节点,用于管理和响应配置参数的变化。
  • Respond方法定期检查参数的变化,并将新的参数值应用到GestureControlEngine

5. gesture_control.launch.py

  • ROS 2启动文件,用于配置和启动GestureControlNodeParametersClass节点。
  • 通过Node动作启动GestureControlNodeParametersClass

调用链路概览

main.cpp
  |
  v
  rclcpp::init
  |
  v
  GestureControlEngine::Instance()
  |                         |
  v                         v
  GestureControlEngine::FeedSmart -> GestureControlEngine::RunStrategy
  |                         |
  v                         v
  GestureControlNode (订阅手势消息) -> ParametersClass (管理参数)
  |                         |
  v                         v
  GestureControlNode::SmartTopicCallback -> ParametersClass::Respond
  |
  v
  GestureControlEngine::DoRotate/DoMove/CancelMove

这个调用链路展示了从手势识别到机器人运动控制的整个流程。每个组件都有明确的职责,通过ROS 2的消息传递和回调机制相互协作。

Launch文件详解

launch文件作为这个功能最终的入口,应当先来梳理清楚这部分的配置。

gesture_control.launch.py 文件内容

这个文件定义了如何启动GestureControlNode和相关的依赖节点。它使用launch库来创建节点的实例,并设置参数。

import os
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from ament_index_python import get_package_share_directory

def generate_launch_description():
    # 手势控制节点
    gesture_control_node = Node(
        package='gesture_control',
        executable='gesture_control',
        output='screen',
        parameters=[
            {"ai_msg_sub_topic_name""/hobot_hand_gesture_detection"},
            {"twist_pub_topic_name""/cmd_vel"},
            {"activate_wakeup_gesture"0},
            {"track_serial_lost_num_thr"100},
            {"move_step"0.5},
            {"rotate_step"0.5}
        ],
        arguments=['--ros-args''--log-level''warn']
    )

    # 包含手势检测节点的启动文件
    hand_gesture_det_node = IncludeLaunchDescription(
        PythonLaunchDescriptionSource(
            os.path.join(
                get_package_share_directory('hand_gesture_detection'),
                'launch/hand_gesture_detection.launch.py'
            ))
    )

    return LaunchDescription([
        hand_gesture_det_node,
        gesture_control_node
    ])

参数解释

generate_launch_description函数中,我们可以看到Node动作创建了gesture_control节点的实例,并传递了一系列参数:

  • package: 指定节点所属的ROS 2包名。
  • executable: 指定要执行的可执行文件名。
  • output: 指定节点输出的控制台。
  • parameters: 一系列键值对参数,用于配置节点的行为。
    • ai_msg_sub_topic_name: 订阅的手势识别消息主题。
    • twist_pub_topic_name: 发布机器人运动控制消息的主题。
    • activate_wakeup_gesture: 是否启用唤醒手势。
    • track_serial_lost_num_thr: 目标连续消失帧数阈值。
    • move_step: 线速度步长。
    • rotate_step: 角速度步长。
  • arguments: 传递给ROS 2的额外参数,用于设置日志级别。

结合源代码

GestureControlNode的源代码中,我们可以看到这些参数是如何被使用的:

GestureControlNode::GestureControlNode(const std::string& node_name, SmartCbType smart_cb)
    : Node(node_name), smart_cb_(smart_cb) {
    // ...省略其他代码...

    this->declare_parameter<std::string>("twist_pub_topic_name", twist_pub_topic_name_);
    this->declare_parameter<std::string>("ai_msg_sub_topic_name", ai_msg_sub_topic_name_);

    // ...省略其他代码...

    twist_publisher_ = this->create_publisher

在这个构造函数中,declare_parameter方法用于声明参数,这样它们就可以在节点的生命周期中被访问和修改。twist_publisher_是用于发布Twist消息的发布器,它使用twist_pub_topic_name参数来确定发布到哪个主题。

代码详解

手势控制中最终的两个部分就是手势识别和运动控制,下面分别来讲。

手势控制功能在OriginBot中是通过一系列精心设计的组件和流程来实现的。下面我们将详细介绍与手势识别和运动控制相关的源代码。

手势识别

手势识别是通过hand_gesture_detection节点来实现的,这个节点订阅来自摄像头或其他图像源的视觉数据,并运行手势识别算法来检测和识别手势。识别到的手势会被封装成PerceptionTargets消息类型,并发布到指定的主题上。

gesture_control.launch.py中,我们看到了如何启动这个节点的引用:

hand_gesture_det_node = IncludeLaunchDescription(
    PythonLaunchDescriptionSource(
        os.path.join(
            get_package_share_directory('hand_gesture_detection'),
            'launch/hand_gesture_detection.launch.py'
        ))
)

这个启动文件会包含手势识别节点的配置和启动指令。具体的手势识别逻辑是在hand_gesture_detection包中的,这个代码并不直接包含在OriginBot的代码库中,而是调用了相机模组的代码,就不再继续深入解释。

运动控制

运动控制部分是在GestureControlNode中实现的。这个节点订阅手势识别消息,并根据识别到的手势来控制机器人的运动。下面是GestureControlNode的一些关键部分:

  1. 「订阅手势识别消息」:
   smart_subscription_ =
       this->create_subscription

这里,create_subscription方法用于创建一个订阅者,它订阅ai_msgs::msg::PerceptionTargets类型的消息。SmartTopicCallback是接收到消息时的回调函数。

  1. 「处理手势识别消息」:
   void GestureControlNode::SmartTopicCallback(
       const ai_msgs::msg::PerceptionTargets::ConstSharedPtr msg)
 
{
       // ...处理手势消息...
   }

SmartTopicCallback中,节点会解析接收到的手势识别消息,并根据识别到的手势来决定下一步的动作。

  1. 「控制机器人运动」:
   void GestureControlNode::RobotCtl(const Twist &msg) const {
       twist_publisher_->publish(msg);
   }

RobotCtl方法负责将计算出的运动控制指令(Twist类型的消息)发布到/cmd_vel主题上,从而控制机器人的运动。

手势控制逻辑

手势控制的逻辑主要在GestureControlEngine中实现。这个类处理手势识别消息,并根据识别到的手势生成相应的运动控制指令。以下是一些关键的方法:

  • FeedSmart: 将新的手势识别消息添加到队列中。
  • RunStrategy: 从队列中取出手势消息,并根据手势类型决定机器人的动作。
  • DoRotateDoMove: 根据手势的方向和步长计算出机器人的运动指令。
  • CancelMove: 取消当前的运动指令。

这里跟人体跟踪部分有一点类似

这些方法会根据GestureControlEngine中的跟踪信息和配置参数来生成运动控制指令,并通过RobotCtl方法发布这些指令。

总结

OriginBot的手势控制功能通过结合手势识别和运动控制两个关键部分来实现。手势识别节点负责检测和识别手势,而GestureControlNodeGestureControlEngine则负责解析这些手势并生成相应的运动控制指令。这些指令通过ROS 2的消息系统发布给机器人,从而实现手势控制。