optimal & fix file cmake

This commit is contained in:
2026-08-03 22:32:40 +07:00
parent 89add78c7f
commit 887bff1b97
98 changed files with 8971 additions and 1339 deletions

View File

@@ -1,46 +1,106 @@
# Kiến Trúc recovery_core
# Kiến trúc recovery_core
## Vị trí trong stack
## Vị trí
`recovery_core` nằm ở tầng `Navigations/Libraries`, cạnh `robot_clear_costmap_recovery`. Nó
giữ vai trò interface tương tự `robot_nav_core::RecoveryBehavior` cho các recovery cần **trả
về output** (path/vận tốc), đồng thời vẫn dùng global path/costmap/tf như bản gốc.
Gói nằm ở `Test/recovery_core`. Nó định nghĩa interface recovery **duy nhất** của workspace
(`CLAUDE.md`); bản legacy `robot_nav_core::RecoveryBehavior` đã dừng phát triển và không được port
sang.
## Các thành phần
Consumer đầu tiên là `RecoveryRunner` của `move_base2` — nó include thẳng
`recovery_core/recovery_behavior.h`, nên contract được compiler kiểm đầy đủ ở một nơi.
- `RecoveryBehavior` (interface, template-method): API non-virtual `configure`/`start`/`update`/
`cancel`; plugin chỉ triển khai hook `onConfigure`/`onStart(goal)`/`onUpdate()`.
- `RecoveryContext`: gói con trỏ ngữ cảnh (tf/costmap/global_path) truyền một lần qua `configure`.
- `RecoveryGoal`: mục tiêu RUNTIME mỗi lượt (angle/distance/target_pose/params).
- `RecoveryResult` / `RecoveryStatus` / `RecoveryOutputType`: hợp đồng output hợp nhất 3 họ +
rich feedback (`progress`/`remaining`/`elapsed`/`message`), thêm trạng thái `kCancelled`.
- Plugin mẫu:
- `ClearCostmapRecovery`: clear layer costmap theo tên, one-shot no-output.
- `RotateRecovery`: quay tới `goal.angle` (rad); sinh `Twist.angular.z` mỗi cycle.
- `BackUpRecovery`: lùi tới `goal.distance` (m); sinh `Twist.linear.x < 0` mỗi cycle.
- `RegenPathRecovery`: trả lại `robot_nav_msgs::Path` từ `global_path` hiện tại.
## Vì sao tick-based
Recovery thế hệ trước chạy blocking bên trong một lời gọi và không trả gì. Hệ quả: không cancel
được giữa chừng, không báo tiến độ, và **không phát được vận tốc** — nên mọi behavior cần robot cử
động phải tự quay vòng lặp bên trong, tranh quyền phát `cmd_vel` với controller.
Bản này trả kết quả từng cycle. Ràng buộc kéo theo: recovery phải được tick từ **đúng thread đang
sở hữu cmd_vel**, không được có thread riêng.
## Thành phần
| Thành phần | Vai trò |
|---|---|
| `RecoveryBehavior` | Base template-method. Giữ toàn bộ bất biến; plugin chỉ triển khai hook |
| `RecoveryContext` | Các **cổng** môi trường: `PoseProvider`, `CollisionChecker`, `PlanProvider`, con trỏ costmap |
| `RecoveryGoal` | Mục tiêu runtime mỗi lượt: `trigger`, `angle`, `distance`, `target_pose`, `params` |
| `RecoveryResult` | Kết quả một tick: status, output theo họ, progress/remaining/elapsed/message |
| `RecoveryRegistry` | Danh sách behavior **có thứ tự**, nạp từ YAML qua Boost.DLL |
| `adapters/` | `CostmapPoseProvider`, `CostmapCollisionChecker` — nối cổng vào costmap thật |
| `plugins/` | Bộ mặc định: wait, clear costmap, rotate, back up |
## Ai giữ bất biến gì
Đây là điểm khác quan trọng nhất so với bản trước, nơi state là `protected` và plugin ghi thẳng
được vào `status_`.
**Base giữ:**
- guard vòng đời: `start` sau `configure`, `update` sau `start`, `configure` chỉ một lần;
- cổng bắt buộc theo `outputKind()` — họ velocity không có `PoseProvider`/`CollisionChecker` thì
`configure()` trả `false` ngay, chứ không để plugin phát hiện lúc đang lái;
- validate goal (NaN/Inf, `distance > 0`) trước khi plugin nhìn thấy;
- đo `dt``elapsed` bằng đồng hồ **thật**, ép `timeout`;
- cưỡng chế `output_type ∈ {outputKind(), kNone}`;
- chặn NaN/Inf trong lệnh vận tốc;
- sinh **stop output đúng họ** ở mọi nhánh guard/cancel/timeout.
**Plugin giữ:** đọc param riêng, chốt mục tiêu lượt này, và sinh một tick an toàn. Toàn bộ state của
base là `private`; plugin truy cập qua accessor `const`.
## Luồng runtime
```
configure(name, ctx) // 1 lần: cache ctx, đọc config chung, onConfigure()
configure(name, ctx, nh) # 1 lần. Kiểm cổng bắt buộc, đọc timeout, gọi onConfigure(nh).
start(goal) // mỗi lượt: chốt mc tiêu runtime, onStart()
start(goal, now) # mỗi lượt. Validate goal, mc thời gian, gọi onStart(goal) -> bool.
├── one-shot (họ A/B): update() 1 lần ───────────► RecoveryResult{status, path|none, msg}
├── kNone : update() -> làm việc / đếm giờ ─────► {status, elapsed, message}
├── kVelocity : loop { update(now) } ───────────────► {status, Twist, progress, remaining}
└── kPath : update() -> sinh path ──────────────► {status, Path}
└── per-cycle (họ C): loop { update() } ────────► RecoveryResult{status, velocity,
(tới khi status != kRunning) progress, remaining}
cancel() # tick kế tiếp gọi onCancel() -> stop output + kCancelled.
```
## Ghi chú thiết kế
## Quyết định thiết kế
- Template-method: base xử lý guard vòng đời (configure→start→update) và `cancel` một chỗ;
plugin không lặp lại các guard này.
- Mục tiêu là RUNTIME qua `RecoveryGoal` (không cố định trong config): cùng plugin phục vụ nhiều
yêu cầu góc/khoảng khác nhau. Field = 0 → dùng default plugin đọc ở `onConfigure()`.
- `onUpdate()` lấy pose robot từ costmap/tf bên trong; không truyền pose qua tham số.
- Sau `cancel()`, base tự trả stop output (Twist 0) + `kCancelled`.
- Param RIÊNG của plugin đọc trong `onConfigure()`.
- Interface KHÔNG include Boost.DLL; export/import là việc của plugin/loader.
- Plugin mẫu có dùng Boost.DLL alias, nhưng core contract vẫn không biết loader/adaptor.
**Cổng, không phải dữ liệu, nằm trong context.** `PlanProvider` là accessor chứ không phải con trỏ
tới `std::vector` vì runtime dùng triple-buffer plan và **xoay con trỏ** giữa ba bộ đệm — một con
trỏ cache lúc `configure()` sẽ trỏ vào bộ đệm scratch sau vài chu kỳ planner. Cùng lý do đó, con trỏ
costmap phải được caller làm mới trước mỗi lượt và plugin không được cache `Costmap2D*` bên trong.
**`std::optional` thay cho sentinel 0.** `goal.angle = 0` là yêu cầu hợp lệ ("đừng quay") và phải
phân biệt được với "caller không đặt". Quy ước cũ "0 nghĩa là dùng default" khiến một góc tính từ
hình học ra ~0 bị âm thầm thay bằng π/2.
**Họ output nằm trong kiểu.** `outputKind()` khai một lần và base cưỡng chế. Trước kia mỗi kết quả
tự mang `output_type` nên nó không dùng để route được: một behavior họ path báo `kVelocity` ở tick
đầu, còn base thì sinh `Velocity(zero)` cho mọi họ ở nhánh terminal.
**Param do caller cấp namespace.** `configure()` nhận `robot::NodeHandle&` đã scope sẵn; plugin
không tự dựng `NodeHandle("~/" + name)` và tự nạp YAML từ disk. Nhờ vậy test chỉ cần trỏ vào cây
config của mình, không phải dựng cây config production.
**Không có `RecoveryDirective`.** Từng cân nhắc thêm kênh "xin lập plan lại", nhưng state machine
của `move_base2` đã chuyển `RECOVERING → PLANNING` với `start_planner = true` trên **cả**
`kSucceeded` lẫn `kFailed` — replan sau recovery đã là hành vi mặc định, thêm kênh riêng là thêm thứ
không ai đọc.
**`kCancelled` không qua ranh giới port.** `move_base2` không tick recovery sau khi cancel (state
chuyển sang `CANCELLING`, nơi mọi nguồn vận tốc bị khoá). Đường `onCancel()` vẫn phải đúng vì nó là
hàng rào cho host khác, nhưng `RecoveryTick` của `move_base2` không cần giá trị tương ứng.
## Điều kiện đóng lại phần legacy
`robot_nav_core::RecoveryBehavior``robot_clear_costmap_recovery` chỉ còn được tham chiếu ở hai
nơi, và cả hai biến mất cùng lúc khi `move_base2` thay xong `move_base` cũ:
| Nơi | Xử lý |
|---|---|
| `move_base/src/move_base.cpp` (loader gen-1) | Bị `move_base2` thay, không sửa |
| `robot_clear_costmap_recovery` | Bị `ClearCostmapRecovery` gen-2 thay, không port |
Khoá `recovery_behaviors:` trong `move_base_common_params.yaml` đã được gỡ: các entry cũ trỏ tên
alias vào `.so` gen-2 trong khi loader ở đó import theo chữ ký gen-1, và Boost.DLL không kiểm kiểu
qua ranh giới `.so`.