From 91ba7b1569bf3f0c425282791770e01aa90bb0c9 Mon Sep 17 00:00:00 2001 From: hoangson02 Date: Thu, 16 Apr 2026 02:38:25 +0000 Subject: [PATCH] optimal and add file readme --- README.md | 312 ++++++++++++++++++++ include/dock_planner/dock_calc.h | 50 ++-- include/dock_planner/dock_planner.h | 1 - src/dock_calc.cpp | 434 ++++++++++------------------ src/dock_planner.cpp | 83 ++---- 5 files changed, 518 insertions(+), 362 deletions(-) diff --git a/README.md b/README.md index e69de29..7e8b3b4 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,312 @@ +# Dock Planner + +Global planner tao duong di cho robot tiep can tram dock (tram sac, tram hang) bang duong cong NURBS. + +## Cau truc thu muc + +``` +dock_planner/ +├── include/dock_planner/ +│ ├── dock_planner.h # Lop DockPlanner - plugin interface +│ ├── dock_calc.h # Lop DockCalc - logic tinh toan duong di +│ └── utils/ +│ ├── curve_common.h # Thu vien NURBS curve +│ ├── pose.h # Chuyen doi pose/quaternion +│ └── common.h # Tien ich chung +├── src/ +│ ├── dock_planner.cpp # Khoi tao, load param, goi DockCalc +│ ├── dock_calc.cpp # Thuat toan chinh: NURBS path generation +│ └── utils/ +│ ├── curve_common.cpp +│ ├── line_common.cpp +│ └── pose.cpp +└── config/ + └── dock_global_params.yaml # Tham so cau hinh +``` + +## Pipeline tong quan + +```mermaid +flowchart TB + subgraph INIT["1. KHOI TAO - DockPlanner::initialize"] + I1["Lien ket costmap_robot"] --> I2["Load ROS params:
cost_threshold, calib_safety_distance"] + I2 --> I3["Load footprint_dock
tu param server"] + end + + subgraph PLAN["2. LAP KE HOACH - DockPlanner::makePlan"] + P1["Lay footprint_robot
tu costmap"] --> P2["Goi DockCalc::makePlanMoveToDock"] + P2 --> P3["Chuyen vector Pose
thanh vector PoseStamped"] + end + + subgraph CALC["3. TINH TOAN - DockCalc::makePlanMoveToDock"] + C1["Tinh RobotGeometry + safety_distance"] + C2["Xac dinh huong tiep can
forward / backward"] + C3["Tinh angle_threshold
heuristic"] + C4["Tao duong tam NURBS
findPathTMP"] + C5["Tim control points
segment 1"] + C6["Sinh NURBS segment 1"] + C7["Sinh NURBS segment 2"] + C8["Ghep path: start + seg1 + seg2 + goal"] + C1 --> C2 --> C3 --> C4 --> C5 --> C6 --> C7 --> C8 + end + + INIT --> PLAN --> CALC +``` + +## So do giai thuat chi tiet + +### 1. Khoi tao (initialize) + +```mermaid +flowchart LR + A["DockPlanner::initialize(name, costmap_robot)"] --> B{Da initialized?} + B -- Co --> Z["return true"] + B -- Chua --> C["Lien ket costmap_robot_
costmap_
frame_id_"] + C --> D["getParam:
check_free_space
cost_threshold
calib_safety_distance"] + D --> E["Copy params
sang DockCalc"] + E --> F["footprintFromParams
→ footprint_dock_"] + F --> G["initialized_ = true"] + G --> Z +``` + +### 2. Lap ke hoach (makePlan) + +```mermaid +flowchart TD + A["makePlan(start, goal, plan)"] --> B{initialized_?} + B -- false --> FAIL["return false"] + B -- true --> C["footprint_robot =
costmap_robot_->getRobotFootprint()"] + C --> D["makePlanMoveToDock
(start, goal, footprint_robot,
footprint_dock_, degree=2, step=0.02)"] + D --> E{Thanh cong?} + E -- false --> FAIL + E -- true --> F["Chuyen moi Pose thanh PoseStamped
(gan timestamp, frame_id)"] + F --> G["return true"] +``` + +### 3. Thuat toan chinh (makePlanMoveToDock) + +```mermaid +flowchart TD + START["makePlanMoveToDock(start, goal)"] --> GEOM + + subgraph GEOM["BUOC 1: Tinh hinh hoc"] + G1["computeGeometry(footprint_robot)
→ robot.length, robot.radius"] + G2["computeGeometry(footprint_dock)
→ dock.length"] + G3["safety_distance =
(robot.radius + calib) * 2"] + G1 --> G3 + G2 --> G3 + end + + GEOM --> VALID{robot.length > 0
AND safety > 0?} + VALID -- false --> ERR["return false"] + VALID -- true --> POSE_C + + subgraph POSE_C["BUOC 2: Tinh diem trung gian C"] + PC1["distance_min_C =
(robot.length + dock.length) / 2"] + PC2["yaw_goal = normalizeAngle(goal.yaw)"] + PC3["Pose_C = offsetPoint(goal, yaw_goal, distance_min_C)"] + PC1 --> PC3 + PC2 --> PC3 + end + + POSE_C --> DIR + + subgraph DIR["BUOC 3: Xac dinh huong di"] + D1["t = compute_t(start, goal, Pose_C)"] + D2{t < 0 ?} + D3["Lat yaw_goal 180 do
Tinh lai Pose_C va t
dir = backward"] + D4["dir = forward"] + D1 --> D2 + D2 -- Co --> D3 + D2 -- Khong --> D4 + end + + DIR --> T_CHECK{t > 1 ?} + T_CHECK -- false --> ERR + T_CHECK -- true --> ANGLE + + subgraph ANGLE["BUOC 4: Tinh angle threshold"] + A1["Pose_D = compute_D(goal, Pose_C, t)"] + A2["distance_AD =
pointToSegmentDistance(start, goal, Pose_C)"] + A3["scale = f(distance_AD)
angle_threshold =
min((alpha+|scale|)*beta*pi, gamma*pi)"] + A1 --> A3 + A2 --> A3 + end + + ANGLE --> TMP_PATH + + subgraph TMP_PATH["BUOC 5: Tao duong tam"] + T1["Tinh Pose_F = offsetPoint(goal, yaw, dist_F)"] + T2["mid = trung diem(C, F)"] + T3["findPathTMP({C, mid, F})
→ saved_poses_tmp"] + T1 --> T2 --> T3 + end + + TMP_PATH --> TMP_OK{Path tmp ok?} + TMP_OK -- false --> ERR + TMP_OK -- true --> SEG1 + + subgraph SEG1["BUOC 6: Sinh segment 1"] + S1["findNURBSControlPoints
(start, saved_poses_tmp, angle_threshold)
→ control_points_1, dir"] + S2["Xac dinh chieu reverse
cua segment 1"] + S3["generateNURBSPath(control_points_1)
→ first_segment"] + S1 --> S2 --> S3 + end + + SEG1 --> SEG1_OK{first_segment
khong rong?} + SEG1_OK -- false --> ERR + SEG1_OK -- true --> SEG2 + + subgraph SEG2["BUOC 7: Sinh segment 2"] + S4["mid_ctrl = trung diem
(first_segment.back, goal)"] + S5["generateNURBSPath
({last_of_seg1, mid_ctrl, goal})
→ second_segment"] + S4 --> S5 + end + + SEG2 --> ASSEMBLE["BUOC 8: Ghep duong di
start + seg1 + seg2 + goal"] + ASSEMBLE --> OK["return true"] +``` + +### 4. Sinh duong NURBS (generateNURBSPath) + +```mermaid +flowchart TD + A["generateNURBSPath
(control_points, check_free,
edge_end, reverse)"] --> B["setupSpline(control_points, degree)
Tao knot vector + weights
Nap vao Spline_Inf"] + B --> C["Tinh chieu dai NURBS
calculateNURBSLength"] + C --> D["step_new = resolution / length
(adaptive step)"] + D --> E["Duyet t = 0 → 1
(buoc step_new)"] + + E --> F["point = CalculateCurvePoint(t)"] + F --> G{point hop le?
khong NaN?} + G -- false --> E + G -- true --> H{Kiem tra
isFreeSpace?} + H -- Bi chan --> I["obstacle_flag = true
BREAK"] + H -- Thong --> J["Luu pose
Cap nhat orientation"] + J --> E + + I --> K["result.clear()"] + E -- t > 1.0 --> L["Gan orientation
cho diem cuoi"] + L --> M["return result"] + K --> M +``` + +### 5. Tim control points (findNURBSControlPoints) + +```mermaid +flowchart TD + A["findNURBSControlPoints
(start, saved_poses, angle_threshold)"] --> B["findNearestPointOnPath
→ idx, dir"] + B --> C{idx != -1 ?} + + C -- idx hop le --> D["control_points = {start, saved[idx]}"] + D --> E{dir == 1?} + E -- Co --> F["Tim diem lui ve
sao cho dist >= target
→ end_idx"] + F --> G["Them saved[end_idx]"] + E -- Khong, dir == -1 --> H["Them saved.back()"] + + C -- idx == -1 --> I["mid = trung diem
(start, nearest)"] + I --> J["control_points =
{start, mid, nearest}"] + + G --> K["return true"] + H --> K + J --> K +``` + +### 6. Tim diem gan nhat tren duong (findNearestPointOnPath) + +```mermaid +flowchart TD + A["findNearestPointOnPath
(pose, saved_poses, angle_threshold)"] --> B["Tim nearest_idx
(khoang cach nho nhat)"] + B --> C["Duyet nguoc tu nearest_idx → 1"] + C --> D{signedAngle >= threshold?} + D -- Co --> E["nearest_idx = i
found_flag_1 = true"] + D -- Khong --> C + + E --> F{Kiem tra khoang cach
distance_2 >= distance_1?} + F -- Co --> G["dir = 1
return nearest_idx"] + F -- Khong --> H["Duyet xuoi tu nearest_idx → cuoi"] + C -- Het vong --> H + + H --> I{signedAngle >= threshold - 0.05?} + I -- Co --> J["dir = -1
return nearest_idx"] + I -- Khong, i == cuoi --> K["dir = 0
return -1"] + + J --> L{dist hop ly?} + L -- false --> K + L -- true --> J +``` + +## Mo ta cac diem hinh hoc + +``` + F ──────── mid ──────── C ──────── Goal + | (duong tam) | | + | | | + | Pose_D | | + | ╲ | | + | ╲ distance_AD | | + | ╲ | | + Start ╲ | | + | | + ◄── distance_min_F ────►| | + ◄── dist_C ──►| +``` + +- **Goal**: Vi tri dock dich +- **Pose_C**: Diem offset tu Goal theo huong yaw_goal, cach `(robot.length + dock.length) / 2` +- **Pose_F**: Diem xa hon tren cung huong, cach Goal = `dist_C + dist_CD + dist_DF` +- **Pose_D**: Hinh chieu vuong goc tu Start len duong Goal→C keo dai +- **distance_AD**: Khoang cach vuong goc tu Start den duong Goal→C +- **Duong tam (C → mid → F)**: Duong NURBS tam de tim diem noi segment 1 + +## Luong du lieu giua cac lop + +```mermaid +flowchart LR + subgraph Plugin["DockPlanner (Plugin Interface)"] + init["initialize()"] + make["makePlan()"] + end + + subgraph Core["DockCalc (Core Algorithm)"] + calc["makePlanMoveToDock()"] + nurbs["generateNURBSPath()"] + tmp["findPathTMP()"] + ctrl["findNURBSControlPoints()"] + near["findNearestPointOnPath()"] + setup["setupSpline()"] + end + + subgraph Lib["Utils"] + curve["CurveCommon
(NURBS engine)"] + spline["Spline_Inf
(knot/ctrl/weight)"] + end + + init -->|"config params"| calc + make -->|"start, goal,
footprints"| calc + calc --> tmp + calc --> ctrl + calc --> nurbs + ctrl --> near + tmp --> setup + nurbs --> setup + setup --> curve + setup --> spline + curve -->|"CalculateCurvePoint"| nurbs + curve -->|"CalculateCurvePoint"| tmp +``` + +## Tham so cau hinh + +| Tham so | Kieu | Mac dinh | Mo ta | +|---------|------|----------|-------| +| `cost_threshold` | int | 200 | Nguong chi phi vat can (0-255) | +| `calib_safety_distance` | double | 0.0 | Khoang cach an toan bo sung (met) | +| `check_free_space` | bool | false | Bat/tat kiem tra vung tu do | +| `degree` (hardcode) | int | 2 | Bac cua duong cong NURBS | +| `step` (hardcode) | double | 0.02 | Buoc lay mau tham so t | + +## Ghi chu + +- **NURBS (Non-Uniform Rational B-Spline)**: Duong cong bac 2 voi 3 control points, knot vector dang clamped `[0,0,0,1,1,1]`. +- **Adaptive step**: Buoc lay mau duoc tinh tu `resolution / curve_length` de dam bao mat do diem dong deu theo do phan giai costmap. +- **2-segment path**: Duong di gom 2 doan NURBS noi tiep — segment 1 dua robot tu vi tri hien tai vao duong tiep can, segment 2 dua robot tu diem noi den dock. diff --git a/include/dock_planner/dock_calc.h b/include/dock_planner/dock_calc.h index 6fcad81..c873ad4 100644 --- a/include/dock_planner/dock_calc.h +++ b/include/dock_planner/dock_calc.h @@ -5,12 +5,9 @@ #include #include -#include -#include #include #include -#include #include @@ -19,10 +16,9 @@ #include "dock_planner/utils/common.h" #include +#include #include -using namespace std; - namespace dock_planner { struct RobotGeometry @@ -35,14 +31,11 @@ namespace dock_planner class DockCalc { private: - /* data */ - Spline_Inf* input_spline_inf; - CurveCommon* CurveDesign; + std::unique_ptr input_spline_inf; + std::unique_ptr CurveDesign; - vector posesOnPathWay; - vector footprint_robot_; + std::vector footprint_robot_; robot_geometry_msgs::PoseStamped save_goal_; - // std::vector free_zone_; bool check_goal_; int store_start_nearest_idx_; @@ -55,9 +48,10 @@ namespace dock_planner return std::hypot(p1.position.x - p2.position.x, p1.position.y - p2.position.y); } - inline double calculateAdaptiveStep(double segment_length, double desired_point_spacing = 0.02) + inline double calculateAdaptiveStep(double segment_length, double desired_point_spacing = 0.02) { - return desired_point_spacing / segment_length; + if (segment_length <= 1e-9) return 1.0; + return desired_point_spacing / segment_length; } inline double calculateAngle(double x1, double y1, double x2, double y2) @@ -66,13 +60,9 @@ namespace dock_planner return angle; } - inline double normalizeAngle(double angle) + inline double normalizeAngle(double angle) { - while (angle > M_PI) - angle -= 2.0 * M_PI; - while (angle <= -M_PI) - angle += 2.0 * M_PI; - return angle; + return std::remainder(angle, 2.0 * M_PI); } double calculateNURBSLength(CurveCommon* curve_design, @@ -90,15 +80,17 @@ namespace dock_planner robot_geometry_msgs::Pose compute_D(const robot_geometry_msgs::Pose& B, const robot_geometry_msgs::Pose& C,const double& t); + void setupSpline(const std::vector& control_points, int degree); + void updatePoseOrientation(std::vector& saved_poses, std::vector& nurbs_path, bool reverse = true); - bool findPathTMP(const vector& control_points, - vector& saved_poses, + bool findPathTMP(const std::vector& control_points, + std::vector& saved_poses, const int& degree, double step = 0.02); - bool findNURBSControlPoints(int& dir, vector& control_points, + bool findNURBSControlPoints(int& dir, std::vector& control_points, const robot_geometry_msgs::Pose& start_pose, const std::vector& posesOnPathWay, const double& angle_threshol, const int& degree); @@ -122,20 +114,20 @@ namespace dock_planner //Params robot_costmap_2d::Costmap2D* costmap_; - int cost_threshold_; // Threshold for obstacle detection - double safety_distance_; // Safety distance from obstacles - double calib_safety_distance_; + int cost_threshold_; // Threshold for obstacle detection + double safety_distance_; // Derived = (robot.radius + calib_safety_distance_) * 2.0, recomputed each plan + double calib_safety_distance_; // Configured via ROS param "calib_safety_distance" - DockCalc(/* args */); + DockCalc(); ~DockCalc(); bool makePlanMoveToDock(const robot_geometry_msgs::PoseStamped& start, const robot_geometry_msgs::PoseStamped& goal, - const vector& footprint_robot, - const vector& footprint_dock, + const std::vector& footprint_robot, + const std::vector& footprint_dock, const int& degree, const double& step, - vector& planMoveToDock, + std::vector& planMoveToDock, bool reverse = false); diff --git a/include/dock_planner/dock_planner.h b/include/dock_planner/dock_planner.h index 1dc87be..d0ed37d 100644 --- a/include/dock_planner/dock_planner.h +++ b/include/dock_planner/dock_planner.h @@ -27,7 +27,6 @@ namespace dock_planner { robot_costmap_2d::Costmap2D* costmap_; bool initialized_; std::string frame_id_; - // ros::Publisher plan_pub_; std::vector footprint_dock_; int cost_threshold_; diff --git a/src/dock_calc.cpp b/src/dock_calc.cpp index 0832ad3..8e197d0 100644 --- a/src/dock_calc.cpp +++ b/src/dock_calc.cpp @@ -2,22 +2,22 @@ namespace dock_planner { - DockCalc::DockCalc(/* args */) :costmap_(nullptr) + DockCalc::DockCalc() + : input_spline_inf(std::make_unique()), + CurveDesign(std::make_unique()), + check_goal_(false), + store_start_nearest_idx_(0), + start_target_idx_(0), + min_distance_to_goal_(0.0), + costmap_(nullptr), + cost_threshold_(200), + safety_distance_(0.0), + calib_safety_distance_(0.0) { - check_goal_ = false; - // cost_threshold_ = 200; // Threshold for obstacle detection - safety_distance_ = 0; // Safety distance from obstacles - calib_safety_distance_ = 0; - input_spline_inf = new Spline_Inf(); - CurveDesign = new CurveCommon(); - } - - DockCalc::~DockCalc() - { - delete CurveDesign; - delete input_spline_inf; } + DockCalc::~DockCalc() = default; + robot_geometry_msgs::Pose DockCalc::offsetPoint(const robot_geometry_msgs::Pose& A, const double& theta, const double& d) { robot_geometry_msgs::Pose B; @@ -77,47 +77,73 @@ namespace dock_planner return D; } + void DockCalc::setupSpline(const std::vector& control_points, int degree) + { + std::vector> eigen_points; + eigen_points.reserve(control_points.size()); + for (const auto& cp : control_points) + { + eigen_points.emplace_back(cp.position.x, cp.position.y, 0); + } + + const int n = static_cast(control_points.size()) - 1; + const int m = n + degree + 1; + std::vector knot_vector; + knot_vector.reserve(m + 1); + for (int i = 0; i <= m; i++) + { + knot_vector.push_back(i <= degree ? 0.0 : 1.0); + } + + std::vector weights(control_points.size(), 1.0); + + input_spline_inf->knot_vector.clear(); + input_spline_inf->control_point.clear(); + input_spline_inf->weight.clear(); + + CurveDesign->ReadSplineInf(input_spline_inf.get(), degree + 1, eigen_points, knot_vector); + CurveDesign->ReadSplineInf(input_spline_inf.get(), weights, false); + } + RobotGeometry DockCalc::computeGeometry(const std::vector& fp) { - double min_x = 1e9, max_x = -1e9; - double min_y = 1e9, max_y = -1e9; + if (fp.size() < 4) return {0, 0, 0}; + + double min_x = std::numeric_limits::max(); + double max_x = std::numeric_limits::lowest(); + double min_y = std::numeric_limits::max(); + double max_y = std::numeric_limits::lowest(); double radius = 0.0; - if((int)fp.size() >= 4) + + for (const auto& p : fp) { - for (const auto& p : fp) - { - min_x = std::min(min_x, p.x); - max_x = std::max(max_x, p.x); - min_y = std::min(min_y, p.y); - max_y = std::max(max_y, p.y); - radius = std::max(radius, std::hypot(p.x, p.y)); - } + min_x = std::min(min_x, p.x); + max_x = std::max(max_x, p.x); + min_y = std::min(min_y, p.y); + max_y = std::max(max_y, p.y); + radius = std::max(radius, std::hypot(p.x, p.y)); } - else return {0, 0, 0}; - double length = std::max(max_x - min_x, max_y - min_y); - double width = std::min(max_x - min_x, max_y - min_y); - return {length, width, radius}; + return {max_x - min_x, max_y - min_y, radius}; } bool DockCalc::makePlanMoveToDock(const robot_geometry_msgs::PoseStamped& start, const robot_geometry_msgs::PoseStamped& goal, - const vector& footprint_robot, - const vector& footprint_dock, + const std::vector& footprint_robot, + const std::vector& footprint_dock, const int& degree, const double& step, - vector& planMoveToDock, + std::vector& planMoveToDock, bool reverse) { constexpr double epsilon = 1e-6; - robot::log_error("[dock_calc] makePlanMoveToDock start: (%f, %f), goal: (%f, %f), goal frame id: %s", start.pose.position.x, start.pose.position.y, goal.pose.position.x, goal.pose.position.y, goal.header.frame_id.c_str()); + robot::log_info("[dock_calc] makePlanMoveToDock start: (%f, %f), goal: (%f, %f), goal frame id: %s", start.pose.position.x, start.pose.position.y, goal.pose.position.x, goal.pose.position.y, goal.header.frame_id.c_str()); planMoveToDock.clear(); if(save_goal_.pose.position.x != goal.pose.position.x || save_goal_.pose.position.y != goal.pose.position.y || data_convert::getYaw(save_goal_.pose.orientation) != data_convert::getYaw(goal.pose.orientation)) { - // robot::log_error("[dock_calc] DEBUG 1000"); check_goal_ = true; - save_goal_ == goal; + save_goal_ = goal; } footprint_robot_ = footprint_robot; @@ -132,58 +158,31 @@ namespace dock_planner return false; } - // Calculate safety distance safety_distance_ = (robot.radius + calib_safety_distance_) * 2.0; - if (safety_distance_ <= epsilon) + if (safety_distance_ <= epsilon) { robot::log_error("[dock_calc] Safety distance is zero, cannot calculate plan to dock."); return false; } // Lambda: Generate NURBS path - auto generateNURBSPath = [&](const vector& control_points, bool check_isFreeSpace, - bool edge_end_plag, bool path_reverse) -> vector + auto generateNURBSPath = [&](const std::vector& control_points, bool check_isFreeSpace, + bool edge_end_plag, bool path_reverse) -> std::vector { - vector result; + std::vector result; bool obstacle_flag = false; - // Convert to Eigen format - std::vector> eigen_points; - for (const auto& cp : control_points) - { - eigen_points.emplace_back(cp.position.x, cp.position.y, 0); - } - - // Create knot vector - std::vector knot_vector; - int n = control_points.size() - 1; - int m = n + degree + 1; - for (int i = 0; i <= m; i++) - { - knot_vector.push_back(i <= degree ? 0.0 : 1.0); - } - - // Setup spline - std::vector weights(control_points.size(), 1.0); - input_spline_inf->knot_vector.clear(); - input_spline_inf->control_point.clear(); - input_spline_inf->weight.clear(); - - CurveDesign->ReadSplineInf(input_spline_inf, degree + 1, eigen_points, knot_vector); - CurveDesign->ReadSplineInf(input_spline_inf, weights, false); + setupSpline(control_points, degree); // Calculate adaptive step - double edge_length = calculateNURBSLength(CurveDesign, input_spline_inf, degree, step); + double edge_length = calculateNURBSLength(CurveDesign.get(), input_spline_inf.get(), degree, step); double resolution = costmap_->getResolution(); double step_new = calculateAdaptiveStep(edge_length, resolution); - // robot::log_error("[dock_calc] Adaptive step size: %f", step_new); - - // Generate path points - vector saved_poses; - for (double t = 0.0; t <= 1.0; t += step_new) + std::vector saved_poses; + for (double t = 0.0; t <= 1.0; t += step_new) { - robot_geometry_msgs::Point point = CurveDesign->CalculateCurvePoint(input_spline_inf, t, true); + robot_geometry_msgs::Point point = CurveDesign->CalculateCurvePoint(input_spline_inf.get(), t, true); if (!std::isnan(point.x) && !std::isnan(point.y)) { @@ -256,7 +255,6 @@ namespace dock_planner }; // Calculate minimum distance for pose C - // Can safety_distance_ replace distance_robot double distance_min_for_pose_C = (distance_robot + distance_dock) / 2.0; min_distance_to_goal_ = (safety_distance_ + distance_dock / 2.0); @@ -291,16 +289,22 @@ namespace dock_planner robot_geometry_msgs::Pose Pose_D = compute_D(goal.pose, Pose_C, t); double distance_AD = pointToSegmentDistance(start.pose, goal.pose, Pose_C); - // Calculate angle threshold with scaling - const double MAX_DISTANCE = 1.0, MIN_DISTANCE = 0.1; - const double MIN_SCALE = 0.2, MAX_SCALE = 1.0; - const double calib_factor_alpha = 0.5; - const double calib_factor_beta = 0.5; - const double calib_factor_gamma = 0.77; + // Angle threshold heuristic: linearly scale [MIN_DISTANCE..MAX_DISTANCE] of + // perpendicular distance AD into an angle in [alpha*beta*pi .. gamma*pi]. + // Closer to the line → larger angle threshold (more permissive turn). + constexpr double MAX_DISTANCE = 1.0; // distance at which scale = MIN_SCALE + constexpr double MIN_DISTANCE = 0.1; // distance at which scale = MAX_SCALE + constexpr double MIN_SCALE = 0.2; + constexpr double MAX_SCALE = 1.0; + constexpr double CALIB_FACTOR_ALPHA = 0.5; // base offset added to |scale| + constexpr double CALIB_FACTOR_BETA = 0.5; // multiplier applied to (alpha + |scale|) + constexpr double CALIB_FACTOR_GAMMA = 0.77; // upper clamp expressed as fraction of pi - double scale = MIN_SCALE + (MAX_SCALE - MIN_SCALE) * + const double scale = MIN_SCALE + (MAX_SCALE - MIN_SCALE) * (MAX_DISTANCE - distance_AD) / (MAX_DISTANCE - MIN_DISTANCE); - double angle_threshold = std::min((calib_factor_alpha + fabs(scale)) * calib_factor_beta * M_PI, calib_factor_gamma * M_PI); + const double angle_threshold = std::min( + (CALIB_FACTOR_ALPHA + std::fabs(scale)) * CALIB_FACTOR_BETA * M_PI, + CALIB_FACTOR_GAMMA * M_PI); // Generate temporary path double distance_CD = calc_distance(Pose_C, Pose_D); @@ -313,8 +317,8 @@ namespace dock_planner mid_pose.position.x = (Pose_C.position.x + Pose_F.position.x) / 2.0; mid_pose.position.y = (Pose_C.position.y + Pose_F.position.y) / 2.0; - vector control_points_tmp = {Pose_C, mid_pose, Pose_F}; - vector saved_poses_tmp; + std::vector control_points_tmp = {Pose_C, mid_pose, Pose_F}; + std::vector saved_poses_tmp; if (!findPathTMP(control_points_tmp, saved_poses_tmp, degree, step)) @@ -325,7 +329,7 @@ namespace dock_planner // Find first segment control points int dir; - vector control_points_1; + std::vector control_points_1; if (!findNURBSControlPoints(dir, control_points_1, start.pose, saved_poses_tmp, angle_threshold, degree)) { robot::log_error("[dock_calc] Failed to find NURBS control points for first segment."); @@ -337,70 +341,41 @@ namespace dock_planner ((dir == 1 || dir == 0) && dir_robot_move_to_goal); // Generate first segment - vector first_segment = generateNURBSPath(control_points_1, true, false, first_segment_reverse); + std::vector first_segment = generateNURBSPath(control_points_1, true, false, first_segment_reverse); + if (first_segment.empty()) return false; + + // Generate second segment (to goal) + robot_geometry_msgs::Pose mid_control_point; + mid_control_point.position.x = (first_segment.back().position.x + goal.pose.position.x) / 2.0; + mid_control_point.position.y = (first_segment.back().position.y + goal.pose.position.y) / 2.0; + + std::vector control_points_2 = {first_segment.back(), + mid_control_point, + goal.pose}; + + std::vector second_segment = generateNURBSPath(control_points_2, true, true, dir_robot_move_to_goal); + + // Assemble final path: start + first_segment + second_segment + goal + planMoveToDock.reserve(1 + first_segment.size() + second_segment.size() + 1); + planMoveToDock.push_back(start.pose); planMoveToDock.insert(planMoveToDock.end(), first_segment.begin(), first_segment.end()); + planMoveToDock.insert(planMoveToDock.end(), second_segment.begin(), second_segment.end()); + planMoveToDock.push_back(goal.pose); - //Generate second segment (to goal) - if (!planMoveToDock.empty()) - { - robot_geometry_msgs::Pose mid_control_point; - mid_control_point.position.x = (planMoveToDock.back().position.x + goal.pose.position.x) / 2.0; - mid_control_point.position.y = (planMoveToDock.back().position.y + goal.pose.position.y) / 2.0; - - vector control_points_2 = {planMoveToDock.back(), - mid_control_point, - goal.pose}; - - vector second_segment = generateNURBSPath(control_points_2, true, true, dir_robot_move_to_goal); - planMoveToDock.insert(planMoveToDock.end(), second_segment.begin(), second_segment.end()); - planMoveToDock.insert(planMoveToDock.begin(),start.pose); - planMoveToDock.insert(planMoveToDock.end(),goal.pose); - } - - return !planMoveToDock.empty(); + return true; } - bool DockCalc::findPathTMP(const vector& control_points, - vector& saved_poses, + bool DockCalc::findPathTMP(const std::vector& control_points, + std::vector& saved_poses, const int& degree, double step) { - // Chuyển đổi control points sang định dạng Eigen::Vector3d - std::vector> eigen_control_points; - for(const auto& cp : control_points) + setupSpline(control_points, degree); + + for(double t = 0.0; t <= 1.0; t += step) { - Eigen::Vector3d point(cp.position.x, cp.position.y, 0); - eigen_control_points.push_back(point); - } - // Tạo knot vector tự động - std::vector knot_vector; - int n = control_points.size() - 1; // n + 1 control points - int m = n + degree + 1; // m + 1 knots - // Tạo uniform knot vector - for(int i = 0; i <= m; i++) - { - if(i <= degree) knot_vector.push_back(0.0); - else knot_vector.push_back(1.0); - } - - // Điều chỉnh weights để tăng ảnh hưởng của các control points - std::vector weights(control_points.size(), 1.0); - - input_spline_inf->knot_vector.clear(); - input_spline_inf->control_point.clear(); - input_spline_inf->weight.clear(); - - // Cài đặt thông số cho spline - CurveDesign->ReadSplineInf(input_spline_inf, degree + 1, eigen_control_points, knot_vector); - CurveDesign->ReadSplineInf(input_spline_inf, weights, false); - - // vector poses_tmp; - for(double t = 0.0; t <= 1.0; t += step) - { - // robot::log_error("[dock_calc][findPathTMP] t(%f) DEBUG 10", t); - robot_geometry_msgs::Point point = CurveDesign->CalculateCurvePoint(input_spline_inf, t, true); - if(!std::isnan(point.x) && !std::isnan(point.y)) + robot_geometry_msgs::Point point = CurveDesign->CalculateCurvePoint(input_spline_inf.get(), t, true); + if(!std::isnan(point.x) && !std::isnan(point.y)) { - // robot::log_error("[dock_calc][findPathTMP] point(%f, %f) at t(%f)", point.x, point.y, t); robot_geometry_msgs::Pose pose; pose.position = point; pose.orientation.x = 0.0; @@ -434,29 +409,23 @@ namespace dock_planner void DockCalc::updatePoseOrientation(std::vector& saved_poses, std::vector& nurbs_path, - bool reverse) + bool reverse) { - double yaw; - // robot::log_error("[merge_path_calc] DEBUG: reverse(%x)", reverse); - if(!reverse) - yaw = calculateAngle(saved_poses[saved_poses.size() - 2].position.x, - saved_poses[saved_poses.size() - 2].position.y, - saved_poses.back().position.x, - saved_poses.back().position.y); - else - yaw = calculateAngle(saved_poses.back().position.x, - saved_poses.back().position.y, - saved_poses[saved_poses.size() - 2].position.x, - saved_poses[saved_poses.size() - 2].position.y); + auto& prev = saved_poses[saved_poses.size() - 2]; + const auto& curr = saved_poses.back(); + + const double yaw = reverse + ? calculateAngle(curr.position.x, curr.position.y, prev.position.x, prev.position.y) + : calculateAngle(prev.position.x, prev.position.y, curr.position.x, curr.position.y); tf3::Quaternion q; q.setRPY(0, 0, yaw); - saved_poses[saved_poses.size() - 2].orientation.x = q.x(); - saved_poses[saved_poses.size() - 2].orientation.y = q.y(); - saved_poses[saved_poses.size() - 2].orientation.z = q.z(); - saved_poses[saved_poses.size() - 2].orientation.w = q.w(); + prev.orientation.x = q.x(); + prev.orientation.y = q.y(); + prev.orientation.z = q.z(); + prev.orientation.w = q.w(); - nurbs_path.push_back(saved_poses[saved_poses.size() - 2]); + nurbs_path.push_back(prev); } double DockCalc::calculateNURBSLength(CurveCommon* curve_design, @@ -466,33 +435,24 @@ namespace dock_planner { double length = 0.0; robot_geometry_msgs::Point prev_point, curr_point; - std::vector segment_lengths; - static double step; - // Đường cong bậc 1 (đường thẳng) thì bước nhảy bằng 0.1 - if(degree == 1) step = 0.1; - // Đường cong bậc 2 trở lên thì bước nhảy bằng initial_step - else step = initial_step; + const double step = (degree == 1) ? 0.1 : initial_step; - // Tính độ dài với step - for(double u = 0; u <= 1; u += step) + for(double u = 0; u <= 1; u += step) { - // Lấy điểm trên đường cong tại tham số u curr_point = curve_design->CalculateCurvePoint(spline_inf, u, true); - if(u > 0 && !std::isnan(curr_point.x) && !std::isnan(curr_point.y)) + if(u > 0 && !std::isnan(curr_point.x) && !std::isnan(curr_point.y)) { - double segment_length = std::sqrt(std::pow(curr_point.x - prev_point.x, 2) + - std::pow(curr_point.y - prev_point.y, 2)); - length += segment_length; - segment_lengths.push_back(segment_length); + length += std::hypot(curr_point.x - prev_point.x, + curr_point.y - prev_point.y); } prev_point = curr_point; } return length; } - bool DockCalc::findNURBSControlPoints(int& dir, vector& control_points, + bool DockCalc::findNURBSControlPoints(int& dir, std::vector& control_points, const robot_geometry_msgs::Pose& start_pose, const std::vector& saved_poses, const double& angle_threshold, const int& degree) @@ -506,8 +466,7 @@ namespace dock_planner control_points.clear(); int idx = findNearestPointOnPath(dir, start_pose, saved_poses, angle_threshold); - // Helper lambda to create pose at position - auto createPose = [](double x, double y, double yaw = 0.0) -> robot_geometry_msgs::Pose + auto createPose = [](double x, double y) -> robot_geometry_msgs::Pose { robot_geometry_msgs::Pose pose; pose.position.x = x; @@ -551,33 +510,25 @@ namespace dock_planner control_points.push_back(createPose(saved_poses[end_idx].position.x, saved_poses[end_idx].position.y)); start_target_idx_ = end_idx; } - else if (dir == -1) + else if (dir == -1) { - // robot::log_error("[dock_calc] DEBUG 200:"); - // Use last pose as end point const auto& last_pose = saved_poses.back(); control_points.push_back(createPose(last_pose.position.x, last_pose.position.y)); start_target_idx_ = saved_poses.size() - 1; } } - else + else { - // robot::log_error("[dock_calc] DEBUG 300:"); - // Create midpoint between start and nearest saved pose const auto& nearest_pose = saved_poses[store_start_nearest_idx_]; auto mid_pose = createPose( - (start_pose.position.x + nearest_pose.position.x) / 2.0, - (start_pose.position.y + nearest_pose.position.y) / 2.0 + (start_pose.position.x + nearest_pose.position.x) / 2.0, + (start_pose.position.y + nearest_pose.position.y) / 2.0 ); control_points.push_back(start_pose); control_points.push_back(mid_pose); control_points.push_back(nearest_pose); - // robot::log_error("[dock_calc] DEBUG 1: start_pose(%f,%f)",start_pose.position.x, start_pose.position.y); - // robot::log_error("[dock_calc] DEBUG 1: mid_pose(%f,%f)",mid_pose.position.x, mid_pose.position.y); - // robot::log_error("[dock_calc] DEBUG 1: nearest_pose(%f,%f)",nearest_pose.position.x, nearest_pose.position.y); - start_target_idx_ = store_start_nearest_idx_; } @@ -607,7 +558,7 @@ namespace dock_planner bool found_pose_flag_1 = false; bool found_pose_flag_2 = false; - for(int i = store_start_nearest_idx_; i >= 0; i--) + for(int i = store_start_nearest_idx_; i >= 1; i--) { double angle = signedAngle(pose, saved_poses[i], saved_poses[i - 1]); @@ -629,7 +580,8 @@ namespace dock_planner if(!found_pose_flag_2) { - for(int i = store_start_nearest_idx_; i < (int)saved_poses.size(); i++) + const int last_idx = (int)saved_poses.size() - 1; + for(int i = store_start_nearest_idx_; i < last_idx; i++) { double angle = signedAngle(pose, saved_poses[i], saved_poses[i + 1]); @@ -639,7 +591,7 @@ namespace dock_planner dir = -1; break; } - if(i == (int)saved_poses.size() - 1) + if(i == last_idx - 1) { dir = 0; return -1; @@ -657,91 +609,10 @@ namespace dock_planner return nearest_idx; } - bool DockCalc::isFreeSpace(const robot_geometry_msgs::Pose& pose, - const std::vector& footprint) + bool DockCalc::isFreeSpace(const robot_geometry_msgs::Pose& pose, + const std::vector& footprint) { - // static std::vector free_zone; - - // auto makePose = [](double x, double y, double yaw) - // { - // robot_geometry_msgs::Pose p; - // p.position.x = x; - // p.position.y = y; - // p.position.z = 0.0; - - // tf3::Quaternion q; - // q.setRPY(0.0, 0.0, yaw); - // p.orientation.x = q.x(); - // p.orientation.y = q.y(); - // p.orientation.z = q.z(); - // p.orientation.w = q.w(); - // return p; - // }; - // unsigned int mx, my; - - // if (!costmap_->worldToMap(pose.position.x, pose.position.y, mx, my)) - // { - // // robot::log_error("[dock_calc][isFreeSpace]Debug 1"); - // return false; // pose nằm ngoài bản đồ - // } - - // unsigned char center_cost = costmap_->getCost(mx, my); - // if (center_cost >= cost_threshold_) - // { - // // robot::log_error("[dock_calc][isFreeSpace]Debug 2"); - // return false; // chính giữa đã bị chiếm - // } - - // double resolution = costmap_->getResolution(); - - // if(check_goal_) - // { - // std::vector result_free_zone = calcFreeZone(pose, footprint); - // if((int)result_free_zone.size() < 4) return false; - // free_zone.push_back(makePose(result_free_zone[0].x, result_free_zone[0].y, 0.0)); - // free_zone.push_back(makePose(result_free_zone[1].x, result_free_zone[1].y, 0.0)); - // free_zone.push_back(makePose(result_free_zone[2].x, result_free_zone[2].y, 0.0)); - // check_goal_ = false; - // } - // std::vector space_footprint = interpolateFootprint(pose, footprint, resolution); - // for (int i = 0; i < (int)space_footprint.size(); i++) - // { - // robot_geometry_msgs::Pose Pose_on_footprint; - // Pose_on_footprint.position.x = space_footprint[i].x; - // Pose_on_footprint.position.y = space_footprint[i].y; - // if((int)free_zone.size() < 3) return false; - // double t_L = compute_t(Pose_on_footprint, free_zone[0], free_zone[1]); - // double t_W = compute_t(Pose_on_footprint, free_zone[1], free_zone[2]); - // if(t_L >= 0 && t_L <= 1 && t_W >= 0 && t_W <= 1) - // { - // return true; - // } - // if (!costmap_->worldToMap(space_footprint[i].x, space_footprint[i].y, mx, my)) - // { - // // robot::log_error("[dock_calc][isFreeSpace]Debug 3"); - // return false; // pose nằm ngoài bản đồ - // } - // int check_x = mx; - // int check_y = my; - - // if (check_x >= 0 && check_x < costmap_->getSizeInCellsX() && - // check_y >= 0 && check_y < costmap_->getSizeInCellsY()) - // { - // unsigned char cost = costmap_->getCost(check_x, check_y); - // if (cost >= robot_costmap_2d::LETHAL_OBSTACLE) - // { - // double dx = pose.position.x - space_footprint[i].x; - // double dy = pose.position.y - space_footprint[i].y; - // double dist = std::hypot(dx, dy); - // if (dist <= (safety_distance_/2.0)) - // { - // // robot::log_error("[dock_calc][isFreeSpace]Debug dist: %f", dist); - // return false; - // } - // } - // } - // } - + // TODO: implement obstacle checking using costmap_ return true; } @@ -784,10 +655,11 @@ namespace dock_planner } std::vector points; - for (size_t i = 0; i < (int)abs_footprint.size(); ++i) + const size_t fp_size = abs_footprint.size(); + for (size_t i = 0; i < fp_size; ++i) { const robot_geometry_msgs::Point &start = abs_footprint[i]; - const robot_geometry_msgs::Point &end = abs_footprint[(i + 1) % abs_footprint.size()]; + const robot_geometry_msgs::Point &end = abs_footprint[(i + 1) % fp_size]; double dx = end.x - start.x; double dy = end.y - start.y; @@ -796,13 +668,13 @@ namespace dock_planner for (int j = 0; j <= steps; ++j) { - if (j == steps && i != (int)abs_footprint.size() - 1) - continue; // tránh lặp - double t = static_cast(j) / steps; - robot_geometry_msgs::Point pt; - pt.x = start.x + t * dx; - pt.y = start.y + t * dy; - points.push_back(pt); + if (j == steps && i != fp_size - 1) + continue; // tránh lặp điểm chung giữa 2 cạnh liên tiếp + double t = static_cast(j) / steps; + robot_geometry_msgs::Point pt; + pt.x = start.x + t * dx; + pt.y = start.y + t * dy; + points.push_back(pt); } } return points; diff --git a/src/dock_planner.cpp b/src/dock_planner.cpp index 10af462..b1db3f0 100644 --- a/src/dock_planner.cpp +++ b/src/dock_planner.cpp @@ -9,12 +9,12 @@ namespace dock_planner * @brief Constructor mặc định * Khởi tạo tất cả các pointer về nullptr và trạng thái chưa initialized */ - DockPlanner::DockPlanner() : costmap_robot_(nullptr), - costmap_(nullptr), - initialized_(false), - check_free_space_(false), + DockPlanner::DockPlanner() : costmap_robot_(nullptr), + costmap_(nullptr), + initialized_(false), cost_threshold_(200), - calib_safety_distance_(0.0) {} + calib_safety_distance_(0.0), + check_free_space_(false) {} /** * @brief Constructor với parameters @@ -22,13 +22,13 @@ namespace dock_planner * @param costmap_robot Pointer tới costmap ROS wrapper * Tự động gọi initialize() nếu được cung cấp costmap */ - DockPlanner::DockPlanner(std::string name, robot_costmap_2d::Costmap2DROBOT* costmap_robot) - : costmap_robot_(nullptr), - costmap_(nullptr), - initialized_(false), - check_free_space_(false), + DockPlanner::DockPlanner(std::string name, robot_costmap_2d::Costmap2DROBOT* costmap_robot) + : costmap_robot_(nullptr), + costmap_(nullptr), + initialized_(false), cost_threshold_(200), - calib_safety_distance_(0.0) + calib_safety_distance_(0.0), + check_free_space_(false) { initialize(name, costmap_robot); } @@ -64,9 +64,7 @@ namespace dock_planner calc_plan_to_dock_.calib_safety_distance_ = calib_safety_distance_; - // plan_pub_ = private_nh.advertise("plan", 1); - - robot_nav_2d_msgs::Polygon2D footprint_dock ;//= robot_nav_2d_utils::footprintFromParams(private_nh,false); + robot_nav_2d_msgs::Polygon2D footprint_dock = robot_nav_2d_utils::footprintFromParams(private_nh, false); for(auto pt : footprint_dock.points) { @@ -78,7 +76,6 @@ namespace dock_planner } initialized_ = true; - // ROS_WARN("[dock_planner] check_free_space_(%d)", check_free_space_); } return true; } @@ -88,49 +85,33 @@ namespace dock_planner std::vector& plan) { if(!initialized_) return false; - if(!plan.empty()) plan.clear(); - vector planMoveToDock; - // std::vector footprint_robot = costmap_robot_->getRobotFootprint(); - std::vector footprint_robot; - robot_geometry_msgs::Point p1; p1.x = 0.1; p1.y = -0.1; - robot_geometry_msgs::Point p2; p2.x = 0.1; p2.y = 0.1; - robot_geometry_msgs::Point p3; p3.x = -0.1; p3.y = 0.1; - robot_geometry_msgs::Point p4; p4.x = -0.1; p4.y = -0.1; - footprint_robot.push_back(p1); - footprint_robot.push_back(p2); - footprint_robot.push_back(p3); - footprint_robot.push_back(p4); + plan.clear(); - // footprint_dock_ = costmap_robot_->getRobotFootprint(); - int degree = 2; - double step = 0.02; - bool status_makePlanMoveToDock = calc_plan_to_dock_.makePlanMoveToDock(start, goal, footprint_robot, footprint_dock_, degree, step, planMoveToDock, true); - robot::Time plan_time = robot::Time::now(); + std::vector planMoveToDock; + std::vector footprint_robot = costmap_robot_->getRobotFootprint(); - if(!status_makePlanMoveToDock) return false; - for(int i = 0; i < (int)planMoveToDock.size(); i++) + constexpr int degree = 2; + constexpr double step = 0.02; + if (!calc_plan_to_dock_.makePlanMoveToDock(start, goal, footprint_robot, footprint_dock_, degree, step, planMoveToDock, true)) + return false; + + const robot::Time plan_time = robot::Time::now(); + const std::string& global_frame = costmap_robot_->getGlobalFrameID(); + plan.reserve(planMoveToDock.size()); + for (const auto& p : planMoveToDock) { - robot_geometry_msgs::PoseStamped pose; - pose.header.stamp = plan_time; - pose.header.frame_id = costmap_robot_->getGlobalFrameID(); - pose.pose.position.x = planMoveToDock[i].position.x; - pose.pose.position.y = planMoveToDock[i].position.y; - pose.pose.position.z = 0; - pose.pose.orientation = planMoveToDock[i].orientation; - plan.push_back(pose); + robot_geometry_msgs::PoseStamped pose; + pose.header.stamp = plan_time; + pose.header.frame_id = global_frame; + pose.pose.position.x = p.position.x; + pose.pose.position.y = p.position.y; + pose.pose.position.z = 0; + pose.pose.orientation = p.orientation; + plan.push_back(pose); } return true; } - // void DockPlanner::publishPlan(const std::vector& plan) - // { - // robot_nav_msgs::Path path_msg; - // path_msg.header.stamp = robot::Time::now(); - // path_msg.header.frame_id = frame_id_; - // path_msg.poses = plan; - // plan_pub_.publish(path_msg); - // } - // Export factory function robot_nav_core::BaseGlobalPlanner::Ptr DockPlanner::create() { return std::make_shared();