Develop strat 7 (#32)

Co-authored-by: modelec <modelec-isen@gmail.com>
This commit is contained in:
Ackimixs
2026-03-11 16:38:23 +01:00
committed by GitHub
parent a8be82c450
commit d9b1080516
80 changed files with 1566 additions and 1155 deletions

8
Desktop/cam.desktop Normal file
View File

@@ -0,0 +1,8 @@
[Desktop Entry]
Type=Application
Name=Cam
Comment=Cam
Exec=/home/modelec/Modelec-ROS2/start_cam_ros2.sh
Icon=utilities-terminal
Terminal=true
Categories=Utility;

View File

@@ -1,4 +1,4 @@
#!/bin/bash
MAKEFLAGS="-j1" colcon build --symlink-install --executor sequential # --cmake-args -DCMAKE_BUILD_TYPE=Debug
MAKEFLAGS="-j1" colcon build --symlink-install --executor sequential --cmake-args -DBUILD_FOR_RPI=ON --no-warn-unused-cli
source install/setup.bash

View File

@@ -21,7 +21,7 @@ sudo apt upgrade -y
sudo apt install ros-dev-tools ros-jazzy-desktop -y
sudo apt-get install qt6-base-dev qt6-svg-dev libxml2-dev socat -y
sudo apt-get install qt6-base-dev qt6-svg-dev qt6-multimedia-dev libxml2-dev socat -y
git submodule init
git submodule update
@@ -45,4 +45,4 @@ gio set ~/Desktop/no_lidar.joy.ros2_launch_marcel.desktop "metadata::trusted" tr
gio set ~/Desktop/no_lidar.ros2_launch_marcel.desktop "metadata::trusted" true
gio set ~/Desktop/joy.ros2_launch_marcel.desktop "metadata::trusted" true
gio set ~/Desktop/ros2_launch_marcel.desktop "metadata::trusted" true
gio set ~/Desktop/no-gui.ros2_launch_marcel.desktop "metadata::trusted" true
gio set ~/Desktop/no-gui.ros2_launch_marcel.desktop "metadata::trusted" true

View File

@@ -35,16 +35,19 @@ def read_serial(ser, log_lines, log_lock):
log_lines.append(f"[Serial Error] {e}")
time.sleep(0.05)
def curses_main(stdscr, port, baudrate):
def curses_main(stdscr, port, baudrate, filter_start=None):
global stop_thread
curses.curs_set(1)
stdscr.nodelay(True)
stdscr.timeout(100)
if filter_start is None:
filter_start = ["SET;POS"]
log_lines = []
log_lock = threading.Lock()
cmd_history = []
history_index = -1 # For navigating command history
history_index = -1
with serial.Serial(port, baudrate, timeout=1) as ser:
time.sleep(2)
@@ -57,24 +60,36 @@ def curses_main(stdscr, port, baudrate):
stdscr.clear()
h, w = stdscr.getmaxyx()
# Split screen horizontally (70% logs, 30% command history)
split_x = int(w * 0.7)
# --- Split into 3 vertical columns ---
col_width = w // 3
col1_x = 0
col2_x = col_width
col3_x = 2 * col_width
log_height = h - 2
# --- Left panel: logs ---
# --- Separate logs into filtered and others ---
with log_lock:
visible_logs = log_lines[-log_height:]
for i, line in enumerate(visible_logs):
stdscr.addnstr(i, 0, line, split_x - 1)
filtered_logs = [line for line in log_lines if any(line.startswith(p) for p in filter_start)]
other_logs = [line for line in log_lines if not any(line.startswith(p) for p in filter_start)]
visible_filtered = filtered_logs[-log_height:]
visible_other = other_logs[-log_height:]
visible_history = cmd_history[-log_height:]
# --- Right panel: command history ---
stdscr.vline(0, split_x, "|", log_height)
history_start_x = split_x + 2
stdscr.addstr(0, history_start_x, "Command History:")
# --- Column 1: Filtered serial output ---
stdscr.addstr(0, col1_x, f"Filtered ({', '.join(filter_start)}):")
for i, line in enumerate(visible_filtered):
stdscr.addnstr(i + 1, col1_x, line, col_width - 1)
visible_history = cmd_history[-(log_height - 2):]
# --- Column 2: Other serial output ---
stdscr.addstr(0, col2_x, "Other Logs:")
for i, line in enumerate(visible_other):
stdscr.addnstr(i + 1, col2_x, line, col_width - 1)
# --- Column 3: Command history ---
stdscr.addstr(0, col3_x, "Command History:")
for i, cmd in enumerate(visible_history):
stdscr.addnstr(i + 1, history_start_x, cmd, w - history_start_x - 1)
stdscr.addnstr(i + 1, col3_x, cmd, col_width - 1)
# --- Input line ---
stdscr.addstr(log_height, 0, "-" * (w - 1))

View File

@@ -5,6 +5,8 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
option(BUILD_FOR_RPI "Build with Raspberry Pi libcamera support" OFF)
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
@@ -16,6 +18,18 @@ find_package(ament_index_cpp REQUIRED)
find_package(fmt)
find_package(OpenCV REQUIRED)
find_package(PkgConfig REQUIRED)
if(BUILD_FOR_RPI)
message(STATUS "Configuring for Raspberry Pi Hardware...")
add_definitions(-DRPI_BUILD)
pkg_check_modules(LIBCAMERA REQUIRED libcamera libcamera-base)
include_directories(${LIBCAMERA_INCLUDE_DIRS})
link_directories(${LIBCAMERA_LIBRARY_DIRS} /usr/local/lib/aarch64-linux-gnu)
add_definitions(${LIBCAMERA_CFLAGS_OTHER})
else()
message(STATUS "Configuring for PC (standard OpenCV)...")
endif()
find_package(modelec_interfaces REQUIRED)
find_package(modelec_utils REQUIRED)
@@ -57,6 +71,16 @@ target_link_libraries(color_detector
modelec_utils::config
)
if(BUILD_FOR_RPI)
target_link_libraries(color_detector
-Wl,--no-as-needed
"/usr/local/lib/aarch64-linux-gnu/libcamera.so"
"/usr/local/lib/aarch64-linux-gnu/libcamera-base.so"
-Wl,--as-needed
cam2opencv
)
endif()
target_include_directories(color_detector PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>

View File

@@ -5,9 +5,18 @@
#include <std_srvs/srv/trigger.hpp>
#include <std_msgs/msg/empty.hpp>
#include <std_msgs/msg/string.hpp>
#include <modelec_utils/config.hpp>
#include <mutex>
#include <memory>
#ifdef RPI_BUILD
#include <libcam2opencv.h>
#endif
namespace Modelec
{
struct CamCallback;
struct ColorSetting
{
std::string name;
@@ -34,6 +43,17 @@ namespace Modelec
std::string generateImagePath() const;
#ifdef RPI_BUILD
Libcam2OpenCV camera_;
std::unique_ptr<CamCallback> my_callback_;
#else
cv::VideoCapture pc_cap_;
#endif
cv::Mat latest_frame_;
std::mutex frame_mutex_;
rclcpp::Service<std_srvs::srv::Trigger>::SharedPtr service_;
rclcpp::Subscription<std_msgs::msg::Empty>::SharedPtr ask_sub_;
@@ -49,4 +69,45 @@ namespace Modelec
std::vector<ColorSetting> color_configs_;
};
template<>
inline std::vector<ColorSetting>
Config::get<std::vector<ColorSetting>>(
const std::string& prefix,
const std::vector<ColorSetting>& default_value, bool)
{
auto result = Config::getArray<ColorSetting>(
prefix,
[](const std::string& base)
{
return ColorSetting{
Config::get<std::string>(base + "@name"),
Config::get<double>(base + "@hue_min"),
Config::get<double>(base + "@hue_max")
};
});
return result.empty() ? default_value : result;
}
template<>
inline std::vector<cv::Rect>
Config::get<std::vector<cv::Rect>>(
const std::string& prefix,
const std::vector<cv::Rect>& default_value, bool)
{
auto result = Config::getArray<cv::Rect>(
prefix,
[](const std::string& base)
{
return cv::Rect(
Config::get<int>(base + "@x", 0),
Config::get<int>(base + "@y", 0),
Config::get<int>(base + "@w", 100),
Config::get<int>(base + "@h", 100)
);
});
return result.empty() ? default_value : result;
}
}

View File

@@ -12,6 +12,8 @@
#include <modelec_interfaces/msg/action_servo_timed_array.hpp>
#include <modelec_interfaces/msg/action_servo_timed.hpp>
#include "modelec_utils/config.hpp"
#define TIMER_SERVO_TIMED_MS 10 // 100 Hz
namespace Modelec
@@ -37,10 +39,10 @@ namespace Modelec
std::map<int, int> asc_value_mapper_;
int asc_state_ = 0;
std::map<int, double> servo_value_;
std::map<int, bool> relay_value_;
std::unordered_map<int, double> servo_value_;
std::unordered_map<int, bool> relay_value_;
std::map<int, ServoTimedSet> servo_timed_buffer_;
std::vector<ServoTimedSet> servo_timed_buffer_;
rclcpp::TimerBase::SharedPtr servo_timed_timer_;
@@ -82,10 +84,29 @@ namespace Modelec
void SendToPCB(const std::string& order, const std::string& elem,
const std::vector<std::string>& data = {});
// TODO redo thos func to accept arrays, poc without them atm
void GetData(const std::string& elem, const std::vector<std::string>& data = {});
void SendOrder(const std::string& elem, const std::vector<std::string>& data = {});
void SendMove(const std::string& elem, const std::vector<std::string>& data = {});
void RespondEvent(const std::string& elem, const std::vector<std::string>& data = {});
};
template<>
inline std::unordered_map<int, double>
Config::get<std::unordered_map<int, double>>(
const std::string& prefix,
const std::unordered_map<int, double>& default_value, bool)
{
auto result = Config::getMap<int, double>(
prefix,
[](const std::string& base)
{
return std::make_pair(
Config::get<int>(base + "@id"),
Config::get<double>(base + "@angle")
);
});
return result.empty() ? default_value : result;
}
}

View File

@@ -19,6 +19,8 @@
#include <sensor_msgs/msg/joy.hpp>
#include "modelec_utils/config.hpp"
namespace Modelec
{
class PCBOdoInterface : public rclcpp::Node, public SerialListener
@@ -37,9 +39,11 @@ namespace Modelec
struct PIDData
{
float p;
float i;
float d;
float p = 0.0f;
float i = 0.0f;
float d = 0.0f;
std::optional<float> min;
std::optional<float> max;
};
private:
@@ -95,7 +99,26 @@ namespace Modelec
void GetPID();
void SetPID(const modelec_interfaces::msg::OdometryPid::SharedPtr msg);
void SetPID(std::string name, float p, float i, float d, std::optional<float> min = std::nullopt, std::optional<float> max = std::nullopt);
void SetPID(std::string name, const PIDData& pid_data);
void SetMotor(int left, int right);
};
template<>
inline PCBOdoInterface::PIDData
Config::get<PCBOdoInterface::PIDData>(
const std::string& prefix,
const PCBOdoInterface::PIDData& default_value, bool)
{
PCBOdoInterface::PIDData result;
result.p = Config::get<float>(prefix + "@p", default_value.p);
result.i = Config::get<float>(prefix + "@i", default_value.i);
result.d = Config::get<float>(prefix + "@d", default_value.d);
if (Config::has(prefix + "@min"))
result.min = Config::get<float>(prefix + "@min");
if (Config::has(prefix + "@max"))
result.max = Config::get<float>(prefix + "@max");
return result;
}
} // namespace Modelec

View File

