ROS学习之动作通信

发布于:2025-06-20 ⋅ 阅读:(15) ⋅ 点赞:(0)

在b站学习赵老师的ROS通信,下面给出相关学习笔记

2.4.5_动作通信_小结_哔哩哔哩_bilibili

首先,服务端的目标:

可以提取客户端请求提交的整形数据,并且累加从1到该数据之间所有整数之和

完成每累加一次都计算当前运算进度,连续返回到客户端,在最后显示求和

关于C++实现代码:

#include "rclcpp/rclcpp.hpp"
#include "rclcpp_action/rclcpp_action.hpp"
#include "base_interfaces/action/progress.hpp"

using base_interfaces::action::Progress;
using namespace std::chrono_literals;
using std::placeholders::_1;
using std::placeholders::_2;


class ProgressActionServer: public rclcpp::Node{
public:
  ProgressActionServer():Node("progress_action_server_node_cpp"){
    RCLCPP_INFO(this->get_logger(),"Action service create");
    server_ = rclcpp_action::create_server<Progress>(
      this,
      "get_sum",
      std::bind(&ProgressActionServer::handle_goal,this,_1,_2),
      std::bind(&ProgressActionServer::handle_cancel,this,_1),
      std::bind(&ProgressActionServer::handle_accepcted,this,_1)
      );


  }
  // 处理提交目标值
  rclcpp_action::GoalResponse handle_goal(const rclcpp_action::GoalUUID & uuid, std::shared_ptr<const Progress::Goal> goal){
    (void)uuid;// uuid dont used.
    if(goal->num <= 1){
      RCLCPP_INFO(this->get_logger(),"goal must biger than 1");
      return rclcpp_action::GoalResponse::REJECT;
    }
    RCLCPP_INFO(this->get_logger(),"goal rigth!");
    return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
  }
  //处理取消请求
  rclcpp_action::CancelResponse handle_cancel(std::shared_ptr<rclcpp_action::ServerGoalHandle<Progress>> goal_handle){
    (void)goal_handle;
    RCLCPP_INFO(this->get_logger(),"receive cancel request...");
    return rclcpp_action::CancelResponse::ACCEPT;

  }
  void execute(std::shared_ptr<rclcpp_action::ServerGoalHandle<Progress>> goal_handle){
    int num = goal_handle->get_goal()->num;//获得目标值
    int sum = 0;
    auto feedback = std::make_shared<Progress::Feedback>();
    // sleep:
    rclcpp::Rate rate(1.0);
    auto result = std::make_shared<Progress::Result>();
    //goal_handle->publish_feedback();// 连续反馈
    for (int i =1; i<= num;i++ )
    {
      sum += i;
      double progress = i /(double)num;
      feedback->progress = progress;

      goal_handle->publish_feedback(feedback);
      RCLCPP_INFO(this->get_logger(),"连续反馈中,进度%.2f",progress);
      // 判断是否接受到了取消请求
      //goal_handle->is_canceling()
      //goal_handle->canceled()
      if (goal_handle->is_canceling())
      {
        result->sum = sum;
        goal_handle->canceled(result);
        RCLCPP_INFO(this->get_logger(),"任务被取消");
        return;
        /* code */
      }
      
      rate.sleep();
    }
    
    
    //goal_handle->succeed();// 最终响应
    if (rclcpp::ok())
    {
      auto result = std::make_shared<Progress::Result>();
      result->sum = sum;
      goal_handle->succeed(result);
      RCLCPP_INFO(this->get_logger(),"最终相应结果%d",sum);

      /* code */
    }
    

  }
  //生成连续反馈和最终响应
  void handle_accepcted(std::shared_ptr<rclcpp_action::ServerGoalHandle<Progress>> goal_handle){
    // 子线成处理耗时主逻辑
    std::thread(std::bind(&ProgressActionServer::execute, this,goal_handle)).detach();

  }
private:
rclcpp_action::Server<Progress>::SharedPtr server_;
  void on_timer(){

  }
  //rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
  //rclcpp::TimerBase::SharedPtr timer_;
  //size_t count;
};
int main(int argc, char ** argv)
{

  rclcpp::init(argc,argv);
  rclcpp::spin(std::make_shared<ProgressActionServer>());

  rclcpp::shutdown();
  return 0;
}

关于Python实现代码

import rclpy
from rclpy.node import Node
from rclpy.action import ActionServer
from base_interfaces.action import Progress
import time


