first commit

This commit is contained in:
2026-07-29 15:45:16 +07:00
commit 4762a3032c
56 changed files with 15310 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
/*********************************************************************
*
* Software License Agreement (BSD License)
*
* move_base2 — contract cho plugin thực thi một loại action.
*
* Author: DuongTD
*********************************************************************/
#ifndef MOVE_BASE2_RUNNERS_ACTION_HANDLER_H_
#define MOVE_BASE2_RUNNERS_ACTION_HANDLER_H_
#include <memory>
#include <string>
#include <vector>
// robot_protocol_msgs/Action.h khai boost::shared_ptr nhưng không tự include — phải nạp trước nó,
// nếu không translation unit nào include Action.h đầu tiên sẽ hỏng.
#include <boost/shared_ptr.hpp>
#include <robot/node_handle.h>
#include <robot/time.h>
#include <robot_protocol_msgs/Action.h>
#include <move_base2/ports/action_port.h>
namespace move_base2
{
/**
* @class ActionHandler
* @brief Thực thi một hoặc nhiều loại action (`actionType` của VDA5050).
*
* Tick-based, cùng mô hình với recovery behavior và cùng lý do (D5): handler được gọi từ **control
* thread** ở `controller_frequency`, nên nó **không được block**. Chờ thiết bị thì giữ state nội bộ
* và trả `kRunning`; nhờ vậy cancel/pause/emergency luôn được phản hồi trong một cycle.
*
* @warning **Handler phải tự timeout.** Đây là tầng 1 của contract 3 tầng và là tầng chính: chỉ
* handler biết ngưỡng đúng cho thiết bị của nó ("nâng kệ quá 20 s là bất thường" khác hẳn
* "sạc 30 phút là bình thường"). `action_patience` của state machine chỉ là lưới cuối và
* mặc định tắt. Một handler trả `kRunning` vĩnh viễn là handler viết sai contract.
*
* @note Trong lúc action chạy, **không ai được phát cmd_vel** (D8). Action cần chuyển động phải
* được mô hình hoá thành motion profile của navigation, không phải làm trong handler.
*/
class ActionHandler
{
public:
using Ptr = std::shared_ptr<ActionHandler>;
virtual ~ActionHandler() = default;
/**
* @brief Cấu hình một lần.
* @param name Tên instance, dùng cho log.
* @param nh NodeHandle **đã được caller scope sẵn** vào namespace param của instance này.
* @return false nếu không chạy được với cấu hình này.
*/
virtual bool configure(const std::string& name, robot::NodeHandle& nh) = 0;
/// @brief Các `actionType` mà handler này nhận. Rỗng = không nhận gì (registry sẽ từ chối nạp).
virtual std::vector<std::string> supportedActionTypes() const = 0;
/**
* @brief Bắt đầu thực thi @p action.
* @param now Thời điểm hiện tại — mốc cho timeout của chính handler.
* @return false = từ chối khởi động; bên gọi coi như action thất bại và **không** tick tiếp.
*/
virtual bool start(const robot_protocol_msgs::Action& action, const robot::Time& now) = 0;
/// @brief Một control cycle. Chỉ được gọi sau khi @ref start trả true.
virtual ActionTick update(const robot::Time& now) = 0;
/// @brief Yêu cầu dừng an toàn. Handler phải đưa thiết bị về trạng thái an toàn, không treo.
virtual void cancel() = 0;
};
} // namespace move_base2
#endif // MOVE_BASE2_RUNNERS_ACTION_HANDLER_H_

View File

