Files
move_base2/test/plugins/test_local_planner.cpp
2026-07-30 09:24:47 +07:00

232 lines
6.8 KiB
C++

/*********************************************************************
*
* Software License Agreement (BSD License)
*
* move_base2 — plugin local planner CHỈ dùng cho test.
*
* Instance được nạp qua Boost.DLL nên test không giữ được con trỏ tới nó. Thay vì mở một cửa hậu để
* đọc trạng thái, planner này **phản ánh** thứ nó nhận được vào chính lệnh vận tốc nó trả về:
*
* cmd.linear.x = clamp(kBaseSpeed + vận_tốc_đo_được.x, trần_tiến)
* cmd.angular.z = clamp(kBaseYawRate, trần_góc)
*
* Nhờ vậy "trần đã tới plugin chưa" và "vận tốc đo được đã tới plugin chưa" kiểm được qua đúng API
* mà runtime dùng, không cần cơ chế quan sát riêng nào.
*
* Không alias nào chạm vào con trỏ TF hay costmap — đó là điều kiện để test truyền con trỏ giả thay
* vì phải dựng `tf3::BufferCore` và `Costmap2DROBOT` thật.
*
* Author: DuongTD
*********************************************************************/
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <limits>
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
#include <boost/config.hpp>
#include <boost/dll/alias.hpp>
#include <robot_nav_2d_utils/conversions.h>
#include <robot_nav_core2/local_planner.h>
namespace move_base2
{
namespace testing
{
constexpr double kBaseSpeed = 0.25; ///< [m/s] lệnh nền khi chưa có trần nào
constexpr double kBaseYawRate = 0.40; ///< [rad/s]
/**
* @class TestLocalPlanner
* @brief Local planner giả, hành vi cố định theo tham số dựng.
*/
class TestLocalPlanner : public robot_nav_core2::LocalPlanner
{
public:
enum class Behavior
{
kOk, ///< Sinh lệnh hợp lệ, phản ánh trần và vận tốc đo được.
kNoCommand, ///< computeVelocityCommands trả false.
kNaN, ///< Sinh lệnh chứa NaN — phải bị chặn tại biên.
kThrow, ///< Ném exception khi tính lệnh.
kRefusesLimits ///< setTwistLinear/Angular trả false (planner không hỗ trợ đặt trần).
};
explicit TestLocalPlanner(Behavior behavior) : behavior_(behavior)
{
}
void initialize(robot::NodeHandle& /*parent*/, const std::string& name,
std::shared_ptr<tf3::BufferCore> /*tf*/,
robot_costmap_2d::Costmap2DROBOT* /*costmap*/) override
{
// Cố ý KHÔNG chạm tf hay costmap — xem chú thích đầu file.
name_ = name;
}
void setGoalPose(const robot_nav_2d_msgs::Pose2DStamped& /*goal_pose*/) override
{
saw_goal_ = true;
}
void setPlan(const robot_nav_2d_msgs::Path2D& path) override
{
plan_size_ = path.poses.size();
}
void getPlan(robot_nav_2d_msgs::Path2D& path) override
{
path = robot_nav_2d_msgs::Path2D();
}
void getGlobalPlan(robot_nav_2d_msgs::Path2D& path) override
{
path = robot_nav_2d_msgs::Path2D();
}
robot_nav_2d_msgs::Twist2DStamped computeVelocityCommands(
const robot_nav_2d_msgs::Pose2DStamped& /*pose*/,
const robot_nav_2d_msgs::Twist2D& velocity) override
{
robot_nav_2d_msgs::Twist2DStamped cmd;
switch (behavior_)
{
case Behavior::kThrow:
throw std::runtime_error("TestLocalPlanner được yêu cầu ném exception");
case Behavior::kNoCommand:
// Gen-2 không có cờ thành công/thất bại: "không sinh được lệnh" biểu đạt bằng exception.
throw std::runtime_error("TestLocalPlanner: không sinh được lệnh");
case Behavior::kNaN:
cmd.velocity.x = std::numeric_limits<double>::quiet_NaN();
return cmd;
case Behavior::kOk:
case Behavior::kRefusesLimits:
break;
}
double linear = kBaseSpeed + velocity.x;
if (has_limit_forward_)
{
linear = std::min(linear, limit_forward_);
}
double yaw = kBaseYawRate;
if (has_limit_angular_)
{
yaw = std::min(yaw, limit_angular_);
}
cmd.velocity.x = linear;
cmd.velocity.theta = yaw;
return cmd;
}
bool isGoalReached(const robot_nav_2d_msgs::Pose2DStamped& /*pose*/,
const robot_nav_2d_msgs::Twist2D& /*velocity*/) override
{
return false;
}
bool setTwistLinear(robot_geometry_msgs::Vector3 linear) override
{
if (behavior_ == Behavior::kRefusesLimits)
{
return false;
}
// Dấu chọn chiều, đúng quy ước của interface gen-1.
if (linear.x < 0.0)
{
limit_backward_ = linear.x;
}
else
{
limit_forward_ = linear.x;
has_limit_forward_ = true;
}
return true;
}
robot_geometry_msgs::Vector3 getTwistLinear(bool direct) override
{
robot_geometry_msgs::Vector3 out;
out.x = direct ? limit_forward_ : limit_backward_;
return out;
}
bool setTwistAngular(robot_geometry_msgs::Vector3 angular) override
{
if (behavior_ == Behavior::kRefusesLimits)
{
return false;
}
limit_angular_ = angular.z;
has_limit_angular_ = true;
return true;
}
robot_geometry_msgs::Vector3 getTwistAngular(bool /*direct*/) override
{
robot_geometry_msgs::Vector3 out;
out.z = limit_angular_;
return out;
}
private:
Behavior behavior_;
std::string name_;
std::size_t plan_size_ = 0;
bool saw_goal_ = false;
double limit_forward_ = 0.0; ///< [m/s]
double limit_backward_ = 0.0; ///< [m/s], âm
double limit_angular_ = 0.0; ///< [rad/s]
bool has_limit_forward_ = false;
bool has_limit_angular_ = false;
};
robot_nav_core2::LocalPlanner::Ptr createOk()
{
return std::make_shared<TestLocalPlanner>(TestLocalPlanner::Behavior::kOk);
}
robot_nav_core2::LocalPlanner::Ptr createSecondary()
{
return std::make_shared<TestLocalPlanner>(TestLocalPlanner::Behavior::kOk);
}
robot_nav_core2::LocalPlanner::Ptr createNoCommand()
{
return std::make_shared<TestLocalPlanner>(TestLocalPlanner::Behavior::kNoCommand);
}
robot_nav_core2::LocalPlanner::Ptr createNaN()
{
return std::make_shared<TestLocalPlanner>(TestLocalPlanner::Behavior::kNaN);
}
robot_nav_core2::LocalPlanner::Ptr createThrowing()
{
return std::make_shared<TestLocalPlanner>(TestLocalPlanner::Behavior::kThrow);
}
robot_nav_core2::LocalPlanner::Ptr createRefusingLimits()
{
return std::make_shared<TestLocalPlanner>(TestLocalPlanner::Behavior::kRefusesLimits);
}
} // namespace testing
} // namespace move_base2
BOOST_DLL_ALIAS(move_base2::testing::createOk, TestControllerOk)
BOOST_DLL_ALIAS(move_base2::testing::createSecondary, TestControllerSecondary)
BOOST_DLL_ALIAS(move_base2::testing::createNoCommand, TestControllerNoCommand)
BOOST_DLL_ALIAS(move_base2::testing::createNaN, TestControllerNaN)
BOOST_DLL_ALIAS(move_base2::testing::createThrowing, TestControllerThrowing)
BOOST_DLL_ALIAS(move_base2::testing::createRefusingLimits, TestControllerRefusesLimits)