@@ -1,48 +1,42 @@
#include <modelec_com/color_detector.hpp>
#include <ament_index_cpp/get_package_share_directory.hpp>
#include <modelec_utils/utils.hpp>
#include <mutex>
#ifdef RPI_BUILD
#include <libcam2opencv.h>
#endif
namespace Modelec
{
#ifdef RPI_BUILD
struct CamCallback : Libcam2OpenCV::Callback {
cv::Mat* target_frame;
std::mutex* frame_mutex;
CamCallback(cv::Mat& frame, std::mutex& mtx) : target_frame(&frame), frame_mutex(&mtx) {}
void hasFrame(const cv::Mat &frame, const libcamera::ControlList &) override {
std::lock_guard<std::mutex> lock(*frame_mutex);
frame.copyTo(*target_frame);
}
};
#endif
ColorDetector::ColorDetector()
: Node("color_detector")
: Node("color_detector")
{
this->declare_parameter("enabled", true);
this->declare_parameter("link", "");
this->declare_parameter("headless", true);
this->declare_parameter("save_to_file.enabled", true);
this->declare_parameter("save_to_file.path", "./");
this->declare_parameter("rois", rclcpp::PARAMETER_INTEGER_ARRAY);
this->declare_parameter("colors.blue", rclcpp::PARAMETER_INTEGER_ARRAY);
this->declare_parameter("colors.yellow", rclcpp::PARAMETER_INTEGER_ARRAY);
std::string config_path = ament_index_cpp::get_package_share_directory("modelec_strat") + "/data/config.xml";
if (!Config::load(config_path))
{
RCLCPP_ERROR(get_logger(), "Failed to load config file: %s", config_path.c_str());
}
enable_ = this->get_parameter("enabled").as_bool();
link_ = this->get_parameter("link").as_string();
headless_ = this->get_parameter("headless").as_bool();
save_to_file_ = this->get_parameter("save_to_file.enabled").as_bool();
save_directory_ = this->get_parameter("save_to_file.path").as_string();
enable_ = Config::get<bool>("config.cam.enabled", false);
link_ = Config::get<std::string>("config.cam.link", "/dev/video0");
headless_ = Config::get<bool>("config.cam.headless", true);
save_to_file_ = Config::get<bool>("config.cam.save_to_file.enabled", false);
save_directory_ = Config::get<std::string>("config.cam.save_to_file.directory", "./");
auto rois_param = this->get_parameter("rois").as_integer_array();
for (size_t i = 0; i + 3 < rois_param.size(); i += 4)
{
rois_.emplace_back(rois_param[i], rois_param[i + 1], rois_param[i + 2], rois_param[i + 3]);
}
auto blue_param = this->get_parameter("colors.blue").as_integer_array();
if (blue_param.size() >= 2)
{
color_configs_.push_back({
"blue", static_cast<double>(blue_param[0]), static_cast<double>(blue_param[1])
});
}
auto yellow_param = this->get_parameter("colors.yellow").as_integer_array();
if (yellow_param.size() >= 2)
{
color_configs_.push_back({
"yellow", static_cast<double>(yellow_param[0]), static_cast<double>(yellow_param[1])
});
}
rois_ = Config::get<std::vector<cv::Rect>>("config.cam.rois.roi", {});
color_configs_ = Config::get<std::vector<ColorSetting>>("config.cam.colors.color", {});
if (!enable_)
{
@@ -51,6 +45,22 @@ namespace Modelec
return;
}
#ifdef RPI_BUILD
RCLCPP_INFO(get_logger(), "Starting RPi Libcamera (Full FOV mode)...");
Libcam2OpenCVSettings settings;
settings.width = 2304;
settings.height = 1296;
settings.framerate = 30;
settings.cameraIndex = 0;
my_callback_ = std::make_unique<CamCallback>(latest_frame_, frame_mutex_);
camera_.registerCallback(my_callback_.get());
camera_.start(settings);
#else
RCLCPP_INFO(get_logger(), "Starting PC Webcam (OpenCV)...");
pc_cap_.open(link_);
#endif
service_ = create_service<std_srvs::srv::Trigger>(
"action/detect_color",
std::bind(&ColorDetector::onRequest, this,
@@ -85,6 +95,8 @@ namespace Modelec
if (!headless_)
{
cv::namedWindow("color_detector", cv::WINDOW_NORMAL);
cv::setWindowProperty("color_detector", cv::WND_PROP_AUTOSIZE, cv::WINDOW_GUI_EXPANDED);
}
RCLCPP_INFO(get_logger(), "Color detector service ready");
@@ -92,6 +104,12 @@ namespace Modelec
ColorDetector::~ColorDetector()
{
#ifdef RPI_BUILD
camera_.stop();
#else
if(pc_cap_.isOpened()) pc_cap_.release();
#endif
if (!headless_)
{
cv::destroyAllWindows();
@@ -100,21 +118,21 @@ namespace Modelec
bool ColorDetector::processSnapshot(std::vector<std::string>& colors, std::string& error)
{
cv::VideoCapture cap(link_);
if (!cap.isOpened())
{
RCLCPP_ERROR(get_logger(), "Failed to open camera at %s", link_.c_str());
error = "Failed to open camera";
return false;
}
cap.set(cv::CAP_PROP_BUFFERSIZE, 1);
cv::Mat frame;
for (int i = 0; i < 3; ++i)
cap >> frame;
#ifdef RPI_BUILD
{
std::lock_guard<std::mutex> lock(frame_mutex_);
if (latest_frame_.empty()) {
RCLCPP_WARN(get_logger(), "No frame received from libcamera yet");
error = "Empty frame";
return false;
}
latest_frame_.copyTo(frame);
}
#else
if(!pc_cap_.isOpened()) { error = "PC Cam not open"; return false; }
pc_cap_ >> frame;
#endif
if (frame.empty())
{
@@ -139,11 +157,9 @@ namespace Modelec
{
std::string path = save_directory_ + generateImagePath();
cv::imwrite(path, frame);
RCLCPP_DEBUG(get_logger(), "Saved snapshot to %s", path.c_str());
RCLCPP_INFO(get_logger(), "Saved snapshot to %s", path.c_str());
}
cap.release();
return true;
}

View File

@@ -1,19 +1,31 @@
#include <modelec_com/pcb_action_interface.hpp>
#include <modelec_utils/utils.hpp>
#include <fmt/core.h>
#include <ament_index_cpp/get_package_share_directory.hpp>
#include <modelec_utils/config.hpp>
#include <algorithm>
namespace Modelec
{
PCBActionInterface::PCBActionInterface() : Node("pcb_action_interface")
{
// Service to create a new serial listener
declare_parameter<std::string>("serial_port", "/dev/USB_ACTION");
declare_parameter<int>("baudrate", 115200);
std::string config_path = ament_index_cpp::get_package_share_directory("modelec_strat") + "/data/config.xml";
if (!Config::load(config_path))
{
RCLCPP_ERROR(get_logger(), "Failed to load config file: %s", config_path.c_str());
}
auto serial_port = get_parameter("serial_port").as_string();
auto baudrate = get_parameter("baudrate").as_int();
std::string action_path = ament_index_cpp::get_package_share_directory("modelec_strat") + "/data/action.xml";
if (!Config::load(action_path))
{
RCLCPP_ERROR(get_logger(), "Failed to load config file: %s", action_path.c_str());
}
RCLCPP_INFO(this->get_logger(), "Starting PCB Odometry Interface on port %s with baudrate %ld", serial_port.c_str(), baudrate);
auto serial_port = Config::get<std::string>("config.usb.action.port", "/dev/ttyUSB0");
auto baudrate = Config::get<int>("config.usb.action.baudrate", 115200);
auto timed_servo_timer_ms = Config::get<int>("config.timer.action.timed_servo.ms", TIMER_SERVO_TIMED_MS);
RCLCPP_INFO(this->get_logger(), "Starting PCB Odometry Interface on port %s with baudrate %d", serial_port.c_str(), baudrate);
asc_get_sub_ = this->create_subscription<modelec_interfaces::msg::ActionAscPos>(
"action/get/asc", 10,
@@ -157,14 +169,14 @@ namespace Modelec
{
ServoTimedSet servo_timed_set;
servo_timed_set.servo_timed = item;
servo_timed_set.start_time = now;
servo_timed_set.end_time = now + rclcpp::Duration::from_seconds(item.duration_s);
servo_timed_set.start_time = now + rclcpp::Duration::from_seconds(item.delay_s);
servo_timed_set.end_time = now + rclcpp::Duration::from_seconds(item.duration_s + item.delay_s);
servo_timed_set.active = true;
RCLCPP_DEBUG(this->get_logger(), "Scheduled timed move for Servo ID %d from %.3f to %.3f over %.3f seconds",
item.id, item.start_angle, item.end_angle, item.duration_s);
servo_timed_buffer_[item.id] = servo_timed_set;
servo_timed_buffer_.push_back(servo_timed_set);
}
});
@@ -172,23 +184,32 @@ namespace Modelec
"action/move/servo/timed/res", 10);
servo_timed_timer_ = this->create_wall_timer(
std::chrono::milliseconds(TIMER_SERVO_TIMED_MS),
std::chrono::milliseconds(timed_servo_timer_ms),
[this]()
{
if (servo_timed_buffer_.empty()) {
return;
}
rclcpp::Time now = this->now();
modelec_interfaces::msg::ActionServoTimedArray servo_timed_msg;
std::map<int, double> to_send;
std::vector<std::pair<int, double>> to_send;
for (auto& [id, servo_timed_set] : servo_timed_buffer_)
for (auto& servo_timed_set : servo_timed_buffer_)
{
if (servo_timed_set.active && now.nanoseconds() < servo_timed_set.start_time.nanoseconds())
{
continue;
}
if (servo_timed_set.active && now.nanoseconds() >= servo_timed_set.end_time.nanoseconds())
{
RCLCPP_DEBUG(this->get_logger(), "Timed move for Servo ID %d completed. Setting to final angle %.3f",
id, servo_timed_set.servo_timed.end_angle);
servo_timed_set.servo_timed.id, servo_timed_set.servo_timed.end_angle);
to_send.emplace_back(servo_timed_set.servo_timed.id, servo_timed_set.servo_timed.end_angle);
to_send[id] = servo_timed_set.servo_timed.end_angle;
servo_timed_set.active = false;
servo_timed_msg.items.push_back(servo_timed_set.servo_timed);
@@ -199,12 +220,13 @@ namespace Modelec
double duration = (servo_timed_set.end_time - servo_timed_set.start_time).seconds();
double progress = elapsed / duration;
RCLCPP_DEBUG(this->get_logger(), "Servo ID %d progress: %.3f | %.3f %.3f", id, progress, elapsed, duration);
RCLCPP_DEBUG(this->get_logger(), "Servo ID %d progress: %.3f | %.3f %.3f",
servo_timed_set.servo_timed.id, progress, elapsed, duration);
double intermediate_angle = servo_timed_set.servo_timed.start_angle +
progress * (servo_timed_set.servo_timed.end_angle - servo_timed_set.servo_timed.start_angle);
progress * (servo_timed_set.servo_timed.end_angle - servo_timed_set.servo_timed.start_angle);
to_send[id] = intermediate_angle;
to_send.emplace_back(servo_timed_set.servo_timed.id, intermediate_angle);
}
}
@@ -228,47 +250,22 @@ namespace Modelec
servo_move_timed_res_pub_->publish(servo_timed_msg);
}
servo_timed_buffer_.erase(
std::remove_if(
servo_timed_buffer_.begin(),
servo_timed_buffer_.end(),
[&](const ServoTimedSet& s)
{
return s.active == false;
}),
servo_timed_buffer_.end());
});
this->open(baudrate, serial_port, MAX_MESSAGE_LEN);
// TODO : check for real value there
/*asc_value_mapper_ = {
{0, 0},
{1, 100},
{2, 200},
{3, 300}
};*/
/*for (auto & [id, v] : asc_value_mapper_)
{
SendOrder("ASC", {std::to_string(id), std::to_string(v)});
}*/
// asc_state_ = 3;
// SendMove("ASC", {std::to_string(asc_state_)});
// rclcpp::sleep_for(std::chrono::milliseconds(100));
/*servo_value_ = {
{0, 1},
{1, 1},
{2, 0},
{3, 0},
{4, 1},
{5, 0}
};*/
servo_value_ = {
{0, 2.93},
{1, 0.91},
{2, 3.05},
{3, 0.3},
{4, 1},
{5, 1},
{6, 1},
{7, 1},
};
servo_value_ = Config::get<std::unordered_map<int, double>>("action.init.servo");
std::string data = "MOV;SERVO;" + std::to_string(servo_value_.size()) + ";";
@@ -280,31 +277,6 @@ namespace Modelec
data += "\n";
SendToPCB(data);
/*relay_value_ = {
{1, false},
{2, false},
{3, false},
};*/
/*rclcpp::sleep_for(std::chrono::milliseconds(100));
data = "SET;RELAY;STATE;";
data += std::to_string(relay_value_.size()) + ";";
for (auto & [id, v] : relay_value_)
{
data += std::to_string(id) + ";" + std::to_string(v) + ";";
}
SendToPCB(data);*/
/*for (auto & [id, v] : relay_value_)
{
rclcpp::sleep_for(std::chrono::milliseconds(100));
SendMove("RELAY" + std::to_string(id), {std::to_string(v)});
}*/
}
PCBActionInterface::~PCBActionInterface()
@@ -533,7 +505,6 @@ namespace Modelec
SendToPCB(command);
}
// TODO CHANGE TO AAA;BBB;N;X;Y;X;Y;X;Y where N is number of elem you want to send
void PCBActionInterface::GetData(const std::string& elem, const std::vector<std::string>& data)
{
SendToPCB("GET", elem, data);

View File

@@ -1,17 +1,22 @@
#include <modelec_com/pcb_odo_interface.hpp>
#include <modelec_utils/utils.hpp>
#include <ament_index_cpp/get_package_share_directory.hpp>
#include <modelec_utils/config.hpp>
namespace Modelec
{
PCBOdoInterface::PCBOdoInterface() : Node("pcb_odo_interface")
{
declare_parameter<std::string>("serial_port", "/dev/USB_ODO");
declare_parameter<int>("baudrate", 115200);
std::string config_path = ament_index_cpp::get_package_share_directory("modelec_strat") + "/data/config.xml";
if (!Config::load(config_path))
{
RCLCPP_ERROR(get_logger(), "Failed to load config file: %s", config_path.c_str());
}
auto serial_port = get_parameter("serial_port").as_string();
auto baudrate = get_parameter("baudrate").as_int();
auto serial_port = Config::get<std::string>("config.usb.odo.port", "/dev/ttyUSB0");
auto baudrate = Config::get<int>("config.usb.odo.baudrate", 115200);
RCLCPP_INFO(this->get_logger(), "Starting PCB Odometry Interface on port %s with baudrate %ld", serial_port.c_str(), baudrate);
RCLCPP_INFO(this->get_logger(), "Starting PCB Odometry Interface on port %s with baudrate %d", serial_port.c_str(), baudrate);
odo_pos_publisher_ = this->create_publisher<modelec_interfaces::msg::OdometryPos>(
"odometry/position", 10);
@@ -104,10 +109,10 @@ namespace Modelec
this->open(baudrate, serial_port, MAX_MESSAGE_LEN);
SetPID("THETA", 14, 0, 0);
SetPID("POS", 5, 0, 0);
SetPID("LEFT", 5, 0, 0);
SetPID("RIGHT", 5, 0, 0);
SetPID("THETA", Config::get<PIDData>("config.odo.pid.theta"));
SetPID("POS", Config::get<PIDData>("config.odo.pid.pos"));
SetPID("LEFT", Config::get<PIDData>("config.odo.pid.left"));
SetPID("RIGHT", Config::get<PIDData>("config.odo.pid.right"));
}
PCBOdoInterface::~PCBOdoInterface()
@@ -431,6 +436,11 @@ namespace Modelec
SendOrder("PID", data);
}
void PCBOdoInterface::SetPID(std::string name, const PIDData& pid_data)
{
SetPID(name, pid_data.p, pid_data.i, pid_data.d, pid_data.min, pid_data.max);
}
void PCBOdoInterface::SetMotor(int left, int right)
{
std::vector<std::string> data = {

View File

@@ -14,7 +14,6 @@ from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
def generate_launch_description():
pkg_name = 'modelec_core'
# -------------------------------------------------
# Launch arguments
# -------------------------------------------------
@@ -50,12 +49,6 @@ def generate_launch_description():
angle_compensate = LaunchConfiguration('angle_compensate', default='false')
scan_mode = LaunchConfiguration('scan_mode', default='Standard')
param_file = os.path.join(
get_package_share_directory(pkg_name),
'config',
'config.yml'
)
# -------------------------------------------------
# RPLIDAR with auto-restart
# -------------------------------------------------
@@ -64,7 +57,7 @@ def generate_launch_description():
package='rplidar_ros',
executable='rplidar_node',
name='rplidar_node',
parameters=[param_file, {
parameters=[{
'channel_type': channel_type,
'serial_port': serial_port,
'serial_baudrate': serial_baudrate,
@@ -113,7 +106,6 @@ def generate_launch_description():
package='modelec_gui',
executable='modelec_gui',
name='modelec_gui',
parameters=[param_file],
)
shutdown = RegisterEventHandler(
@@ -135,19 +127,17 @@ def generate_launch_description():
package='modelec_com',
executable='pcb_odo_interface',
name='pcb_odo_interface',
parameters=[param_file],
),
Node(
package='modelec_com',
executable='pcb_action_interface',
name='pcb_action_interface',
parameters=[param_file],
),
]
return []
# -------------------------------------------------
# Strategy nodes (WITHOUT enemy_manager)
# Strategy nodes
# -------------------------------------------------
def launch_strat(context, *args, **kwargs):
if context.launch_configurations.get('with_strat') == 'true':
@@ -156,20 +146,18 @@ def generate_launch_description():
package='modelec_strat',
executable='strat_fsm',
name='strat_fsm',
parameters=[param_file],
# prefix=['xterm -e gdb -ex run --args'],
),
Node(
package='modelec_strat',
executable='pami_manager',
name='pami_manager',
parameters=[param_file],
),
]
return []
# -------------------------------------------------
# Enemy manager (standalone)
# Enemy manager
# -------------------------------------------------
def launch_enemy_manager(context, *args, **kwargs):
if context.launch_configurations.get('with_enemy_manager') == 'true':
@@ -178,7 +166,6 @@ def generate_launch_description():
package='modelec_strat',
executable='enemy_manager',
name='enemy_manager',
parameters=[param_file],
)
]
return []
@@ -193,7 +180,6 @@ def generate_launch_description():
package='joy',
executable='joy_node',
name='joy_node',
parameters=[param_file],
)
]
return []
@@ -205,7 +191,6 @@ def generate_launch_description():
package='modelec_com',
executable='color_detector',
name='color_detector',
parameters=[param_file],
)
]
return []

View File

@@ -25,6 +25,7 @@ find_package(Qt6 COMPONENTS
Gui
Widgets
Svg
Multimedia
REQUIRED)
# Add the executable
@@ -63,6 +64,7 @@ target_link_libraries(modelec_gui
Qt6::Gui
Qt6::Widgets
Qt6::Svg
Qt6::Multimedia
modelec_utils::utils
modelec_utils::config
)

View File

@@ -10,7 +10,8 @@
#include <modelec_gui/pages/map_page.hpp>
#include <modelec_gui/pages/action_page.hpp>
#include <modelec_gui/pages/alim_page.hpp>
#include <QMediaPlayer>
#include <QAudioOutput>
namespace ModelecGUI
{
@@ -50,5 +51,11 @@ namespace ModelecGUI
QAction* toggle_show_obstacle_action_;
QMediaPlayer* media_player_;
QAudioOutput* audio_output_;
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr audio_sub_;
rclcpp::Subscription<std_msgs::msg::Empty>::SharedPtr quit_sub_;
};
}

View File

@@ -9,5 +9,6 @@
<file>img/logo/ISEN-Nantes-fond-blanc.png</file>
<file>img/logo/modelec.png</file>
<file>img/wood.jpg</file>
<file>sound/test.mp3</file>
</qresource>
</RCC>

Binary file not shown.

View File

