optimal & fix file cmake

This commit is contained in:
2026-08-03 22:41:32 +07:00
parent d8babff20b
commit 701d25f952
70 changed files with 5572 additions and 1146 deletions

View 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 &middot; 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 &rarr; /amr_node (amr_control) &rarr; BaseNavigation factory &rarr; NavigationServer (move_base2)<br>
&rarr; 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&lt;BaseNavigation::Ptr()&gt;(path, "MoveBase")
factory() -&gt; NavigationHandle</pre>
<p><code>libmove_base2.so</code> export cả hai alias <code>MoveBase2</code><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 &amp;out_pose</code>
<code>size_t &amp;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><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>&lt;!-- move_base2_control.launch đã làm hai việc quan trọng --&gt;
&lt;env name="PNKX_NAV_CORE_CONFIG_DIR"
value="$(find move_base2)/config/runtime" /&gt;
# 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><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>