@@ -0,0 +1,112 @@
/*********************************************************************
*
* Software License Agreement (BSD License)
*
* move_base2 — hiện thực ActionPort bằng các ActionHandler plugin.
*
* Author: DuongTD
*********************************************************************/
#ifndef MOVE_BASE2_RUNNERS_ACTION_RUNNER_H_
#define MOVE_BASE2_RUNNERS_ACTION_RUNNER_H_
#include <cstddef>
#include <functional>
#include <map>
#include <string>
#include <vector>
#include <move_base2/ports/action_port.h>
#include <move_base2/ports/clock_port.h>
#include <move_base2/runners/action_handler.h>
namespace move_base2
{
/**
* @class ActionRunner
* @brief Bảng tra `actionType` -> handler, nạp từ YAML bằng Boost.DLL.
*
* Cấu hình mong đợi:
*
* @code{.yaml}
* actions:
* handlers:
* - {name: noop, type: NoopActionHandler}
* noop:
* action_types: [wait, pick, drop]
* duration: 0.0 # [s]
*
* NoopActionHandler:
* library_path: libmove_base2_noop_action_handler
* @endcode
*
* Danh sách handler **được phép rỗng**: một hệ không có thiết bị nào thì mọi mission đều là
* nav-only, và khi đó `ControlLoop::submit` đã từ chối yêu cầu mang action ngay tại cửa.
*
* @note Không thread-safe. Chỉ control thread được gọi.
*/
class ActionRunner final : public ActionPort
{
public:
ActionRunner() = default;
~ActionRunner() override;
ActionRunner(const ActionRunner&) = delete;
ActionRunner& operator=(const ActionRunner&) = delete;
/// @brief Đồng hồ runtime. Bắt buộc đặt trước @ref configure.
void setClock(ClockPort* clock);
/// @brief Namespace YAML chứa `<ns>/handlers`. Mặc định "actions".
void setNamespace(const std::string& ns);
/**
* @brief Đăng ký một handler dựng sẵn (test, hoặc handler biên dịch thẳng vào host).
* @return false nếu handler null, không khai `actionType` nào, hoặc trùng type đã có chủ.
*/
bool registerHandler(const ActionHandler::Ptr& handler);
bool configure(robot::NodeHandle& nh) override;
bool start(const robot_protocol_msgs::Action& action) override;
ActionTick update() override;
void cancel() override;
/// @brief Số handler đã nạp.
std::size_t handlerCount() const
{
return handlers_.size();
}
/// @brief Các `actionType` đã có handler nhận. Dùng cho log và test.
std::vector<std::string> supportedActionTypes() const;
/// @brief Handler nhận @p action_type, hoặc nullptr.
ActionHandler* find(const std::string& action_type) const;
private:
/// Nạp một handler. Trả false kèm log lý do nếu hỏng ở bất kỳ bước nào.
bool loadOne(const std::string& name, const std::string& type, robot::NodeHandle& nh,
const std::string& ns);
ClockPort* clock_ = nullptr; ///< non-owning
std::string namespace_ = "actions";
bool configured_ = false;
std::vector<ActionHandler::Ptr> handlers_;
std::map<std::string, ActionHandler*> by_type_; ///< non-owning, trỏ vào handlers_
ActionHandler* active_ = nullptr; ///< non-owning
std::string active_action_id_;
/**
* Giữ factory của Boost.DLL sống đúng bằng vòng đời runner.
*
* Không phải biến thừa: factory nắm `shared_library` bên trong, thả nó ra là `.so` bị unload
* trong khi handler tạo từ nó vẫn còn sống — vtable trỏ vào vùng đã gỡ.
*/
std::vector<std::function<ActionHandler::Ptr()>> factories_;
};
} // namespace move_base2
#endif // MOVE_BASE2_RUNNERS_ACTION_RUNNER_H_

View File