class ProgressActionServer(Node):
    def __init__(self):
        super().__init__("progress_action_server_node_py")
        self.get_logger().info("client create!(py)")
        self.server = ActionServer(
            self,
            Progress,
            "get_sum",
            self.execute_callback

        )
    def execute_callback(self,goal_handle):
        num = goal_handle.request.num
        sum = 0
        for i in range(1,num+1):
            sum += i
            feedback = Progress.Feedback()
            feedback.progress = i /num
            goal_handle.publish_feedback(feedback)
            self.get_logger().info("连续反馈路:%.2f"% feedback.progress)
            time.sleep(1.0)
        goal_handle.succeed()
        result = Progress.Result()
        result.sum = sum
        self.get_logger().info("计算结果:%d"% result.sum)
        return result



    def add(self,request,response):
        response.sum = request.num1 + request.num2
        self.get_logger().info("%d + %d = %d" % (request.num1,request.num2,response.sum))
        return response


def main():

    rclpy.init()
    rclpy.spin(ProgressActionServer())
    rclpy.shutdown()


if __name__ == '__main__':
    main()

对于客户端,需要实现:提交整型数据,并且处理响应结果

下面是C++实现代码:

#include "rclcpp/rclcpp.hpp"
#include "rclcpp_action/rclcpp_action.hpp"
#include "base_interfaces/action/progress.hpp"

using base_interfaces::action::Progress;

using namespace std::chrono_literals;
using std::placeholders::_1;
using std::placeholders::_2;


class ProgressActionClient: public rclcpp::Node{
public:
  ProgressActionClient():Node("progress_action_client_node_cpp"){
    RCLCPP_INFO(this->get_logger(),"Action Client create");
    client_ = rclcpp_action::create_client<Progress>(this,"get_sum");//创建客户端

  }
  //发送目标
void send_goal(int num){
  //确保连接到服务端
  if(!client_->wait_for_action_server(10s)){
    RCLCPP_ERROR(this->get_logger(),"服务链接失败!");
    return;
  }
  // 发送请求
  auto goal = Progress::Goal();
  goal.num = num;
  rclcpp_action::Client<Progress>::SendGoalOptions options;
  options.goal_response_callback = std::bind(&ProgressActionClient::goal_response_callback,this,_1);
  options.feedback_callback = std::bind(&ProgressActionClient::feedback_callback,this,_1,_2);
  options.result_callback = std::bind(&ProgressActionClient::result_callback,this,_1);

  auto future = client_->async_send_goal(goal,options);
}

void goal_response_callback(rclcpp_action::ClientGoalHandle<Progress>::SharedPtr goal_handle){
  if(!goal_handle){
    RCLCPP_INFO(this->get_logger(),"目标请求被拒绝");
  }
  else{
    RCLCPP_INFO(this->get_logger(),"目标处理中");
  }

}
void feedback_callback(rclcpp_action::ClientGoalHandle<Progress>::SharedPtr goal_handle,const std::shared_ptr<const Progress::Feedback> feedback){
  (void)goal_handle;
  double progress = feedback->progress;
  int pro = (int) (progress*100);
  RCLCPP_INFO(this->get_logger(),"当前进度:%d%%",pro);
}
void result_callback(const rclcpp_action::ClientGoalHandle<Progress>::WrappedResult & result){
  //result.code
  
  if(result.code == rclcpp_action::ResultCode::SUCCEEDED)
  {
    RCLCPP_INFO(this->get_logger(),"最总结果:%d",result.result->sum);
  }
  else if (result.code == rclcpp_action::ResultCode::ABORTED)
  {
    RCLCPP_INFO(this->get_logger(),"被阻断");

    /* code */
  }
  else if (result.code == rclcpp_action::ResultCode::CANCELED)
  {
    RCLCPP_INFO(this->get_logger(),"未知异常!");
    /* code */
  }
  
  

}
private:
  rclcpp_action::Client<Progress>::SharedPtr client_;
};

int main(int argc, char ** argv)
{
  rclcpp::init(argc,argv);
  if (argc != 2)
  {
    RCLCPP_ERROR(rclcpp::get_logger("rclcpp"),"ERROR:need two ints");
    return 1 ;
    /* code */
  }
  auto node = std::make_shared<ProgressActionClient>();
  node->send_goal(atoi(argv[1]));
  
  rclcpp::spin(node);

  rclcpp::shutdown();
  return 0;
}

下面是Python实现代码:

import rclpy
from rclpy.node import Node
import sys
from rclpy.logging import get_logger
from rclpy.action import ActionClient
from base_interfaces.action import Progress

