first update
This commit is contained in:
18
CHANGELOG.rst
Normal file
18
CHANGELOG.rst
Normal file
@@ -0,0 +1,18 @@
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Changelog for package homing_local_planner
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
0.1.2 (2024-11-26)
|
||||
------------------
|
||||
* Parameter `acc_lim_x` and `acc_lim_theta` added via ros parameter server.
|
||||
* Fixed compilation errors on ROS Noetic (see `#1 <https://github.com/zengxiaolei/homing_local_planner/issues/1>`_)
|
||||
* Parameter `min_turn_radius` added via ros parameter server.
|
||||
* Parameter `stop_dist` and `dec_dist for` added via ros parameter server.
|
||||
* Parameter `turn_around_priority` added via ros parameter server.
|
||||
* Contributors: Lei Zeng
|
||||
|
||||
0.1.1 (2023-11-02)
|
||||
------------------
|
||||
* All files added to the noetic-devel branch
|
||||
* Initial commit
|
||||
* Contributors: Lei Zeng
|
||||
264
CMakeLists.txt
Normal file
264
CMakeLists.txt
Normal file
@@ -0,0 +1,264 @@
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
project(mission_adapters VERSION 1.0.0 LANGUAGES CXX)
|
||||
|
||||
# ========================================================
|
||||
# Detect build mode
|
||||
# ========================================================
|
||||
if(DEFINED CATKIN_DEVEL_PREFIX OR DEFINED CATKIN_TOPLEVEL)
|
||||
set(BUILDING_WITH_CATKIN TRUE)
|
||||
message(STATUS "Building mission_adapters with Catkin")
|
||||
else()
|
||||
set(BUILDING_WITH_CATKIN FALSE)
|
||||
message(STATUS "Building mission_adapters with Standalone CMake")
|
||||
endif()
|
||||
|
||||
# ========================================================
|
||||
# C++ Standard
|
||||
# ========================================================
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
# ========================================================
|
||||
# Common dependencies
|
||||
# ========================================================
|
||||
find_package(PCL REQUIRED)
|
||||
find_package(OpenMP REQUIRED)
|
||||
find_package(Boost REQUIRED COMPONENTS system)
|
||||
find_package(Eigen REQUIRED)
|
||||
|
||||
# ========================================================
|
||||
# Standalone mode
|
||||
# ========================================================
|
||||
if (NOT BUILDING_WITH_CATKIN)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(CMAKE_BUILD_RPATH_USE_ORIGIN TRUE)
|
||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
set(CMAKE_BUILD_RPATH "${CMAKE_BINARY_DIR}")
|
||||
|
||||
# ⚠️ NOTE: Đây KHÔNG phải package thật -> chỉ để placeholder
|
||||
set(PACKAGES_DIR
|
||||
robot_costmap_2d
|
||||
robot_nav_core
|
||||
robot_nav_core2
|
||||
robot_nav_msgs
|
||||
robot_std_msgs
|
||||
robot_geometry_msgs
|
||||
robot_cpp
|
||||
robot_tf3_geometry_msgs
|
||||
robot_visualization_msgs
|
||||
robot_nav_2d_utils
|
||||
data_convert
|
||||
)
|
||||
|
||||
find_library(TF3_LIBRARY
|
||||
NAMES tf3
|
||||
PATHS /usr/lib /usr/local/lib /usr/lib/x86_64-linux-gnu
|
||||
)
|
||||
|
||||
if(NOT TF3_LIBRARY)
|
||||
message(FATAL_ERROR "❌ tf3 library not found")
|
||||
endif()
|
||||
|
||||
# ========================================================
|
||||
# Catkin mode
|
||||
# ========================================================
|
||||
else()
|
||||
|
||||
find_package(catkin REQUIRED COMPONENTS
|
||||
robot_costmap_2d
|
||||
robot_nav_core
|
||||
robot_nav_core2
|
||||
robot_nav_msgs
|
||||
robot_std_msgs
|
||||
robot_geometry_msgs
|
||||
robot_cpp
|
||||
robot_tf3_geometry_msgs
|
||||
robot_visualization_msgs
|
||||
robot_nav_2d_utils
|
||||
data_convert
|
||||
)
|
||||
|
||||
find_library(TF3_LIBRARY
|
||||
NAMES tf3
|
||||
PATHS /usr/lib /usr/local/lib /usr/lib/x86_64-linux-gnu
|
||||
)
|
||||
|
||||
if(NOT TF3_LIBRARY)
|
||||
message(FATAL_ERROR "❌ tf3 library not found")
|
||||
endif()
|
||||
|
||||
catkin_package(
|
||||
INCLUDE_DIRS include
|
||||
LIBRARIES ${PROJECT_NAME} ${PROJECT_NAME}_utils
|
||||
CATKIN_DEPENDS
|
||||
robot_costmap_2d
|
||||
robot_nav_core
|
||||
robot_nav_core2
|
||||
robot_nav_msgs
|
||||
robot_std_msgs
|
||||
robot_geometry_msgs
|
||||
robot_cpp
|
||||
robot_tf3_geometry_msgs
|
||||
robot_visualization_msgs
|
||||
robot_nav_2d_utils
|
||||
data_convert
|
||||
DEPENDS PCL Boost Eigen
|
||||
)
|
||||
|
||||
include_directories(
|
||||
include
|
||||
${catkin_INCLUDE_DIRS}
|
||||
${PCL_INCLUDE_DIRS}
|
||||
${Boost_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
# ========================================================
|
||||
# Libraries
|
||||
# ========================================================
|
||||
|
||||
# # utils lib
|
||||
# add_library(${PROJECT_NAME}_utils SHARED
|
||||
# src/angle_utils.cpp
|
||||
# src/config.cpp
|
||||
# )
|
||||
|
||||
# main planner lib
|
||||
add_library(${PROJECT_NAME} SHARED
|
||||
src/mission_adapters.cpp
|
||||
)
|
||||
|
||||
# ========================================================
|
||||
# Catkin linking
|
||||
# ========================================================
|
||||
if(BUILDING_WITH_CATKIN)
|
||||
|
||||
# add_dependencies(${PROJECT_NAME}_utils ${catkin_EXPORTED_TARGETS})
|
||||
add_dependencies(${PROJECT_NAME} ${catkin_EXPORTED_TARGETS})
|
||||
|
||||
# target_include_directories(${PROJECT_NAME}_utils
|
||||
# PUBLIC
|
||||
# $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
# $<INSTALL_INTERFACE:include>
|
||||
# )
|
||||
|
||||
target_include_directories(${PROJECT_NAME}
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
)
|
||||
|
||||
# target_link_libraries(${PROJECT_NAME}_utils
|
||||
# PUBLIC ${catkin_LIBRARIES}
|
||||
# PRIVATE Boost::system
|
||||
# ${TF3_LIBRARY}
|
||||
# )
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
# PUBLIC ${PROJECT_NAME}_utils
|
||||
PUBLIC ${catkin_LIBRARIES}
|
||||
PUBLIC ${PCL_LIBRARIES}
|
||||
PRIVATE Boost::system
|
||||
${TF3_LIBRARY}
|
||||
OpenMP::OpenMP_CXX
|
||||
)
|
||||
|
||||
# ========================================================
|
||||
# Standalone linking
|
||||
# ========================================================
|
||||
else()
|
||||
|
||||
# target_include_directories(${PROJECT_NAME}_utils
|
||||
# PUBLIC
|
||||
# $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
# $<INSTALL_INTERFACE:include>
|
||||
# )
|
||||
|
||||
target_include_directories(${PROJECT_NAME}
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
)
|
||||
|
||||
# target_link_libraries(${PROJECT_NAME}_utils
|
||||
# PUBLIC ${PACKAGES_DIR} # ⚠️ placeholder
|
||||
# PRIVATE Boost::system
|
||||
# ${TF3_LIBRARY}
|
||||
# )
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
# PUBLIC ${PROJECT_NAME}_utils
|
||||
PUBLIC ${PACKAGES_DIR} # ⚠️ placeholder
|
||||
PUBLIC ${PCL_LIBRARIES}
|
||||
PRIVATE Boost::system
|
||||
${TF3_LIBRARY}
|
||||
OpenMP::OpenMP_CXX
|
||||
)
|
||||
|
||||
# set_target_properties(${PROJECT_NAME}_utils PROPERTIES
|
||||
# LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
# BUILD_RPATH "${CMAKE_BINARY_DIR}"
|
||||
# INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib"
|
||||
# )
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
BUILD_RPATH "${CMAKE_BINARY_DIR}"
|
||||
INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib"
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
# ========================================================
|
||||
# Install
|
||||
# ========================================================
|
||||
if(BUILDING_WITH_CATKIN)
|
||||
|
||||
# install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_utils
|
||||
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||
# RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
|
||||
# )
|
||||
|
||||
install(DIRECTORY include/${PROJECT_NAME}/
|
||||
DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
|
||||
FILES_MATCHING PATTERN "*.h"
|
||||
)
|
||||
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/plugins.xml)
|
||||
install(FILES plugins.xml
|
||||
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
||||
)
|
||||
endif()
|
||||
|
||||
else()
|
||||
|
||||
# install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_utils
|
||||
# EXPORT ${PROJECT_NAME}-targets
|
||||
# ARCHIVE DESTINATION lib
|
||||
# LIBRARY DESTINATION lib
|
||||
# RUNTIME DESTINATION bin
|
||||
# )
|
||||
|
||||
install(EXPORT ${PROJECT_NAME}-targets
|
||||
FILE ${PROJECT_NAME}-targets.cmake
|
||||
NAMESPACE ${PROJECT_NAME}::
|
||||
DESTINATION lib/cmake/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
install(DIRECTORY include/${PROJECT_NAME}/
|
||||
DESTINATION include
|
||||
FILES_MATCHING PATTERN "*.h"
|
||||
)
|
||||
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/plugins.xml)
|
||||
install(FILES plugins.xml
|
||||
DESTINATION share/${PROJECT_NAME}
|
||||
)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
217
README.md
Normal file
217
README.md
Normal file
@@ -0,0 +1,217 @@
|
||||
# 🎯 Stanley Local Planner
|
||||
|
||||
**Stanley Local Planner** là một bộ điều khiển cấp địa phương (Local Planner) sử dụng **Stanley Method** cho robot di động. Bộ điều khiển này được thiết kế để hoạt động trên hai nền tảng:
|
||||
|
||||
- 🤖 **ROS Noetic** - Tích hợp vào ROS Navigation Stack
|
||||
- ⚙️ **C++ Standalone** - Thư viện C++ thuần cho các ứng dụng không dùng ROS
|
||||
|
||||
Bộ điều khiển này sử dụng **Bicycle Kinematic Model** và kết hợp hai thành phần điều khiển:
|
||||
- **Heading Error** - Lỗi góc định hướng
|
||||
- **Cross-track Error** - Sai lệch ngang so với đường dẫn
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Đặc điểm chính
|
||||
|
||||
✅ **Stanley Method kinh điển** - Thuật toán được chứng minh hiệu quả trong tự lái
|
||||
✅ **Bicycle Kinematic Model** - Mô hình chính xác cho xe, xe tải, xe điện
|
||||
✅ **Dual Platform Support** - ROS Noetic + C++ Standalone
|
||||
✅ **Hiệu suất cao** - CPU usage < 5%
|
||||
✅ **Cấu hình đơn giản** - Chỉ cần 3-4 tham số chính
|
||||
✅ **Theo dõi đường chính xác** - Tracking error < 0.1m
|
||||
✅ **Chuyển động mượt mà** - Điều khiển lái góc mức** (smooth steering)
|
||||
|
||||
---
|
||||
|
||||
## 📚 Nguyên lý Stanley Method
|
||||
|
||||
### Bicycle Model
|
||||
|
||||
Mô hình bicycle kinematic mô tả động học của robot:
|
||||
|
||||
```
|
||||
θ (yaw angle)
|
||||
↑
|
||||
| Front Axle (steering angle δ)
|
||||
| /
|
||||
|___/______ Rear Axle (reference point)
|
||||
L (wheelbase)
|
||||
```
|
||||
|
||||
Phương trình:
|
||||
```
|
||||
ẋ = v * cos(θ)
|
||||
ẏ = v * sin(θ)
|
||||
θ̇ = v * tan(δ) / L
|
||||
```
|
||||
|
||||
### Stanley Control Law
|
||||
|
||||
Stanley controller kết hợp hai thành phần lỗi:
|
||||
|
||||
```
|
||||
δ = atan2(k_e * e_y, v) + e_θ
|
||||
|
||||
Trong đó:
|
||||
δ - Góc lái (steering angle)
|
||||
k_e - Cross-track error gain
|
||||
e_y - Sai lệch ngang (cross-track error)
|
||||
v - Vận tốc tiến (forward velocity)
|
||||
e_θ - Sai lệch góc (heading error)
|
||||
```
|
||||
|
||||
**Thành phần 1: Cross-track Error (e_y)**
|
||||
- Khoảng cách ngang từ robot đến đường dẫn
|
||||
- Được cân nhân bằng vận tốc để ổn định (adaptive gain)
|
||||
- Ưu điểm: Quay về đường nhanh chóng
|
||||
|
||||
**Thành phần 2: Heading Error (e_θ)**
|
||||
- Sai lệch góc so với hướng mong muốn của đường dẫn
|
||||
- Đảm bảo robot song song với đường dẫn
|
||||
- Ưu điểm: Tránh dao động, điều khiển ổn định
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Cài đặt
|
||||
|
||||
### Yêu cầu hệ thống
|
||||
|
||||
**Chung:**
|
||||
- C++17 trở lên
|
||||
- Eigen 3.3+
|
||||
- CMake 3.10+
|
||||
|
||||
### Build từ source
|
||||
|
||||
```bash
|
||||
# Clone repository
|
||||
cd ~/ros_workspace/src
|
||||
git clone link_to_repo
|
||||
cd ..
|
||||
|
||||
# Build với ROS
|
||||
catkin_make
|
||||
|
||||
# Hoặc build standalone C++ library
|
||||
cd src/stanley_local_planner
|
||||
mkdir build && cd build
|
||||
cmake -DBUILD_ROS=OFF ..
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Cấu hình
|
||||
|
||||
### Tham số chính
|
||||
|
||||
Tất cả tham số được cấu hình trong `config/stanley_local_planner_params.yaml`:
|
||||
|
||||
```yaml
|
||||
# Robot Parameters
|
||||
robot_max_v: 1.0 # Vận tốc tối đa (m/s)
|
||||
robot_max_a: 1.0 # Gia tốc tối đa (m/s²)
|
||||
robot_wheelbase: 0.5 # Khoảng cách trục bánh (m)
|
||||
robot_max_steering_angle: 0.5 # Góc lái tối đa (rad ≈ 28°)
|
||||
robot_min_radius: 0.3 # Bán kính quay tối thiểu (m)
|
||||
|
||||
# Stanley Controller Parameters
|
||||
k_e: 2.0 # Cross-track error gain
|
||||
k_theta: 0.5 # Heading error gain
|
||||
time_step: 0.05 # Chu kỳ điều khiển (s)
|
||||
|
||||
# Trajectory Parameters
|
||||
lookahead_distance: 1.0 # Tầm nhìn trước (m)
|
||||
path_resolution: 0.1 # Độ phân giải đường dẫn (m)
|
||||
|
||||
# Goal Tolerance
|
||||
xy_goal_tolerance: 0.1 # Sai số vị trí (m)
|
||||
yaw_goal_tolerance: 0.1 # Sai số định hướng (rad)
|
||||
```
|
||||
|
||||
### Điều chỉnh hiệu suất
|
||||
|
||||
| Tham số | Tăng | Giảm |
|
||||
|---------|------|------|
|
||||
| `k_e` | Quay nhanh, có thể dao động | Quay chậm, trễ |
|
||||
| `k_theta` | Định hướng ổn định hơn | Có thể dao động |
|
||||
| `lookahead_distance` | Chuyển động mượt, mục tiêu xa | Phản ứng chậm, sai lệch lớn |
|
||||
|
||||
**Khuyến nghị:**
|
||||
- Start với `k_e=2.0, k_theta=0.5`
|
||||
- Nếu quá khúc: giảm `k_e` hoặc tăng `lookahead_distance`
|
||||
- Nếu quá mận: tăng `k_e` hoặc giảm `lookahead_distance`
|
||||
|
||||
---
|
||||
|
||||
## 📊 Hiệu suất
|
||||
|
||||
### Benchmark Results
|
||||
|
||||
| Metric | Value | Ghi chú |
|
||||
|--------|-------|--------|
|
||||
| Tracking Error | < 0.08m | Path deviation |
|
||||
| Heading Error | < 0.05 rad | Angular accuracy |
|
||||
| Response Time | < 50ms | Control loop |
|
||||
| CPU Usage | ~2-3% | Single core |
|
||||
| Memory | ~5MB | Runtime |
|
||||
| Max Speed | 1.5 m/s | Tested |
|
||||
|
||||
---
|
||||
|
||||
## 📐 Mô hình toán học
|
||||
|
||||
### Cross-track Error Calculation
|
||||
|
||||
```
|
||||
Cho đường dẫn từ điểm P_i đến P_{i+1}
|
||||
Vị trí robot: R = (x, y)
|
||||
|
||||
1. Tìm hình chiếu vuông góc từ R lên đường P_i → P_{i+1}
|
||||
2. e_y = khoảng cách từ R đến điểm chiếu
|
||||
3. Dấu: dương nếu robot ở bên trái, âm nếu ở bên phải
|
||||
```
|
||||
|
||||
### Heading Error Calculation
|
||||
|
||||
```
|
||||
ψ_desired = atan2(P_{i+1}.y - P_i.y, P_{i+1}.x - P_i.x)
|
||||
ψ_current = θ (yaw angle của robot)
|
||||
e_θ = normalize(ψ_desired - ψ_current) [-π, π]
|
||||
```
|
||||
|
||||
### Steering Angle Computation
|
||||
|
||||
```
|
||||
δ_ff = atan(tan(path_curvature) * wheelbase) // Feedforward
|
||||
δ_fb = atan2(k_e * e_y, v) + k_theta * e_θ // Feedback
|
||||
δ = δ_ff + δ_fb
|
||||
δ = clamp(δ, -max_angle, max_angle)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Tài liệu tham khảo
|
||||
|
||||
- **Stanley Method Paper**: "Journal of Field Robotics" - Thrun et al. (2006)
|
||||
- **Bicycle Kinematic Model**: Classic control theory
|
||||
- **ROS Navigation**: [http://wiki.ros.org/navigation](http://wiki.ros.org/navigation)
|
||||
- **Eigen Documentation**: [https://eigen.tuxfamily.org/](https://eigen.tuxfamily.org/)
|
||||
|
||||
---
|
||||
|
||||
## 📄 Giấy phép
|
||||
|
||||
**MIT License** - Tự do sử dụng, sửa đổi, phân phối
|
||||
|
||||
---
|
||||
|
||||
## ✉️ Liên hệ & Support
|
||||
|
||||
- **Email**: duongtd@phenikaa-x.com
|
||||
|
||||
---
|
||||
|
||||
**Cảm ơn bạn đã sử dụng Stanley Local Planner!** 🚗
|
||||
|
||||
11
cfg/stanley_local_planner_params.yaml
Normal file
11
cfg/stanley_local_planner_params.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
LocalPlannerAdapter:
|
||||
library_path: liblocal_planner_adapter
|
||||
yaw_goal_tolerance: 0.017
|
||||
xy_goal_tolerance: 0.03
|
||||
min_approach_linear_velocity: 0.06
|
||||
|
||||
StanleyLocalPlanner:
|
||||
# base_local_planner: "hybrid_local_planner/HybridLocalPlanner"
|
||||
# HybridLocalPlanner:
|
||||
library_path: libstanley_local_planner
|
||||
|
||||
0
include/mission_adapters/adapter.h
Normal file
0
include/mission_adapters/adapter.h
Normal file
0
include/mission_adapters/event.h
Normal file
0
include/mission_adapters/event.h
Normal file
0
include/mission_adapters/event_processor.h
Normal file
0
include/mission_adapters/event_processor.h
Normal file
279
include/mission_adapters/mission_adapters.h
Normal file
279
include/mission_adapters/mission_adapters.h
Normal file
@@ -0,0 +1,279 @@
|
||||
#ifndef MISSION_ADAPTERS_MISSION_ADAPTERS_H
|
||||
#define MISSION_ADAPTERS_MISSION_ADAPTERS_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
|
||||
#include <robot/robot.h>
|
||||
#include <robot_protocol_msgs/Order.h>
|
||||
#include <robot_geometry_msgs/PoseStamped.h>
|
||||
|
||||
namespace mission_adapters
|
||||
{
|
||||
// ── Priority constants ────────────────────────────────────────────────────
|
||||
// Số càng nhỏ = ưu tiên càng cao (min-heap)
|
||||
static constexpr int PRIORITY_EMERGENCY = 0;
|
||||
static constexpr int PRIORITY_CANCEL = 1; // resume > pause
|
||||
static constexpr int PRIORITY_RESUME = 2;
|
||||
static constexpr int PRIORITY_PAUSE = 3;
|
||||
static constexpr int PRIORITY_NAV_DONE = 4;
|
||||
static constexpr int PRIORITY_ACT_DONE = 4;
|
||||
static constexpr int PRIORITY_ORDER = 5;
|
||||
|
||||
enum class MissionState
|
||||
{
|
||||
IDLE,
|
||||
QUEUED,
|
||||
RUNNING,
|
||||
PAUSED,
|
||||
WAITING_ACTION,
|
||||
RECOVERY,
|
||||
COMPLETED,
|
||||
FAILED,
|
||||
CANCELLED,
|
||||
EMERGENCY
|
||||
};
|
||||
|
||||
enum class EventType
|
||||
{
|
||||
SUBMIT_MISSIONS,
|
||||
NAV_DONE,
|
||||
NAV_FAILED,
|
||||
PAUSE,
|
||||
RESUME,
|
||||
CANCEL,
|
||||
EMERGENCY
|
||||
};
|
||||
|
||||
enum class MissionType
|
||||
{
|
||||
SIMPLE_GOAL,
|
||||
VDA5050_ORDER
|
||||
};
|
||||
|
||||
enum class ActionType
|
||||
{
|
||||
NODE_ACTION,
|
||||
EDGE_ACTION
|
||||
};
|
||||
|
||||
class Action
|
||||
{
|
||||
public:
|
||||
int sequenceId; //id này theo id của action trong node hoặc edge, để đảm bảo thứ tự thực hiện
|
||||
ActionType type;
|
||||
robot_protocol_msgs::Action action;
|
||||
};
|
||||
|
||||
class Mission
|
||||
{
|
||||
public:
|
||||
int sequenceId;
|
||||
|
||||
MissionType type;
|
||||
|
||||
int priority;
|
||||
|
||||
robot_geometry_msgs::PoseStamped start;
|
||||
|
||||
robot_geometry_msgs::PoseStamped goal;
|
||||
|
||||
std::vector<robot_protocol_msgs::Node> nodes;
|
||||
|
||||
std::vector<robot_protocol_msgs::Edge> edges;
|
||||
|
||||
std::vector<Action> actions;
|
||||
};
|
||||
|
||||
struct Event
|
||||
{
|
||||
EventType type;
|
||||
|
||||
int priority;
|
||||
|
||||
std::vector<std::shared_ptr<Mission>> missions;
|
||||
};
|
||||
|
||||
struct EventCompare
|
||||
{
|
||||
bool operator()(const Event& lhs, const Event& rhs) const
|
||||
{
|
||||
return lhs.priority > rhs.priority;
|
||||
}
|
||||
};
|
||||
|
||||
class EventBus
|
||||
{
|
||||
private:
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
std::priority_queue<Event, std::vector<Event>, EventCompare> queue_;
|
||||
std::condition_variable cv_;
|
||||
|
||||
bool stop_ = false;
|
||||
|
||||
public:
|
||||
void push(const Event& event);
|
||||
|
||||
bool pop(Event& event);
|
||||
|
||||
void stop();
|
||||
|
||||
void reset();
|
||||
|
||||
inline bool empty() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
return queue_.empty();
|
||||
}
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
while(!queue_.empty())
|
||||
{
|
||||
queue_.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class VDA5050Adapter
|
||||
{
|
||||
public:
|
||||
std::vector<std::shared_ptr<Mission>> convert(const robot_protocol_msgs::Order& order);
|
||||
};
|
||||
|
||||
class GoalAdapter
|
||||
{
|
||||
public:
|
||||
std::vector<std::shared_ptr<Mission>> convert(const robot_geometry_msgs::PoseStamped& goal);
|
||||
};
|
||||
|
||||
class MissionManager
|
||||
{
|
||||
public:
|
||||
|
||||
void submit(const std::vector<std::shared_ptr<Mission>>& missions);
|
||||
|
||||
std::shared_ptr<Mission> nextMission();
|
||||
|
||||
void onNavigationDone();
|
||||
|
||||
void onNavigationFailed();
|
||||
|
||||
void cancel();
|
||||
|
||||
void pause();
|
||||
|
||||
void resume();
|
||||
|
||||
void emergency();
|
||||
|
||||
MissionState state() const;
|
||||
|
||||
bool hasMission() const;
|
||||
|
||||
private:
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
MissionState state_ = MissionState::IDLE;
|
||||
|
||||
std::queue<std::shared_ptr<Mission>> mission_queue_;
|
||||
|
||||
std::shared_ptr<Mission> current_mission_;
|
||||
};
|
||||
|
||||
class EventProcessor
|
||||
{
|
||||
public:
|
||||
explicit EventProcessor(MissionManager& mission_manager);
|
||||
|
||||
~EventProcessor();
|
||||
|
||||
void start();
|
||||
|
||||
void stop();
|
||||
|
||||
void goalEvent(const robot_geometry_msgs::PoseStamped& goal);
|
||||
|
||||
void orderEvent(const robot_protocol_msgs::Order& order);
|
||||
|
||||
void cancelEvent();
|
||||
|
||||
void pauseEvent();
|
||||
|
||||
void resumeEvent();
|
||||
|
||||
void navDoneEvent();
|
||||
|
||||
void navFailedEvent();
|
||||
|
||||
void emergencyEvent();
|
||||
|
||||
private:
|
||||
void spin();
|
||||
|
||||
void process(const Event& event);
|
||||
|
||||
private:
|
||||
EventBus event_bus_;
|
||||
|
||||
GoalAdapter goal_adapter_;
|
||||
|
||||
VDA5050Adapter vda5050_adapter_;
|
||||
|
||||
MissionManager& mission_manager_;
|
||||
|
||||
std::thread worker_;
|
||||
|
||||
std::atomic<bool> running_{false};
|
||||
};
|
||||
|
||||
class MissionExecutor
|
||||
{
|
||||
public:
|
||||
using MissionCallback =
|
||||
std::function<void(const std::shared_ptr<Mission>&)>;
|
||||
|
||||
explicit MissionExecutor(MissionManager& manager);
|
||||
~MissionExecutor();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
void setMissionCallback(MissionCallback cb)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
mission_callback_ = std::move(cb);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Mission> mission_excute_;
|
||||
|
||||
// Mission cuối cùng đã dispatch
|
||||
std::shared_ptr<Mission> last_dispatched_mission_;
|
||||
|
||||
MissionCallback mission_callback_;
|
||||
|
||||
MissionManager& mission_manager_;
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
std::thread worker_;
|
||||
|
||||
std::atomic<bool> running_{false};
|
||||
|
||||
void spin();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // MISSION_ADAPTERS_MISSION_ADAPTERS_H
|
||||
0
include/mission_adapters/mission_excutor.h
Normal file
0
include/mission_adapters/mission_excutor.h
Normal file
0
include/mission_adapters/mission_manager.h
Normal file
0
include/mission_adapters/mission_manager.h
Normal file
0
include/mission_adapters/types.h
Normal file
0
include/mission_adapters/types.h
Normal file
50
package.xml
Normal file
50
package.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<package>
|
||||
<name>mission_adapters</name>
|
||||
<version>0.7.10</version>
|
||||
<description>
|
||||
mission_adapters is the second generation of the transform library, which lets
|
||||
the user keep track of multiple coordinate frames over time. mission_adapters
|
||||
maintains the relationship between coordinate frames in a tree
|
||||
structure buffered in time, and lets the user transform points,
|
||||
vectors, etc between any two coordinate frames at any desired
|
||||
point in time.
|
||||
</description>
|
||||
<author>Tully Foote</author>
|
||||
<author>Eitan Marder-Eppstein</author>
|
||||
<author>Wim Meeussen</author>
|
||||
<maintainer email="tfoote@osrfoundation.org">Tully Foote</maintainer>
|
||||
<license>BSD</license>
|
||||
|
||||
<url type="website">http://www.ros.org/wiki/mission_adapters</url>
|
||||
|
||||
<buildtool_depend version_gte="0.5.68">catkin</buildtool_depend>
|
||||
|
||||
<build_depend>libconsole-bridge-dev</build_depend>
|
||||
<run_depend>libconsole-bridge-dev</run_depend>
|
||||
|
||||
<build_depend>robot_costmap_2d</build_depend>
|
||||
<build_depend>robot_nav_core</build_depend>
|
||||
<build_depend>robot_nav_core2</build_depend>
|
||||
<build_depend>robot_nav_msgs</build_depend>
|
||||
<build_depend>robot_std_msgs</build_depend>
|
||||
<build_depend>robot_geometry_msgs</build_depend>
|
||||
<build_depend>robot_cpp</build_depend>
|
||||
<build_depend>tf3</build_depend>
|
||||
<build_depend>robot_tf3_geometry_msgs</build_depend>
|
||||
<build_depend>robot_visualization_msgs</build_depend>
|
||||
<build_depend>robot_nav_2d_utils</build_depend>
|
||||
<build_depend>data_convert</build_depend>
|
||||
|
||||
<run_depend>robot_costmap_2d</run_depend>
|
||||
<run_depend>robot_nav_core</run_depend>
|
||||
<run_depend>robot_nav_core2</run_depend>
|
||||
<run_depend>robot_nav_msgs</run_depend>
|
||||
<run_depend>robot_std_msgs</run_depend>
|
||||
<run_depend>robot_geometry_msgs</run_depend>
|
||||
<run_depend>robot_cpp</run_depend>
|
||||
<run_depend>tf3</run_depend>
|
||||
<run_depend>robot_tf3_geometry_msgs</run_depend>
|
||||
<run_depend>robot_visualization_msgs</run_depend>
|
||||
<run_depend>robot_nav_2d_utils</run_depend>
|
||||
<run_depend>data_convert</run_depend>
|
||||
</package>
|
||||
649
src/mission_adapters.cpp
Normal file
649
src/mission_adapters.cpp
Normal file
@@ -0,0 +1,649 @@
|
||||
#include <algorithm>
|
||||
#include <mission_adapters/mission_adapters.h>
|
||||
|
||||
namespace mission_adapters
|
||||
{
|
||||
void EventBus::push(const Event& event)
|
||||
{
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
queue_.push(event);
|
||||
|
||||
cv_.notify_one();
|
||||
}
|
||||
|
||||
bool EventBus::pop(Event& event)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
|
||||
cv_.wait(lock,
|
||||
[this]
|
||||
{
|
||||
return stop_ || !queue_.empty();
|
||||
});
|
||||
|
||||
if(stop_ || queue_.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
event = queue_.top();
|
||||
|
||||
queue_.pop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EventBus::stop()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
stop_ = true;
|
||||
}
|
||||
|
||||
cv_.notify_all();
|
||||
}
|
||||
|
||||
void EventBus::reset()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
stop_ = false;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Mission>>
|
||||
GoalAdapter::convert(const robot_geometry_msgs::PoseStamped& goal)
|
||||
{
|
||||
std::vector<std::shared_ptr<Mission>> missions;
|
||||
auto mission = std::make_shared<Mission>();
|
||||
|
||||
mission->type = MissionType::SIMPLE_GOAL;
|
||||
|
||||
mission->priority = 0;
|
||||
|
||||
/////////////////////////// Để tạm thời ///////////////////////////
|
||||
mission->goal = goal;
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
missions.push_back(mission);
|
||||
|
||||
return missions;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Mission>>
|
||||
VDA5050Adapter::convert(const robot_protocol_msgs::Order& order)
|
||||
{
|
||||
std::vector<std::shared_ptr<Mission>> missions;
|
||||
std::vector<size_t> action_node_indices;
|
||||
|
||||
// Tìm tất cả node có action
|
||||
for(size_t i = 0; i < order.nodes.size(); ++i)
|
||||
{
|
||||
if(!order.nodes[i].actions.empty())
|
||||
{
|
||||
action_node_indices.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
// if(action_node_indices.empty())
|
||||
// {
|
||||
// return missions;
|
||||
// }
|
||||
|
||||
size_t start_node_idx = 0;
|
||||
|
||||
for(size_t i = 0; i < action_node_indices.size(); ++i)
|
||||
{
|
||||
size_t end_node_idx = action_node_indices[i];
|
||||
|
||||
auto mission = std::make_shared<Mission>();
|
||||
|
||||
mission->type = MissionType::VDA5050_ORDER;
|
||||
mission->priority = 0;
|
||||
|
||||
// Nodes [start_node_idx, end_node_idx]
|
||||
mission->nodes.assign(
|
||||
order.nodes.begin() + start_node_idx,
|
||||
order.nodes.begin() + end_node_idx + 1);
|
||||
|
||||
// Edges [start_node_idx, end_node_idx - 1]
|
||||
if(end_node_idx > start_node_idx)
|
||||
{
|
||||
mission->edges.assign(
|
||||
order.edges.begin() + start_node_idx,
|
||||
order.edges.begin() + end_node_idx);
|
||||
}
|
||||
|
||||
// /////////////////////////// Để tạm thời ///////////////////////////
|
||||
// robot_geometry_msgs::PoseStamped start, goal;
|
||||
// start.header = ;
|
||||
// start.pose = ;
|
||||
// goal.header = ;
|
||||
// goal.pose = ;
|
||||
// ////////////////////////////////////////////////////////////////////
|
||||
|
||||
// mission->start = start;
|
||||
// mission->goal = goal;
|
||||
|
||||
missions.push_back(mission);
|
||||
|
||||
// Mission tiếp theo bắt đầu sau node action hiện tại
|
||||
start_node_idx = end_node_idx;
|
||||
}
|
||||
|
||||
// Phần còn lại sau action cuối cùng
|
||||
if(start_node_idx < order.nodes.size() - 1)
|
||||
{
|
||||
auto mission = std::make_shared<Mission>();
|
||||
|
||||
mission->type = MissionType::VDA5050_ORDER;
|
||||
mission->priority = 0;
|
||||
|
||||
mission->nodes.assign(
|
||||
order.nodes.begin() + start_node_idx,
|
||||
order.nodes.end());
|
||||
|
||||
if(start_node_idx < order.edges.size())
|
||||
{
|
||||
mission->edges.assign(
|
||||
order.edges.begin() + start_node_idx,
|
||||
order.edges.end());
|
||||
}
|
||||
|
||||
// /////////////////////////// Để tạm thời ///////////////////////////
|
||||
// robot_geometry_msgs::PoseStamped start, goal;
|
||||
// start.header.frame_id = "map"
|
||||
// start.pose = ;
|
||||
// goal.header = ;
|
||||
// goal.pose = ;
|
||||
// ////////////////////////////////////////////////////////////////////
|
||||
|
||||
// mission->start = start;
|
||||
// mission->goal = goal;
|
||||
|
||||
missions.push_back(mission);
|
||||
}
|
||||
|
||||
// Collect actions
|
||||
for(auto& mission : missions)
|
||||
{
|
||||
for(const auto& edge : mission->edges)
|
||||
{
|
||||
for(const auto& action : edge.actions)
|
||||
{
|
||||
Action mission_action;
|
||||
mission_action.sequenceId = edge.sequenceId; // Giữ nguyên sequenceId từ action gốc
|
||||
mission_action.type = ActionType::EDGE_ACTION;
|
||||
mission_action.action = action;
|
||||
mission->actions.push_back(std::move(mission_action));
|
||||
}
|
||||
}
|
||||
|
||||
const auto& node = mission->nodes.back();
|
||||
if(node.actions.empty()) continue;
|
||||
|
||||
for(const auto& action : node.actions)
|
||||
{
|
||||
Action mission_action;
|
||||
mission_action.sequenceId = node.sequenceId; // Giữ nguyên sequenceId từ action gốc
|
||||
mission_action.type = ActionType::NODE_ACTION;
|
||||
mission_action.action = action;
|
||||
mission->actions.push_back(std::move(mission_action));
|
||||
}
|
||||
// xắp xếp actions theo sequenceId để đảm bảo thứ tự thực hiện
|
||||
std::sort(mission->actions.begin(), mission->actions.end(),
|
||||
[](const Action& a, const Action& b) {
|
||||
return a.sequenceId < b.sequenceId;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return missions;
|
||||
}
|
||||
|
||||
void MissionManager::submit(const std::vector<std::shared_ptr<Mission>>& missions)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for(auto& mission : missions)
|
||||
{
|
||||
mission_queue_.push(mission);
|
||||
}
|
||||
|
||||
if(!missions.empty())
|
||||
{
|
||||
if(state_ == MissionState::IDLE ||
|
||||
state_ == MissionState::FAILED ||
|
||||
state_ == MissionState::CANCELLED ||
|
||||
state_ == MissionState::EMERGENCY)
|
||||
{
|
||||
state_ = MissionState::QUEUED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Mission> MissionManager::nextMission()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if(state_ == MissionState::PAUSED ||
|
||||
state_ == MissionState::FAILED ||
|
||||
state_ == MissionState::CANCELLED ||
|
||||
state_ == MissionState::EMERGENCY)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(current_mission_)
|
||||
{
|
||||
return current_mission_;
|
||||
}
|
||||
|
||||
if(mission_queue_.empty())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(current_mission_ == nullptr)
|
||||
{
|
||||
current_mission_ = mission_queue_.front();
|
||||
|
||||
mission_queue_.pop();
|
||||
|
||||
state_ = MissionState::RUNNING;
|
||||
}
|
||||
|
||||
return current_mission_;
|
||||
}
|
||||
|
||||
void MissionManager::onNavigationDone()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
current_mission_.reset();
|
||||
|
||||
if(mission_queue_.empty())
|
||||
{
|
||||
state_ = MissionState::IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = MissionState::QUEUED;
|
||||
}
|
||||
}
|
||||
|
||||
void MissionManager::onNavigationFailed()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
state_ = MissionState::FAILED;
|
||||
|
||||
current_mission_.reset();
|
||||
|
||||
while(!mission_queue_.empty())
|
||||
{
|
||||
mission_queue_.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void MissionManager::cancel()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
current_mission_.reset();
|
||||
|
||||
while(!mission_queue_.empty())
|
||||
{
|
||||
mission_queue_.pop();
|
||||
}
|
||||
|
||||
state_ = MissionState::CANCELLED;
|
||||
}
|
||||
|
||||
void MissionManager::emergency()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
current_mission_.reset();
|
||||
|
||||
while(!mission_queue_.empty())
|
||||
{
|
||||
mission_queue_.pop();
|
||||
}
|
||||
|
||||
state_ = MissionState::EMERGENCY;
|
||||
}
|
||||
|
||||
void MissionManager::pause()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if(state_ == MissionState::RUNNING)
|
||||
{
|
||||
state_ = MissionState::PAUSED;
|
||||
}
|
||||
}
|
||||
|
||||
void MissionManager::resume()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if(state_ != MissionState::PAUSED)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(current_mission_)
|
||||
{
|
||||
state_ = MissionState::RUNNING;
|
||||
}
|
||||
else if(!mission_queue_.empty())//Nếu đang thực hiện mission cuối cần xem lại logic
|
||||
{
|
||||
state_ = MissionState::QUEUED;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = MissionState::IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
MissionState MissionManager::state() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
return state_;
|
||||
}
|
||||
|
||||
bool MissionManager::hasMission() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
return !mission_queue_.empty();
|
||||
}
|
||||
|
||||
EventProcessor::EventProcessor(MissionManager& mission_manager)
|
||||
: mission_manager_(mission_manager)
|
||||
{}
|
||||
|
||||
EventProcessor::~EventProcessor()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void EventProcessor::start()
|
||||
{
|
||||
if(running_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
running_ = true;
|
||||
|
||||
event_bus_.reset();
|
||||
|
||||
worker_ =
|
||||
std::thread(
|
||||
&EventProcessor::spin,
|
||||
this);
|
||||
}
|
||||
|
||||
void EventProcessor::stop()
|
||||
{
|
||||
running_ = false;
|
||||
|
||||
event_bus_.stop();
|
||||
|
||||
if(worker_.joinable())
|
||||
{
|
||||
worker_.join();
|
||||
}
|
||||
}
|
||||
|
||||
void EventProcessor::spin()
|
||||
{
|
||||
Event event;
|
||||
|
||||
// robot::Rate rate(50);
|
||||
|
||||
while(running_)
|
||||
{
|
||||
if(event_bus_.pop(event))
|
||||
{
|
||||
process(event);
|
||||
}
|
||||
|
||||
// rate.sleep();
|
||||
}
|
||||
}
|
||||
|
||||
void EventProcessor::process(const Event& event)
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
case EventType::SUBMIT_MISSIONS:
|
||||
{
|
||||
mission_manager_.submit(
|
||||
event.missions);
|
||||
break;
|
||||
}
|
||||
|
||||
case EventType::NAV_DONE:
|
||||
{
|
||||
mission_manager_.onNavigationDone();
|
||||
break;
|
||||
}
|
||||
|
||||
case EventType::NAV_FAILED:
|
||||
{
|
||||
mission_manager_.onNavigationFailed();
|
||||
break;
|
||||
}
|
||||
|
||||
case EventType::PAUSE:
|
||||
{
|
||||
mission_manager_.pause();
|
||||
break;
|
||||
}
|
||||
|
||||
case EventType::RESUME:
|
||||
{
|
||||
mission_manager_.resume();
|
||||
break;
|
||||
}
|
||||
|
||||
case EventType::CANCEL:
|
||||
{
|
||||
mission_manager_.cancel();
|
||||
break;
|
||||
}
|
||||
|
||||
case EventType::EMERGENCY:
|
||||
{
|
||||
mission_manager_.emergency();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
robot::log_error("Unknown event type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EventProcessor::orderEvent(const robot_protocol_msgs::Order& order)
|
||||
{
|
||||
auto missions = vda5050_adapter_.convert(order);
|
||||
|
||||
Event event;
|
||||
|
||||
event.type = EventType::SUBMIT_MISSIONS;
|
||||
|
||||
event.priority = PRIORITY_ORDER;
|
||||
|
||||
event.missions = std::move(missions);
|
||||
|
||||
event_bus_.push(event);
|
||||
}
|
||||
|
||||
void EventProcessor::goalEvent(const robot_geometry_msgs::PoseStamped& goal)
|
||||
{
|
||||
auto missions = goal_adapter_.convert(goal);
|
||||
|
||||
Event event;
|
||||
|
||||
event.type = EventType::SUBMIT_MISSIONS;
|
||||
|
||||
event.priority = PRIORITY_ORDER;
|
||||
|
||||
event.missions = std::move(missions);
|
||||
|
||||
event_bus_.push(event);
|
||||
}
|
||||
|
||||
void EventProcessor::pauseEvent()
|
||||
{
|
||||
Event event;
|
||||
|
||||
event.type = EventType::PAUSE;
|
||||
|
||||
event.priority = PRIORITY_PAUSE;
|
||||
|
||||
event_bus_.push(event);
|
||||
}
|
||||
|
||||
void EventProcessor::resumeEvent()
|
||||
{
|
||||
Event event;
|
||||
|
||||
event.type = EventType::RESUME;
|
||||
|
||||
event.priority = PRIORITY_RESUME;
|
||||
|
||||
event_bus_.push(event);
|
||||
}
|
||||
|
||||
void EventProcessor::cancelEvent()
|
||||
{
|
||||
Event event;
|
||||
|
||||
event.type =
|
||||
EventType::CANCEL;
|
||||
|
||||
event.priority =
|
||||
PRIORITY_CANCEL;
|
||||
|
||||
event_bus_.push(event);
|
||||
}
|
||||
|
||||
void EventProcessor::navDoneEvent()
|
||||
{
|
||||
Event event;
|
||||
|
||||
event.type =
|
||||
EventType::NAV_DONE;
|
||||
|
||||
event.priority =
|
||||
PRIORITY_NAV_DONE;
|
||||
|
||||
event_bus_.push(event);
|
||||
}
|
||||
|
||||
void EventProcessor::navFailedEvent()
|
||||
{
|
||||
Event event;
|
||||
|
||||
event.type = EventType::NAV_FAILED;
|
||||
|
||||
event.priority = PRIORITY_NAV_DONE;
|
||||
|
||||
event_bus_.push(event);
|
||||
}
|
||||
|
||||
void EventProcessor::emergencyEvent()
|
||||
{
|
||||
Event event;
|
||||
|
||||
event.type = EventType::EMERGENCY;
|
||||
|
||||
event.priority = PRIORITY_EMERGENCY;
|
||||
|
||||
event_bus_.push(event);
|
||||
}
|
||||
|
||||
MissionExecutor::MissionExecutor(MissionManager& manager)
|
||||
: mission_manager_(manager)
|
||||
{}
|
||||
|
||||
MissionExecutor::~MissionExecutor()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void MissionExecutor::start()
|
||||
{
|
||||
if(running_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
running_ = true;
|
||||
|
||||
worker_ =
|
||||
std::thread(
|
||||
&MissionExecutor::spin,
|
||||
this);
|
||||
}
|
||||
|
||||
void MissionExecutor::stop()
|
||||
{
|
||||
running_ = false;
|
||||
|
||||
if(worker_.joinable())
|
||||
{
|
||||
worker_.join();
|
||||
}
|
||||
}
|
||||
|
||||
void MissionExecutor::spin()
|
||||
{
|
||||
robot::Rate rate(20);
|
||||
|
||||
while(running_)
|
||||
{
|
||||
auto mission =
|
||||
mission_manager_.nextMission();
|
||||
|
||||
if(mission)
|
||||
{
|
||||
bool is_new_mission = false;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if(mission != last_dispatched_mission_)
|
||||
{
|
||||
last_dispatched_mission_ = mission;
|
||||
mission_excute_ = mission;
|
||||
is_new_mission = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(is_new_mission)
|
||||
{
|
||||
MissionCallback callback;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
callback = mission_callback_;
|
||||
}
|
||||
|
||||
if(callback)
|
||||
{
|
||||
callback(mission);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!mission)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
last_dispatched_mission_.reset();
|
||||
}
|
||||
|
||||
rate.sleep();
|
||||
}
|
||||
}
|
||||
}
|
||||
87
src/robot_control_test.cpp
Normal file
87
src/robot_control_test.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
#include <mission_adapters/mission_adapters.h>
|
||||
#include <move_base_core/navigation.h>
|
||||
|
||||
using namespace mission_adapters;
|
||||
class RobotControlTest
|
||||
{
|
||||
// Giả sử chúng ta có một implementation của BaseNavigation, ví dụ MoveBase và đã được load vào move_base_ptr_
|
||||
robot::move_base_core::BaseNavigation::Ptr move_base_ptr_;
|
||||
|
||||
mission_adapters::MissionManager mission_manager_;
|
||||
mission_adapters::EventProcessor event_processor_{mission_manager_};
|
||||
mission_adapters::MissionExecutor mission_executor_{mission_manager_};
|
||||
std::shared_ptr<Mission> mission_prev_ = nullptr;
|
||||
robot::move_base_core::State prev_state_ = PENDING;
|
||||
|
||||
public:
|
||||
RobotControlTest() = default;
|
||||
~RobotControlTest();
|
||||
void run();
|
||||
};
|
||||
|
||||
RobotControlTest::~RobotControlTest()
|
||||
{
|
||||
event_processor_.stop();
|
||||
|
||||
mission_executor_.stop();
|
||||
}
|
||||
|
||||
void RobotControlTest::run()
|
||||
{
|
||||
robot::Rate rate(50);
|
||||
event_processor_.start();
|
||||
mission_executor_.start();
|
||||
while(true)
|
||||
{
|
||||
// chạy trong một luồng riêng
|
||||
auto state = move_base_ptr_->getFeedback()->navigation_state;
|
||||
|
||||
bool action_done = true;
|
||||
//kiểm tra acction done chưa
|
||||
|
||||
//thêm kiểm tra navDoneEvent state == robot::move_base_core::State::SUCCEEDED và action_done == true
|
||||
if(state != prev_state_)
|
||||
{
|
||||
if(state ==
|
||||
robot::move_base_core::State::SUCCEEDED)
|
||||
{
|
||||
event_processor_.navDoneEvent();
|
||||
}
|
||||
|
||||
if(state ==
|
||||
robot::move_base_core::State::ABORTED)
|
||||
{
|
||||
event_processor_.navFailedEvent();
|
||||
}
|
||||
|
||||
prev_state_ = state;
|
||||
}
|
||||
//nhận oder trong một luồng riêng
|
||||
//ví dụ: robot_protocol_msgs::Order order;
|
||||
if(/* có order mới */)
|
||||
{
|
||||
robot_protocol_msgs::Order order;
|
||||
// ... nhận order từ đâu đó, ví dụ qua ROS topic hoặc service
|
||||
event_processor_.orderEvent(order);
|
||||
}
|
||||
|
||||
|
||||
mission_executor_.setMissionCallback(
|
||||
[&](const std::shared_ptr<Mission>& mission)
|
||||
{
|
||||
// ví dụ hàm executeMission đã có, nhận mission và đẩy dữ liệu goal sang move_base_ptr_
|
||||
executeMission(*mission);
|
||||
});
|
||||
|
||||
|
||||
rate.sleep();
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
RobotControlTest test;
|
||||
test.run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user