帮助文档
专业提供香港服务器、香港云服务器、香港高防服务器租用、香港云主机、台湾服务器、美国服务器、美国云服务器vps租用、韩国高防服务器租用、新加坡服务器、日本服务器租用 一站式全球网络解决方案提供商!专业运营维护IDC数据中心,提供高质量的服务器托管,服务器机房租用,服务器机柜租用,IDC机房机柜租用等服务,稳定、安全、高性能的云端计算服务,实时满足您的多样性业务需求。 香港大带宽稳定可靠,高级工程师提供基于服务器硬件、操作系统、网络、应用环境、安全的免费技术支持。
服务器资讯 / 香港服务器租用 / 香港VPS租用 / 香港云服务器 / 美国服务器租用 / 台湾服务器租用 / 日本服务器租用 / 官方公告 / 帮助文档
3-编写动作服务器和客户端(c++)
发布时间:2024-03-05 17:18:10   分类:帮助文档
3-编写动作服务器和客户端(c++) 背景     动作是ROS中的一种异步通信形式。操作客户端向操作服务器发送目标请求。操作服务器向操作客户端发送目标反馈和结果。 基础准备     需要用到上一教程中定义action_tutorials_interfaces包和Fibonacci.action。 任务 1创建包 1.1创建action_tutorials_cpp包     进入在上一教程中创建的动作工作区(请记住将工作区source),并为c++操作服务器创建一个新包: cd ~/ros2_ws/src ros2 pkg create --dependencies action_tutorials_interfaces rclcpp rclcpp_action rclcpp_components -- action_tutorials_cpp 2 编写一个动作服务端     编写一个操作服务端,该服务器使用我们在创建动作教程中创建的动作来计算斐波那契数列。 2.1编写动作服务端代码     打开action_tutorials_cpp/src/fibonacci_action_server.cpp,放入以下代码: #include #include #include #include "action_tutorials_interfaces/action/fibonacci.hpp" #include "rclcpp/rclcpp.hpp" #include "rclcpp_action/rclcpp_action.hpp" #include "rclcpp_components/register_node_macro.hpp" #include "action_tutorials_cpp/visibility_control.h" namespace action_tutorials_cpp { class FibonacciActionServer : public rclcpp::Node { public: using Fibonacci = action_tutorials_interfaces::action::Fibonacci; using GoalHandleFibonacci = rclcpp_action::ServerGoalHandle; ACTION_TUTORIALS_CPP_PUBLIC explicit FibonacciActionServer(const rclcpp::NodeOptions & options = rclcpp::NodeOptions()) : Node("fibonacci_action_server", options) { using namespace std::placeholders; this->action_server_ = rclcpp_action::create_server( this, "fibonacci", std::bind(&FibonacciActionServer::handle_goal, this, _1, _2), std::bind(&FibonacciActionServer::handle_cancel, this, _1), std::bind(&FibonacciActionServer::handle_accepted, this, _1)); } private: rclcpp_action::Server::SharedPtr action_server_; rclcpp_action::GoalResponse handle_goal( const rclcpp_action::GoalUUID & uuid, std::shared_ptr goal) { RCLCPP_INFO(this->get_logger(), "Received goal request with order %d", goal->order); (void)uuid; return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE; } rclcpp_action::CancelResponse handle_cancel( const std::shared_ptr goal_handle) { RCLCPP_INFO(this->get_logger(), "Received request to cancel goal"); (void)goal_handle; return rclcpp_action::CancelResponse::ACCEPT; } void handle_accepted(const std::shared_ptr goal_handle) { using namespace std::placeholders; // this needs to return quickly to avoid blocking the executor, so spin up a new thread std::thread{std::bind(&FibonacciActionServer::execute, this, _1), goal_handle}.detach(); } void execute(const std::shared_ptr goal_handle) { RCLCPP_INFO(this->get_logger(), "Executing goal"); rclcpp::Rate loop_rate(1); const auto goal = goal_handle->get_goal(); auto feedback = std::make_shared(); auto & sequence = feedback->partial_sequence; sequence.push_back(0); sequence.push_back(1); auto result = std::make_shared(); for (int i = 1; (i < goal->order) && rclcpp::ok(); ++i) { // Check if there is a cancel request if (goal_handle->is_canceling()) { result->sequence = sequence; goal_handle->canceled(result); RCLCPP_INFO(this->get_logger(), "Goal canceled"); return; } // Update sequence sequence.push_back(sequence[i] + sequence[i - 1]); // Publish feedback goal_handle->publish_feedback(feedback); RCLCPP_INFO(this->get_logger(), "Publish feedback"); loop_rate.sleep(); } // Check if goal is done if (rclcpp::ok()) { result->sequence = sequence; goal_handle->succeed(result); RCLCPP_INFO(this->get_logger(), "Goal succeeded"); } } }; // class FibonacciActionServer } // namespace action_tutorials_cpp RCLCPP_COMPONENTS_REGISTER_NODE(action_tutorials_cpp::FibonacciActionServer)     前几行包含我们需要编译的所有头文件。     接下来,我们创建一个类,它是rclcpp::Node的派生类: class FibonacciActionServer : public rclcpp::Node     FibonacciActionServer类的构造函数初始化节点名为fibonacci_action_server: explicit FibonacciActionServer(const rclcpp::NodeOptions & options = rclcpp::NodeOptions()) : Node("fibonacci_action_server", options)     构造函数还实例化了一个新的操作服务端: this->action_server_ = rclcpp_action::create_server( this, "fibonacci", std::bind(&FibonacciActionServer::handle_goal, this, _1, _2), std::bind(&FibonacciActionServer::handle_cancel, this, _1), std::bind(&FibonacciActionServer::handle_accepted, this, _1));     动作服务端需要6个条件:     1.模板化的动作类型名称:Fibonacci。     2.要向其中添加动作的ROS 2节点:this。     3.动作名称:'fibonacci'。     4.处理目标的回调函数:handle_goal     5.处理取消的回调函数:handle_cancel。     6.处理目标接收的回调函数:handle_accept。     文件的下一部分是各种回调函数的实现。请注意,所有回调都需要快速返回,否则可能会使执行器挂机。     我们从处理新目标的回调开始: rclcpp_action::GoalResponse handle_goal( const rclcpp_action::GoalUUID & uuid, std::shared_ptr goal) { RCLCPP_INFO(this->get_logger(), "Received goal request with order %d", goal->order); (void)uuid; return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE; }     这个实现只是接受所有的目标。     下一个是处理取消的回调: rclcpp_action::CancelResponse handle_cancel( const std::shared_ptr goal_handle) { RCLCPP_INFO(this->get_logger(), "Received request to cancel goal"); (void)goal_handle; return rclcpp_action::CancelResponse::ACCEPT; }     这个实现只是告诉客户端它接受了取消。     最后一个回调函数接受一个新目标并开始处理它: void handle_accepted(const std::shared_ptr goal_handle) { using namespace std::placeholders; // this needs to return quickly to avoid blocking the executor, so spin up a new thread std::thread{std::bind(&FibonacciActionServer::execute, this, _1), goal_handle}.detach(); }     由于执行是一个长时间运行的操作,因此我们派生出一个线程来完成实际工作,并快速地从handle_accepted返回。     所有进一步的处理和更新都在新线程的execute方法中完成: void execute(const std::shared_ptr goal_handle) { RCLCPP_INFO(this->get_logger(), "Executing goal"); rclcpp::Rate loop_rate(1); const auto goal = goal_handle->get_goal(); auto feedback = std::make_shared(); auto & sequence = feedback->partial_sequence; sequence.push_back(0); sequence.push_back(1); auto result = std::make_shared(); for (int i = 1; (i < goal->order) && rclcpp::ok(); ++i) { // Check if there is a cancel request if (goal_handle->is_canceling()) { result->sequence = sequence; goal_handle->canceled(result); RCLCPP_INFO(this->get_logger(), "Goal canceled"); return; } // Update sequence sequence.push_back(sequence[i] + sequence[i - 1]); // Publish feedback goal_handle->publish_feedback(feedback); RCLCPP_INFO(this->get_logger(), "Publish feedback"); loop_rate.sleep(); } // Check if goal is done if (rclcpp::ok()) { result->sequence = sequence; goal_handle->succeed(result); RCLCPP_INFO(this->get_logger(), "Goal succeeded"); } }     这个工作线程每秒处理一个斐波那契数列的序号,为每一步发布一个反馈更新。当它完成处理时,它将goal_handle标记为成功,并退出。     我们现在有了一个动作服务端,接下来就可以编译运行。 2.2编译动作服务端      在前一节中,已经有了动作服务端代码。为了编译和运行它,我们需要做一些额外的事情。     首先,我们需要设置CMakeLists.txt,以便编译动作服务端。打开action_tutorials_cpp/CMakeLists.txt,在find_package调用之后添加以下内容: add_library(action_server SHARED src/fibonacci_action_server.cpp) target_include_directories(action_server PRIVATE $ $) target_compile_definitions(action_server PRIVATE "ACTION_TUTORIALS_CPP_BUILDING_DLL") ament_target_dependencies(action_server "action_tutorials_interfaces" "rclcpp" "rclcpp_action" "rclcpp_components") rclcpp_components_register_node(action_server PLUGIN "action_tutorials_cpp::FibonacciActionServer" EXECUTABLE fibonacci_action_server) install(TARGETS action_server ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin)     现在我们可以编译这个包了。转到ros2_ws的顶层目录,然后运行: colcon build     这将编译整个工作空间,包括action_tutorials_cpp包中的fibonacci_action_server。 2.3 运行动作服务端     现在我们已经构建了操作服务端,可以运行它了。找到我们刚刚构建的工作区(ros2_ws),并尝试运行动作服务端: ros2 run action_tutorials_cpp fibonacci_action_server 3 编写动作客户端 3.1 编写动作客户端代码     打开action_tutorials_cpp/src/fibonacci_action_client.cpp,放入以下代码: #include #include #include #include #include #include "action_tutorials_interfaces/action/fibonacci.hpp" #include "rclcpp/rclcpp.hpp" #include "rclcpp_action/rclcpp_action.hpp" #include "rclcpp_components/register_node_macro.hpp" namespace action_tutorials_cpp { class FibonacciActionClient : public rclcpp::Node { public: using Fibonacci = action_tutorials_interfaces::action::Fibonacci; using GoalHandleFibonacci = rclcpp_action::ClientGoalHandle; explicit FibonacciActionClient(const rclcpp::NodeOptions & options) : Node("fibonacci_action_client", options) { this->client_ptr_ = rclcpp_action::create_client( this, "fibonacci"); this->timer_ = this->create_wall_timer( std::chrono::milliseconds(500), std::bind(&FibonacciActionClient::send_goal, this)); } void send_goal() { using namespace std::placeholders; this->timer_->cancel(); if (!this->client_ptr_->wait_for_action_server()) { RCLCPP_ERROR(this->get_logger(), "Action server not available after waiting"); rclcpp::shutdown(); } auto goal_msg = Fibonacci::Goal(); goal_msg.order = 10; RCLCPP_INFO(this->get_logger(), "Sending goal"); auto send_goal_options = rclcpp_action::Client::SendGoalOptions(); send_goal_options.goal_response_callback = std::bind(&FibonacciActionClient::goal_response_callback, this, _1); send_goal_options.feedback_callback = std::bind(&FibonacciActionClient::feedback_callback, this, _1, _2); send_goal_options.result_callback = std::bind(&FibonacciActionClient::result_callback, this, _1); this->client_ptr_->async_send_goal(goal_msg, send_goal_options); } private: rclcpp_action::Client::SharedPtr client_ptr_; rclcpp::TimerBase::SharedPtr timer_; void goal_response_callback(const GoalHandleFibonacci::SharedPtr & goal_handle) { if (!goal_handle) { RCLCPP_ERROR(this->get_logger(), "Goal was rejected by server"); } else { RCLCPP_INFO(this->get_logger(), "Goal accepted by server, waiting for result"); } } void feedback_callback( GoalHandleFibonacci::SharedPtr, const std::shared_ptr feedback) { std::stringstream ss; ss << "Next number in sequence received: "; for (auto number : feedback->partial_sequence) { ss << number << " "; } RCLCPP_INFO(this->get_logger(), ss.str().c_str()); } void result_callback(const GoalHandleFibonacci::WrappedResult & result) { switch (result.code) { case rclcpp_action::ResultCode::SUCCEEDED: break; case rclcpp_action::ResultCode::ABORTED: RCLCPP_ERROR(this->get_logger(), "Goal was aborted"); return; case rclcpp_action::ResultCode::CANCELED: RCLCPP_ERROR(this->get_logger(), "Goal was canceled"); return; default: RCLCPP_ERROR(this->get_logger(), "Unknown result code"); return; } std::stringstream ss; ss << "Result received: "; for (auto number : result.result->sequence) { ss << number << " "; } RCLCPP_INFO(this->get_logger(), ss.str().c_str()); rclcpp::shutdown(); } }; // class FibonacciActionClient } // namespace action_tutorials_cpp RCLCPP_COMPONENTS_REGISTER_NODE(action_tutorials_cpp::FibonacciActionClient)     前几行包含我们需要编译的所有头文件。     接下来,我们创建一个类,它是rclcpp::Node的派生类: class FibonacciActionClient : public rclcpp::Node     构造函数还实例化了一个新的action客户端: this->client_ptr_ = rclcpp_action::create_client( this, "fibonacci");     动作客户端需要3个条件:     1.模板化的操作类型名称:Fibonacci。     2.要向其中添加操作客户端的ROS 2节点:this     3.动作名称:'fibonacci'。     我们还实例化了一个ROS计时器,它将启动对send_goal的唯一调用: this->timer_ = this->create_wall_timer( std::chrono::milliseconds(500), std::bind(&FibonacciActionClient::send_goal, this));     当计时器到时,它将调用send_goal: void send_goal() { using namespace std::placeholders; this->timer_->cancel(); if (!this->client_ptr_->wait_for_action_server()) { RCLCPP_ERROR(this->get_logger(), "Action server not available after waiting"); rclcpp::shutdown(); } auto goal_msg = Fibonacci::Goal(); goal_msg.order = 10; RCLCPP_INFO(this->get_logger(), "Sending goal"); auto send_goal_options = rclcpp_action::Client::SendGoalOptions(); send_goal_options.goal_response_callback = std::bind(&FibonacciActionClient::goal_response_callback, this, _1); send_goal_options.feedback_callback = std::bind(&FibonacciActionClient::feedback_callback, this, _1, _2); send_goal_options.result_callback = std::bind(&FibonacciActionClient::result_callback, this, _1); this->client_ptr_->async_send_goal(goal_msg, send_goal_options); }     这个函数的作用如下:     取消计时器(因此它只被调用一次)。     等待动作服务器启动。     实例化一个新的Fibonacci::Goal。     设置响应、反馈和结果回调。     将目标发送到服务端。     当服务器接收并接受目标时,它将向客户端发送响应。该响应由goal_response_callback处理: void goal_response_callback(const GoalHandleFibonacci::SharedPtr & goal_handle) { if (!goal_handle) { RCLCPP_ERROR(this->get_logger(), "Goal was rejected by server"); } else { RCLCPP_INFO(this->get_logger(), "Goal accepted by server, waiting for result"); } }     假设目标被服务器接受,它将开始处理。对客户端的任何反馈都将由feedback_callback处理: void feedback_callback( GoalHandleFibonacci::SharedPtr, const std::shared_ptr feedback) { std::stringstream ss; ss << "Next number in sequence received: "; for (auto number : feedback->partial_sequence) { ss << number << " "; } RCLCPP_INFO(this->get_logger(), ss.str().c_str()); }     当服务端完成处理后,它将返回一个结果给客户端。结果由result_callback处理: void result_callback(const GoalHandleFibonacci::WrappedResult & result) { switch (result.code) { case rclcpp_action::ResultCode::SUCCEEDED: break; case rclcpp_action::ResultCode::ABORTED: RCLCPP_ERROR(this->get_logger(), "Goal was aborted"); return; case rclcpp_action::ResultCode::CANCELED: RCLCPP_ERROR(this->get_logger(), "Goal was canceled"); return; default: RCLCPP_ERROR(this->get_logger(), "Unknown result code"); return; } std::stringstream ss; ss << "Result received: "; for (auto number : result.result->sequence) { ss << number << " "; } RCLCPP_INFO(this->get_logger(), ss.str().c_str()); rclcpp::shutdown(); } }; // class FibonacciActionClient     现在我们有了一个动作客户端,接下来进行编译及运行。 3.2 编译动作客户端     在前一节中,我们已经有了动作客户端代码。为了编译和运行它,我们需要做一些额外的事情。     首先,我们需要设置CMakeLists.txt,以便编译动作客户端。打开action_tutorials_cpp/CMakeLists.txt,在find_package调用之后添加以下内容: add_library(action_client SHARED src/fibonacci_action_client.cpp) target_include_directories(action_client PRIVATE $ $) target_compile_definitions(action_client PRIVATE "ACTION_TUTORIALS_CPP_BUILDING_DLL") ament_target_dependencies(action_client "action_tutorials_interfaces" "rclcpp" "rclcpp_action" "rclcpp_components") rclcpp_components_register_node(action_client PLUGIN "action_tutorials_cpp::FibonacciActionClient" EXECUTABLE fibonacci_action_client) install(TARGETS action_client ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin)     现在我们可以编译这个包了。转到ros2_ws的顶层,然后运行: colcon build     这将编译整个工作空间,包括action_tutorials_cpp包中的fibonacci_action_client。 3.3运行动作客户端     现在我们已经构建了动作客户端,可以运行它了。首先,确保动作服务端在单独的终端中运行。现在找到我们刚刚构建的工作区(ros2_ws),并尝试运行action客户端: ros2 run action_tutorials_cpp fibonacci_action_client     您应该看到目标的日志消息被接受,反馈被打印,以及最终结果。 总结     在本教程中,您将一行一行地将c++操作服务器和操作客户端组合在一起,并配置它们进行目标、反馈和结果的通信。 相关内容     有几种方法可以在c++中编写操作服务器和客户端;查看ros2/examples repo中的minimal_action_server和minimal_action_client包(https://github.com/ros2/examples/tree/humble/rclcpp)。    有关ROS动作的更多详细信息,请参阅(Actions)。
香港云服务器租用推荐
服务器租用资讯
·广东云服务有限公司怎么样
·广东云服务器怎么样
·广东锐讯网络有限公司怎么样
·广东佛山的蜗牛怎么那么大
·广东单位电话主机号怎么填写
·管家婆 花生壳怎么用
·官网域名过期要怎么办
·官网邮箱一般怎么命名
·官网网站被篡改怎么办
服务器租用推荐
·美国服务器租用
·台湾服务器租用
·香港云服务器租用
·香港裸金属服务器
·香港高防服务器租用
·香港服务器租用特价