class ProgressActionClient(Node):
    def __init__(self):
        super().__init__("progress_action_client_node_py")
        self.get_logger().info("server create!(py)")
        self.client = ActionClient(self,Progress,"get_sum")#话题名称为get_sum 服务端和客户端要一致

    def send_goal(self,num):
        # 连接服务端
        self.client.wait_for_server()
        # 发送请求
        goal = Progress.Goal()
        goal.num = num
        self.future = self.client.send_goal_async(goal,self.fb_callback)
        self.future.add_done_callback(self.goal_response_callback)

    def goal_response_callback(self,future):
        # 获取目标句并
        goal_handle = future.result()

        # 判断目标是否正常处理
        if not goal_handle.accepted:
            self.get_logger().error("目标被拒绝")
            return
        self.get_logger().info("目标被接受,正在处理中")
        self.result_future = goal_handle.get_result_async()
        self.result_future.add_done_callback(self.get_result_callback)

    # 回调函数:
    def fb_callback(self,fb_msg):
        progress = fb_msg.feedback.progress
        self.get_logger().info("连续反馈数据:%.2f"% progress)
    
    # 目标最终响应结果

    # 
    def get_result_callback(self,future):
        result = future.result().result
        self.get_logger().info("最终结果:%d"%result.sum)


def main():

    rclpy.init()
    if len(sys.argv) != 2:
        get_logger("rclpy").error("清提交一个整形数据")

        return
    action_client = ProgressActionClient()
    action_client.send_goal(int(sys.argv[1]))
    rclpy.spin(action_client)
    rclpy.shutdown()


if __name__ == '__main__':
    main()

给出C++实现中,需要修改的Cmakelist文件:

cmake_minimum_required(VERSION 3.8)
project(cpp03_action)

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(rclcpp_action REQUIRED)
find_package(base_interfaces REQUIRED)

add_executable(demo01_action_server src/demo01_action_server.cpp)
add_executable(demo02_action_client src/demo02_action_client.cpp)

target_include_directories(demo01_action_server PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>)
target_compile_features(demo01_action_server PUBLIC c_std_99 cxx_std_17)  # Require C99 and C++17
ament_target_dependencies(
  demo01_action_server
  "rclcpp"
  "rclcpp_action"
  "base_interfaces"
)

ament_target_dependencies(
  demo02_action_client
  "rclcpp"
  "rclcpp_action"
  "base_interfaces"
)

install(TARGETS demo01_action_server
  DESTINATION lib/${PROJECT_NAME})

install(TARGETS demo02_action_client
  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()

Python的setup.py文件:

from setuptools import find_packages, setup

package_name = 'py03_action'

setup(
    name=package_name,
    version='0.0.0',
    packages=find_packages(exclude=['test']),
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='xwh',
    maintainer_email='xwh@todo.todo',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
            'demo01_action_server_py = py03_action.demo01_action_server_py:main',
            'demo02_action_client_py = py03_action.demo02_action_client_py:main'
        ],
    },
)

先:colcon build --packages-select cpp03_action

. install/setup.bash

分别打开两个终端:

1、ros2 run cpp03_action demo01_action_server

2、ros2 run cpp03_action demo02_action_client 10

 给出C++下实现的结果:

先:colcon build --packages-select cpp03_action

. install/setup.bash

分别打开两个终端:

1、ros2 run py03_action demo01_action_server_py

2、ros2 run py03_action demo02_action_client_py 15

给出Python环境下的结果:

关于博主在学习时候,遇到的坑

在修改py的package.xml中 注意标点符号的使用 
            'demo01_talker_str_py = py01_topic.demo01_talker_str_py:main',
            'demo02_listener_str_py = py01_topic.demo02_listener_str_py:main',
            'demo03_talker_stu_py = py01_topic.demo03_talker_stu_py:main',
            'demo04_listener_stu_py = py01_topic.demo04_listener_stu_py:main'
            如果没有后面的逗号 是无法执行ros2 run---

在写完好service.cpp文件后 利用终端进行发送订阅的数据
ros2 service call /add_ints base_interfaces/srv/Addints "{'num1':10,'num2':20}"

在action中的 客户端在终端中 输入的指令为:
ros2 action send_goal /get_sum base_interfaces/action/Progress -f "{'num':10}"

上面仅仅只是代码,如果只看代码是无法完全了解的,建议跟着赵老师视频学习,并且自己手敲几遍,熟悉熟悉。

所谓,键盘敲烂,年薪千万。(改用赵老师的话送给大家,希望看到这里的各位将来都能找到心满意足的好工作!)


网站公告

今日签到

点亮在社区的每一天
去签到