@@ -4,6 +4,7 @@
#include "modelec_gui/modelec_gui.hpp"
#include <ament_index_cpp/get_package_share_directory.hpp>
#include <modelec_utils/config.hpp>
int main(int argc, char **argv)
{
@@ -13,14 +14,11 @@ int main(int argc, char **argv)
auto node = rclcpp::Node::make_shared("qt_gui_node");
node->declare_parameter("map.size.map_width_mm", 3000);
node->declare_parameter("map.size.map_height_mm", 2000);
node->declare_parameter("robot.size.length_mm", 200);
node->declare_parameter("robot.size.width_mm", 300);
node->declare_parameter("enemy.size.length_mm", 200);
node->declare_parameter("enemy.size.width_mm", 300);
std::string config_path = ament_index_cpp::get_package_share_directory("modelec_strat") + "/data/config.xml";
if (!Modelec::Config::load(config_path))
{
RCLCPP_ERROR(node->get_logger(), "Failed to load config file: %s", config_path.c_str());
}
ModelecGUI::ROS2QtGUI window(node);
window.show();

View File

@@ -2,7 +2,7 @@
#include <rclcpp/rclcpp.hpp>
#include <QMenuBar>
#include <utility>
#include <modelec_utils/config.hpp>
namespace ModelecGUI {
@@ -34,6 +34,32 @@ namespace ModelecGUI {
{
stackedWidget->setCurrentWidget(map_page_);
});
if (Modelec::Config::get<bool>("config.ihm.sound.enabled", false))
{
RCLCPP_INFO(get_node()->get_logger(), "Sound enabled, setting up audio player");
media_player_ = new QMediaPlayer(this);
audio_output_ = new QAudioOutput(this);
media_player_->setAudioOutput(audio_output_);
audio_sub_ = node_->create_subscription<std_msgs::msg::String>(
"audio/play", 10, [this](const std_msgs::msg::String::SharedPtr msg)
{
if (Modelec::Config::has("config.ihm.sound." + msg->data + "@path"))
{
QString audio_file = QString::fromStdString(Modelec::Config::get<std::string>("config.ihm.sound." + msg->data + "@path"));
media_player_->setSource(QUrl::fromLocalFile(audio_file));
media_player_->play();
}
});
}
quit_sub_ = node_->create_subscription<std_msgs::msg::Empty>(
"gui/quit", 10, [this](const std_msgs::msg::Empty::SharedPtr)
{
close();
});
}
void ROS2QtGUI::setupMenu() {

View File

@@ -5,6 +5,8 @@
#include <utility>
#include <cmath>
#include <modelec_utils/config.hpp>
namespace ModelecGUI
{
MapPage::MapPage(rclcpp::Node::SharedPtr node, QWidget* parent) : QWidget(parent),
@@ -48,14 +50,14 @@ namespace ModelecGUI
qpoints = {};
map_height_ = node_->get_parameter("map.size.map_height_mm").as_int();
map_width_ = node_->get_parameter("map.size.map_width_mm").as_int();
map_height_ = Modelec::Config::get<int>("config.map.size.map_height_mm", 2000);
map_width_ = Modelec::Config::get<int>("config.map.size.map_width_mm", 3000);
robot_length_ = node_->get_parameter("robot.size.length_mm").as_int();
robot_width_ = node_->get_parameter("robot.size.width_mm").as_int();
robot_length_ = Modelec::Config::get<int>("config.robot.size.length_mm", 300);
robot_width_ = Modelec::Config::get<int>("config.robot.size.width_mm", 200);
enemy_length_ = node_->get_parameter("enemy.size.length_mm").as_int();
enemy_width_ = node_->get_parameter("enemy.size.width_mm").as_int();
enemy_length_ = Modelec::Config::get<int>("config.enemy.size.length_mm", 300);
enemy_width_ = Modelec::Config::get<int>("config.enemy.size.width_mm", 300);
add_waypoint_sub_ = node_->create_subscription<modelec_interfaces::msg::OdometryWaypoint>(
"odometry/add_waypoint", 100,

View File

@@ -1,5 +1,6 @@
#include <modelec_gui/pages/test_map_page.hpp>
#include <modelec_utils/utils.hpp>
#include <modelec_utils/config.hpp>
#include <QMouseEvent>
#include <utility>
@@ -22,14 +23,14 @@ namespace ModelecGUI
qpoints = {};
map_height_ = node_->get_parameter("map.size.map_height_mm").as_int();
map_width_ = node_->get_parameter("map.size.map_width_mm").as_int();
map_height_ = Modelec::Config::get<int>("config.map.size.map_height_mm", 2000);
map_width_ = Modelec::Config::get<int>("config.map.size.map_width_mm", 3000);
robot_length_ = node_->get_parameter("robot.size.length_mm").as_int();
robot_width_ = node_->get_parameter("robot.size.width_mm").as_int();
robot_length_ = Modelec::Config::get<int>("config.robot.size.length_mm", 300);
robot_width_ = Modelec::Config::get<int>("config.robot.size.width_mm", 200);
enemy_length_ = node_->get_parameter("enemy.size.length_mm").as_int();
enemy_width_ = node_->get_parameter("enemy.size.width_mm").as_int();
enemy_length_ = Modelec::Config::get<int>("config.enemy.size.length_mm", 300);
enemy_width_ = Modelec::Config::get<int>("config.enemy.size.width_mm", 300);
add_waypoint_sub_ = node_->create_subscription<modelec_interfaces::msg::OdometryWaypoint>(
"odometry/add_waypoint", 100,

View File

@@ -2,4 +2,5 @@ int32 id
float64 start_angle
float64 end_angle
float64 duration_s
bool success
float64 delay_s
bool success

View File

@@ -26,13 +26,16 @@ set(strat_fsm_sources
src/action/free_action.cpp
src/action/take_action.cpp
src/action/toggle_servo_action.cpp
src/action/look_on_action.cpp
src/action/thermo_action.cpp
src/missions/go_home_mission.cpp
src/missions/take_mission.cpp
src/missions/free_mission.cpp
src/missions/thermo_mission.cpp
src/missions/action_mission.cpp
src/missions/move_mission.cpp
src/missions/mine_time_mission.cpp
src/missions/mission_base.cpp
src/obstacle/obstacle.cpp
src/obstacle/box.cpp

View File

@@ -0,0 +1,118 @@
<action>
<init>
<servo id="0" angle="3.12" />
<servo id="1" angle="0.91" />
<servo id="2" angle="3.45" />
<servo id="3" angle="0" />
<servo id="4" angle="1" />
<servo id="5" angle="1" />
<servo id="6" angle="1" />
<servo id="7" angle="1" />
<!--<servo id="8" angle="3.1" />-->
<!--<servo id="9" angle="0.96" />-->
<!--<servo id="10" angle="1" />-->
<!--<servo id="11" angle="1" />-->
<!--<servo id="12" angle="1" />-->
<!--<servo id="13" angle="1" />-->
<!--<servo id="14" angle="1" />-->
<!--<servo id="15" angle="1" />-->
<!--<servo id="16" angle="1" />-->
<!--<servo id="17" angle="1" />-->
<!--<servo id="18" angle="1" />-->
</init>
<down>
<front>
<unrotated>
<msg id="0" start_angle="1.84" end_angle="3.12" duration_s="2" />
<msg id="1" start_angle="2.15" end_angle="0.91" duration_s="2" />
<msg id="2" start_angle="0.5" end_angle="2.85" duration_s="0.9" delay_s="0.4" />
<msg id="2" start_angle="2.85" end_angle="3.45" duration_s="0.7" delay_s="1.3" />
<msg id="3" start_angle="2.9" end_angle="0.9" duration_s="0.9" delay_s="0.4" />
<msg id="3" start_angle="0.9" end_angle="0" duration_s="0.7" delay_s="1.3" />
</unrotated>
<rotated>
<msg id="0" start_angle="1.84" end_angle="2.98" duration_s="2" />
<msg id="1" start_angle="2.15" end_angle="1.07" duration_s="2" />
<msg id="2" start_angle="0.5" end_angle="0" duration_s="1" />
<msg id="3" start_angle="2.9" end_angle="3.3" duration_s="1" />
</rotated>
</front>
<back>
<unrotated>
<msg id="8" start_angle="1.88" end_angle="2.93" duration_s="2" />
<msg id="9" start_angle="2.2" end_angle="0.91" duration_s="2" />
<msg id="10" start_angle="0.5" end_angle="2.45" duration_s="1.3" />
<msg id="10" start_angle="2.45" end_angle="3.05" duration_s="0.7" delay_s="1.3" />
<msg id="11" start_angle="2.6" end_angle="0.9" duration_s="1.3" />
<msg id="11" start_angle="0.9" end_angle="0.3" duration_s="0.7" delay_s="1.3" />
</unrotated>
<rotated>
<msg id="8" start_angle="1.88" end_angle="2.93" duration_s="2" />
<msg id="9" start_angle="2.2" end_angle="0.91" duration_s="2" />
<msg id="10" start_angle="0.5" end_angle="0" duration_s="2" />
<msg id="11" start_angle="2.6" end_angle="3.4" duration_s="2" />
</rotated>
</back>
</down>
<up>
<front>
<unrotated>
<msg id="0" start_angle="3.12" end_angle="1.84" duration_s="2" />
<msg id="1" start_angle="0.91" end_angle="2.15" duration_s="2" />
<msg id="2" start_angle="3.45" end_angle="2.95" duration_s="0.7" />
<msg id="2" start_angle="2.95" end_angle="0.5" duration_s="0.7" delay_s="0.7" />
<msg id="3" start_angle="0" end_angle="0.6" duration_s="0.7" />
<msg id="3" start_angle="0.6" end_angle="2.9" duration_s="0.7" delay_s="0.7" />
</unrotated>
<rotated>
<msg id="0" start_angle="2.98" end_angle="1.84" duration_s="2" />
<msg id="1" start_angle="1.07" end_angle="2.15" duration_s="2" />
<msg id="2" start_angle="0" end_angle="0.5" duration_s="2" />
<msg id="3" start_angle="3.3" end_angle="2.9" duration_s="2" />
</rotated>
</front>
<back>
<rotated>
<msg id="8" start_angle="2.93" end_angle="1.76" duration_s="2" />
<msg id="9" start_angle="0.91" end_angle="2.06" duration_s="2" />
<msg id="10" start_angle="0" end_angle="0.5" duration_s="2" />
<msg id="11" start_angle="3.1" end_angle="2.6" duration_s="2" />
</rotated>
<unrotated>
<msg id="8" start_angle="2.93" end_angle="1.76" duration_s="2" />
<msg id="9" start_angle="0.91" end_angle="2.06" duration_s="2" />
<msg id="10" start_angle="3.2" end_angle="0.5" duration_s="2" />
<msg id="11" start_angle="0" end_angle="2.6" duration_s="2" />
</unrotated>
</back>
</up>
<free>
<id first="4" second="12" />
<msg start_angle="3" end_angle="1" duration_s="0.5" />
</free>
<take>
<id first="4" second="12" />
<msg start_angle="1" end_angle="3" duration_s="0.5" />
</take>
<toggle>
<id first="4" second="12" />
<msg start_angle="1" end_angle="3" duration_s="0.5" />
</toggle>
<look_on>
<id>18</id>
<center>1</center>
<front>0</front>
<back>2</back>
<duration_s>0.5</duration_s>
</look_on>
<thermo>
<left>
<deploy id="16" start_angle="1" end_angle="2" duration_s="0.5" />
<undeploy id="16" start_angle="2" end_angle="1" duration_s="0.5" />
</left>
<right>
<deploy id="17" start_angle="0" end_angle="1.5" duration_s="0.5" />
<undeploy id="17" start_angle="1.5" end_angle="0" duration_s="0.5" />
</right>
</thermo>
</action>

View File

@@ -3,7 +3,7 @@
<size>
<width_mm>300</width_mm>
<length_mm>300</length_mm>
<margin_mm>50</margin_mm>
<margin_mm>75</margin_mm>
</size>
<detection>
<min_move_threshold_mm>50</min_move_threshold_mm>
@@ -12,21 +12,20 @@
<min_emergency_distance_mm>300</min_emergency_distance_mm>
<margin_detection_table_mm>50</margin_detection_table_mm>
</detection>
<factor_close_enemy>-2</factor_close_enemy>
</enemy>
<robot>
<size>
<width_mm>300</width_mm>
<length_mm>300</length_mm>
<margin_mm>100</margin_mm>
<margin_mm>20</margin_mm>
</size>
</robot>
<map>
<size>
<grid_width>600</grid_width>
<grid_height>400</grid_height>
<grid_width>3000</grid_width>
<grid_height>2000</grid_height>
<map_width_mm>3000</map_width_mm>
<map_height_mm>2000</map_height_mm>
@@ -50,18 +49,61 @@
<height_mm>450</height_mm>
</spawn>
<mission_score>
<go_home>10</go_home>
</mission_score>
<mission>
<action>
<timeout>
<s>10</s>
</timeout>
</action>
<min_time>
<min_time_duration>
<s>0.1</s>
</min_time_duration>
</min_time>
<move>
<min_ask_waypoint>
<s>3</s>
</min_ask_waypoint>
<ask_waypoint_interval>
<s>2</s>
</ask_waypoint_interval>
<go_timeout>
<s>10</s>
</go_timeout>
</move>
</mission>
<pami>
<time_to_put_zone>77</time_to_put_zone>
<time_to_remove_top_pot>65</time_to_remove_top_pot>
</pami>
<static_strat>false</static_strat>
<static_strat>
<enabled>false</enabled>
<missions>
<mission name="TAKE_MISSION" />
<mission name="FREE_MISSION" />
<mission name="THERMO_MISSION" />
</missions>
</static_strat>
<factor>
<theta>20</theta>
<obs>0.6</obs>
<thermo>0.7</thermo>
<close_enemy>-2</close_enemy>
</factor>
<timer_period_ms>20</timer_period_ms>
<timer>
<strat>
<ms>20</ms>
</strat>
<action>
<timed_servo>
<ms>
10
</ms>
</timed_servo>
</action>
</timer>
<thermo>
<yellow>
<start x="200" y="175" theta="0" />
@@ -75,21 +117,56 @@
<cam>
<save_to_file>
<enabled>true</enabled>
<path>./cam/</path>
<directory>./cam/</directory>
</save_to_file>
<enabled>true</enabled>
<!--<link>/dev/video0</link>-->
<link>http://192.168.1.21:8080/video</link>
<headless>true</headless>
<link>http://192.168.1.10:8080/video</link>
<headless>false</headless>
<rois>
<roi x="300" y="700" w="50" h="50" />
<roi x="700" y="700" w="50" h="50" />
<roi x="1200" y="700" w="50" h="50" />
<roi x="1500" y="700" w="50" h="50" />
<roi x="400" y="200" w="150" h="150" />
<roi x="400" y="500" w="150" h="150" />
<roi x="400" y="700" w="150" h="150" />
<roi x="400" y="1000" w="150" h="150" />
<roi x="1900" y="200" w="150" h="150" />
<roi x="1900" y="500" w="150" h="150" />
<roi x="1900" y="700" w="150" h="150" />
<roi x="1900" y="1000" w="150" h="150" />
</rois>
<colors>
<color name="blue" hue_min="90" hue_max="130" />
<color name="yellow" hue_min="20" hue_max="40" />
<color name="red" hue_min="0" hue_max="10" />
<color name="red" hue_min="160" hue_max="180" />
</colors>
</cam>
</config>
<usb>
<odo>
<port>/dev/USB_ODO</port>
<baudrate>115200</baudrate>
</odo>
<action>
<port>/dev/USB_ACTION</port>
<baudrate>115200</baudrate>
</action>
<timeout_ms>300</timeout_ms>
<attempt>5</attempt>
</usb>
<ihm>
<sound>
<enabled>false</enabled>
<test path=":/sound/test.mp3" />
</sound>
</ihm>
<odo>
<pid>
<left p="4" i="0.1" d="0" />
<right p="4" i="0.1" d="0" />
<pos p="6" i="0" d="0.1" />
<theta p="10" i="0" d="0.1" />
</pid>
</odo>
</config>

View File

@@ -1,23 +1,23 @@
<Map>
<DepositeZone id="0">
<epositeZone id="0">
<Pos x="1250" y="1450" theta="0" w="200" h="200" />
<TakeAngle>
<Angle>-1.5708</Angle>
</TakeAngle>
</DepositeZone>
</epositeZone>
<DepositeZone id="1">
<Pos x="1750" y="1450" theta="0" w="200" h="200" />
<TakeAngle>
<Angle>-1.5708</Angle>
</TakeAngle>
</DepositeZone>
<DepositeZone id="2">
<epositeZone id="2">
<Pos x="100" y="800" theta="0" w="200" h="200" />
<TakeAngle>
<Angle>0</Angle>
</TakeAngle>
</DepositeZone>
<DepositeZone id="3">
</epositeZone>
<epositeZone id="3">
<Pos x="800" y="800" theta="0" w="200" h="200" />
<TakeAngle>
<Angle>0</Angle>
@@ -25,7 +25,7 @@
<Angle>-1.5708</Angle>
<Angle>3.1415</Angle>
</TakeAngle>
</DepositeZone>
</epositeZone>
<DepositeZone id="4">
<Pos x="1500" y="800" theta="0" w="200" h="200" />
<TakeAngle>
@@ -50,12 +50,12 @@
<Angle>3.1415</Angle>
</TakeAngle>
</DepositeZone>
<DepositeZone id="7">
<epositeZone id="7">
<Pos x="700" y="100" theta="0" w="200" h="200" />
<TakeAngle>
<Angle>1.5708</Angle>
</TakeAngle>
</DepositeZone>
</epositeZone>
<DepositeZone id="8">
<Pos x="1500" y="100" theta="0" w="200" h="200" />
<TakeAngle>

View File

@@ -3,20 +3,20 @@
<Obstacle id="2" x="1500" y="1775" theta="1.5708" width="1800" height="450" type="estrade"/>
<!-- Box TOP TO BOTTOM LEFT -->
<Box id="10" x="175" y="1200" theta="0" width="200" height="150" type="box" />
<Box id="11" x="1150" y="800" theta="1.5708" width="200" height="150" type="box" >
<!--<Obstacle id="10" x="175" y="1200" theta="0" width="200" height="150" type="box" />
<Obstacle id="11" x="1150" y="800" theta="1.5708" width="200" height="150" type="box" >
<possible-angle theta="-1.5708" />
</Box>
<Box id="12" x="175" y="400" theta="0" width="200" height="150" type="box" />
<Box id="13" x="1100" y="175" theta="1.5708" width="200" height="150" type="box" />
</Obstacle>
<Obstacle id="12" x="175" y="400" theta="0" width="200" height="150" type="box" />
<Obstacle id="13" x="1100" y="175" theta="1.5708" width="200" height="150" type="box" />-->
<!-- Box TOP TO BOTTOM RIGHT -->
<Box id="20" x="2825" y="1200" theta="3.1415" width="200" height="150" type="box" />
<Box id="21" x="1850" y="800" theta="1.5708" width="200" height="150" type="box">
<Obstacle id="20" x="2825" y="1200" theta="3.1415" width="200" height="150" type="box" />
<Obstacle id="21" x="1850" y="800" theta="1.5708" width="200" height="150" type="box">
<possible-angle theta="-1.5708" />
</Box>
<Box id="22" x="2825" y="400" theta="3.1415" width="200" height="150" type="box"/>
<Box id="23" x="1900" y="175" theta="1.5708" width="200" height="150" type="box"/>
</Obstacle>
<Obstacle id="22" x="2825" y="400" theta="3.1415" width="200" height="150" type="box"/>
<Obstacle id="23" x="1900" y="175" theta="1.5708" width="200" height="150" type="box"/>
<!-- PAMI Start -->
<Obstacle id="30" x="75" y="1750" theta="1.5708" width="150" height="450" type="pami-start"/>

View File

@@ -12,6 +12,8 @@
#include <modelec_interfaces/msg/action_servo_pos_array.hpp>
#include <modelec_interfaces/msg/action_servo_pos.hpp>
#include <modelec_utils/config.hpp>
namespace Modelec
{
class ActionExecutor;
@@ -79,4 +81,30 @@ namespace Modelec
return nullptr;
}
template<>
inline ActionServoTimed
Config::get<ActionServoTimed>(
const std::string& prefix,
const ActionServoTimed&, bool)
{
ActionServoTimed msg;
msg.id = get<int>(prefix + "@id", 0);
msg.start_angle = get<double>(prefix + "@start_angle", 0);
msg.end_angle = get<double>(prefix + "@end_angle", 0);
msg.duration_s = get<double>(prefix + "@duration_s", 0);
msg.delay_s = get<double>(prefix + "@delay_s", 0, true);
return msg;
}
template<>
inline std::vector<ActionServoTimed>
Config::get<std::vector<ActionServoTimed>>(
const std::string& prefix,
const std::vector<ActionServoTimed>& default_value, bool)
{
auto result = getArray<ActionServoTimed>(prefix);
return result.empty() ? default_value : result;
}
}

View File

@@ -15,6 +15,7 @@ namespace Modelec
void Init(const std::vector<std::string>& params) override;
void SetSide(Side side);
void SetInverted(bool inverted);
static void InitConfig();
void End() override;
@@ -27,6 +28,13 @@ namespace Modelec
std::queue<int> steps_;
static inline std::vector<ActionServoTimed> front_unrotated_msg_;
static inline std::vector<ActionServoTimed> front_rotated_msg_;
static inline std::vector<ActionServoTimed> back_unrotated_msg_;
static inline std::vector<ActionServoTimed> back_rotated_msg_;
static inline bool isConfigInit_ = false;
inline static const bool registered_ =
[]()
{

View File

@@ -18,6 +18,7 @@ namespace Modelec
void AddServo(int id, Side side);
void AddServo(std::pair<int, Side> servo);
void AddServos(const std::vector<std::pair<int, Side>>& servos);
static void InitConfig();
void End() override;
@@ -28,6 +29,14 @@ namespace Modelec
std::queue<int> steps_;
static inline int first_servo_;
static inline int second_servo_;
static inline double start_angle_;
static inline double end_angle_;
static inline double duration_s_;
static inline bool isConfigInit_ = false;
inline static const bool registered_ =
[]()
{

View File

@@ -1,38 +0,0 @@
#pragma once
#include <queue>
#include <modelec_strat/action/base_action.hpp>
namespace Modelec
{
class LookOnAction : public BaseAction
{
public:
LookOnAction(const std::shared_ptr<ActionExecutor>& action_executor);
LookOnAction(const std::shared_ptr<ActionExecutor>& action_executor, Side side);
void Next() override;
void Init(const std::vector<std::string>& params) override;
void SetSide(Side side);
void End() override;
inline static const std::string Name = ActionExec::LOOK_ON;
private:
Side side_ = CENTER;
std::queue<int> steps_;
inline static const bool registered_ =
[]()
{
BaseAction::Registry()[Name] =
[](const std::shared_ptr<ActionExecutor>& exec)
{
return std::make_shared<LookOnAction>(exec);
};
return true;
}();
};
}

View File

@@ -18,6 +18,7 @@ namespace Modelec
void AddServo(int id, Side side);
void AddServo(std::pair<int, Side> servo);
void AddServos(const std::vector<std::pair<int, Side>>& servos);
static void InitConfig();
void End() override;
@@ -28,6 +29,14 @@ namespace Modelec
std::queue<int> steps_;
static inline int first_servo_;
static inline int second_servo_;
static inline double start_angle_;
static inline double end_angle_;
static inline double duration_s_;
static inline bool isConfigInit_ = false;
inline static const bool registered_ =
[]()
{

View File

@@ -15,6 +15,7 @@ namespace Modelec
void Init(const std::vector<std::string>& params) override;
void SetSide(Side side);
void SetDeploy(bool deploy);
static void InitConfig();
void End() override;
@@ -26,6 +27,13 @@ namespace Modelec
std::queue<int> steps_;
static inline std::vector<ActionServoTimed> left_deploy_msg_;
static inline std::vector<ActionServoTimed> left_undeploy_msg_;
static inline std::vector<ActionServoTimed> right_deploy_msg_;
static inline std::vector<ActionServoTimed> right_undeploy_msg_;
static inline bool isConfigInit_ = false;
inline static const bool registered_ =
[]()
{

View File

@@ -18,6 +18,7 @@ namespace Modelec
void AddServo(int id, Side side);
void AddServo(std::pair<int, Side> servo);
void AddServos(const std::vector<std::pair<int, Side>>& servos);
static void InitConfig();
void End() override;
@@ -28,6 +29,14 @@ namespace Modelec
std::queue<int> steps_;
static inline int first_servo_;
static inline int second_servo_;
static inline double start_angle_;
static inline double end_angle_;
static inline double duration_s_;
static inline bool isConfigInit_ = false;
inline static const bool registered_ =
[]()
{

View File

@@ -14,6 +14,7 @@ namespace Modelec
void Next() override;
void Init(const std::vector<std::string>& params) override;
void SetSide(Side side);
static void InitConfig();
void End() override;
@@ -24,6 +25,13 @@ namespace Modelec
std::queue<int> steps_;
static inline std::vector<ActionServoTimed> front_rotated_msg_;
static inline std::vector<ActionServoTimed> front_unrotated_msg_;
static inline std::vector<ActionServoTimed> back_rotated_msg_;
static inline std::vector<ActionServoTimed> back_unrotated_msg_;
static inline bool isConfigInit_ = false;
inline static const bool registered_ =
[]()
{

View File

@@ -9,11 +9,11 @@
#include <sensor_msgs/msg/joy.hpp>
#include <std_msgs/msg/int64.hpp>
#include <std_msgs/msg/empty.hpp>
#include <std_msgs/msg/string.hpp>
#include <std_srvs/srv/trigger.hpp>
#include "action/base_action.hpp"
#include "missions/thermo_mission.hpp"
#include "obstacle/box.hpp"
namespace Modelec
@@ -55,8 +55,6 @@ namespace Modelec
void ActivateThermo(BaseAction::Side side, bool deploy, bool force = false);
void LookOn(BaseAction::Side side, bool force = false);
void ActionFinished(const std::shared_ptr<BaseAction>& action);
std::array<std::shared_ptr<BoxObstacle>, 2> box_obstacles_;
@@ -68,7 +66,10 @@ namespace Modelec
bool down;
bool rotated;
};
std::array<ArmState, 2> arm_pos_;
std::array<ArmState, 2> arm_pos_ = {
ArmState{false, false},
ArmState{false, false},
};
std::array<float, 16> servo_value_ = {
2.91,
@@ -86,10 +87,6 @@ namespace Modelec
{BaseAction::RIGHT, false},
};
BaseAction::Side cam_side_ = BaseAction::Side::CENTER;
bool looking_on_front_ = true;
bool IsEmpty() const;
bool IsFull() const;
@@ -143,6 +140,9 @@ namespace Modelec
float last_left_trig = 1.0f;
float last_right_trig = 1.0f;
bool last_l3 = false;
bool last_r3 = false;
private:
rclcpp::Node::SharedPtr node_;
};

View File

@@ -1,7 +1,6 @@
#pragma once
#include <queue>
#include <tinyxml2.h>
#include <modelec_utils/config.hpp>
#include <modelec_utils/point.hpp>
namespace Modelec
@@ -9,7 +8,7 @@ namespace Modelec
class DepositeZone
{
public:
DepositeZone(tinyxml2::XMLElement* obstacleElem);
DepositeZone(int id);
Point GetPosition() const;
@@ -20,13 +19,12 @@ namespace Modelec
bool Validate() const;
int GetId() const;
int GetMaxPot() const;
int GetWidth() const;
int GetHeight() const;
protected:
int id_, max_pot_;
int id_;
int w_, h_;
Point position_;

View File

@@ -0,0 +1,26 @@
#pragma once
#include <modelec_strat/missions/mission_base.hpp>
#include <modelec_strat/action_executor.hpp>
namespace Modelec {
class ActionMission : virtual public Mission {
public:
ActionMission(const std::shared_ptr<ActionExecutor>& action_executor);
static void InitConfig();
void Start(const rclcpp::Node::SharedPtr& node) override;
bool Update() override;
std::string GetName() const override { return "Action"; }
protected:
std::shared_ptr<ActionExecutor> action_executor_;
rclcpp::Time deploy_time_;
static double TIMEOUT;
static bool IsInit;
};
}

View File

@@ -1,20 +1,19 @@
#pragma once
#include <modelec_strat/missions/mission_base.hpp>
#include <modelec_strat/navigation_helper.hpp>
#include <modelec_strat/action_executor.hpp>
#include <modelec_strat/missions/action_mission.hpp>
#include <modelec_strat/missions/move_mission.hpp>
#include <modelec_strat/missions/min_time_mission.hpp>
namespace Modelec {
class FreeMission : public Mission {
class FreeMission : public ActionMission, public MoveMission, public MinTimeMission {
public:
FreeMission(const std::shared_ptr<NavigationHelper>& nav,
const std::shared_ptr<ActionExecutor>& action_executor,
BaseAction::Side side = BaseAction::FRONT);
void Start(rclcpp::Node::SharedPtr node) override;
void Update() override;
void Start(const rclcpp::Node::SharedPtr& node) override;
bool Update() override;
void Clear() override;
MissionStatus GetStatus() const override;
std::string GetName() const override { return "Free"; }
private:
@@ -33,20 +32,8 @@ namespace Modelec {
BaseAction::Side side_;
MissionStatus status_;
std::shared_ptr<NavigationHelper> nav_;
std::shared_ptr<ActionExecutor> action_executor_;
rclcpp::Node::SharedPtr node_;
std::shared_ptr<DepositeZone> target_deposite_zone_;
double angle_;
rclcpp::Time go_timeout_;
rclcpp::Time deploy_time_;
std::optional<rclcpp::Time> min_time_;
rclcpp::Time last_ask_waypoint_time_;
};
}

View File

@@ -1,20 +1,18 @@
#pragma once
#include <modelec_strat/missions/mission_base.hpp>
#include <modelec_strat/navigation_helper.hpp>
#include <modelec_strat/missions/move_mission.hpp>
#include <std_msgs/msg/int64.hpp>
namespace Modelec
{
class GoHomeMission : public Mission
class GoHomeMission : public MoveMission
{
public:
GoHomeMission(const std::shared_ptr<NavigationHelper>& nav, const rclcpp::Time& start_time);
void Start(rclcpp::Node::SharedPtr node) override;
void Update() override;
void Start(const rclcpp::Node::SharedPtr& node) override;
bool Update() override;
void Clear() override;
MissionStatus GetStatus() const override;
std::string GetName() const override { return "GoHome"; }
private:
@@ -26,18 +24,10 @@ namespace Modelec
DONE,
};
MissionStatus status_;
std::shared_ptr<NavigationHelper> nav_;
rclcpp::Time go_home_time_;
rclcpp::Node::SharedPtr node_;
rclcpp::Time start_time_;
Point home_point_;
rclcpp::Publisher<std_msgs::msg::Int64>::SharedPtr score_pub_;
int mission_score_ = 0;
rclcpp::Time go_timeout_;
std::optional<rclcpp::Time> min_time_;
rclcpp::Time last_ask_waypoint_time_;
};
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include <modelec_strat/missions/mission_base.hpp>
#include <optional>
namespace Modelec {
class MinTimeMission : virtual public Mission {
public:
MinTimeMission();
static void InitConfig();
void Start(const rclcpp::Node::SharedPtr& node) override;
bool Update() override;
std::string GetName() const override { return "MinTime"; }
protected:
std::optional<rclcpp::Time> min_time_;
static double MIN_TIME_DURATION;
static bool IsInit;
};
}

View File

@@ -2,7 +2,7 @@
#include <rclcpp/rclcpp.hpp>
#include <string>
#include <queue>
namespace Modelec
{
@@ -18,14 +18,19 @@ namespace Modelec
class Mission
{
public:
Mission(MissionStatus status = MissionStatus::READY);
virtual ~Mission() = default;
virtual void Start(rclcpp::Node::SharedPtr node) = 0;
virtual void Update() = 0;
virtual void Start(const rclcpp::Node::SharedPtr& node);
virtual bool Update() = 0;
virtual void Clear() = 0;
virtual MissionStatus GetStatus() const = 0;
virtual MissionStatus GetStatus() const;
virtual std::string GetName() const = 0;
protected:
std::queue<int> steps_;
rclcpp::Node::SharedPtr node_;
MissionStatus status_;
};
}
}

View File

@@ -0,0 +1,30 @@
#pragma once
#include <modelec_strat/missions/mission_base.hpp>
#include <modelec_strat/navigation_helper.hpp>
namespace Modelec {
class MoveMission : virtual public Mission {
public:
MoveMission(const std::shared_ptr<NavigationHelper>& nav);
static void InitConfig();
void Start(const rclcpp::Node::SharedPtr& node) override;
bool Update() override;
std::string GetName() const override { return "Move"; }
protected:
std::shared_ptr<NavigationHelper> nav_;
rclcpp::Time go_timeout_;
rclcpp::Time last_ask_waypoint_time_;
static double MIN_ASK_WAYPOINT;
static double ASK_WAYPOINT_INTERVAL;
static double GO_TIMEOUT;
static bool IsInit;
};
}

View File

@@ -1,20 +1,19 @@
#pragma once
#include <modelec_strat/missions/mission_base.hpp>
#include <modelec_strat/navigation_helper.hpp>
#include <modelec_strat/action_executor.hpp>
#include <modelec_strat/missions/action_mission.hpp>
#include <modelec_strat/missions/min_time_mission.hpp>
#include <modelec_strat/missions/move_mission.hpp>
namespace Modelec {
class TakeMission : public Mission {
class TakeMission : public MinTimeMission, public ActionMission, public MoveMission {
public:
TakeMission(const std::shared_ptr<NavigationHelper>& nav,
const std::shared_ptr<ActionExecutor>& action_executor,
BaseAction::Side side = BaseAction::FRONT);
void Start(rclcpp::Node::SharedPtr node) override;
void Update() override;
void Start(const rclcpp::Node::SharedPtr& node) override;
bool Update() override;
void Clear() override;
MissionStatus GetStatus() const override;
std::string GetName() const override { return "Take"; }
private:
@@ -31,16 +30,5 @@ namespace Modelec {
BaseAction::Side side_;
std::shared_ptr<BoxObstacle> closestBox;
MissionStatus status_;
std::shared_ptr<NavigationHelper> nav_;
std::shared_ptr<ActionExecutor> action_executor_;
rclcpp::Node::SharedPtr node_;
rclcpp::Time go_timeout_;
rclcpp::Time deploy_time_;
std::optional<rclcpp::Time> min_time_;
rclcpp::Time last_ask_waypoint_time_;
};
}
}

View File

@@ -1,19 +1,17 @@
#pragma once
#include <modelec_strat/missions/mission_base.hpp>
#include <modelec_strat/navigation_helper.hpp>
#include <modelec_strat/action_executor.hpp>
#include <modelec_strat/missions/action_mission.hpp>
#include <modelec_strat/missions/move_mission.hpp>
namespace Modelec {
class ThermoMission : public Mission {
class ThermoMission : public ActionMission, public MoveMission {
public:
ThermoMission(const std::shared_ptr<NavigationHelper>& nav,
const std::shared_ptr<ActionExecutor>& action_executor);
void Start(rclcpp::Node::SharedPtr node) override;
void Update() override;
void Start(const rclcpp::Node::SharedPtr& node) override;
bool Update() override;
void Clear() override;
MissionStatus GetStatus() const override;
std::string GetName() const override { return "Thermo"; }
static bool IsThermoDone;
@@ -32,16 +30,5 @@ namespace Modelec {
std::array<Point, 2> thermo_positions_;
std::shared_ptr<BoxObstacle> closestBox;
MissionStatus status_;
std::shared_ptr<NavigationHelper> nav_;
std::shared_ptr<ActionExecutor> action_executor_;
rclcpp::Node::SharedPtr node_;
rclcpp::Time go_timeout_;
rclcpp::Time deploy_time_;
std::optional<rclcpp::Time> min_time_;
rclcpp::Time last_ask_waypoint_time_;
};
}

View File

@@ -85,26 +85,26 @@ namespace Modelec
PosMsg::SharedPtr GetCurrentPos() const;
bool LoadDepositeZoneFromXML(const std::string& filename);
void LoadDepositeZoneFromXML();
std::shared_ptr<DepositeZone> GetClosestDepositeZone(const PosMsg::SharedPtr& pos,
const std::vector<int>& blacklistedId = {}, bool only_free = false);
template <typename T,
typename = std::enable_if_t<std::is_base_of<Obstacle, T>::value>>
std::shared_ptr<T> GetClosestObstacle(const PosMsg::SharedPtr& pos) const;
std::shared_ptr<T> GetClosestObstacle(const PosMsg::SharedPtr& pos, bool countObjective = false) const;
PosMsg::SharedPtr GetHomePosition();
std::array<Point, 2> GetThermoPositions();
void OnEnemyPosition(const modelec_interfaces::msg::OdometryPos::SharedPtr msg);
void OnEnemyPosition(modelec_interfaces::msg::OdometryPos::SharedPtr msg);
void OnEnemyPositionLongTime(const modelec_interfaces::msg::OdometryPos::SharedPtr msg);
void OnEnemyPositionLongTime(modelec_interfaces::msg::OdometryPos::SharedPtr msg);
bool DoesLineIntersectCircle(const Point& start, const Point& end,
const Point& center, float radius);
bool EnemyOnPath(const modelec_interfaces::msg::OdometryPos msg);
bool EnemyOnPath(modelec_interfaces::msg::OdometryPos msg);
bool Replan(bool force = false);
@@ -152,7 +152,7 @@ namespace Modelec
PosMsg::SharedPtr current_pos_;
std::map<int, std::shared_ptr<DepositeZone>> deposite_zones_;
std::vector<std::shared_ptr<DepositeZone>> deposite_zones_;
rclcpp::Subscription<WaypointMsg>::SharedPtr waypoint_reach_sub_;
rclcpp::Publisher<WaypointMsg>::SharedPtr waypoint_pub_;
@@ -181,7 +181,7 @@ namespace Modelec
};
template <typename T, typename>
std::shared_ptr<T> NavigationHelper::GetClosestObstacle(const PosMsg::SharedPtr& pos) const
std::shared_ptr<T> NavigationHelper::GetClosestObstacle(const PosMsg::SharedPtr& pos, bool countObjective) const
{
std::shared_ptr<T> closest_obstacle = nullptr;
auto robotPos = Point(pos->x, pos->y, pos->theta);
@@ -190,9 +190,9 @@ namespace Modelec
for (const auto& obstacle : GetPathfinding()->GetObstacles())
{
if (auto obs = std::dynamic_pointer_cast<T>(obstacle.second))
if (auto obs = std::dynamic_pointer_cast<T>(obstacle))
{
if (!obs->IsAtObjective())
if (!obs->IsAtObjective() || countObjective)
{
auto obsPoint = obs->GetPosition();
double distance = Point::distance(robotPos, obsPoint);

View File

@@ -2,6 +2,7 @@
#include "obstacle.hpp"
#include <modelec_interfaces/msg/odometry_pos.hpp>
#include <modelec_utils/config.hpp>
namespace Modelec
{
@@ -18,7 +19,7 @@ namespace Modelec
BoxObstacle(const BoxObstacle&) = default;
BoxObstacle(int id, int x, int y, double theta, int w, int h, const std::string& type);
BoxObstacle(tinyxml2::XMLElement* obstacleElem);
BoxObstacle(int id);
BoxObstacle(const modelec_interfaces::msg::Obstacle& msg);
Point GetOptimizedGetPos(const modelec_interfaces::msg::OdometryPos::SharedPtr& msg) const;
@@ -42,8 +43,8 @@ namespace Modelec
std::array<Team, 4> colors_ = {
BLUE,
BLUE,
BLUE,
BLUE
YELLOW,
YELLOW
};
};
}

View File

@@ -18,8 +18,8 @@ namespace Modelec
}
Obstacle(int id, int x, int y, double theta, int w, int h, const std::string& type);
Obstacle(tinyxml2::XMLElement* obstacleElem);
Obstacle(const modelec_interfaces::msg::Obstacle& msg);
Obstacle(int id);
Obstacle(const Obstacle& other) = default;
virtual ~Obstacle() = default;
@@ -32,7 +32,7 @@ namespace Modelec
double GetTheta() const;
int GetWidth() const;
int GetHeight() const;
const std::string& Type() const;
const std::string& GetType() const;
Point GetPosition() const;
void SetId(int id);
@@ -52,11 +52,15 @@ namespace Modelec
bool IsAtObjective() const;
void SetAtObjective(bool atObjective);
bool ShouldTakeCountInPathfinding() const;
void SetTakeCountInPathfinding(bool takeCount);
protected:
int id_, x_, y_, w_, h_;
double theta_;
std::string type_;
bool isAtObjective = false;
bool takeCountInPathfinding = true;
};
}

View File

@@ -2,39 +2,11 @@
#include <rclcpp/rclcpp.hpp>
#include <modelec_utils/point.hpp>
#include <modelec_strat/obstacle/obstacle.hpp>
#include <tinyxml2.h>
#include <std_msgs/msg/int64.hpp>
#include <std_msgs/msg/empty.hpp>
namespace Modelec
{
class PamiManger : public rclcpp::Node
{
public:
PamiManger();
bool ReadFromXML(const std::string& filename);
protected:
std::vector<Obstacle> zones_;
int time_to_put_zone_ = 0;
int time_to_remove_top_pot_ = 0;
int score_to_add_ = 0;
rclcpp::TimerBase::SharedPtr timer_add_;
// rclcpp::TimerBase::SharedPtr timer_remove_;
rclcpp::Subscription<std_msgs::msg::Int64>::SharedPtr start_time_sub_;
rclcpp::Publisher<modelec_interfaces::msg::Obstacle>::SharedPtr add_obs_pub_;
rclcpp::Publisher<modelec_interfaces::msg::Obstacle>::SharedPtr remove_obs_pub_;
rclcpp::Publisher<std_msgs::msg::Int64>::SharedPtr score_pub_;
rclcpp::Subscription<std_msgs::msg::Empty>::SharedPtr reset_strat_sub_;
};
}

View File

@@ -73,14 +73,14 @@ namespace Modelec
//void SetStartAndGoal(const PosMsg::SharedPtr& start, const PosMsg::SharedPtr& goal);
bool LoadObstaclesFromXML(const std::string& filename);
void LoadObstaclesFromXML();
void SetCurrentPos(const PosMsg::SharedPtr& pos);
[[nodiscard]] std::shared_ptr<Obstacle> GetObstacle(int id) const;
std::map<int, std::shared_ptr<Obstacle>> GetObstacles() const {
return obstacle_map_;
std::vector<std::shared_ptr<Obstacle>> GetObstacles() const {
return obstacles_;
}
void RemoveObstacle(int id);
@@ -114,7 +114,7 @@ namespace Modelec
std::vector<std::vector<int>> grid_;
std::map<int, std::shared_ptr<Obstacle>> obstacle_map_;
std::vector<std::shared_ptr<Obstacle>> obstacles_;
PosMsg::SharedPtr current_start_;
PosMsg::SharedPtr current_goal_;

View File

@@ -2,13 +2,17 @@
#include "modelec_strat/action_executor.hpp"
Modelec::DownAction::DownAction(const std::shared_ptr<ActionExecutor>& action_executor) : BaseAction(action_executor), side_(BOTH), inverted_(false)
Modelec::DownAction::DownAction(const std::shared_ptr<ActionExecutor>& action_executor) : BaseAction(action_executor),
side_(BOTH), inverted_(false)
{
steps_.push(ActionExec::DOWN_STEP);
steps_.push(ActionExec::DONE_STEP);
InitConfig();
}
Modelec::DownAction::DownAction(const std::shared_ptr<ActionExecutor>& action_executor, Side side, bool inverted) : DownAction(action_executor)
Modelec::DownAction::DownAction(const std::shared_ptr<ActionExecutor>& action_executor, Side side,
bool inverted) : DownAction(action_executor)
{
side_ = side;
inverted_ = inverted;
@@ -30,53 +34,27 @@ void Modelec::DownAction::Next()
case ActionExec::DOWN_STEP:
{
ActionServoTimedArray msg;
msg.items.resize(side_ == BOTH ? 8 : 4);
if (side_ == FRONT || side_ == BOTH)
{
msg.items[0].id = 0;
msg.items[0].start_angle = 1.76;
msg.items[0].end_angle = 2.93;
msg.items[0].duration_s = 2;
msg.items[1].id = 1;
msg.items[1].start_angle = 2.06;
msg.items[1].end_angle = 0.91;
msg.items[1].duration_s = 2;
msg.items[2].id = 2;
msg.items[2].start_angle = 0.5;
msg.items[2].end_angle = inverted_ ? 0 : 3.05;
msg.items[2].duration_s = 1.7;
msg.items[3].id = 3;
msg.items[3].start_angle = 2.6;
msg.items[3].end_angle = inverted_ ? 3.1 : 0.3;
msg.items[3].duration_s = 1.7;
if (inverted_)
{
msg.items.insert(msg.items.end(), front_rotated_msg_.begin(), front_rotated_msg_.end());
} else
{
msg.items.insert(msg.items.end(), front_unrotated_msg_.begin(), front_unrotated_msg_.end());
}
}
if (side_ == BACK || side_ == BOTH)
{
int i = side_ == BOTH ? 4 : 0;
msg.items[i].id = 8;
msg.items[i].start_angle = 0;
msg.items[i].end_angle = 0;
msg.items[i].duration_s = 0.5;
msg.items[i+1].id = 9;
msg.items[i+1].start_angle = 0;
msg.items[i+1].end_angle = 0;
msg.items[i+1].duration_s = 0.5;
msg.items[i+2].id = 10;
msg.items[i+2].start_angle = 0;
msg.items[i+2].end_angle = 0;
msg.items[i+2].duration_s = 0.5;
msg.items[i+3].id = 11;
msg.items[i+3].start_angle = 0;
msg.items[i+3].end_angle = 0;
msg.items[i+3].duration_s = 0.5;
if (inverted_)
{
msg.items.insert(msg.items.end(), back_rotated_msg_.begin(), back_rotated_msg_.end());
} else
{
msg.items.insert(msg.items.end(), back_unrotated_msg_.begin(), back_unrotated_msg_.end());
}
}
action_executor_->MoveServoTimed(msg);
@@ -111,6 +89,17 @@ void Modelec::DownAction::SetInverted(bool inverted)
inverted_ = inverted;
}
void Modelec::DownAction::InitConfig()
{
if (isConfigInit_) return;
isConfigInit_ = true;
front_unrotated_msg_ = Config::get<std::vector<ActionServoTimed>>("action.down.front.unrotated.msg");
front_rotated_msg_ = Config::get<std::vector<ActionServoTimed>>("action.down.front.rotated.msg");
back_unrotated_msg_ = Config::get<std::vector<ActionServoTimed>>("action.down.back.unrotated.msg");
back_rotated_msg_ = Config::get<std::vector<ActionServoTimed>>("action.down.back.rotated.msg");
}
void Modelec::DownAction::End()
{
if (side_ == BOTH)

View File

@@ -6,6 +6,8 @@ Modelec::FreeAction::FreeAction(const std::shared_ptr<ActionExecutor>& action_ex
{
steps_.push(ActionExec::FREE_STEP);
steps_.push(ActionExec::DONE_STEP);
InitConfig();
}
Modelec::FreeAction::FreeAction(const std::shared_ptr<ActionExecutor>& action_executor, Side side, int n) : FreeAction(action_executor)
@@ -45,9 +47,9 @@ void Modelec::FreeAction::Next()
for (size_t i = 0; i < servos_.size(); i++)
{
msg.items[i].id = servos_[i].first + (servos_[i].second ? 4 : 12);
msg.items[i].start_angle = 3;
msg.items[i].end_angle = 1;
msg.items[i].duration_s = 0.5;
msg.items[i].start_angle = start_angle_;
msg.items[i].end_angle = end_angle_;
msg.items[i].duration_s = duration_s_;
}
action_executor_->MoveServoTimed(msg);
@@ -91,6 +93,18 @@ void Modelec::FreeAction::AddServos(const std::vector<std::pair<int, Side>>& ser
servos_.insert(servos_.end(), servos.begin(), servos.end());
}
void Modelec::FreeAction::InitConfig()
{
if (isConfigInit_) return;
isConfigInit_ = true;
first_servo_ = Config::get<int>("action.free.id@first", 0);
second_servo_ = Config::get<int>("action.free.id@second", 0);
start_angle_ = Config::get<double>("action.free.msg@start_angle", 3);
end_angle_ = Config::get<double>("action.free.msg@end_angle", 1);
duration_s_ = Config::get<double>("action.free.msg@duration_s", 0.5);
}
void Modelec::FreeAction::End()
{
for (auto servo : servos_)

View File

@@ -1,69 +0,0 @@
#include <modelec_strat/action/look_on_action.hpp>
#include "modelec_strat/action_executor.hpp"
Modelec::LookOnAction::LookOnAction(const std::shared_ptr<ActionExecutor>& action_executor) : BaseAction(action_executor)
{
steps_.push(ActionExec::LOOK_ON_STEP);
steps_.push(ActionExec::DONE_STEP);
}
Modelec::LookOnAction::LookOnAction(const std::shared_ptr<ActionExecutor>& action_executor, Side side) : LookOnAction(action_executor)
{
side_ = side;
}
void Modelec::LookOnAction::Next()
{
if (steps_.empty())
{
done_ = true;
return;
}
auto step = steps_.front();
steps_.pop();
switch (step)
{
case ActionExec::LOOK_ON_STEP:
{
modelec_interfaces::msg::ActionServoTimedArray msg;
msg.items.resize(1);
msg.items[0].id = 18;
msg.items[0].start_angle = action_executor_->cam_side_ == CENTER ? 1 : action_executor_->cam_side_ == FRONT ? 0 : 2;
msg.items[0].end_angle = side_ == CENTER ? 1 : side_ == FRONT ? 0 : 2;
msg.items[0].duration_s = 0.5;
action_executor_->MoveServoTimed(msg);
}
break;
case ActionExec::DONE_STEP:
{
done_ = true;
}
break;
default:
break;
}
}
void Modelec::LookOnAction::Init(const std::vector<std::string>& params)
{
if (!params.empty())
{
SetSide(static_cast<Side>(std::stoi(params[0])));
}
}
void Modelec::LookOnAction::SetSide(Side side)
{
side_ = side;
}
void Modelec::LookOnAction::End()
{
action_executor_->cam_side_ = side_;
}

View File

@@ -6,6 +6,8 @@ Modelec::TakeAction::TakeAction(const std::shared_ptr<ActionExecutor>& action_ex
{
steps_.push(ActionExec::TAKE_STEP);
steps_.push(ActionExec::DONE_STEP);
InitConfig();
}
Modelec::TakeAction::TakeAction(const std::shared_ptr<ActionExecutor>& action_executor, Side side, int n) : TakeAction(action_executor)
@@ -44,10 +46,10 @@ void Modelec::TakeAction::Next()
for (size_t i = 0; i < servos_.size(); i++)
{
msg.items[i].id = servos_[i].first + (servos_[i].second ? 4 : 12);
msg.items[i].start_angle = 0;
msg.items[i].end_angle = 3;
msg.items[i].duration_s = 0.5;
msg.items[i].id = servos_[i].first + (servos_[i].second ? first_servo_ : second_servo_);
msg.items[i].start_angle = start_angle_;
msg.items[i].end_angle = end_angle_;
msg.items[i].duration_s = duration_s_;
}
action_executor_->MoveServoTimed(msg);
@@ -91,6 +93,18 @@ void Modelec::TakeAction::AddServos(const std::vector<std::pair<int, Side>>& ser
servos_.insert(servos_.end(), servos.begin(), servos.end());
}
void Modelec::TakeAction::InitConfig()
{
if (isConfigInit_) return;
isConfigInit_ = true;
first_servo_ = Config::get<int>("action.take.id@first", 0);
second_servo_ = Config::get<int>("action.take.id@second", 0);
start_angle_ = Config::get<double>("action.take.msg@start_angle", 3);
end_angle_ = Config::get<double>("action.take.msg@end_angle", 1);
duration_s_ = Config::get<double>("action.take.msg@duration_s", 0.5);
}
void Modelec::TakeAction::End()
{
for (auto servo : servos_)

View File

@@ -6,6 +6,8 @@ Modelec::ThermoAction::ThermoAction(const std::shared_ptr<ActionExecutor>& actio
{
steps_.push(ActionExec::THERMO_STEP);
steps_.push(ActionExec::DONE_STEP);
InitConfig();
}
Modelec::ThermoAction::ThermoAction(const std::shared_ptr<ActionExecutor>& action_executor, Side side, bool deploy) : ThermoAction(action_executor)
@@ -31,22 +33,26 @@ void Modelec::ThermoAction::Next()
{
modelec_interfaces::msg::ActionServoTimedArray msg;
msg.items.resize(side_ == BOTH ? 2 : 1);
if (side_ == LEFT || side_ == BOTH)
{
msg.items[0].id = 16;
msg.items[0].start_angle = deploy_ ? 1 : 2;
msg.items[0].end_angle = deploy_ ? 2 : 1;
msg.items[0].duration_s = 0.5;
if (deploy_)
{
msg.items.insert(msg.items.end(), left_deploy_msg_.begin(), left_deploy_msg_.end());
} else
{
msg.items.insert(msg.items.end(), left_undeploy_msg_.begin(), left_undeploy_msg_.end());
}
}
if (side_ == RIGHT || side_ == BOTH)
{
msg.items[side_ == BOTH ? 1 : 0].id = 17;
msg.items[side_ == BOTH ? 1 : 0].start_angle = deploy_ ? 1 : 2;
msg.items[side_ == BOTH ? 1 : 0].end_angle = deploy_ ? 2 : 1;
msg.items[side_ == BOTH ? 1 : 0].duration_s = 0.5;
if (deploy_)
{
msg.items.insert(msg.items.end(), right_deploy_msg_.begin(), right_deploy_msg_.end());
} else
{
msg.items.insert(msg.items.end(), right_undeploy_msg_.begin(), right_undeploy_msg_.end());
}
}
action_executor_->MoveServoTimed(msg);
@@ -81,6 +87,17 @@ void Modelec::ThermoAction::SetDeploy(bool deploy)
deploy_ = deploy;
}
void Modelec::ThermoAction::InitConfig()
{
if (isConfigInit_) return;
isConfigInit_ = true;
left_deploy_msg_ = Config::get<std::vector<ActionServoTimed>>("action.thermo.left.deploy");
left_undeploy_msg_ = Config::get<std::vector<ActionServoTimed>>("action.thermo.left.undeploy");
right_deploy_msg_ = Config::get<std::vector<ActionServoTimed>>("action.thermo.right.deploy");
right_undeploy_msg_ = Config::get<std::vector<ActionServoTimed>>("action.thermo.right.undeploy");
}
void Modelec::ThermoAction::End()
{
action_executor_->thermo_state_[side_] = deploy_;

View File

@@ -6,6 +6,8 @@ Modelec::ToggleServoAction::ToggleServoAction(const std::shared_ptr<ActionExecut
{
steps_.push(ActionExec::TOGGLE_SERVO_STEP);
steps_.push(ActionExec::DONE_STEP);
InitConfig();
}
Modelec::ToggleServoAction::ToggleServoAction(const std::shared_ptr<ActionExecutor>& action_executor, Side side, int n) : ToggleServoAction(action_executor)
@@ -44,10 +46,10 @@ void Modelec::ToggleServoAction::Next()
for (size_t i = 0; i < servos_.size(); i++)
{
msg.items[i].id = servos_[i].first + (servos_[i].second ? 4 : 12);
msg.items[i].start_angle = action_executor_->servo_pos_[servos_[i].first + (servos_[i].second ? 0 : 4)] ? 1 : 3;
msg.items[i].end_angle = action_executor_->servo_pos_[servos_[i].first + (servos_[i].second ? 0 : 4)] ? 3 : 1;
msg.items[i].duration_s = 0.5;
msg.items[i].id = servos_[i].first + (servos_[i].second ? first_servo_ : second_servo_);
msg.items[i].start_angle = action_executor_->servo_pos_[servos_[i].first + (servos_[i].second ? 0 : 4)] ? start_angle_ : end_angle_;
msg.items[i].end_angle = action_executor_->servo_pos_[servos_[i].first + (servos_[i].second ? 0 : 4)] ? end_angle_ : start_angle_;
msg.items[i].duration_s = duration_s_;
}
action_executor_->MoveServoTimed(msg);
@@ -91,6 +93,18 @@ void Modelec::ToggleServoAction::AddServos(const std::vector<std::pair<int, Side
servos_.insert(servos_.end(), servos.begin(), servos.end());
}
void Modelec::ToggleServoAction::InitConfig()
{
if (isConfigInit_) return;
isConfigInit_ = true;
first_servo_ = Config::get<int>("action.toggle.id@first", 0);
second_servo_ = Config::get<int>("action.toggle.id@second", 0);
start_angle_ = Config::get<double>("action.toggle.msg@start_angle", 3);
end_angle_ = Config::get<double>("action.toggle.msg@end_angle", 1);
duration_s_ = Config::get<double>("action.toggle.msg@duration_s", 0.5);
}
void Modelec::ToggleServoAction::End()
{
for (auto servo : servos_)

View File

@@ -6,6 +6,8 @@ Modelec::UPAction::UPAction(const std::shared_ptr<ActionExecutor>& action_execut
{
steps_.push(ActionExec::UP_STEP);
steps_.push(ActionExec::DONE_STEP);
InitConfig();
}
Modelec::UPAction::UPAction(const std::shared_ptr<ActionExecutor>& action_executor, Side side) : UPAction(action_executor)
@@ -29,53 +31,27 @@ void Modelec::UPAction::Next()
case ActionExec::UP_STEP:
{
ActionServoTimedArray msg;
msg.items.resize(side_ == BOTH ? 8 : 4);
if (side_ == FRONT || side_ == BOTH)
{
msg.items[0].id = 0;
msg.items[0].start_angle = 2.93;
msg.items[0].end_angle = 1.76;
msg.items[0].duration_s = 2;
msg.items[1].id = 1;
msg.items[1].start_angle = 0.91;
msg.items[1].end_angle = 2.06;
msg.items[1].duration_s = 2;
msg.items[2].id = 2;
msg.items[2].start_angle = action_executor_->arm_pos_[FRONT].rotated ? 0 : 3.2;
msg.items[2].end_angle = 0.5;
msg.items[2].duration_s = 2;
msg.items[3].id = 3;
msg.items[3].start_angle = action_executor_->arm_pos_[FRONT].rotated ? 3.1 : 0;
msg.items[3].end_angle = 2.6;
msg.items[3].duration_s = 2;
if (action_executor_->arm_pos_[FRONT].rotated)
{
msg.items.insert(msg.items.end(), front_rotated_msg_.begin(), front_rotated_msg_.end());
} else
{
msg.items.insert(msg.items.end(), front_unrotated_msg_.begin(), front_unrotated_msg_.end());
}
}
if (side_ == BACK || side_ == BOTH) {
int i = side_ == BOTH ? 4 : 0;
msg.items[i].id = 8;
msg.items[i].start_angle = 0;
msg.items[i].end_angle = 0;
msg.items[i].duration_s = 0.5;
msg.items[i+1].id = 9;
msg.items[i+1].start_angle = 0;
msg.items[i+1].end_angle = 0;
msg.items[i+1].duration_s = 0.5;
msg.items[i+2].id = 10;
msg.items[i+2].start_angle = 0;
msg.items[i+2].end_angle = 0;
msg.items[i+2].duration_s = 0.5;
msg.items[i+3].id = 11;
msg.items[i+3].start_angle = 0;
msg.items[i+3].end_angle = 0;
msg.items[i+3].duration_s = 0.5;
if (side_ == BACK || side_ == BOTH)
{
if (action_executor_->arm_pos_[BACK].rotated)
{
msg.items.insert(msg.items.end(), back_rotated_msg_.begin(), back_rotated_msg_.end());
} else
{
msg.items.insert(msg.items.end(), back_unrotated_msg_.begin(), back_unrotated_msg_.end());
}
}
action_executor_->MoveServoTimed(msg);
@@ -104,6 +80,17 @@ void Modelec::UPAction::SetSide(Side side)
side_ = side;
}
void Modelec::UPAction::InitConfig()
{
if (isConfigInit_) return;
isConfigInit_ = true;
front_rotated_msg_ = Config::get<std::vector<ActionServoTimed>>("action.up.front.rotated.msg");
front_unrotated_msg_ = Config::get<std::vector<ActionServoTimed>>("action.up.front.unrotated.msg");
back_rotated_msg_ = Config::get<std::vector<ActionServoTimed>>("action.up.back.rotated.msg");
back_unrotated_msg_ = Config::get<std::vector<ActionServoTimed>>("action.up.back.unrotated.msg");
}
void Modelec::UPAction::End()
{
if (side_ == BOTH)

View File

@@ -5,7 +5,6 @@
#include "modelec_strat/action/free_action.hpp"
#include "modelec_strat/action/take_action.hpp"
#include "modelec_strat/action/toggle_servo_action.hpp"
#include "modelec_strat/action/look_on_action.hpp"
#include "modelec_strat/action/thermo_action.hpp"
#include "modelec_utils/utils.hpp"
@@ -87,7 +86,6 @@ namespace Modelec
joy_sub_ = node_->create_subscription<sensor_msgs::msg::Joy>(
"/joy", 10, [this](const sensor_msgs::msg::Joy::SharedPtr msg)
{
// use game controller to manually control all the action. make it carefully
if (msg->buttons.size() >= 15)
{
if (msg->buttons[0] == 1) // A button
@@ -132,13 +130,19 @@ namespace Modelec
RotateArm(BaseAction::FRONT, false, !arm_pos_[BaseAction::FRONT].rotated);
}
}
else if (msg->buttons[14] == 1) // LT button
else if (msg->buttons[13] == 1) // LT button
{
ActivateThermo(BaseAction::Side::LEFT, !thermo_state_[BaseAction::Side::LEFT]);
if (action_done_)
{
ActivateThermo(BaseAction::Side::LEFT, !thermo_state_[BaseAction::Side::LEFT]);
}
}
else if (msg->buttons[15] == 1) // LR button
else if (msg->buttons[14] == 1) // LR button
{
ActivateThermo(BaseAction::Side::RIGHT, !thermo_state_[BaseAction::Side::RIGHT]);
if (action_done_)
{
ActivateThermo(BaseAction::Side::RIGHT, !thermo_state_[BaseAction::Side::RIGHT]);
}
}
}
if (msg->axes.size() == 8)
@@ -223,10 +227,18 @@ namespace Modelec
RCLCPP_DEBUG(node_->get_logger(), "Color detection succeeded: %s", res[1].c_str());
if (box_obstacles_[cam_side_] != nullptr) {
box_obstacles_[cam_side_]->ParseColor(res[1]);
auto color = split(res[1], ';');
if (box_obstacles_[BaseAction::FRONT] != nullptr) {
box_obstacles_[BaseAction::FRONT]->ParseColor(std::vector(color.begin(), color.begin() + 4));
} else {
RCLCPP_WARN(node_->get_logger(), "Color detection succeeded but no box in the cam side");
RCLCPP_WARN(node_->get_logger(), "Color detection succeeded but no box obstacle found to update");
}
if (box_obstacles_[BaseAction::BACK] != nullptr) {
box_obstacles_[BaseAction::BACK]->ParseColor(std::vector(color.begin() + 4, color.begin() + 8));
} else {
RCLCPP_WARN(node_->get_logger(), "Color detection succeeded but no box obstacle found to update");
}
});
@@ -476,22 +488,6 @@ namespace Modelec
}
}
void ActionExecutor::LookOn(BaseAction::Side side, bool force)
{
if (cam_side_ != side || force)
{
auto action = std::make_shared<LookOnAction>(shared_from_this(), side);
action_.push(action);
if (action_done_)
{
step_running_ = 0;
}
action_done_ = false;
Update();
}
}
void ActionExecutor::ActionFinished(const std::shared_ptr<BaseAction>& action)
{
RCLCPP_DEBUG(
@@ -539,27 +535,6 @@ namespace Modelec
void ActionExecutor::AskColor() const
{
/*if (!ask_color_client_->service_is_ready()) {
RCLCPP_ERROR(node_->get_logger(), "Color detection service not ready");
return;
}
auto request = std::make_shared<std_srvs::srv::Trigger::Request>();
ask_color_client_->async_send_request(request,
[this](rclcpp::Client<std_srvs::srv::Trigger>::SharedFuture future) {
auto response = future.get();
if (response->success) {
RCLCPP_INFO(node_->get_logger(), "Color detection succeeded: %s", response->message.c_str());
if (box_obstacles_[cam_side_] != nullptr) {
box_obstacles_[cam_side_]->ParseColor(response->message);
} else {
RCLCPP_WARN(node_->get_logger(), "Color detection succeeded but no box in the cam side");
}
} else {
RCLCPP_ERROR(node_->get_logger(), "Color detection failed: %s", response->message.c_str());
}
});*/
ask_pub_->publish(std_msgs::msg::Empty());
}
}

View File

@@ -4,27 +4,24 @@
namespace Modelec
{
DepositeZone::DepositeZone(tinyxml2::XMLElement* obstacleElem)
DepositeZone::DepositeZone(int id)
{
obstacleElem->QueryIntAttribute("id", &id_);
id_ = Config::get<int>("Map.DepositeZone[" + std::to_string(id) + "]@id", id);
w_ = Config::get<int>("Map.DepositeZone[" + std::to_string(id) + "].Pos@w");
h_ = Config::get<int>("Map.DepositeZone[" + std::to_string(id) + "].Pos@h");
position_ = Point(
Config::get<int>("Map.DepositeZone[" + std::to_string(id) + "].Pos@x"),
Config::get<int>("Map.DepositeZone[" + std::to_string(id) + "].Pos@y"),
Config::get<double>("Map.DepositeZone[" + std::to_string(id) + "].Pos@theta")
);
auto posElem = obstacleElem->FirstChildElement("Pos");
if (posElem)
if (Config::count("Map.DepositeZone[" + std::to_string(id) + "].TakeAngle.Angle") > 0)
{
posElem->QueryIntAttribute("x", &position_.x);
posElem->QueryIntAttribute("y", &position_.y);
posElem->QueryDoubleAttribute("theta", &position_.theta);
posElem->QueryIntAttribute("w", &w_);
posElem->QueryIntAttribute("h", &h_);
if (auto TakeAngleElem = obstacleElem->FirstChildElement("TakeAngle"))
{
for (auto takePos = TakeAngleElem->FirstChildElement("Angle"); takePos; takePos = takePos->NextSiblingElement("Angle"))
{
double angle = takePos->DoubleText(0);
take_angle_.push_back(angle);
}
}
take_angle_ = Config::getArray<double>("Map.DepositeZone[" + std::to_string(id) + "].TakeAngle.Angle");
} else
{
auto take_angle = Config::get<double>("Map.DepositeZone[" + std::to_string(id) + "].TakeAngle.Angle", position_.theta);
take_angle_ = {take_angle};
}
}
@@ -76,9 +73,6 @@ namespace Modelec
int DepositeZone::GetId() const
{ return id_; }
int DepositeZone::GetMaxPot() const
{ return max_pot_; }
int DepositeZone::GetWidth() const
{ return w_; }

View File

@@ -7,29 +7,22 @@ namespace Modelec
{
EnemyManager::EnemyManager() : Node("enemy_manager")
{
this->declare_parameter("enemy.detection.min_move_threshold_mm", 50.0);
this->declare_parameter("enemy.detection.refresh_rate", 1.0);
this->declare_parameter("enemy.detection.max_stationary_time_s", 10.0);
this->declare_parameter("enemy.detection.margin_detection_table_mm", 50.0);
this->declare_parameter("enemy.detection.min_emergency_distance_mm", 500.0);
if (!Config::load(ament_index_cpp::get_package_share_directory("modelec_strat") + "/data/config.xml"))
{
RCLCPP_ERROR(get_logger(), "Failed to load config file");
}
this->declare_parameter("map.size.map_width_mm", 3000.0);
this->declare_parameter("map.size.map_height_mm", 2000.0);
min_move_threshold_mm_ = Config::get<double>("config.enemy.detection.min_move_threshold_mm");
refresh_rate_s_ = Config::get<double>("config.enemy.detection.refresh_rate");
max_stationary_time_s_ = Config::get<double>("config.enemy.detection.max_stationary_time_s");
margin_detection_table_ = Config::get<double>("config.enemy.detection.margin_detection_table_mm");
min_emergency_distance_ = Config::get<double>("config.enemy.detection.min_emergency_distance_mm");
this->declare_parameter("robot.size.width_mm", 500.0);
this->declare_parameter("robot.size.length_mm", 500.0);
map_width_ = Config::get<double>("config.map.size.map_width_mm");
map_height_ = Config::get<double>("config.map.size.map_height_mm");
min_move_threshold_mm_ = this->get_parameter("enemy.detection.min_move_threshold_mm").as_double();
refresh_rate_s_ = this->get_parameter("enemy.detection.refresh_rate").as_double();
max_stationary_time_s_ = this->get_parameter("enemy.detection.max_stationary_time_s").as_double();
margin_detection_table_ = this->get_parameter("enemy.detection.margin_detection_table_mm").as_double();
min_emergency_distance_ = this->get_parameter("enemy.detection.min_emergency_distance_mm").as_double();
map_width_ = this->get_parameter("map.size.map_width_mm").as_double();
map_height_ = this->get_parameter("map.size.map_height_mm").as_double();
robot_width_ = this->get_parameter("robot.size.width_mm").as_double();
robot_length_ = this->get_parameter("robot.size.length_mm").as_double();
robot_width_ = Config::get<double>("config.robot.size.width_mm");
robot_length_ = Config::get<double>("config.robot.size.length_mm");
current_pos_sub_ = this->create_subscription<modelec_interfaces::msg::OdometryPos>(
"odometry/position", 10,

View File

@@ -0,0 +1,47 @@
#include <modelec_strat/missions/action_mission.hpp>
#include <modelec_strat/action_executor.hpp>
#include <modelec_utils/config.hpp>
namespace Modelec {
double ActionMission::TIMEOUT = 5.0;
bool ActionMission::IsInit = false;
ActionMission::ActionMission(const std::shared_ptr<ActionExecutor>& action_executor)
: action_executor_(action_executor)
{
}
void ActionMission::InitConfig()
{
TIMEOUT = Config::get<double>("config.mission.action.timeout.s", 5.0);
IsInit = true;
}
void ActionMission::Start(const rclcpp::Node::SharedPtr& node)
{
Mission::Start(node);
if (!IsInit)
{
InitConfig();
}
deploy_time_ = node->now();
}
bool ActionMission::Update()
{
if (!action_executor_->IsActionDone())
{
if ((node_->now() - deploy_time_).seconds() > TIMEOUT)
{
RCLCPP_WARN(node_->get_logger(), "ActionMission Update TIMEOUT");
return true;
}
return false;
}
return true;
}
}

View File

@@ -4,19 +4,17 @@
namespace Modelec {
FreeMission::FreeMission(const std::shared_ptr<NavigationHelper>& nav,
const std::shared_ptr<ActionExecutor>& action_executor,
BaseAction::Side side)
: side_(side), status_(MissionStatus::READY), nav_(nav), action_executor_(action_executor)
const std::shared_ptr<ActionExecutor>& action_executor, BaseAction::Side side) :
Mission(MissionStatus::READY), ActionMission(action_executor), MoveMission(nav),
side_(side)
{
}
void FreeMission::Start(rclcpp::Node::SharedPtr node)
void FreeMission::Start(const rclcpp::Node::SharedPtr& node)
{
node_ = node;
go_timeout_ = node_->now();
deploy_time_ = node_->now();
last_ask_waypoint_time_ = node_->now();
ActionMission::Start(node);
MoveMission::Start(node);
MinTimeMission::Start(node);
status_ = MissionStatus::RUNNING;
@@ -34,40 +32,13 @@ namespace Modelec {
steps_.push(DONE);
}
void FreeMission::Update()
bool FreeMission::Update()
{
if (!action_executor_->IsActionDone())
if (!ActionMission::Update() || !MoveMission::Update() || !MinTimeMission::Update())
{
return;
return false;
}
if (!nav_->HasArrived())
{
if ((node_->now() - go_timeout_).seconds() < 2 && (node_->now() - last_ask_waypoint_time_).seconds() > 1)
{
nav_->AskWaypoint();
last_ask_waypoint_time_ = node_->now();
return;
}
if ((node_->now() - go_timeout_).seconds() < 10)
{
return;
}
}
if (min_time_.has_value())
{
if ((node_->now() - min_time_.value()).seconds() < 0.1)
{
return;
}
else
{
min_time_.reset();
}
}
auto step_ = steps_.front();
steps_.pop();
@@ -82,7 +53,7 @@ namespace Modelec {
if (target_deposite_zone_ == nullptr)
{
status_ = MissionStatus::FAILED;
return;
return false;
}
auto depoPoint = target_deposite_zone_->GetBestTakePosition(Point(currPos->x, currPos->y, currPos->theta));
@@ -96,12 +67,10 @@ namespace Modelec {
if (nav_->GoToRotateFirst(pos, true, Pathfinding::FREE | Pathfinding::OBSTACLE, side_ == BaseAction::FRONT) != Pathfinding::FREE)
{
status_ = MissionStatus::FAILED;
return;
return false;
}
}
action_executor_->LookOn(BaseAction::Side::CENTER);
go_timeout_ = node_->now();
}
break;
@@ -195,7 +164,7 @@ namespace Modelec {
if (nav_->GoTo(pos, true, Pathfinding::FREE | Pathfinding::OBSTACLE) != Pathfinding::FREE)
{
status_ = MissionStatus::FAILED;
return;
return false;
}
go_timeout_ = node_->now();
@@ -210,8 +179,8 @@ namespace Modelec {
auto pos = nav_->GetCurrentPos();
obs->SetPosition(
pos->x + 300 * cos(pos->theta + (side_ == BaseAction::FRONT ? 0 : M_PI)),
pos->y + 300 * sin(pos->theta + (side_ == BaseAction::FRONT ? 0 : M_PI)),
target_deposite_zone_->GetPosition().x,
target_deposite_zone_->GetPosition().y,
pos->theta);
obs->SetAtObjective(true);
@@ -226,15 +195,12 @@ namespace Modelec {
default:
break;
}
return true;
}
void FreeMission::Clear()
{
}
MissionStatus FreeMission::GetStatus() const
{
return status_;
}
}

View File

@@ -5,23 +5,19 @@
namespace Modelec
{
GoHomeMission::GoHomeMission(const std::shared_ptr<NavigationHelper>& nav, const rclcpp::Time& start_time) :
status_(MissionStatus::READY), nav_(nav), start_time_(start_time)
Mission(MissionStatus::READY), MoveMission(nav),
start_time_(start_time)
{
}
void GoHomeMission::Start(rclcpp::Node::SharedPtr node)
void GoHomeMission::Start(const rclcpp::Node::SharedPtr& node)
{
node_ = node;
MoveMission::Start(node);
node->declare_parameter("mission_score.go_home", 0);
mission_score_ = node->get_parameter("mission_score.go_home").as_int();
mission_score_ = Config::get<int>("config.mission_score.go_home", 0);
score_pub_ = node_->create_publisher<std_msgs::msg::Int64>("/strat/score", 10);
go_timeout_ = node_->now();
last_ask_waypoint_time_ = node_->now();
status_ = MissionStatus::RUNNING;
std::queue<int> empty;
@@ -33,32 +29,11 @@ namespace Modelec
steps_.push(DONE);
}
void GoHomeMission::Update()
bool GoHomeMission::Update()
{
if (!nav_->HasArrived())
if (!MoveMission::Update())
{
if ((node_->now() - go_timeout_).seconds() < 2 && (node_->now() - last_ask_waypoint_time_).seconds() > 1)
{
nav_->AskWaypoint();
last_ask_waypoint_time_ = node_->now();
return;
}
if ((node_->now() - go_timeout_).seconds() < 10)
{
return;
}
}
if (min_time_.has_value())
{
if ((node_->now() - min_time_.value()).seconds() < 0.1)
{
return;
}
else
{
min_time_.reset();
}
return false;
}
auto step_ = steps_.front();
@@ -75,7 +50,7 @@ namespace Modelec
if (nav_->CanGoTo(home_point_.GetTakeBasePosition(), true) != Pathfinding::FREE)
{
status_ = MissionStatus::FAILED;
return;
return false;
}
}
nav_->RotateTo(home_point_);
@@ -90,7 +65,7 @@ namespace Modelec
if (nav_->GoTo(home_point_.GetTakeBasePosition(), true) != Pathfinding::FREE)
{
status_ = MissionStatus::FAILED;
return;
return false;
}
}
@@ -99,11 +74,6 @@ namespace Modelec
break;
case GO_CLOSE:
{
/*if ((node_->now() - start_time_).seconds() < 94)
{
break;
}*/
nav_->GoTo(home_point_, true);
go_timeout_ = node_->now();
@@ -121,14 +91,12 @@ namespace Modelec
default:
break;
}
return true;
}
void GoHomeMission::Clear()
{
}
MissionStatus GoHomeMission::GetStatus() const
{
return status_;
}
}

View File

@@ -0,0 +1,44 @@
#include <modelec_strat/missions/min_time_mission.hpp>
#include <modelec_utils/config.hpp>
namespace Modelec {
double MinTimeMission::MIN_TIME_DURATION = 0.1;
bool MinTimeMission::IsInit = false;
MinTimeMission::MinTimeMission() : min_time_(std::nullopt)
{
}
void MinTimeMission::InitConfig()
{
MIN_TIME_DURATION = Config::get<double>("config.mission.min_time.min_time_duration.s", 0.1);
IsInit = true;
}
void MinTimeMission::Start(const rclcpp::Node::SharedPtr& node)
{
Mission::Start(node);
if (!IsInit)
{
InitConfig();
}
}
bool MinTimeMission::Update()
{
if (min_time_.has_value())
{
if ((node_->now() - min_time_.value()).seconds() < MIN_TIME_DURATION)
{
return false;
}
else
{
min_time_.reset();
}
}
return true;
}
}

View File

@@ -0,0 +1,19 @@
#include <modelec_strat/missions/mission_base.hpp>
namespace Modelec
{
Mission::Mission(MissionStatus status)
: status_(status)
{
}
void Mission::Start(const rclcpp::Node::SharedPtr& node)
{
node_ = node;
}
MissionStatus Mission::GetStatus() const
{
return status_;
}
}

View File

@@ -0,0 +1,65 @@
#include <modelec_strat/missions/move_mission.hpp>
#include <modelec_strat/navigation_helper.hpp>
#include <modelec_utils/config.hpp>
namespace Modelec {
double MoveMission::MIN_ASK_WAYPOINT = 3.0;
double MoveMission::ASK_WAYPOINT_INTERVAL = 2.0;
double MoveMission::GO_TIMEOUT = 10.0;
bool MoveMission::IsInit = false;
MoveMission::MoveMission(const std::shared_ptr<NavigationHelper>& nav)
: nav_(nav)
{
}
void MoveMission::InitConfig()
{
MIN_ASK_WAYPOINT = Config::get<double>("config.mission.move.min_ask_waypoint.s", 3.0);
ASK_WAYPOINT_INTERVAL = Config::get<double>("config.mission.move.ask_waypoint_interval.s", 2.0);
GO_TIMEOUT = Config::get<double>("config.mission.move.go_timeout.s", 10.0);
IsInit = true;
}
void MoveMission::Start(const rclcpp::Node::SharedPtr& node)
{
Mission::Start(node);
if (!IsInit)
{
InitConfig();
}
go_timeout_ = node->now();
last_ask_waypoint_time_ = node->now();
}
bool MoveMission::Update()
{
if (!nav_->HasArrived())
{
auto now = node_->now();
double elapsed_total = (now - go_timeout_).seconds();
double elapsed_since_last_ask = (now - last_ask_waypoint_time_).seconds();
if (elapsed_total > MIN_ASK_WAYPOINT && elapsed_since_last_ask > ASK_WAYPOINT_INTERVAL)
{
nav_->AskWaypoint();
last_ask_waypoint_time_ = now;
return false;
}
if (elapsed_total > GO_TIMEOUT)
{
RCLCPP_WARN(node_->get_logger(), "Go Timeout");
return true;
}
return false;
}
return true;
}
}

View File

@@ -4,19 +4,17 @@
namespace Modelec {
TakeMission::TakeMission(const std::shared_ptr<NavigationHelper>& nav,
const std::shared_ptr<ActionExecutor>& action_executor,
BaseAction::Side side)
: side_(side), status_(MissionStatus::READY), nav_(nav), action_executor_(action_executor)
const std::shared_ptr<ActionExecutor>& action_executor, BaseAction::Side side) :
Mission(MissionStatus::READY), ActionMission(action_executor), MoveMission(nav),
side_(side)
{
}
void TakeMission::Start(rclcpp::Node::SharedPtr node)
void TakeMission::Start(const rclcpp::Node::SharedPtr& node)
{
node_ = node;
go_timeout_ = node_->now();
deploy_time_ = node_->now();
last_ask_waypoint_time_ = node_->now();
ActionMission::Start(node);
MoveMission::Start(node);
MinTimeMission::Start(node);
status_ = MissionStatus::RUNNING;
@@ -31,37 +29,11 @@ namespace Modelec {
steps_.push(DONE);
}
void TakeMission::Update()
bool TakeMission::Update()
{
if (!action_executor_->IsActionDone())
if (!ActionMission::Update() || !MoveMission::Update() || !MinTimeMission::Update())
{
return;
}
if (!nav_->HasArrived())
{
if ((node_->now() - go_timeout_).seconds() < 2 && (node_->now() - last_ask_waypoint_time_).seconds() > 1)
{
nav_->AskWaypoint();
last_ask_waypoint_time_ = node_->now();
return;
}
if ((node_->now() - go_timeout_).seconds() < 10)
{
return;
}
}
if (min_time_.has_value())
{
if ((node_->now() - min_time_.value()).seconds() < 0.1)
{
return;
}
else
{
min_time_.reset();
}
return false;
}
auto step_ = steps_.front();
@@ -85,6 +57,8 @@ namespace Modelec {
auto pos = closestBox->GetOptimizedGetPos(nav_->GetCurrentPos()).GetTakeBasePosition();
pos.theta = Point::normalizeAngle(pos.theta + (side_ == BaseAction::FRONT ? 0 : M_PI));
// TODO the robot do not go to the exact position i would like it to go to the exact so he can go forward after to take the box in a good way
if (nav_->GoToRotateFirst(pos, false, Pathfinding::FREE, side_ == BaseAction::FRONT) != Pathfinding::FREE)
{
if (nav_->GoToRotateFirst(pos, true, Pathfinding::FREE, side_ == BaseAction::FRONT) != Pathfinding::FREE)
@@ -92,7 +66,7 @@ namespace Modelec {
if (nav_->GoToRotateFirst(pos, true, Pathfinding::FREE | Pathfinding::OBSTACLE, side_ == BaseAction::FRONT) != Pathfinding::FREE)
{
status_ = MissionStatus::FAILED;
break;
return false;
}
}
}
@@ -105,7 +79,7 @@ namespace Modelec {
if (action_executor_->box_obstacles_[side_] == nullptr)
{
status_ = MissionStatus::FAILED;
break;
return false;
}
auto pos = action_executor_->box_obstacles_[side_]->GetOptimizedGetPos(nav_->GetCurrentPos()).GetTakeClosePosition();
@@ -114,7 +88,7 @@ namespace Modelec {
if (nav_->GoToRotateFirst(pos, true, Pathfinding::FREE | Pathfinding::WALL | Pathfinding::OBSTACLE, side_ == BaseAction::FRONT) != Pathfinding::FREE)
{
status_ = MissionStatus::FAILED;
break;
return false;
}
go_timeout_ = node_->now();
@@ -137,7 +111,6 @@ namespace Modelec {
case UP:
{
action_executor_->Up(side_);
action_executor_->LookOn(side_);
deploy_time_ = node_->now();
}
break;
@@ -146,7 +119,7 @@ namespace Modelec {
if (action_executor_->box_obstacles_[side_] == nullptr)
{
status_ = MissionStatus::FAILED;
break;
return false;
}
nav_->GetPathfinding()->RemoveObstacle(action_executor_->box_obstacles_[side_]->GetId());
@@ -159,15 +132,12 @@ namespace Modelec {
default:
break;
}
return true;
}
void TakeMission::Clear()
{
}
MissionStatus TakeMission::GetStatus() const
{
return status_;
}
}

View File

@@ -7,18 +7,15 @@ namespace Modelec {
bool ThermoMission::IsThermoDone = false;
ThermoMission::ThermoMission(const std::shared_ptr<NavigationHelper>& nav,
const std::shared_ptr<ActionExecutor>& action_executor)
: status_(MissionStatus::READY), nav_(nav), action_executor_(action_executor)
const std::shared_ptr<ActionExecutor>& action_executor) :
Mission(MissionStatus::READY), ActionMission(action_executor), MoveMission(nav)
{
}
void ThermoMission::Start(rclcpp::Node::SharedPtr node)
void ThermoMission::Start(const rclcpp::Node::SharedPtr& node)
{
node_ = node;
go_timeout_ = node_->now();
deploy_time_ = node_->now();
last_ask_waypoint_time_ = node_->now();
ActionMission::Start(node);
MoveMission::Start(node);
status_ = MissionStatus::RUNNING;
@@ -35,37 +32,11 @@ namespace Modelec {
steps_.push(DONE);
}
void ThermoMission::Update()
bool ThermoMission::Update()
{
if (!action_executor_->IsActionDone())
if (!ActionMission::Update() || !MoveMission::Update())
{
return;
}
if (!nav_->HasArrived())
{
if ((node_->now() - go_timeout_).seconds() < 2 && (node_->now() - last_ask_waypoint_time_).seconds() > 1)
{
nav_->AskWaypoint();
last_ask_waypoint_time_ = node_->now();
return;
}
if ((node_->now() - go_timeout_).seconds() < 10)
{
return;
}
}
if (min_time_.has_value())
{
if ((node_->now() - min_time_.value()).seconds() < 0.1)
{
return;
}
else
{
min_time_.reset();
}
return false;
}
auto step_ = steps_.front();
@@ -80,7 +51,7 @@ namespace Modelec {
if (nav_->GoToRotateFirst(start, true, Pathfinding::FREE | Pathfinding::WALL, true) != Pathfinding::FREE)
{
status_ = MissionStatus::FAILED;
break;
return false;
}
go_timeout_ = node_->now();
@@ -93,7 +64,7 @@ namespace Modelec {
if (nav_->GoToRotateFirst(start, true, Pathfinding::FREE | Pathfinding::WALL, true) != Pathfinding::FREE)
{
status_ = MissionStatus::FAILED;
break;
return false;
}
go_timeout_ = node_->now();
@@ -101,7 +72,6 @@ namespace Modelec {
break;
case ACTIVATE_THERMO:
{
RCLCPP_INFO(node_->get_logger(), "Activating thermo");
action_executor_->ActivateThermo(nav_->GetTeamId() == NavigationHelper::YELLOW ? BaseAction::RIGHT : BaseAction::LEFT, true);
deploy_time_ = node_->now();
}
@@ -113,13 +83,12 @@ namespace Modelec {
if (nav_->GoToRotateFirst(pos, true, Pathfinding::FREE | Pathfinding::WALL, true) != Pathfinding::FREE)
{
status_ = MissionStatus::FAILED;
break;
return false;
}
}
break;
case DEACTIVATE_THERMO:
{
RCLCPP_INFO(node_->get_logger(), "Deactivating thermo");
action_executor_->ActivateThermo(nav_->GetTeamId() == NavigationHelper::YELLOW ? BaseAction::RIGHT : BaseAction::LEFT, false);
deploy_time_ = node_->now();
}
@@ -135,15 +104,12 @@ namespace Modelec {
default:
break;
}
return true;
}
void ThermoMission::Clear()
{
}
MissionStatus ThermoMission::GetStatus() const
{
return status_;
}
}

View File

@@ -1,6 +1,7 @@
#include <modelec_strat/navigation_helper.hpp>
#include <utility>
#include <ament_index_cpp/get_package_share_directory.hpp>
#include <modelec_utils/config.hpp>
namespace Modelec
{
@@ -9,41 +10,9 @@ namespace Modelec
NavigationHelper::NavigationHelper(const rclcpp::Node::SharedPtr& node) : node_(node)
{
node_->declare_parameter("factor_close_enemy", -0.5);
node_->declare_parameter("factor.theta", 20.0);
node_->declare_parameter("enemy.detection.min_emergency_distance_mm", 390);
factor_close_enemy_ = node_->get_parameter("factor_close_enemy").as_double();
factor_theta_ = node_->get_parameter("factor.theta").as_double();
enemy_emergency_distance_ = node_->get_parameter("enemy.detection.min_emergency_distance_mm").as_int();
node_->declare_parameter("home.yellow.x", 0);
node_->declare_parameter("home.yellow.y", 0);
node_->declare_parameter("home.yellow.theta", 0.0);
node_->declare_parameter("home.blue.x", 0);
node_->declare_parameter("home.blue.y", 0);
node_->declare_parameter("home.blue.theta", 0.0);
node_->declare_parameter("thermo.yellow.start.x", 0);
node_->declare_parameter("thermo.yellow.start.y", 0);
node_->declare_parameter("thermo.yellow.start.theta", 0.0);
node_->declare_parameter("thermo.yellow.finish.x", 0);
node_->declare_parameter("thermo.yellow.finish.y", 0);
node_->declare_parameter("thermo.yellow.finish.theta", 0.0);
node_->declare_parameter("thermo.blue.start.x", 0);
node_->declare_parameter("thermo.blue.start.y", 0);
node_->declare_parameter("thermo.blue.start.theta", 0.0);
node_->declare_parameter("thermo.blue.finish.x", 0);
node_->declare_parameter("thermo.blue.finish.y", 0);
node_->declare_parameter("thermo.blue.finish.theta", 0.0);
node_->declare_parameter("spawn.yellow_top.x", 0);
node_->declare_parameter("spawn.yellow_top.y", 0);
node_->declare_parameter("spawn.yellow_top.theta", 0.0);
node_->declare_parameter("spawn.blue_top.x", 0);
node_->declare_parameter("spawn.blue_top.y", 0);
node_->declare_parameter("spawn.blue_top.theta", 0.0);
factor_close_enemy_ = Config::get<float>("config.factor.close_enemy", 1.0f);
factor_theta_ = Config::get<float>("config.factor.theta", 1.0f);
enemy_emergency_distance_ = Config::get<int>("config.enemy.detection.min_emergency_distance_mm", 300);
pathfinding_ = std::make_shared<Pathfinding>(node);
@@ -90,13 +59,6 @@ namespace Modelec
start_odo_pub_ = node_->create_publisher<std_msgs::msg::Bool>("odometry/start", 10);
std::string deposite_zone_path = ament_index_cpp::get_package_share_directory("modelec_strat") +
"/data/deposite_zone.xml";
if (!LoadDepositeZoneFromXML(deposite_zone_path))
{
RCLCPP_ERROR(node_->get_logger(), "Failed to load obstacles from XML");
}
spawn_pub_ = node_->create_publisher<modelec_interfaces::msg::Spawn>("nav/spawn", 10);
ask_spawn_srv_ = node->create_service<std_srvs::srv::Empty>(
@@ -130,6 +92,8 @@ namespace Modelec
odo_ask_waypoint_pub_ = node_->create_publisher<std_msgs::msg::Empty>(
"odometry/ask_active_waypoint", 30);
LoadDepositeZoneFromXML();
}
void NavigationHelper::ReInit()
@@ -333,7 +297,7 @@ namespace Modelec
{
double angle = std::atan2(pos.y - current_pos_->y, pos.x - current_pos_->x);
if (Point::angleDiff(angle, (current_pos_->theta + (front ? 0 : M_PI))) > M_PI / 4)
if (std::abs(Point::angleDiff(angle, (current_pos_->theta + (front ? 0 : M_PI)))) > M_PI / 4)
{
Rotate(angle);
return true;
@@ -505,32 +469,14 @@ namespace Modelec
return current_pos_;
}
bool NavigationHelper::LoadDepositeZoneFromXML(const std::string& filename)
void NavigationHelper::LoadDepositeZoneFromXML()
{
tinyxml2::XMLDocument doc;
if (doc.LoadFile(filename.c_str()) != tinyxml2::XML_SUCCESS)
for (size_t i = 0; i < Config::count("Map.DepositeZone"); ++i)
{
RCLCPP_ERROR(node_->get_logger(), "Failed to load obstacle XML file: %s", filename.c_str());
return false;
deposite_zones_.push_back(std::make_shared<DepositeZone>(i));
}
tinyxml2::XMLElement* root = doc.FirstChildElement("Map");
if (!root)
{
RCLCPP_ERROR(node_->get_logger(), "No <Obstacles> root element in file");
return false;
}
for (tinyxml2::XMLElement* elem = root->FirstChildElement("DepositeZone");
elem != nullptr;
elem = elem->NextSiblingElement("DepositeZone"))
{
std::shared_ptr<DepositeZone> obs = std::make_shared<DepositeZone>(elem);
deposite_zones_[obs->GetId()] = obs;
}
RCLCPP_INFO(node_->get_logger(), "Loaded %zu zone from XML", deposite_zones_.size());
return true;
RCLCPP_INFO(node_->get_logger(), "Loaded %zu deposite zones from XML", deposite_zones_.size());
}
std::shared_ptr<DepositeZone> NavigationHelper::GetClosestDepositeZone(
@@ -541,10 +487,10 @@ namespace Modelec
auto posPoint = Point(pos->x, pos->y, pos->theta);
auto enemyPos = Point(last_enemy_pos_.x, last_enemy_pos_.y, last_enemy_pos_.theta);
for (const auto& [id, zone] : deposite_zones_)
for (const auto& zone : deposite_zones_)
{
if (blacklistedId.end() == std::find(
blacklistedId.begin(), blacklistedId.end(), id)
blacklistedId.begin(), blacklistedId.end(), zone->GetId())
&& (!only_free || !zone->Validate()))
{
auto zonePoint = zone->GetPosition();
@@ -567,29 +513,29 @@ namespace Modelec
{
PosMsg::SharedPtr home = std::make_shared<PosMsg>();
std::string prefix = (team_id_ == YELLOW) ? "home.yellow" : "home.blue";
std::string prefix = (team_id_ == YELLOW) ? "config.home.yellow" : "config.home.blue";
home->x = node_->get_parameter(prefix + ".x").as_int();
home->y = node_->get_parameter(prefix + ".y").as_int();
home->theta = node_->get_parameter(prefix + ".theta").as_double();
home->x = Config::get<int>(prefix + "@x");
home->y = Config::get<int>(prefix + "@y");
home->theta = Config::get<double>(prefix + "@theta");
return home;
}
std::array<Point, 2> NavigationHelper::GetThermoPositions()
{
std::string prefix = (team_id_ == YELLOW) ? "thermo.yellow" : "thermo.blue";
std::string prefix = (team_id_ == YELLOW) ? "config.thermo.yellow" : "config.thermo.blue";
Point start(
node_->get_parameter(prefix + ".start.x").as_int(),
node_->get_parameter(prefix + ".start.y").as_int(),
node_->get_parameter(prefix + ".start.theta").as_double()
Config::get<int>(prefix + ".start@x"),
Config::get<int>(prefix + ".start@y"),
Config::get<double>(prefix + ".start@theta")
);
Point finish(
node_->get_parameter(prefix + ".finish.x").as_int(),
node_->get_parameter(prefix + ".finish.y").as_int(),
node_->get_parameter(prefix + ".finish.theta").as_double()
Config::get<int>(prefix + ".finish@x"),
Config::get<int>(prefix + ".finish@y"),
Config::get<double>(prefix + ".finish@theta")
);
return {start, finish};
@@ -653,13 +599,13 @@ namespace Modelec
pathfinding_->OnEnemyPositionLongTime(msg);
Point enemy_pos(msg->x, msg->y, msg->theta);
for (auto& [id, zone] : deposite_zones_)
for (auto& zone : deposite_zones_)
{
auto zonePos = zone->GetPosition();
if (Point::distance(enemy_pos, zonePos) < (zone->GetWidth() / 2) + pathfinding_->enemy_margin_mm_)
{
std::shared_ptr<Obstacle> obs = std::make_shared<Obstacle>(
id, zonePos.x, zonePos.y, zonePos.theta, zone->GetWidth(), zone->GetHeight(), "enemy-zone");
zone->GetId(), zonePos.x, zonePos.y, zonePos.theta, zone->GetWidth(), zone->GetHeight(), "enemy-zone");
pathfinding_->AddObstacle(obs);
}
}
@@ -804,15 +750,15 @@ namespace Modelec
void NavigationHelper::SetupSpawn()
{
spawn_yellow_["top"] = Point(
node_->get_parameter("spawn.yellow_top.x").as_int(),
node_->get_parameter("spawn.yellow_top.y").as_int(),
node_->get_parameter("spawn.yellow_top.theta").as_double()
Config::get<int>("config.spawn.yellow.top@x"),
Config::get<int>("config.spawn.yellow.top@y"),
Config::get<double>("config.spawn.yellow.top@theta")
);
spawn_blue_["top"] = Point(
node_->get_parameter("spawn.blue_top.x").as_int(),
node_->get_parameter("spawn.blue_top.y").as_int(),
node_->get_parameter("spawn.blue_top.theta").as_double()
Config::get<int>("config.spawn.blue.top@x"),
Config::get<int>("config.spawn.blue.top@y"),
Config::get<double>("config.spawn.blue.top@theta")
);
}
}

View File

@@ -9,15 +9,17 @@ namespace Modelec
{
}
BoxObstacle::BoxObstacle(tinyxml2::XMLElement* obstacleElem)
: Obstacle(obstacleElem)
BoxObstacle::BoxObstacle(int id) : Obstacle(id)
{
possible_angles_.push_back(theta_);
for (auto elem = obstacleElem->FirstChildElement("possible-angle");
elem;
elem = elem->NextSiblingElement("possible-angle"))
if (Config::count("Obstacles.Obstacle[" + std::to_string(id) + "].possible-angle") > 0)
{
possible_angles_.push_back(elem->DoubleAttribute("theta"));
possible_angles_ = Config::getArray<double>("Obstacles.Obstacle[" + std::to_string(id) + "].possible-angle", [](const std::string& base)
{
return Config::get<double>(base + "@theta");
});
possible_angles_.push_back(theta_);
} else {
possible_angles_ = {theta_};
}
}

View File

@@ -1,5 +1,7 @@
#include <modelec_strat/obstacle/obstacle.hpp>
#include "modelec_utils/config.hpp"
namespace Modelec
{
Obstacle::Obstacle(int id, int x, int y, double theta, int w, int h, const std::string& type)
@@ -7,28 +9,22 @@ namespace Modelec
{
}
Obstacle::Obstacle(tinyxml2::XMLElement* obstacleElem)
{
const char* type = nullptr;
if (obstacleElem->QueryIntAttribute("id", &id_) != tinyxml2::XML_SUCCESS ||
obstacleElem->QueryIntAttribute("x", &x_) != tinyxml2::XML_SUCCESS ||
obstacleElem->QueryIntAttribute("y", &y_) != tinyxml2::XML_SUCCESS ||
obstacleElem->QueryDoubleAttribute("theta", &theta_) != tinyxml2::XML_SUCCESS ||
obstacleElem->QueryIntAttribute("width", &w_) != tinyxml2::XML_SUCCESS ||
obstacleElem->QueryIntAttribute("height", &h_) != tinyxml2::XML_SUCCESS ||
obstacleElem->QueryStringAttribute("type", &type) != tinyxml2::XML_SUCCESS)
{
RCLCPP_WARN(rclcpp::get_logger("Obstacle"), "Failed to parse obstacle element");
return;
}
type_ = type;
}
Obstacle::Obstacle(const modelec_interfaces::msg::Obstacle& msg)
: id_(msg.id), x_(msg.x), y_(msg.y), w_(msg.width), h_(msg.height), theta_(msg.theta), type_("unknown")
{
}
Obstacle::Obstacle(int id)
{
id_ = Config::get<int>("Obstacles.Obstacle[" + std::to_string(id) + "]@id", id);
x_ = Config::get<int>("Obstacles.Obstacle[" + std::to_string(id) + "]@x");
y_ = Config::get<int>("Obstacles.Obstacle[" + std::to_string(id) + "]@y");
theta_ = Config::get<double>("Obstacles.Obstacle[" + std::to_string(id) + "]@theta");
w_ = Config::get<int>("Obstacles.Obstacle[" + std::to_string(id) + "]@width");
h_ = Config::get<int>("Obstacles.Obstacle[" + std::to_string(id) + "]@height");
type_ = Config::get<std::string>("Obstacles.Obstacle[" + std::to_string(id) + "]@type", "box");
}
modelec_interfaces::msg::Obstacle Obstacle::toMsg() const
{
modelec_interfaces::msg::Obstacle msg;
@@ -62,7 +58,7 @@ namespace Modelec
int Obstacle::GetHeight() const
{ return h_; }
const std::string& Obstacle::Type() const
const std::string& Obstacle::GetType() const
{ return type_; }
Point Obstacle::GetPosition() const
@@ -118,4 +114,14 @@ namespace Modelec
{
isAtObjective = atObjective;
}
bool Obstacle::ShouldTakeCountInPathfinding() const
{
return takeCountInPathfinding;
}
void Obstacle::SetTakeCountInPathfinding(bool takeCount)
{
takeCountInPathfinding = takeCount;
}
}

View File

@@ -5,92 +5,6 @@ namespace Modelec
{
PamiManger::PamiManger() : Node("pami_manager")
{
this->declare_parameter("time_to_put_zone", 80);
this->declare_parameter("time_to_remove_top_pot", 70);
time_to_put_zone_ = this->get_parameter("time_to_put_zone").as_int();
time_to_remove_top_pot_ = this->get_parameter("time_to_remove_top_pot").as_int();
score_to_add_ = 0;
std::string obstacles_path = ament_index_cpp::get_package_share_directory("modelec_strat") +
"/data/pami_zone.xml";
if (!ReadFromXML(obstacles_path))
{
RCLCPP_ERROR(get_logger(), "Failed to load obstacles from XML");
}
start_time_sub_ = create_subscription<std_msgs::msg::Int64>(
"/strat/start_time", 10, [this](const std_msgs::msg::Int64::SharedPtr)
{
/*auto now = std::chrono::system_clock::now().time_since_epoch();
auto goal = std::chrono::nanoseconds(msg->data) + std::chrono::seconds(time_to_remove_top_pot_);
auto second_goal = std::chrono::nanoseconds(msg->data) + std::chrono::seconds(time_to_put_zone_);
timer_add_ = create_wall_timer(
second_goal - now,
[this]()
{
for (const auto& obs : zones_)
{
add_obs_pub_->publish(obs.toMsg());
std_msgs::msg::Int64 msg;
msg.data = score_to_add_;
score_pub_->publish(msg);
}
timer_add_->cancel();
});*/
});
add_obs_pub_ = create_publisher<modelec_interfaces::msg::Obstacle>(
"obstacle/add", 10);
remove_obs_pub_ = create_publisher<modelec_interfaces::msg::Obstacle>(
"obstacle/remove", 10);
score_pub_ = create_publisher<std_msgs::msg::Int64>("/strat/score", 10);
reset_strat_sub_ = create_subscription<std_msgs::msg::Empty>(
"/strat/reset", 10, [this](const std_msgs::msg::Empty::SharedPtr)
{
/*if (timer_add_)
{
timer_add_->cancel();
}*/
/*if (timer_remove_)
{
timer_remove_->cancel();
}*/
});
}
bool PamiManger::ReadFromXML(const std::string& filename)
{
tinyxml2::XMLDocument doc;
if (doc.LoadFile(filename.c_str()) != tinyxml2::XML_SUCCESS)
{
RCLCPP_ERROR(get_logger(), "Failed to load obstacle XML file: %s", filename.c_str());
return false;
}
tinyxml2::XMLElement* root = doc.FirstChildElement("pami_zone");
if (!root)
{
RCLCPP_ERROR(get_logger(), "No <pami-zone> root element in file");
return false;
}
for (tinyxml2::XMLElement* elem = root->FirstChildElement("zone");
elem;
elem = elem->NextSiblingElement("zone"))
{
zones_.push_back(Obstacle(elem));
}
RCLCPP_INFO(get_logger(), "Loaded zone obstacles from XML");
return true;
}
}

View File

@@ -1,5 +1,6 @@
#include <ament_index_cpp/get_package_share_directory.hpp>
#include <modelec_strat/pathfinding.hpp>
#include <modelec_utils/config.hpp>
namespace Modelec {
struct AStarNode {
@@ -22,39 +23,22 @@ namespace Modelec {
}
Pathfinding::Pathfinding(const rclcpp::Node::SharedPtr &node) : node_(node) {
node_->declare_parameter("map.size.map_width_mm", 3000);
node_->declare_parameter("map.size.map_height_mm", 2000);
node_->declare_parameter("map.size.grid_width", 300);
node_->declare_parameter("map.size.grid_height", 200);
map_width_mm_ = node_->get_parameter("map.size.map_width_mm").as_int();
map_height_mm_ = node_->get_parameter("map.size.map_height_mm").as_int();
grid_width_ = node_->get_parameter("map.size.grid_width").as_int();
grid_height_ = node_->get_parameter("map.size.grid_height").as_int();
map_width_mm_ = Config::get<int>("config.map.size.map_width_mm", 3000);
map_height_mm_ = Config::get<int>("config.map.size.map_height_mm", 2000);
grid_width_ = Config::get<int>("config.map.size.grid_width", 300);
grid_height_ = Config::get<int>("config.map.size.grid_height", 200);
node_->declare_parameter("robot.size.length_mm", 300);
node_->declare_parameter("robot.size.width_mm", 300);
node_->declare_parameter("robot.size.margin_mm", 100);
robot_length_mm_ = Config::get<int>("config.robot.size.length_mm", 300);
robot_width_mm_ = Config::get<int>("config.robot.size.width_mm", 200);
margin_mm_ = Config::get<int>("config.robot.size.margin_mm", 50);
robot_length_mm_ = node_->get_parameter("robot.size.length_mm").as_int();
robot_width_mm_ = node_->get_parameter("robot.size.width_mm").as_int();
margin_mm_ = node_->get_parameter("robot.size.margin_mm").as_int();
enemy_length_mm_ = Config::get<int>("config.enemy.size.length_mm", 300);
enemy_width_mm_ = Config::get<int>("config.enemy.size.width_mm", 300);
enemy_margin_mm_ = Config::get<int>("config.enemy.size.margin_mm", 0);
factor_close_enemy_ = Config::get<float>("config.factor.close_enemy", 1.0f);
node_->declare_parameter("enemy.size.length_mm", 300);
node_->declare_parameter("enemy.size.width_mm", 300);
node_->declare_parameter("enemy.size.margin_mm", 50);
node_->declare_parameter("enemy.factor_close_enemy", -0.5);
enemy_length_mm_ = node_->get_parameter("enemy.size.length_mm").as_int();
enemy_width_mm_ = node_->get_parameter("enemy.size.width_mm").as_int();
enemy_margin_mm_ = node_->get_parameter("enemy.size.margin_mm").as_int();
factor_close_enemy_ = node_->get_parameter("enemy.factor_close_enemy").as_double();
std::string obstacles_path = ament_index_cpp::get_package_share_directory("modelec_strat") +
"/data/obstacles.xml";
if (!LoadObstaclesFromXML(obstacles_path)) {
RCLCPP_ERROR(node_->get_logger(), "Failed to load obstacles from XML");
}
LoadObstaclesFromXML();
obstacle_add_sub_ = node_->create_subscription<modelec_interfaces::msg::Obstacle>(
"obstacle/add", 10,
@@ -201,7 +185,11 @@ namespace Modelec {
// 2. Fill obstacles with inflation
// TODO some bug exist with the inflate
for (const auto &[id, obstacle]: obstacle_map_) {
for (const auto &obstacle: obstacles_) {
if (!obstacle->ShouldTakeCountInPathfinding())
{
continue;
}
float cx = obstacle->GetX();
float cy = obstacle->GetY();
float width = obstacle->GetWidth() + inflate_x * 2 * cell_size_mm_x;
@@ -286,10 +274,10 @@ namespace Modelec {
if (!TestCollision(start_x, start_y, collisionMask) || !TestCollision(goal_x, goal_y, collisionMask)) {
if (!TestCollision(start_x, start_y, collisionMask)) {
RCLCPP_WARN(node_->get_logger(), "Start inside an obstacle");
RCLCPP_WARN(node_->get_logger(), "Start inside an obstacle x=%d y=%d mask=%d | %d", start_x, start_y, collisionMask, grid_[start_y][start_x]);
return {grid_[start_y][start_x], waypoints};
} else {
RCLCPP_WARN(node_->get_logger(), "Goal inside an obstacle");
RCLCPP_WARN(node_->get_logger(), "Goal inside an obstacle x=%d y=%d mask=%d | %d", goal_x, goal_y, collisionMask, grid_[goal_y][goal_x]);
return {grid_[goal_y][goal_x], waypoints};
}
}
@@ -505,11 +493,13 @@ namespace Modelec {
}
std::shared_ptr<Obstacle> Pathfinding::GetObstacle(int id) const {
return obstacle_map_.at(id);
return obstacles_.at(id);
}
void Pathfinding::RemoveObstacle(int id) {
obstacle_map_.erase(id);
obstacles_.erase(std::remove_if(obstacles_.begin(), obstacles_.end(),
[id](const std::shared_ptr<Obstacle> &obs) { return obs->GetId() == id; }),
obstacles_.end());
modelec_interfaces::msg::Obstacle msg;
msg.id = id;
@@ -517,7 +507,7 @@ namespace Modelec {
}
void Pathfinding::AddObstacle(const std::shared_ptr<Obstacle> &obstacle) {
obstacle_map_[obstacle->GetId()] = obstacle;
obstacles_.push_back(obstacle);
modelec_interfaces::msg::Obstacle msg = obstacle->toMsg();
obstacle_add_pub_->publish(msg);
}
@@ -542,7 +532,7 @@ namespace Modelec {
void Pathfinding::HandleAskObstacleRequest(const std::shared_ptr<std_srvs::srv::Empty::Request>,
const std::shared_ptr<std_srvs::srv::Empty::Response>) {
for (auto &[index, obs]: obstacle_map_) {
for (auto &obs: obstacles_) {
obstacle_add_pub_->publish(obs->toMsg());
}
}
@@ -554,7 +544,7 @@ namespace Modelec {
void Pathfinding::OnEnemyPositionLongTime(const modelec_interfaces::msg::OdometryPos::SharedPtr msg) {
Point enemyPos(msg->x, msg->y, msg->theta);
for (auto &[index, obs]: obstacle_map_) {
for (auto &obs: obstacles_) {
if (auto column = std::dynamic_pointer_cast<BoxObstacle>(obs)) {
if (Point::distance(enemyPos, column->GetPosition()) < enemy_width_mm_ + (column->GetWidth() / 2) +
enemy_margin_mm_) {
@@ -568,41 +558,29 @@ namespace Modelec {
if (y < 0 || y >= static_cast<int>(grid_.size()) ||
x < 0 || x >= static_cast<int>(grid_[y].size())) {
RCLCPP_WARN(node_->get_logger(), "TestCollision: access out of bounds x=%d y=%d", x, y);
return false; // ou true, selon ce que tu veux (false = pas de collision)
return false;
}
return (grid_[y][x] & collisionMask) && !(grid_[y][x] & ~collisionMask);
}
bool Pathfinding::LoadObstaclesFromXML(const std::string &filename) {
tinyxml2::XMLDocument doc;
if (doc.LoadFile(filename.c_str()) != tinyxml2::XML_SUCCESS) {
RCLCPP_ERROR(node_->get_logger(), "Failed to load obstacle XML file: %s", filename.c_str());
return false;
void Pathfinding::LoadObstaclesFromXML() {
auto obs = Config::getArray<std::string>("Obstacles.Obstacle", [](const std::string& prefix)
{
return Config::get<std::string>(prefix + "@type", "box");
});
for (size_t i = 0; i < obs.size(); ++i)
{
if (obs[i] == "box")
{
obstacles_.push_back(std::make_shared<BoxObstacle>(i));
} else
{
obstacles_.push_back(std::make_shared<Obstacle>(i));
}
}
tinyxml2::XMLElement *root = doc.FirstChildElement("Obstacles");
if (!root) {
RCLCPP_ERROR(node_->get_logger(), "No <Obstacles> root element in file");
return false;
}
for (tinyxml2::XMLElement *obstacleElem = root->FirstChildElement("Obstacle");
obstacleElem != nullptr;
obstacleElem = obstacleElem->NextSiblingElement("Obstacle")) {
std::shared_ptr<Obstacle> obs = std::make_shared<Obstacle>(obstacleElem);
obstacle_map_[obs->GetId()] = obs;
}
for (tinyxml2::XMLElement *obstacleElem = root->FirstChildElement("Box");
obstacleElem != nullptr;
obstacleElem = obstacleElem->NextSiblingElement("Box")) {
std::shared_ptr<BoxObstacle> obs = std::make_shared<BoxObstacle>(obstacleElem);
obstacle_map_[obs->GetId()] = obs;
}
RCLCPP_INFO(node_->get_logger(), "Loaded %zu obstacles from XML", obstacle_map_.size());
return true;
RCLCPP_INFO(node_->get_logger(), "Loaded %zu obstacles from XML", obstacles_.size());
}
Waypoint::Waypoint(const modelec_interfaces::msg::OdometryPos &pos, int index, bool isLast) {

View File

@@ -15,15 +15,28 @@ namespace Modelec
StratFMS::StratFMS() : Node("start_fms")
{
this->declare_parameter<bool>("static_strat", false);
this->declare_parameter<double>("factor.obs", 1.0);
this->declare_parameter<double>("factor.thermo", 0.5);
this->declare_parameter<int>("timer_period_ms", 100);
std::string data_dir = ament_index_cpp::get_package_share_directory("modelec_strat") + "/data/";
if (!Config::load(data_dir + "/config.xml"))
{
RCLCPP_ERROR(get_logger(), "Failed to load config file: %s", (data_dir + "/config.xml").c_str());
}
if (!Config::load(data_dir + "/obstacles.xml"))
{
RCLCPP_ERROR(get_logger(), "Failed to load config file: %s", (data_dir + "/obstacles.xml").c_str());
}
if (!Config::load(data_dir + "/deposite_zone.xml"))
{
RCLCPP_ERROR(get_logger(), "Failed to load config file: %s", (data_dir + "/deposite_zone.xml").c_str());
}
if (!Config::load(data_dir + "/action.xml"))
{
RCLCPP_ERROR(get_logger(), "Failed to load config file: %s", (data_dir + "/action.xml").c_str());
}
static_strat_ = this->get_parameter("static_strat").as_bool();
factor_obs_ = this->get_parameter("factor.obs").as_double();
factor_thermo_ = this->get_parameter("factor.thermo").as_double();
timer_period_ms_ = this->get_parameter("timer_period_ms").as_int();
static_strat_ = Config::get<bool>("config.static_strat.enabled", false);
factor_obs_ = Config::get<float>("config.factor.obs", 1.0);
factor_thermo_ = Config::get<float>("config.factor.thermo", 1.0);
timer_period_ms_ = Config::get<int>("config.timer.strat.ms", 100);
tir_sub_ = create_subscription<std_msgs::msg::Empty>(
"/action/tir/start", 10, [this](const std_msgs::msg::Empty::SharedPtr)
@@ -153,8 +166,6 @@ namespace Modelec
game_action_sequence_.push(State::TAKE_MISSION);
game_action_sequence_.push(State::TAKE_MISSION);
game_action_sequence_.push(State::FREE_MISSION);
game_action_sequence_.push(State::FREE_MISSION);
game_action_sequence_.push(State::THERMO_MISSION);
Transition(State::WAIT_START, "System ready");
}

View File

@@ -6,6 +6,7 @@
#include <sstream>
#include <vector>
#include <functional>
#include <iostream>
namespace Modelec
{
@@ -17,19 +18,33 @@ namespace Modelec
template<typename T>
using BuilderFunc = std::function<T(const std::string& base_key)>;
template<typename K, typename V>
using MapBuilderFunc = std::function<std::pair<K, V>(const std::string& base_key)>;
static bool load(const std::string& filepath);
template<typename T>
static T get(const std::string& key, const T& default_value = T());
static T get(const std::string& key, const T& default_value = T(), bool is_optional = false);
template<typename T>
static std::vector<T> getArray(const std::string& prefix,
BuilderFunc<T> builder = [](const std::string& base) { return get<T>(base); });
template<typename K, typename V>
static std::unordered_map<K, V> getMap(
const std::string& prefix,
MapBuilderFunc<K, V> builder);
static size_t count(const std::string& prefix);
static void clear(const std::string& prefix);
static bool has(const std::string& prefix);
static void printAll();
static void print(const std::string& prefix);
private:
static void parseNode(tinyxml2::XMLElement* element, const std::string& key);
@@ -37,25 +52,47 @@ namespace Modelec
};
template<typename T>
T Config::get(const std::string& key, const T& default_value) {
T Config::get(const std::string& key, const T& default_value, bool is_optional) {
auto it = values_.find(key);
if (it == values_.end()) return default_value;
if (it == values_.end())
{
if (!is_optional)
{
std::cerr << "Config key not found: " << key << std::endl;
}
return default_value;
}
std::istringstream iss(it->second);
T result;
if (!(iss >> result)) return default_value;
if (!(iss >> result))
{
if (!is_optional)
{
std::cerr << "Config key has invalid format: " << key << " = " << it->second << std::endl;
}
return default_value;
}
return result;
}
template<>
inline std::string Config::get<std::string>(const std::string& key, const std::string& default_value) {
inline std::string Config::get<std::string>(const std::string& key, const std::string& default_value, bool is_optional) {
auto it = values_.find(key);
return it != values_.end() ? it->second : default_value;
if (it == values_.end())
{
if (is_optional)
{
std::cerr << "Config key not found: " << key << std::endl;
}
return default_value;
}
return it->second;
}
template<>
inline bool Config::get<bool>(const std::string& key, const bool& default_value) {
auto str = get<std::string>(key, default_value ? "true" : "false");
inline bool Config::get<bool>(const std::string& key, const bool& default_value, bool is_optional) {
auto str = get<std::string>(key, default_value ? "true" : "false", is_optional);
return str == "true" || str == "1";
}
@@ -65,13 +102,49 @@ namespace Modelec
{
std::vector<T> result;
size_t n = Config::count(prefix);
size_t n = count(prefix);
result.reserve(n);
if (n > 0)
{
if (n == 1 && !has(prefix + "[0]"))
{
result.emplace_back(builder(prefix));
} else
{
for (size_t i = 0; i < n; ++i)
{
std::string base = prefix + "[" + std::to_string(i) + "]";
result.emplace_back(builder(base));
}
}
} else
{
std::cerr << "Config array not found: " << prefix << std::endl;
}
return result;
}
template<typename K, typename V>
std::unordered_map<K, V> Config::getMap(
const std::string& prefix,
MapBuilderFunc<K, V> builder)
{
std::unordered_map<K, V> result;
size_t n = count(prefix);
if (n == 0)
{
std::cerr << "Config map not found: " << prefix << std::endl;
return result;
}
for (size_t i = 0; i < n; ++i)
{
std::string base = prefix + "[" + std::to_string(i) + "]";
result.emplace_back(builder(base));
auto [key, value] = builder(base);
result.emplace(key, value);
}
return result;

View File

@@ -1,7 +1,7 @@
#pragma once
#define CLOSE_DISTANCE 155
#define BASE_DISTANCE 310
#define CLOSE_DISTANCE 170
#define BASE_DISTANCE 305
namespace Modelec
{

View File

@@ -1,4 +1,3 @@
#include <iostream>
#include <modelec_utils/config.hpp>
namespace Modelec
@@ -40,14 +39,49 @@ namespace Modelec
}
}
return found ? max_index + 1 : 0;
return found ? max_index + 1 : has(prefix) ? 1 : 0;
}
void Config::clear(const std::string& prefix)
{
for (auto it = values_.begin(); it != values_.end();)
{
if (it->first.rfind(prefix, 0) == 0)
{
it = values_.erase(it);
}
else
{
++it;
}
}
}
bool Config::has(const std::string& prefix)
{
return std::any_of(values_.begin(), values_.end(), [&prefix](const auto& pair) {
return pair.first.rfind(prefix, 0) == 0;
});
}
void Config::printAll()
{
std::cerr << "Config values:" << std::endl;
for (const auto& [key, value] : values_)
{
std::cout << key << " = " << value << std::endl;
std::cerr << " " << key << " = " << value << std::endl;
}
}
void Config::print(const std::string& prefix)
{
std::cerr << "Config values with prefix '" << prefix << "':" << std::endl;
for (const auto& [key, value] : values_)
{
if (key.rfind(prefix, 0) == 0)
{
std::cerr << " " << key << " = " << value << std::endl;
}
}
}
@@ -60,20 +94,17 @@ namespace Modelec
}
}
// Store attributes
for (auto* attr = element->FirstAttribute(); attr; attr = attr->Next())
{
values_[key + "@" + attr->Name()] = attr->Value();
}
// Count children by name
std::unordered_map<std::string, int> child_count;
for (auto* child = element->FirstChildElement(); child; child = child->NextSiblingElement())
{
child_count[child->Name()]++;
}
// Index children
std::unordered_map<std::string, int> child_index;
for (auto* child = element->FirstChildElement(); child; child = child->NextSiblingElement())

View File

@@ -34,6 +34,9 @@ TEST(ConfigTest, LoadValidXML)
// Deep nested node
EXPECT_EQ(Modelec::Config::get<std::string>("root.nested.deep", ""), "hello");
EXPECT_TRUE(Modelec::Config::has("root.child1"));
EXPECT_FALSE(Modelec::Config::has("root.child11T1435TRERGFSRVSPKRMGHJNIUSRJOGFH"));
}
TEST(ConfigTest, LoadInvalidXML)
@@ -113,4 +116,33 @@ TEST(ConfigTest, GetArray)
EXPECT_EQ(array2[1].attr, "c");
EXPECT_EQ(array2[2].value, 3);
EXPECT_EQ(array2[2].attr, "d");
}
TEST(ConfigTest, Clear)
{
// Create temporary XML file
const std::string filepath = "test_array_config.xml";
std::ofstream file(filepath);
file <<
"<root>"
" <item a=\"b\">1</item>"
" <item a=\"c\">2</item>"
" <item a=\"d\">3</item>"
" <test a=\"d\">3</test>"
"</root>";
file.close();
EXPECT_TRUE(Modelec::Config::load(filepath));
EXPECT_TRUE(Modelec::Config::has("root.test"));
Modelec::Config::clear("root.test");
EXPECT_FALSE(Modelec::Config::has("root.test"));
EXPECT_EQ(Modelec::Config::count("root.item"), 3);
Modelec::Config::clear("root.item");
EXPECT_EQ(Modelec::Config::count("root.item"), 0);
Modelec::Config::clear("");
EXPECT_FALSE(Modelec::Config::has("root"));
}

12
start_cam_ros2.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
set -e
source /opt/ros/jazzy/setup.bash
source /home/modelec/Modelec-ROS2/install/setup.bash
export RCL_LOG_LEVEL=info
#export RMW_IMPLEMENTATION=rmw_fastrtps_cpp
#export FASTRTPS_DEFAULT_PROFILES_FILE=/home/modelec/Modelec-ROS2/fastdds_setup.xml
#export ROS_DOMAIN_ID=128
exec ros2 run modelec_com color_detector

View File

@@ -6,7 +6,7 @@ source /home/modelec/Modelec-ROS2/install/setup.bash
export RCL_LOG_LEVEL=info
#export RMW_IMPLEMENTATION=rmw_fastrtps_cpp
export FASTRTPS_DEFAULT_PROFILES_FILE=/home/modelec/Modelec-ROS2/fastdds_setup.xml
#export FASTRTPS_DEFAULT_PROFILES_FILE=/home/modelec/Modelec-ROS2/fastdds_setup.xml
#export ROS_DOMAIN_ID=128
exec ros2 launch modelec_core modelec.launch.py with_color_detector:=false "$@"
exec ros2 launch modelec_core modelec.launch.py "$@"

9
start_ros2.zsh Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
set -e
source /opt/ros/jazzy/setup.zsh
source ~/Modelec-ROS2/install/setup.zsh
export RCL_LOG_LEVEL=info
exec ros2 launch modelec_core modelec.launch.py "$@"