optimal & fix file cmake
This commit is contained in:
@@ -50,15 +50,17 @@ lẫn nhau. Hệ quả có thật, không phải hình thức:
|
||||
- `move_base2` không bị khoá cứng vào một hiện thực mission hay recovery cụ thể — đổi framework chỉ
|
||||
cần viết lại lớp nối, không phải sửa lõi.
|
||||
|
||||
Ở Phase 1, chiều này còn được giữ ở mức mạnh hơn: **không file nào trong `move_base2` include hai
|
||||
framework kia**. Lớp nối (`MissionAdapterBridge`, `RecoveryRunner`) được thêm ở bước sau, và chúng
|
||||
mới là chỗ duy nhất được phép include.
|
||||
Ở Phase 1, chiều này còn được giữ ở mức mạnh hơn: **không file nào trong `move_base2` include ba
|
||||
framework kia**. Lớp nối được thêm ở bước sau và chúng mới là chỗ duy nhất được phép include:
|
||||
`RecoveryRunner` cho `recovery_core`; `ActionRunner` cho `action_core`;
|
||||
`bridges/mission_adapter_bridge` (dịch contract) và `bridges/mission_layer` (lắp ráp registry +
|
||||
hàng đợi + hai thread) cho `mission_adapters`.
|
||||
|
||||
Kiểm bằng:
|
||||
|
||||
```bash
|
||||
grep -rn "mission_adapters\|recovery_core" src/AMR_T800/Test/move_base2/include \
|
||||
src/AMR_T800/Test/move_base2/src
|
||||
grep -rn "mission_adapters\|recovery_core\|action_core" src/AMR_T800/Test/move_base2/include \
|
||||
src/AMR_T800/Test/move_base2/src
|
||||
```
|
||||
|
||||
## Vì sao là port chứ không phải gọi thẳng
|
||||
@@ -77,9 +79,13 @@ Bảy port đều nhỏ và đều tồn tại vì một lý do vận hành cụ
|
||||
|
||||
## Quyết định thiết kế đáng ghi lại
|
||||
|
||||
**Sáu entry point gộp thành một.** `moveTo` ×2, `dockTo` ×2, `moveStraightTo`, `rotateTo` của
|
||||
contract host chỉ khác nhau ở kiểu chuyển động và sai số mặc định. Bảng `ProfileBinding` mô tả đúng
|
||||
phần khác nhau đó; sáu hàm còn lại chỉ dựng struct rồi gọi một đường vào duy nhất.
|
||||
**Sáu entry point gộp về một contract lõi.** `moveTo` ×2, `dockTo` ×2, `moveStraightTo`, `rotateTo`
|
||||
của contract host chỉ khác nhau ở kiểu chuyển động. `moveTo(PoseStamped)` là direct **position**
|
||||
goal nên trước tiên đi `MissionLayer::submitGoal()` → `GoalSourceAdapter` → `MissionManager`; nhờ đó
|
||||
nó nhận mission ID và lifecycle/cancel giống VDA5050. Nếu mission layer bị tắt hoặc không nạp source
|
||||
này mới fallback tương thích về `NavigationRequest` trực tiếp. Các API mang profile/marker riêng
|
||||
(`dockTo`, `moveStraightTo`, `rotateTo`) dựng request trực tiếp, vì schema `geometry.pose_stamped`
|
||||
chưa biểu diễn được marker/profile của chúng.
|
||||
|
||||
**Một đường vào duy nhất.** `ControlLoop::submit()` là chỗ duy nhất một goal lọt được vào lõi, và
|
||||
`IDLE → PLANNING` là transition duy nhất bắt đầu một chặng. Nhờ đó việc chống hai nguồn goal tranh
|
||||
@@ -109,14 +115,24 @@ Việc giảm tốc theo động học thuộc về bộ điều khiển bánh x
|
||||
- `getTwist()` trả **lệnh** vận tốc từ `VelocityArbiter`, đóng dấu theo đồng hồ của control loop.
|
||||
- Plugin `libmove_base2.so` export alias `MoveBase2`.
|
||||
|
||||
Chưa có, thuộc bước nối dây runtime:
|
||||
Bước nối dây runtime đã xong (Phase 4):
|
||||
|
||||
- Hiện thực thật của `PlannerPort` / `ControllerPort` / `PosePort`
|
||||
(bọc costmap, boost::dll, TF).
|
||||
- Dựng hai `Costmap2DROBOT` thật. Hiện `NavigationServer::attachCostmaps()` nhận `LayeredCostmap*`
|
||||
từ bên ngoài bơm vào — cố ý, để đường cảm biến kiểm được mà không cần TF và cây config thật.
|
||||
- Thread planner riêng và bộ đệm plan ba lớp. Hiện `ControlLoop` lập plan đồng bộ ngay trong cycle;
|
||||
tách như vậy để phần quyết định kiểm được mà không cần thread.
|
||||
- Lớp nối tới mission framework.
|
||||
- `setTwistLinear` / `setTwistAngular` (hiện trả `false` để host biết lệnh không có hiệu lực, thay vì
|
||||
âm thầm bỏ qua).
|
||||
- `PlannerRunner` / `ControllerRunner` / `RecoveryRunner` / `ActionRunner` nạp plugin thật qua
|
||||
boost::dll; `CostmapPosePort` lấy pose từ costmap — **hai** instance, khác frame (`map` cho
|
||||
planner, `odom` cho controller/recovery).
|
||||
- `NavigationRuntime` dựng hai `Costmap2DROBOT` thật rồi trả về `ControlLoopDeps`.
|
||||
`NavigationServer::attachCostmaps()` vẫn nhận `LayeredCostmap*` từ ngoài để đường cảm biến kiểm
|
||||
được mà không cần TF và cây config thật.
|
||||
- Thread planner riêng + hoán vị ba buffer, `PlannerPort` bất đồng bộ.
|
||||
- `setTwistLinear` / `setTwistAngular` — **trần vận tốc**, không phải lệnh jog.
|
||||
- Lớp nối tới mission framework: `MissionAdapterBridge` (dịch contract) + `MissionLayer` (dựng
|
||||
registry, hàng đợi, hai thread). Order VDA5050 vào bằng `moveTo(Order, …)` được cắt thành từng
|
||||
chặng. Direct position goal vào bằng `moveTo(PoseStamped)` đi `GoalSourceAdapter`, vì vậy cũng có
|
||||
mission ID thay vì `0`; tắt bằng `mission_layer_enabled: false` thì cả hai loại fallback xuống
|
||||
đường direct tương thích.
|
||||
|
||||
Chưa có:
|
||||
|
||||
- Kết xuất lưới costmap cho rviz đã có, nhưng đường `OccupancyGridUpdate` incremental đã bị bỏ —
|
||||
luôn gửi lưới đầy đủ ở 1 Hz.
|
||||
- Action chạy **dọc đường đi** (edge action): mọi action hiện chạy sau khi tới goal của chặng.
|
||||
|
||||
184
docs/MOVE_BASE2_RUNTIME_VA_TUONG_THICH.html
Normal file
184
docs/MOVE_BASE2_RUNTIME_VA_TUONG_THICH.html
Normal file
@@ -0,0 +1,184 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="vi">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>move_base2: kiến trúc, C API và tương thích</title>
|
||||
<style>
|
||||
@page { size: A4; margin: 1.6cm 1.65cm 1.55cm 1.65cm; }
|
||||
body { font-family: "Liberation Sans", "DejaVu Sans", sans-serif; color: #18212b; font-size: 10.5pt; line-height: 1.42; }
|
||||
h1 { color: #0b4f71; font-size: 22pt; margin: 0 0 4px; }
|
||||
h2 { color: #0b4f71; font-size: 15pt; border-bottom: 1px solid #b9d4e3; padding-bottom: 3px; margin: 22px 0 8px; }
|
||||
h3 { color: #215e7a; font-size: 11.7pt; margin: 14px 0 5px; }
|
||||
p { margin: 6px 0; }
|
||||
ul, ol { margin: 5px 0 8px 23px; padding: 0; }
|
||||
li { margin: 3px 0; }
|
||||
.subtitle { color: #536675; font-size: 11pt; margin-bottom: 18px; }
|
||||
.answer { background: #eaf6ed; border-left: 5px solid #31965a; padding: 10px 13px; margin: 13px 0; }
|
||||
.warning { background: #fff5dd; border-left: 5px solid #dc9a22; padding: 9px 12px; margin: 11px 0; }
|
||||
.note { background: #eaf3f8; border-left: 5px solid #287da9; padding: 9px 12px; margin: 11px 0; }
|
||||
table { width: 100%; border-collapse: collapse; margin: 8px 0 12px; font-size: 9.5pt; }
|
||||
th { background: #0b4f71; color: white; text-align: left; padding: 6px; }
|
||||
td { border: 1px solid #c9d4da; padding: 6px; vertical-align: top; }
|
||||
tr:nth-child(even) td { background: #f6f9fa; }
|
||||
code { font-family: "Liberation Mono", "DejaVu Sans Mono", monospace; font-size: 8.8pt; color: #7a2f16; }
|
||||
pre { background: #f1f4f6; border: 1px solid #d7e0e4; padding: 8px; white-space: pre-wrap; font-family: "Liberation Mono", "DejaVu Sans Mono", monospace; font-size: 8.3pt; line-height: 1.33; }
|
||||
.small { font-size: 8.7pt; color: #52616b; }
|
||||
.flow { border: 1px solid #bdd5e2; background: #f7fbfd; padding: 10px; text-align: center; font-weight: bold; color: #215e7a; }
|
||||
.page-break { page-break-before: always; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>move_base2: kiến trúc, C API và tương thích</h1>
|
||||
<p class="subtitle">Phân tích theo mã nguồn và log chạy của T800 · 03/08/2026</p>
|
||||
|
||||
<div class="answer">
|
||||
<strong>Kết luận ngắn.</strong> Khung chương trình T800 đang nạp <code>move_base</code> qua interface
|
||||
<code>robot::move_base_core::BaseNavigation</code> và factory alias <code>"MoveBase"</code> có thể chạy
|
||||
<code>move_base2</code> mà không phải sửa host, nếu đổi đúng thư viện và cây cấu hình runtime.
|
||||
Điều này đúng cho khung T800 hiện tại; không phải quy tắc tự động đúng cho mọi ứng dụng ROS dùng package
|
||||
<code>move_base</code>.
|
||||
</div>
|
||||
|
||||
<h2>1. Khung đang khởi tạo những gì?</h2>
|
||||
<p>Log cho thấy chỉ có một node ROS là <code>/amr_node</code> cho phần điều khiển. Bên trong process đó,
|
||||
<code>amr_control</code> nạp động navigation runtime và các plugin. Global planner, local planner, action,
|
||||
recovery và mission adapter <strong>không</strong> là node ROS riêng.</p>
|
||||
|
||||
<div class="flow">
|
||||
roslaunch → /amr_node (amr_control) → BaseNavigation factory → NavigationServer (move_base2)<br>
|
||||
→ costmap global/local + runners + mission/action/recovery plugins
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<tr><th>Thành phần</th><th>Thời điểm / cách tạo</th><th>Vai trò quan sát được</th></tr>
|
||||
<tr><td><code>amr_control</code></td><td>Node <code>/amr_node</code>; tạo TF, localization, sensor converter, publisher/subscriber rồi nạp navigation.</td><td>Cầu nối ROS/MQTT/OPC-UA với lõi navigation.</td></tr>
|
||||
<tr><td><code>NavigationServer</code></td><td>Factory của <code>libmove_base2.so</code> trả về <code>BaseNavigation::Ptr</code>.</td><td>Vỏ tương thích <code>BaseNavigation</code>; quản lý runtime và control thread 30 Hz.</td></tr>
|
||||
<tr><td>Global/local costmap</td><td>Tạo khi <code>NavigationRuntime::buildCostmaps()</code> chạy.</td><td>Global dùng frame <code>map</code>; local dùng <code>odom</code>; nhận laser, cloud, depth qua SensorGateway.</td></tr>
|
||||
<tr><td>PlannerRunner</td><td>Nạp <code>CustomPlanner</code> lúc boot; nạp <code>SBPLLatticePlanner</code>, <code>DockPlanner</code> khi profile cần.</td><td>Lập global plan, cache instance theo tên planner.</td></tr>
|
||||
<tr><td>ControllerRunner</td><td>Nạp <code>HybridLocalPlanner</code> lúc boot; có thể đổi theo profile.</td><td>Biến plan thành lệnh vận tốc.</td></tr>
|
||||
<tr><td>RecoveryRunner</td><td>Nạp lúc boot từ namespace <code>recovery</code>.</td><td>Trong log: wait, clear-costmap (2 mức), rotate, back-up.</td></tr>
|
||||
<tr><td>ActionRunner</td><td>Nạp lúc boot từ namespace <code>actions</code>.</td><td>Trong log: detect, wait, report, sim_noop.</td></tr>
|
||||
<tr><td>Mission layer</td><td>Nạp source adapter lúc boot từ <code>mission_adapters</code>.</td><td><code>GoalSourceAdapter</code> nhận goal; <code>VDA5050SourceAdapter</code> tách order thành các leg/nav/action.</td></tr>
|
||||
</table>
|
||||
|
||||
<h3>Luồng một VDA5050 order</h3>
|
||||
<ol>
|
||||
<li>MQTT nhận topic order.</li>
|
||||
<li><code>VDA5050SourceAdapter</code> đổi order thành các mission leg: navigation hoặc action-only.</li>
|
||||
<li><code>MissionManager</code> đưa leg vào hàng đợi; <code>MissionExecutor</code> chạy tuần tự.</li>
|
||||
<li>Control loop chọn profile: position, docking, go_straight hoặc rotate; runner lấy planner/local planner phù hợp.</li>
|
||||
<li>Kết quả nav/action trả về mission layer, rồi trạng thái VDA5050.</li>
|
||||
</ol>
|
||||
|
||||
<h2>2. C API đang dùng gì và có dùng được move_base2 không?</h2>
|
||||
<p>C API nằm tại <code>pnkx_nav_core/src/APIs/c_api</code>. Nó không tạo trực tiếp lớp C++
|
||||
<code>move_base::MoveBase</code> hay <code>move_base2::NavigationServer</code>. Hàm
|
||||
<code>navigation_create()</code> làm đúng chuỗi sau:</p>
|
||||
<pre>PluginLoaderHelper::findLibraryPath("MoveBase")
|
||||
boost::dll::import_alias<BaseNavigation::Ptr()>(path, "MoveBase")
|
||||
factory() -> NavigationHandle</pre>
|
||||
<p>Vì <code>libmove_base2.so</code> export cả hai alias <code>MoveBase2</code> và <code>MoveBase</code>,
|
||||
C API sẽ nhận được một <code>NavigationServer</code> của move_base2 khi cấu hình <code>MoveBase</code> trỏ đến
|
||||
<code>libmove_base2</code>. C API phía gọi không cần đổi tên hàm.</p>
|
||||
|
||||
<table>
|
||||
<tr><th>Nhóm C API</th><th>Ví dụ</th><th>Khi dùng move_base2</th></tr>
|
||||
<tr><td>Vòng đời</td><td><code>navigation_create</code>, <code>navigation_initialize</code>, <code>navigation_destroy</code></td><td>Dùng được qua interface chung. Nên bảo đảm host gọi <code>shutdown()</code> ở đường C++ trước khi dỡ process/plugin.</td></tr>
|
||||
<tr><td>Lệnh điều hướng</td><td><code>navigation_move_to</code>, <code>navigation_move_to_order</code>, <code>navigation_dock_to</code>, pause/resume/cancel</td><td>Dùng được. Order/docking được move_base2 đưa vào mission/profile tương ứng.</td></tr>
|
||||
<tr><td>Sensor và map</td><td><code>navigation_add_static_map</code>, <code>navigation_add_laser_scan</code>, <code>navigation_add_point_cloud2</code>, odometry</td><td>Dùng được; tên nguồn phải khớp source trong costmap YAML, ví dụ <code>pc_r_marking</code>.</td></tr>
|
||||
<tr><td>Quan sát</td><td><code>navigation_get_feedback</code>, pose/twist, global/local planner data</td><td>Dùng được cho trạng thái <code>BaseNavigation</code>. Không tự lộ API chi tiết của MissionManager hay ActionRunner.</td></tr>
|
||||
</table>
|
||||
|
||||
<div class="warning">
|
||||
<strong>Lưu ý kỹ thuật C API:</strong> đây là ABI C-linkage (hàm dùng <code>extern "C"</code>), nhưng
|
||||
header hiện có một số tham số tham chiếu C++ như <code>PoseStamped &out_pose</code> và
|
||||
<code>size_t &out_count</code>. Vì vậy nó <strong>chưa là header ISO C thuần</strong> để biên dịch trực tiếp
|
||||
bằng C compiler. Điều này không cản trở việc chọn move_base2, nhưng nếu caller là C thuần thì cần đổi các
|
||||
output reference thành pointer trước khi coi API là C API hoàn chỉnh.
|
||||
</div>
|
||||
|
||||
<h2>3. move_base2 khác move_base ở đâu?</h2>
|
||||
<table>
|
||||
<tr><th>Chủ đề</th><th>move_base cũ trong T800</th><th>move_base2</th></tr>
|
||||
<tr><td>Ranh giới với host</td><td><code>BaseNavigation</code>, plugin được nạp qua alias <code>MoveBase</code>.</td><td>Giữ cùng interface và xuất alias tương thích <code>MoveBase</code>; thêm alias rõ ràng <code>MoveBase2</code>.</td></tr>
|
||||
<tr><td>Tổ chức runtime</td><td>Điều phối kiểu đơn khối hơn.</td><td>Tách NavigationRuntime, ControlLoop, PlannerRunner, ControllerRunner, RecoveryRunner, ActionRunner và MissionLayer.</td></tr>
|
||||
<tr><td>Nhiệm vụ / order</td><td>Host hoặc lớp ngoài thường phải tự điều phối nhiều bước.</td><td>Mission layer có adapter nguồn và executor; VDA5050 order được tách thành leg, action-only leg chạy qua ActionRunner.</td></tr>
|
||||
<tr><td>Profile</td><td>Thường chỉ một bộ planner/controller cho một kiểu goal.</td><td>Profile position, docking, go_straight, rotate; docking có marker profile. Planner/local planner có thể chuyển theo mission.</td></tr>
|
||||
<tr><td>Fallback</td><td>Phụ thuộc implementation cũ.</td><td>Log chứng minh: khi CustomPlanner không hỗ trợ request đơn giản, runtime chuyển một lần sang SBPLLatticePlanner.</td></tr>
|
||||
<tr><td>Safety</td><td>Tùy implementation.</td><td>Có điều kiện <code>require_current_costmap</code>; sensor stale sẽ chặn wheel command để không điều khiển theo thế giới cũ.</td></tr>
|
||||
<tr><td>Local planner plugin</td><td>Không nên giả định plugin cũ có cùng ABI.</td><td>Dùng contract <code>robot_nav_core2::LocalPlanner</code>; local planner cũ chỉ dùng lại khi đã xác nhận cùng interface/ABI hoặc có adapter.</td></tr>
|
||||
</table>
|
||||
|
||||
<div class="note">
|
||||
<strong>Đừng nhầm hai mức tương thích.</strong> Host/API <code>BaseNavigation</code> tương thích là một việc.
|
||||
Plugin local planner, YAML, và hành vi mission/profile tương thích là các việc khác. Việc đổi thư viện thành
|
||||
công không chứng minh tất cả planner cũ sẽ chạy được trong move_base2.
|
||||
</div>
|
||||
|
||||
<div class="page-break"></div>
|
||||
<h2>4. Một khung đang dùng move_base có chạy move_base2 luôn không?</h2>
|
||||
<p><strong>Câu trả lời:</strong> có điều kiện. Với <code>amr_control</code> của T800 thì câu trả lời là
|
||||
<strong>có, theo đúng cơ chế đã được thiết kế</strong>. Với một khung ROS bất kỳ đang dùng package ROS1
|
||||
<code>move_base</code>, câu trả lời là <strong>không thể kết luận là có</strong> nếu chưa kiểm tra interface.</p>
|
||||
|
||||
<table>
|
||||
<tr><th>Loại khung hiện có</th><th>Khả năng chuyển</th><th>Lý do / việc cần làm</th></tr>
|
||||
<tr><td>T800 host nạp <code>BaseNavigation::Ptr</code> từ alias <code>MoveBase</code></td><td><strong>Cao</strong></td><td>move_base2 export alias tương thích. Đặt đúng library/config, rồi kiểm thử runtime.</td></tr>
|
||||
<tr><td>Ứng dụng gọi C API <code>navigation_create()</code></td><td><strong>Cao</strong></td><td>C API cũng tìm <code>MoveBase</code>; config quyết định .so được nạp. Cần cùng ABI của <code>move_base_core</code>.</td></tr>
|
||||
<tr><td>Ứng dụng liên kết trực tiếp class/private header của move_base cũ</td><td><strong>Thấp</strong></td><td>Phải sửa và build lại theo interface công khai hoặc làm adapter; không nên thay .so mù quáng.</td></tr>
|
||||
<tr><td>ROS1 chuẩn dùng action <code>move_base_msgs/MoveBaseAction</code> và pluginlib/nav_core</td><td><strong>Chưa khẳng định</strong></td><td>Đây không phải tự động là contract T800 <code>BaseNavigation</code>; cần kiểm tra node/action/topic/plugin ABI cụ thể.</td></tr>
|
||||
</table>
|
||||
|
||||
<h3>Điều kiện bắt buộc để chuyển khung T800</h3>
|
||||
<ol>
|
||||
<li><strong>Chung ABI:</strong> host, <code>libmove_base2.so</code> và <code>move_base_core</code> phải được build từ cùng workspace/devel hoặc ABI tương thích.</li>
|
||||
<li><strong>Đúng alias:</strong> library phải export factory <code>BaseNavigation::Ptr()</code> dưới tên <code>MoveBase</code>. move_base2 hiện đã có alias này.</li>
|
||||
<li><strong>Đúng config:</strong> <code>PNKX_NAV_CORE_CONFIG_DIR</code> phải trỏ đến <code>move_base2/config/runtime</code>; file <code>move_base_common_params.yaml</code> cần có <code>MoveBase: library_path: libmove_base2</code>.</li>
|
||||
<li><strong>Đủ plugin:</strong> global planner/local planner/recovery/action/mission adapter được khai trong YAML phải có .so, alias factory và dependency đúng.</li>
|
||||
<li><strong>Đúng sensor contract:</strong> map, TF, odom, laser/cloud/depth được bơm bằng đúng topic-key và tần số. Với <code>require_current_costmap: true</code>, sensor stale sẽ chặn lệnh bánh xe.</li>
|
||||
<li><strong>Shutdown rõ ràng:</strong> dừng navigation trước khi destroy loader/process để tránh race thread/plugin.</li>
|
||||
</ol>
|
||||
|
||||
<h3>Cách chuyển an toàn trong launch của T800</h3>
|
||||
<pre><!-- move_base2_control.launch đã làm hai việc quan trọng -->
|
||||
<env name="PNKX_NAV_CORE_CONFIG_DIR"
|
||||
value="$(find move_base2)/config/runtime" />
|
||||
|
||||
# config/runtime/move_base_common_params.yaml
|
||||
MoveBase:
|
||||
library_path: libmove_base2</pre>
|
||||
<p>Không đổi trực tiếp link library trong <code>amr_control</code>. Nó tiếp tục nạp factory
|
||||
<code>"MoveBase"</code>; file cấu hình chọn implementation là move_base2.</p>
|
||||
|
||||
<h2>5. Checklist test trước khi thay cho hệ chạy dài</h2>
|
||||
<ol>
|
||||
<li>Build: xác nhận có <code>devel/lib/libmove_base2.so</code> và các plugin runtime.</li>
|
||||
<li>Boot: log phải có <code>Found library ... libmove_base2.so</code>, <code>NavigationRuntime built</code>, costmap và sensor source tạo thành công.</li>
|
||||
<li>Goal đơn: position, cancel/preempt, pause/resume; xác nhận <code>cmd_vel</code> và feedback.</li>
|
||||
<li>Order: VDA5050 order có nhiều node/edge, action wait/detect/charge và docking marker.</li>
|
||||
<li>Fallback/recovery: tạo tình huống CustomPlanner fail, obstacle/stale sensor có kiểm soát; xác nhận fallback/recovery và robot dừng an toàn.</li>
|
||||
<li>Soak test: chạy 8 giờ, theo dõi CPU/RAM, tần số camera/cloud, message queue Gazebo, reconnect MQTT và tần suất sensor stale.</li>
|
||||
</ol>
|
||||
|
||||
<h2>6. Liên hệ với cảnh báo qua đêm trong log</h2>
|
||||
<p>Cảnh báo <code>/gazebo/default/pose/local/info</code> là hàng đợi của Gazebo Transport đầy ở publisher đó;
|
||||
Gazebo bỏ một message để queue không tăng vô hạn. Nó không chứng minh C API hay move_base2 bị lỗi. Tuy vậy,
|
||||
nó là dấu hiệu nên theo dõi tải mô phỏng/consumer. Cảnh báo ảnh hưởng an toàn hơn trong log là:</p>
|
||||
<pre>/camera_right/depth/points_proc observation buffer has not been updated ...
|
||||
[move_base2] Sensor data is stale — wheel commands blocked</pre>
|
||||
<p>move_base2 đang dừng lệnh bánh xe đúng chủ đích vì local/global costmap không còn mô tả thế giới hiện tại.
|
||||
Cần chẩn đoán đường camera phải/DepthCameraData, callback SensorConverter và tần số thực tế; không quy kết ngay
|
||||
cho nghẽn mạng.</p>
|
||||
|
||||
<h2>7. Dấu vết mã nguồn dùng để kết luận</h2>
|
||||
<ul class="small">
|
||||
<li><code>Controllers/Packages/amr_control/src/amr_control.cpp</code>: host tìm library <code>MoveBase</code> và import factory alias.</li>
|
||||
<li><code>Test/move_base2/src/move_base2_plugin.cpp</code>: factory trả <code>BaseNavigation::Ptr</code>, export cả <code>MoveBase2</code> và <code>MoveBase</code>.</li>
|
||||
<li><code>Test/move_base2/launch/move_base2_control.launch</code>: đặt <code>PNKX_NAV_CORE_CONFIG_DIR</code> sang overlay runtime.</li>
|
||||
<li><code>Test/move_base2/config/runtime/move_base_common_params.yaml</code>: <code>MoveBase.library_path = libmove_base2</code>.</li>
|
||||
<li><code>pnkx_nav_core/src/APIs/c_api/src/nav_c_api.cpp</code>: <code>navigation_create()</code> dùng chính alias <code>MoveBase</code>.</li>
|
||||
<li><code>pnkx_nav_core/src/Navigations/Cores/move_base_core/include/move_base_core/navigation.h</code>: contract chung <code>BaseNavigation</code>.</li>
|
||||
<li><code>Test/move_base2/src/navigation_runtime.cpp</code>: xây costmap, runners và mission layer.</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
BIN
docs/MOVE_BASE2_RUNTIME_VA_TUONG_THICH.pdf
Normal file
BIN
docs/MOVE_BASE2_RUNTIME_VA_TUONG_THICH.pdf
Normal file
Binary file not shown.
@@ -11,9 +11,9 @@ Khi cần đổi hành vi: sửa tài liệu này trước, sửa test, rồi m
|
||||
| State | Ai phát cmd_vel | Vào state khi | Ra khi |
|
||||
|---|---|---|---|
|
||||
| `IDLE` | không ai (0) | khởi động; cycle ngay sau một state terminal | có `NavigationRequest` đang chờ → `PLANNING`; yêu cầu chỉ-có-action (`has_goal == false`, D8) → `EXECUTING_ACTIONS`; yêu cầu không có goal lẫn action → `ABORTED` (tự vệ) |
|
||||
| `PLANNING` | không ai (0) | nhận yêu cầu; controller không sinh được lệnh mà chưa hết kiên nhẫn; recovery vừa chạy xong; tiếp tục sau tạm dừng | có plan hợp lệ → `CONTROLLING`; quá `planner_patience` hoặc quá `max_planning_retries` → `RECOVERING(planning_failed)` |
|
||||
| `PLANNING` | không ai (0) | nhận yêu cầu; controller không sinh được lệnh mà chưa hết kiên nhẫn; recovery vừa chạy xong; tiếp tục sau tạm dừng | có plan hợp lệ → `CONTROLLING`; global planner chính fail/plan rỗng và có backup chưa dùng → đổi sang backup, lập plan lại; backup fail (hoặc không có backup) / quá `planner_patience` / quá `max_planning_retries` → `RECOVERING(planning_failed)` |
|
||||
| `CONTROLLING` | **local planner** | có plan hợp lệ; tiếp tục sau tạm dừng | `isGoalReached` → `SUCCEEDED` (hết action) hoặc `EXECUTING_ACTIONS` (còn action — D8); quá `controller_patience` → `RECOVERING(controlling_failed)`; quá `oscillation_timeout` → `RECOVERING(oscillation)`; không sinh được lệnh (còn kiên nhẫn, còn pose) → `PLANNING` |
|
||||
| `RECOVERING` | **recovery behavior** | ba trigger ở trên | tick trả `succeeded`/`failed` → `PLANNING`, chỉ số behavior tăng 1; hết behavior khi định vào → `ABORTED` |
|
||||
| `RECOVERING` | **recovery behavior** | ba trigger ở trên | tick trả `succeeded`/`failed` → `PLANNING`, cursor của **route thuộc trigger đó** tăng 1; hết route khi định vào → `ABORTED` |
|
||||
| `EXECUTING_ACTIONS` | không ai (0) — **D8** | tới goal mà yêu cầu còn action; nhận yêu cầu `has_goal == false` | action xong mà còn action kế → start action kế (ở nguyên state); action cuối `succeeded` → `SUCCEEDED`; một action `failed` → `ABORTED` (không qua recovery); quá `action_patience` (nếu bật) → cancel action + `ABORTED`; `cancel()` → `CANCELLING`; `pause()` → `PAUSED` (không huỷ action) |
|
||||
| `PAUSED` | không ai (0) | `pause()` từ `PLANNING`/`CONTROLLING`/`RECOVERING`/`EXECUTING_ACTIONS` | `resume()` → về state trước đó; `cancel()` → `CANCELLING` |
|
||||
| `CANCELLING` | không ai (0) | `cancel()` từ mọi state đang chạy | robot đã dừng → `CANCELLED` |
|
||||
@@ -76,7 +76,7 @@ Khi cần đổi hành vi: sửa tài liệu này trước, sửa test, rồi m
|
||||
| `planning_retries_` (đếm `max_planning_retries`) | như trên | — |
|
||||
| `last_valid_control_` (đo `controller_patience`) | nhận yêu cầu mới; tiếp tục sau tạm dừng; recovery chạy xong; controller sinh được lệnh hợp lệ | **có plan mới** |
|
||||
| `last_oscillation_reset_` (đo `oscillation_timeout`) | nhận yêu cầu mới; tiếp tục sau tạm dừng; robot đi được quá `oscillation_distance` | **có plan mới** |
|
||||
| `recovery_index_` | nhận yêu cầu mới | — |
|
||||
| cursor các recovery route | nhận yêu cầu mới | route của trigger khác; cursor chỉ tăng sau lượt recovery của chính trigger đó |
|
||||
| `action_started_at_` (đo `action_patience`, D8) | start một action (trần tính cho TỪNG action); tiếp tục sau tạm dừng (quãng dừng không tính vào trần) | — |
|
||||
|
||||
Hai ô "không đặt lại khi có plan mới" là điểm dễ sai nhất và đã từng sai trong lúc thi công: nếu làm
|
||||
@@ -84,6 +84,57 @@ mới hai đồng hồ đó mỗi lần có plan, vòng lặp `CONTROLLING → P
|
||||
hạn, và một controller hỏng vĩnh viễn sẽ không bao giờ chạm `controller_patience`. Test
|
||||
`ControllerPatienceSurvivesReplanLoop` giữ tính chất này.
|
||||
|
||||
## Global planner dự phòng
|
||||
|
||||
`backup_global_planner` là một alias tùy chọn ở root config. Khi global planner active trả `false`
|
||||
hoặc plan rỗng, `ControlLoop` đổi sang alias này **một lần duy nhất cho mỗi request**, giữ nguyên
|
||||
local planner và state `PLANNING`. Lượt backup thành công đi bình thường vào `CONTROLLING`; lượt
|
||||
backup fail mới được đưa vào `PlannerFeedback::kFailed`, nên state machine đi theo recovery hiện có.
|
||||
|
||||
Backup dùng overload `makePlan(start, goal, plan)` (không mang VDA5050 `Order`) để
|
||||
`SBPLLatticePlanner` dùng được khi `CustomPlanner` của position fail. Đây là đường lùi hình học:
|
||||
không được kỳ vọng giữ trajectory/edge metadata riêng của `CustomPlanner`. Backup không kích hoạt
|
||||
khi planner bị treo — worker plugin không có cancel cưỡng bức; `planner_patience` vẫn là hàng rào
|
||||
cho trường hợp đó.
|
||||
|
||||
## Recovery routes
|
||||
|
||||
`recovery/behaviors` là registry toàn bộ plugin có thể dùng; `recovery/routes` chọn **tên instance**
|
||||
theo trigger, không phụ thuộc thứ tự nạp plugin:
|
||||
|
||||
```yaml
|
||||
recovery:
|
||||
behaviors:
|
||||
- {name: wait, type: WaitRecovery}
|
||||
- {name: clear, type: ClearCostmapRecovery}
|
||||
- {name: detour_path, type: DetourPathRecovery}
|
||||
- {name: rotate, type: RotateRecovery}
|
||||
- {name: back_up, type: BackUpRecovery}
|
||||
routes:
|
||||
planning_failed: [wait, clear, rotate, back_up]
|
||||
controlling_failed: [wait, clear, detour_path, rotate, back_up]
|
||||
oscillation: [detour_path, rotate, back_up]
|
||||
```
|
||||
|
||||
Khi dựng runtime, `RecoveryRunner` nạp registry trước rồi resolve tên route thành index thật; chỉ
|
||||
sau đó `NavigationRuntime` mới gán `recovery_behavior_count` và các route này vào
|
||||
`StateMachineConfig`. Route phải khai đủ cả ba trigger, không rỗng sau resolve, không lặp tên và
|
||||
không có trigger lạ. Schema cũ không có `routes` vẫn tương thích: cả ba trigger dùng toàn bộ registry
|
||||
theo thứ tự nạp.
|
||||
|
||||
`DetourPathRecovery` hiện chưa có plugin/library. Vì vậy entry `detour_path` có thể được commit trước:
|
||||
registry báo plugin thiếu, `RecoveryRunner` cảnh báo và bỏ riêng tên đó khỏi route; không bao giờ
|
||||
đưa index giả vào state machine. Với config hiện tại, trước khi plugin được thêm, route hữu hiệu là
|
||||
`planning=[wait, clear, rotate, back_up]`, `controlling=[wait, clear, rotate, back_up]`,
|
||||
`oscillation=[rotate, back_up]`. Khi plugin SBPL được nạp thành công, hai route sau tự có
|
||||
`detour_path`, không cần sửa move_base2.
|
||||
|
||||
Mỗi request giữ ba cursor độc lập. Một lượt behavior `succeeded` **hoặc** `failed` luôn quay về
|
||||
`PLANNING` để lập đường mới và tiêu thụ một phần tử của route đã kích hoạt; lần lỗi kế tiếp cùng
|
||||
trigger thử phần tử sau. Lỗi bởi trigger khác dùng cursor của route khác. Log runner ghi `registry
|
||||
index`, không phải vị trí trong route, để không đánh lừa vận hành khi một behavior bị dùng ở nhiều
|
||||
route.
|
||||
|
||||
## Mất pose (TF thiếu hoặc quá hạn)
|
||||
|
||||
Không biết robot đang ở đâu thì không được cho nó chạy. Cụ thể:
|
||||
|
||||
Reference in New Issue
Block a user