@@ -0,0 +1,157 @@
/*********************************************************************
*
* Software License Agreement (BSD License)
*
* move_base2 — hiện thực ControllerPort bằng plugin robot_nav_core::BaseLocalPlanner.
*
* Author: DuongTD
*********************************************************************/
#ifndef MOVE_BASE2_RUNNERS_CONTROLLER_RUNNER_H_
#define MOVE_BASE2_RUNNERS_CONTROLLER_RUNNER_H_
#include <cstddef>
#include <functional>
#include <map>
#include <string>
#include <vector>
#include <robot/node_handle.h>
#include <robot_nav_core/base_local_planner.h>
#include <move_base2/ports/controller_port.h>
namespace robot_costmap_2d
{
class Costmap2DROBOT;
}
namespace tf3
{
class BufferCore;
}
namespace move_base2
{
/**
* @class ControllerRunner
* @brief Nạp và chạy local planner thế hệ 1 (`robot_nav_core::BaseLocalPlanner`) qua Boost.DLL.
*
* Đây là chỗ duy nhất trong gói biết tới `robot_nav_core::BaseLocalPlanner`. Lõi quyết định chỉ thấy
* @ref ControllerPort.
*
* ## Chạy đồng bộ, có chủ đích
*
* Khác @ref PlannerRunner, controller **không** cần thread riêng: nó phải trả lệnh trong đúng cycle
* hiện tại. Một local planner mất hơn một chu kỳ điều khiển là lỗi cấu hình của chính nó, không phải
* thứ kiến trúc ở đây che đi được — che đi sẽ thành robot chạy bằng lệnh cũ mà không ai biết.
*
* ## Sai số tại đích đi qua param server
*
* `BaseLocalPlanner` **không có** hàm đặt sai số. Bản cũ đặt qua `NodeHandle::setParam` và để
* planner tự đọc lại (`move_base.cpp:1897-1907`). Kênh đó gián tiếp và mong manh — planner nào đọc
* param một lần lúc `initialize` sẽ không bao giờ thấy giá trị mới — nhưng đổi nó là đổi interface
* gen-1, ảnh hưởng mọi planner đang chạy. Giữ nguyên, và ghi lại ở đây để không ai tưởng nó chắc
* chắn có tác dụng.
*
* ## Trần vận tốc (`setTwistLinear`)
*
* Tên nghe như lệnh jog nhưng thực chất là đặt trần, dấu chọn chiều. Đường này mang **tốc độ đã bị
* tầng an toàn hạ xuống**; bỏ qua nó là bỏ qua yêu cầu giảm tốc của tầng an toàn.
*
* @note **Không thread-safe — mọi hàm chỉ được gọi từ control thread.**
* Host đặt trần vận tốc và bơm odometry từ thread của nó (OPC-UA, VDA5050, ROS), nên
* `NavigationServer` phải cất các giá trị đó lại và đẩy xuống đây trong `spinOnce()`. Gọi
* thẳng từ thread host sẽ chạm `active_` và chạm plugin song song với lúc control thread đang
* tính lệnh.
*/
class ControllerRunner : public ControllerPort
{
public:
ControllerRunner();
~ControllerRunner() override;
ControllerRunner(const ControllerRunner&) = delete;
ControllerRunner& operator=(const ControllerRunner&) = delete;
/**
* @brief Nạp cấu hình và controller khởi đầu.
*
* @param nh NodeHandle để tra `library_path` và để đặt sai số tại đích. Được
* **sao chép** vì cả hai việc đó xảy ra lúc chạy.
* @param tf Buffer TF truyền cho `BaseLocalPlanner::initialize`. **Non-owning**.
* @param costmap Costmap local. **Non-owning**, bắt buộc khác null.
* @param initial_controller Alias plugin nạp ngay. Chuỗi rỗng = chờ @ref swapPlanner.
* @param[out] error Lý do thất bại; chỉ ghi khi hàm trả false.
*/
bool configure(const robot::NodeHandle& nh, tf3::BufferCore* tf,
robot_costmap_2d::Costmap2DROBOT* costmap,
const std::string& initial_controller, std::string& error);
bool configured() const
{
return configured_;
}
// ================================================================================================
// ControllerPort
// ================================================================================================
bool swapPlanner(const std::string& planner_name) override;
void setTolerance(double xy_m, double yaw_rad) override;
bool setPlan(const std::vector<robot_geometry_msgs::PoseStamped>& plan) override;
bool computeVelocityCommands(robot_geometry_msgs::Twist& cmd) override;
bool isGoalReached() override;
void setMeasuredVelocity(const robot_geometry_msgs::Twist& velocity) override;
bool setTwistLinear(const robot_geometry_msgs::Vector3& linear) override;
bool setTwistAngular(const robot_geometry_msgs::Vector3& angular) override;
std::string activeController() const override;
/// @brief Số plugin đã nạp và còn giữ trong cache.
std::size_t loadedCount() const
{
return controllers_.size();
}
private:
/// @brief Một plugin đã nạp: factory phải sống cùng instance — vứt factory là để `.so` unload.
struct Loaded
{
std::function<robot_nav_core::BaseLocalPlanner::Ptr()> factory;
robot_nav_core::BaseLocalPlanner::Ptr instance;
};
/// @brief Nạp @p name nếu chưa có trong cache. @return nullptr khi thất bại (đã log lý do).
robot_nav_core::BaseLocalPlanner* acquire(const std::string& name);
/// @brief Áp lại trần vận tốc và sai số đã lưu lên controller vừa đổi sang.
void applyPendingLimits(robot_nav_core::BaseLocalPlanner* controller);
robot::NodeHandle nh_;
tf3::BufferCore* tf_ = nullptr;
robot_costmap_2d::Costmap2DROBOT* costmap_ = nullptr;
bool configured_ = false;
std::map<std::string, Loaded> controllers_;
std::string active_name_;
robot_nav_core::BaseLocalPlanner* active_ = nullptr; ///< Non-owning, trỏ vào @ref controllers_.
/**
* Trần vận tốc và vận tốc đo được gần nhất.
*
* Giữ lại bản sao vì hai lý do: host đặt trần **trước** khi controller được nạp (thứ tự khởi tạo
* không do move_base2 quyết), và @ref swapPlanner đổi sang một instance chưa biết gì về các trần
* đã đặt — không áp lại là robot lặng lẽ chạy nhanh hơn mức tầng an toàn cho phép.
*/
robot_geometry_msgs::Vector3 limit_linear_forward_;
robot_geometry_msgs::Vector3 limit_linear_backward_;
robot_geometry_msgs::Vector3 limit_angular_;
bool has_limit_linear_forward_ = false;
bool has_limit_linear_backward_ = false;
bool has_limit_angular_ = false;
robot_geometry_msgs::Twist measured_velocity_;
};
} // namespace move_base2
#endif // MOVE_BASE2_RUNNERS_CONTROLLER_RUNNER_H_

