first commit
This commit is contained in:
79
include/move_base2/runners/action_handler.h
Normal file
79
include/move_base2/runners/action_handler.h
Normal 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_
|
||||
112
include/move_base2/runners/action_runner.h
Normal file
112
include/move_base2/runners/action_runner.h
Normal 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_
|
||||
157
include/move_base2/runners/controller_runner.h
Normal file
157
include/move_base2/runners/controller_runner.h
Normal 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_
|
||||
176
include/move_base2/runners/planner_runner.h
Normal file
176
include/move_base2/runners/planner_runner.h
Normal 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_
|
||||
164
include/move_base2/runners/recovery_runner.h
Normal file
164
include/move_base2/runners/recovery_runner.h
Normal 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_
|
||||
Reference in New Issue
Block a user