View File

@@ -0,0 +1,176 @@
/*********************************************************************
*
* Software License Agreement (BSD License)
*
* move_base2 — hiện thực PlannerPort bằng plugin robot_nav_core::BaseGlobalPlanner, chạy trên
* thread riêng.
*
* Author: DuongTD
*********************************************************************/
#ifndef MOVE_BASE2_RUNNERS_PLANNER_RUNNER_H_
#define MOVE_BASE2_RUNNERS_PLANNER_RUNNER_H_
#include <condition_variable>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include <robot/node_handle.h>
#include <robot_nav_core/base_global_planner.h>
#include <move_base2/ports/planner_port.h>
namespace robot_costmap_2d
{
class Costmap2DROBOT;
}
namespace move_base2
{
/**
* @class PlannerRunner
* @brief Nạp và chạy global planner thế hệ 1 (`robot_nav_core::BaseGlobalPlanner`) qua Boost.DLL,
* trên một thread riêng.
*
* Đây là chỗ duy nhất trong gói biết tới `robot_nav_core`. Lõi quyết định chỉ thấy @ref PlannerPort.
*
* ## Vì sao có thread riêng
*
* Global planner nặng mất hàng trăm ms; control loop chạy 20 Hz và là thread duy nhất phát
* `cmd_vel`. Lập plan tại chỗ nghĩa là mỗi lần lập lại plan là ngần ấy thời gian robot chạy bằng
* lệnh cũ không ai giám sát.
*
* ## Bàn giao plan bằng hoán vị, không copy
*
* Một plan toàn cục có thể vài nghìn pose. Copy nó mỗi lượt là cấp phát lớn trên đường nóng. Ở đây
* có **ba** vector luân chuyển bằng `swap`, đúng mô hình triple buffer của bản cũ nhưng bằng giá
* trị thay vì con trỏ thô:
*
* ```
* planning_ thread planner ghi vào
* handoff_ hộp thư, đổi dưới mutex
* (của bên gọi) pollPlan hoán vị với handoff_ -> vector cũ của bên gọi quay lại làm hộp thư
* ```
*
* Mutex chỉ bị giữ trong lúc đổi vector, không bao giờ trong lúc plugin đang tính.
*
* ## Nhãn yêu cầu (tag)
*
* Lượt lập plan sống lâu hơn cái goal sinh ra nó. Mỗi lượt mang một nhãn do bên gọi cấp; bên gọi so
* nhãn và vứt kết quả không khớp. Không có nó thì một plan tới goal đã bị huỷ vẫn được bám theo.
*
* ## Giữ instance sống bằng cách nào
*
* `boost::dll::import_alias` trả về factory **giữ tham chiếu tới thư viện đã nạp**. Vứt factory đi
* trong khi instance nó tạo ra còn sống là để `.so` bị unload dưới chân object. Mỗi entry vì thế
* giữ **cả hai**. Cache cũng **không bao giờ xoá** entry — nhờ đó con trỏ planner mà thread đang
* dùng vẫn hợp lệ kể cả khi @ref swapPlanner đổi sang planner khác giữa chừng.
*
* @note Các hàm public gọi từ control thread. Thread nội bộ chỉ chạm dữ liệu dưới mutex.
* @warning Destructor **chờ** lượt đang chạy kết thúc: plugin là hộp đen, không có đường cắt ngang.
*/
class PlannerRunner : public PlannerPort
{
public:
PlannerRunner();
~PlannerRunner() override;
PlannerRunner(const PlannerRunner&) = delete;
PlannerRunner& operator=(const PlannerRunner&) = delete;
/**
* @brief Nạp cấu hình, planner khởi đầu, và khởi động thread.
*
* @param nh NodeHandle để tra `library_path`. Được **sao chép** vì @ref swapPlanner
* cần tra lại lúc chạy.
* @param costmap Costmap global truyền cho `BaseGlobalPlanner::initialize`. **Non-owning**,
* bắt buộc khác null, phải sống lâu hơn object này.
* @param initial_planner Alias plugin nạp ngay. Chuỗi rỗng = chờ @ref swapPlanner.
* @param[out] error Lý do thất bại; chỉ ghi khi hàm trả false.
*/
bool configure(const robot::NodeHandle& nh, robot_costmap_2d::Costmap2DROBOT* costmap,
const std::string& initial_planner, std::string& error);
bool configured() const
{
return configured_;
}
// ================================================================================================
// PlannerPort
// ================================================================================================
bool swapPlanner(const std::string& planner_name) override;
bool startPlan(const robot_geometry_msgs::PoseStamped& start,
const robot_geometry_msgs::PoseStamped& goal,
const robot_protocol_msgs::Order* order, std::uint64_t tag) override;
bool isPlanning() const override;
bool pollPlan(PlanResult& result) override;
void cancelPlan() override;
std::string activePlanner() const override;
/// @brief Số plugin đã nạp và còn giữ trong cache — để kiểm việc dùng lại, không phải để log.
std::size_t loadedCount() const
{
return planners_.size();
}
private:
/// @brief Một plugin đã nạp: factory phải sống cùng instance, xem doc của lớp.
struct Loaded
{
std::function<robot_nav_core::BaseGlobalPlanner::Ptr()> factory;
robot_nav_core::BaseGlobalPlanner::Ptr instance;
};
/// @brief Nạp @p name nếu chưa có trong cache. @return nullptr khi thất bại (đã log lý do).
robot_nav_core::BaseGlobalPlanner* acquire(const std::string& name);
/// @brief Thân thread: ngủ tới khi có yêu cầu, chạy plugin, đặt kết quả vào hộp thư.
void threadBody();
// --- Chỉ control thread chạm ------------------------------------------------------------------
robot::NodeHandle nh_;
robot_costmap_2d::Costmap2DROBOT* costmap_ = nullptr;
bool configured_ = false;
std::map<std::string, Loaded> planners_;
std::string active_name_;
// --- Chia sẻ giữa hai thread, bảo vệ bởi mutex_ ------------------------------------------------
mutable std::mutex mutex_;
std::condition_variable cv_;
std::thread thread_;
bool shutdown_ = false;
bool pending_ = false; ///< Có yêu cầu chờ thread nhận.
bool running_ = false; ///< Thread đang chạy plugin.
bool discard_ = false; ///< Lượt đang chạy đã bị huỷ — vứt kết quả khi nó về.
bool has_result_ = false;
robot_nav_core::BaseGlobalPlanner* active_ = nullptr; ///< Non-owning, trỏ vào @ref planners_.
robot_geometry_msgs::PoseStamped request_start_;
robot_geometry_msgs::PoseStamped request_goal_;
std::shared_ptr<robot_protocol_msgs::Order> request_order_;
std::uint64_t request_tag_ = 0;
std::vector<robot_geometry_msgs::PoseStamped> planning_; ///< Thread ghi vào.
std::vector<robot_geometry_msgs::PoseStamped> handoff_; ///< Hộp thư.
std::uint64_t result_tag_ = 0;
bool result_ok_ = false;
};
} // namespace move_base2
#endif // MOVE_BASE2_RUNNERS_PLANNER_RUNNER_H_

View File

@@ -0,0 +1,164 @@
/*********************************************************************
*
* Software License Agreement (BSD License)
*
* move_base2 — hiện thực RecoveryPort bằng recovery_core.
*
* Author: DuongTD
*********************************************************************/
#ifndef MOVE_BASE2_RUNNERS_RECOVERY_RUNNER_H_
#define MOVE_BASE2_RUNNERS_RECOVERY_RUNNER_H_
#include <cstddef>
#include <functional>
#include <string>
#include <vector>
#include <recovery_core/adapters/costmap_collision_checker.h>
#include <recovery_core/recovery_registry.h>
#include <move_base2/ports/clock_port.h>
#include <move_base2/ports/pose_port.h>
#include <move_base2/ports/recovery_port.h>
namespace robot_costmap_2d { class Costmap2DROBOT; }
namespace move_base2
{
/**
* @class RecoveryRunner
* @brief Nối @ref RecoveryPort của lõi với framework `recovery_core`.
*
* Đây là **file duy nhất** trong `move_base2` include `recovery_core`. Nhờ vậy hai bên nằm trong
* cùng một translation unit và compiler kiểm được toàn bộ contract, dù plugin vẫn nạp qua
* `boost::dll` lúc chạy.
*
* Nó cũng là chỗ dịch **ba cặp khái niệm song song** giữa hai gói — dịch ở đúng một nơi:
*
* | `move_base2` | `recovery_core` |
* |---|---|
* | `PosePort` | `PoseProvider` (adapter nội bộ bên dưới) |
* | `ClockPort` | tham số `robot::Time` của `update(now)` |
* | `RecoveryTrigger` | `RecoveryTrigger` (`switch` 1:1) |
*
* Trùng khái niệm là **cố ý**: chiều phụ thuộc một chiều (D3) cấm `recovery_core` biết tới
* `move_base2`. Đừng sinh phiên bản thứ ba của cùng khái niệm ở nơi khác.
*
* @note Không thread-safe. Chỉ control thread được gọi — thread duy nhất phát cmd_vel.
*/
class RecoveryRunner final : public RecoveryPort
{
public:
/// @brief Nguồn plan hiện hành, cấp cho behavior họ path. Trả false nếu chưa có plan.
using PlanSource = std::function<bool(std::vector<robot_geometry_msgs::PoseStamped>&)>;
/**
* @struct Deps
* @brief Các cổng runtime mà runner cần. Tất cả **non-owning**.
*
* Con trỏ costmap có thể bị thay giữa hai lượt — dùng @ref setCostmaps để cập nhật thay vì để
* runner giữ một bản cache từ lúc configure.
*/
struct Deps
{
ClockPort* clock = nullptr;
PosePort* pose = nullptr;
robot_costmap_2d::Costmap2DROBOT* local_costmap = nullptr;
robot_costmap_2d::Costmap2DROBOT* global_costmap = nullptr;
};
RecoveryRunner() = default;
/// @brief Nạp cổng. Gọi trước @ref configure.
void setDeps(const Deps& deps);
/// @brief Cập nhật con trỏ costmap khi chúng bị thay. Non-owning.
void setCostmaps(robot_costmap_2d::Costmap2DROBOT* local,
robot_costmap_2d::Costmap2DROBOT* global);
/// @brief Namespace YAML chứa `<ns>/behaviors`. Mặc định "recovery".
void setNamespace(const std::string& ns);
/// @brief Nguồn plan cho behavior họ path. Không đặt = không có plan.
void setPlanSource(PlanSource source);
/**
* @copydoc RecoveryPort::configure
*
* Nạp danh sách behavior qua `recovery_core::RecoveryRegistry`. Behavior nào `configure()` hỏng
* thì bị bỏ và log đích danh — trả `false`, nhưng các behavior còn lại **vẫn** dùng được: một
* đường phục hồi hỏng không nên xoá sạch các đường còn lại.
*/
bool configure(robot::NodeHandle& nh) override;
std::size_t behaviorCount() const override;
RecoveryOutputKind outputKind(std::size_t index) const override;
bool start(std::size_t index, RecoveryTrigger trigger) override;
RecoveryTick update() override;
void cancel() override;
std::string behaviorName(std::size_t index) const override;
private:
/// Chuyển `PosePort` của lõi thành cổng pose của recovery_core.
class PoseBridge final : public recovery_core::PoseProvider
{
public:
explicit PoseBridge(const PosePort* port = nullptr) : port_(port)
{
}
void setPort(const PosePort* port)
{
port_ = port;
}
bool getRobotPose(robot_geometry_msgs::PoseStamped& pose) const override
{
return port_ != nullptr && port_->getRobotPose(pose);
}
private:
const PosePort* port_ = nullptr; ///< non-owning
};
/// Chuyển callback plan của runtime thành cổng plan của recovery_core.
class PlanBridge final : public recovery_core::PlanProvider
{
public:
void setSource(PlanSource source)
{
source_ = std::move(source);
}
bool getGlobalPlan(std::vector<robot_geometry_msgs::PoseStamped>& out) const override
{
return source_ ? source_(out) : false;
}
private:
PlanSource source_;
};
/// Trỏ lại context vào con trỏ costmap hiện hành trước mỗi lượt dùng.
void refreshContext();
/// Dịch kết quả của recovery_core sang ngôn ngữ của lõi.
RecoveryTick toTick(const recovery_core::RecoveryResult& result) const;
Deps deps_;
std::string namespace_ = "recovery";
recovery_core::RecoveryRegistry registry_;
recovery_core::RecoveryContext ctx_;
recovery_core::CostmapCollisionChecker collision_;
PoseBridge pose_bridge_;
PlanBridge plan_bridge_;
bool configured_ = false;
recovery_core::RecoveryBehavior* active_ = nullptr; ///< non-owning, thuộc registry_
};
} // namespace move_base2
#endif // MOVE_BASE2_RUNNERS_RECOVERY_RUNNER_H_