start to be very nice :)

This commit is contained in:
acki
2025-05-04 00:26:13 -04:00
parent 4bd1a1438c
commit e22b745b02
18 changed files with 322 additions and 132 deletions

View File

@@ -4,7 +4,7 @@ import time
import serial
# Set the parameters for the serial connection
serial_port = '/dev/pts/12' # Modify this to your serial port (e.g., 'COM3' on Windows)
serial_port = '/dev/pts/11' # Modify this to your serial port (e.g., 'COM3' on Windows)
baud_rate = 115200 # Modify this to your baud rate
# Open the serial connection

View File

@@ -4,7 +4,7 @@ import math
import threading
class SimulatedPCB:
def __init__(self, port='/dev/pts/6', baud=115200):
def __init__(self, port='/dev/pts/9', baud=115200):
self.ser = serial.Serial(port, baud, timeout=1)
self.running = True
self.start = False
@@ -45,7 +45,7 @@ class SimulatedPCB:
angle_diff = self.normalize_angle(wp['theta'] - self.theta)
if wp['type'] == 1:
if distance < 5.0 and abs(angle_diff) < 0.05:
if distance < 5.0 and abs(angle_diff) < 0.15:
self.vx = 0.0
self.vy = 0.0
self.vtheta = 0.0
@@ -54,12 +54,12 @@ class SimulatedPCB:
self.waypoint_order.pop(0)
del self.waypoints[wp['id']]
else:
speed = min(200.0, distance * 1.5)
speed = min(300.0, distance * 3)
self.vx = speed * math.cos(angle)
self.vy = speed * math.sin(angle)
self.vtheta = max(-1.0, min(1.0, angle_diff))
self.vtheta = max(-9.0, min(9.0, angle_diff * 2))
else:
speed = min(300.0, distance * 2)
speed = min(600.0, distance * 4)
self.vx = speed * math.cos(angle)
self.vy = speed * math.sin(angle)
self.vtheta = 0.0

View File

@@ -10,7 +10,7 @@ namespace Modelec
auto request = std::make_shared<modelec_interfaces::srv::AddSerialListener::Request>();
request->name = "pcb_alim";
request->bauds = 115200;
request->serial_port = "/dev/pts/13"; // TODO : check the real serial port
request->serial_port = "/dev/pts/12"; // TODO : check the real serial port
auto client = this->create_client<modelec_interfaces::srv::AddSerialListener>("add_serial_listener");
while (!client->wait_for_service(std::chrono::seconds(1)))
{

View File

@@ -10,7 +10,7 @@ namespace Modelec
auto request = std::make_shared<modelec_interfaces::srv::AddSerialListener::Request>();
request->name = "pcb_odo";
request->bauds = 115200;
request->serial_port = "/dev/pts/7"; // TODO : check the real serial port
request->serial_port = "/dev/pts/10"; // TODO : check the real serial port
auto client = this->create_client<modelec_interfaces::srv::AddSerialListener>("add_serial_listener");
while (!client->wait_for_service(std::chrono::seconds(1)))
{

View File

@@ -3,7 +3,6 @@
<size>
<width_mm>300</width_mm>
<length_mm>300</length_mm>
<margin_mm>50</margin_mm>
</size>
<detection>
@@ -16,7 +15,7 @@
<size>
<width_mm>300</width_mm>
<length_mm>300</length_mm>
<margin_mm>300</margin_mm>
<margin_mm>100</margin_mm>
</size>
</robot>

View File

@@ -7,11 +7,12 @@
</PotPos>
</DepositeZone>
<DepositeZone id="1" team="0" max_pot="2">
<DepositeZone id="1" team="0" max_pot="3">
<Pos x="1225" y="225" theta="1.5708" />
<PotPos>
<Pos x="1225" y="100" theta="1.5708" />
<Pos x="1225" y="250" theta="1.5708" />
<Pos x="1225" y="75" theta="1.5708" />
<Pos x="1225" y="225" theta="1.5708" />
<Pos x="1225" y="400" theta="1.5708" />
</PotPos>
</DepositeZone>
@@ -22,11 +23,12 @@
</PotPos>
</DepositeZone>
<DepositeZone id="3" team="0" max_pot="2">
<DepositeZone id="3" team="0" max_pot="3">
<Pos x="2775" y="875" theta="3.1415" />
<PotPos>
<Pos x="2875" y="875" theta="3.1415" />
<Pos x="2700" y="875" theta="3.1415" />
<Pos x="2925" y="875" theta="3.1415" />
<Pos x="2775" y="875" theta="3.1415" />
<Pos x="2600" y="875" theta="3.1415" />
</PotPos>
</DepositeZone>
@@ -38,11 +40,12 @@
</PotPos>
</DepositeZone>
<DepositeZone id="11" team="1" max_pot="2">
<DepositeZone id="11" team="1" max_pot="3">
<Pos x="1775" y="225" theta="1.5708" />
<PotPos>
<Pos x="1775" y="100" theta="1.5708" />
<Pos x="1775" y="250" theta="1.5708" />
<Pos x="1775" y="75" theta="1.5708" />
<Pos x="1775" y="225" theta="1.5708" />
<Pos x="1775" y="400" theta="1.5708" />
</PotPos>
</DepositeZone>
@@ -53,11 +56,12 @@
</PotPos>
</DepositeZone>
<DepositeZone id="13" team="1" max_pot="2">
<DepositeZone id="13" team="1" max_pot="3">
<Pos x="225" y="875" theta="0" />
<PotPos>
<Pos x="125" y="875" theta="0" />
<Pos x="300" y="875" theta="0" />
<Pos x="75" y="875" theta="0" />
<Pos x="225" y="875" theta="0" />
<Pos x="400" y="875" theta="0" />
</PotPos>
</DepositeZone>
</Map>

View File

@@ -5,16 +5,20 @@
<Obstacle id="3" x="2150" y="1900" theta="1.5708" width="400" height="200" type="estrade"/>
<!-- Gradin TOP TO BOTTOM LEFT -->
<Gradin id="10" x="825" y="1725" theta="-1.5708" width="400" height="100" type="gradin"/>
<Gradin id="10" x="825" y="1725" theta="-1.5708" width="400" height="100" type="gradin" />
<Gradin id="11" x="75" y="1325" theta="0" width="400" height="100" type="gradin"/>
<Gradin id="12" x="1100" y="950" theta="1.5708" width="400" height="100" type="gradin"/>
<Gradin id="12" x="1100" y="950" theta="1.5708" width="400" height="100" type="gradin" >
<possible-angle theta="-1.5708" />
</Gradin>
<Gradin id="13" x="75" y="400" theta="0" width="400" height="100" type="gradin"/>
<Gradin id="14" x="775" y="250" theta="1.5708" width="400" height="100" type="gradin"/>
<!-- Gradin TOP TO BOTTOM RIGHT -->
<Gradin id="20" x="2175" y="1725" theta="-1.5708" width="400" height="100" type="gradin"/>
<Gradin id="20" x="2175" y="1725" theta="-1.5708" width="400" height="100" type="gradin" />
<Gradin id="21" x="2925" y="1325" theta="3.1415" width="400" height="100" type="gradin"/>
<Gradin id="22" x="1900" y="950" theta="1.5708" width="400" height="100" type="gradin"/>
<Gradin id="22" x="1900" y="950" theta="1.5708" width="400" height="100" type="gradin">
<possible-angle theta="-1.5708" />
</Gradin>
<Gradin id="23" x="2925" y="400" theta="3.1415" width="400" height="100" type="gradin"/>
<Gradin id="24" x="2225" y="250" theta="1.5708" width="400" height="100" type="gradin"/>

View File

@@ -1,29 +1,44 @@
#pragma once
#include <limits>
#include <queue>
#include <tinyxml2.h>
#include <modelec_utils/point.hpp>
namespace Modelec {
namespace Modelec
{
class DepositeZone
{
public:
DepositeZone(tinyxml2::XMLElement * obstacleElem);
DepositeZone(tinyxml2::XMLElement* obstacleElem);
Point GetPosition() const { return position_; }
Point GetNextPotPos()
{
if (pot_queue_.empty())
return Point();
auto elem = pot_queue_.front();
return Point(std::numeric_limits<int>::min(), std::numeric_limits<int>::min(), 0);
return pot_queue_.front();
}
Point ValidNextPotPos()
{
if (pot_queue_.empty())
return Point(std::numeric_limits<int>::min(), std::numeric_limits<int>::min(), 0);
Point p = pot_queue_.front();
pot_queue_.pop();
return elem;
return p;
}
int GetTeam() const { return team_; }
int GetId() const { return id_; }
int GetMaxPot() const { return max_pot_; }
int RemainingPotPos() const
{
return pot_queue_.size();
}
protected:
int id_, team_, max_pot_;
Point position_;

View File

@@ -16,9 +16,9 @@ namespace Modelec {
NavigationHelper(const rclcpp::Node::SharedPtr& node);
rclcpp::Node::SharedPtr getNode() const;
rclcpp::Node::SharedPtr GetNode() const;
std::shared_ptr<Pathfinding> getPathfinding() const;
std::shared_ptr<Pathfinding> GetPathfinding() const;
void SendWaypoint() const;
void SendWaypoint(const std::vector<WaypointMsg> &waypoints) const;
@@ -33,16 +33,20 @@ namespace Modelec {
bool HasArrived() const;
void GoTo(const PosMsg::SharedPtr &goal, bool isClose = false);
void GoTo(int x, int y, double theta, bool isClose = false);
int GoTo(const PosMsg::SharedPtr& goal, bool isClose = false, int collisionMask = Pathfinding::FREE);
int GoTo(int x, int y, double theta, bool isClose = false, int collisionMask = Pathfinding::FREE);
WaypointListMsg FindPath(const PosMsg::SharedPtr &goal, bool isClose = false);
int CanGoTo(const PosMsg::SharedPtr& goal, bool isClose = false, int collisionMask = Pathfinding::FREE);
int CanGoTo(int x, int y, double theta, bool isClose = false, int collisionMask = Pathfinding::FREE);
std::pair<int, WaypointListMsg> FindPath(const PosMsg::SharedPtr& goal, bool isClose = false,
int collisionMask = Pathfinding::FREE);
PosMsg::SharedPtr GetCurrentPos() const;
bool LoadDepositeZoneFromXML(const std::string &filename);
std::shared_ptr<DepositeZone> GetClosestDepositeZone(const PosMsg::SharedPtr &pos, int teamId);
std::shared_ptr<DepositeZone> GetClosestDepositeZone(const PosMsg::SharedPtr &pos, int teamId, const std::vector<int>& blacklistedId = {});
protected:
void OnWaypointReach(const WaypointReachMsg::SharedPtr msg);

View File

@@ -1,6 +1,7 @@
#pragma once
#include "obstacle.hpp"
#include <modelec_interfaces/msg/odometry_pos.hpp>
namespace Modelec
{
@@ -17,7 +18,14 @@ namespace Modelec
bool IsAtObjective() const;
void SetAtObjective(bool atObjective);
Point GetOptimizedGetPos(const modelec_interfaces::msg::OdometryPos::SharedPtr& msg) const;
Point GetOptimizedGetPos(const Point& currentPos) const;
std::vector<Point> GetAllPossiblePositions() const;
protected:
bool isAtObjective = false;
std::vector<double> possible_angles_;
};
}

View File

@@ -40,14 +40,23 @@ namespace Modelec {
class Pathfinding {
public:
enum
{
FREE = 1 << 0,
WALL = 1 << 1,
OBSTACLE = 1 << 2,
ENEMY = 1 << 3,
};
Pathfinding();
Pathfinding(const rclcpp::Node::SharedPtr& node);
rclcpp::Node::SharedPtr getNode() const;
WaypointListMsg FindPath(const PosMsg::SharedPtr& start,
const PosMsg::SharedPtr& goal, bool isClose = false);
std::pair<int, WaypointListMsg> FindPath(const PosMsg::SharedPtr& start,
const PosMsg::SharedPtr& goal, bool isClose = false,
int collisionMask = FREE);
//void SetStartAndGoal(const PosMsg::SharedPtr& start, const PosMsg::SharedPtr& goal);
@@ -65,7 +74,7 @@ namespace Modelec {
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<ColumnObstacle> GetClosestColumn(const PosMsg::SharedPtr& pos);
std::shared_ptr<ColumnObstacle> GetClosestColumn(const PosMsg::SharedPtr& pos, const std::vector<int>& blacklistedId = {});
protected:
void HandleMapRequest(
@@ -85,6 +94,8 @@ namespace Modelec {
//bool EnemyOnPath(const modelec_interfaces::msg::OdometryPos& enemy_pos);
//void Replan();
bool TestCollision(int x, int y, int collisionMask = FREE);
private:
rclcpp::Node::SharedPtr node_;

View File

@@ -10,7 +10,7 @@ namespace Modelec
void PrepareConcertMission::start(rclcpp::Node::SharedPtr node)
{
if (auto col = nav_->getPathfinding()->GetClosestColumn(nav_->GetCurrentPos()))
if (auto col = nav_->GetPathfinding()->GetClosestColumn(nav_->GetCurrentPos()))
{
column_ = col;
} else
@@ -21,9 +21,9 @@ namespace Modelec
node_ = node;
auto pos = column_->position().GetTakeBasePosition();
auto pos = column_->GetOptimizedGetPos(nav_->GetCurrentPos()).GetTakeBasePosition();
nav_->GoTo(pos.x, pos.y, pos.theta);
auto res = nav_->GoTo(pos.x, pos.y, pos.theta, false, Pathfinding::FREE | Pathfinding::WALL);
status_ = MissionStatus::RUNNING;
}
@@ -38,31 +38,64 @@ namespace Modelec
{
case GO_TO_COLUMN:
{
auto pos = column_->position().GetTakeClosePosition();
nav_->GoTo(pos.x, pos.y, pos.theta, true);
auto pos = column_->GetOptimizedGetPos(nav_->GetCurrentPos()).GetTakeClosePosition();
nav_->GoTo(pos.x, pos.y, pos.theta, true, Pathfinding::FREE | Pathfinding::WALL);
}
step_ = GO_CLOSE_TO_COLUMN;
break;
case GO_CLOSE_TO_COLUMN:
nav_->getPathfinding()->RemoveObstacle(column_->id());
nav_->GetPathfinding()->RemoveObstacle(column_->id());
step_ = TAKE_COLUMN;
break;
case TAKE_COLUMN:
{
auto pos = column_->position().GetTakeBasePosition();
nav_->GoTo(pos.x, pos.y, pos.theta, true);
// TODO
closestDepoZone_ = nav_->GetClosestDepositeZone(nav_->GetCurrentPos(), 0);
if (!closestDepoZone_)
{
status_ = MissionStatus::FAILED;
return;
}
closestDepoZonePoint_ = closestDepoZone_->GetNextPotPos();
auto p = closestDepoZonePoint_.GetTakeBasePosition();
auto res = nav_->CanGoTo(p.x, p.y, p.theta, false, Pathfinding::FREE | Pathfinding::WALL);
if (res != Pathfinding::FREE)
{
auto pos = column_->GetOptimizedGetPos(nav_->GetCurrentPos()).GetTakeBasePosition();
nav_->GoTo(pos.x, pos.y, pos.theta, true, Pathfinding::FREE | Pathfinding::WALL);
step_ = GO_BACK;
}
else
{
nav_->GoTo(p.x, p.y, p.theta, false, Pathfinding::FREE | Pathfinding::WALL);
closestDepoZone_->ValidNextPotPos();
step_ = GO_TO_PLATFORM;
}
}
step_ = GO_BACK;
break;
case GO_BACK:
{
closestDepoZone_ = nav_->GetClosestDepositeZone(nav_->GetCurrentPos(), 0);
closestDepoZonePoint_ = closestDepoZone_->GetNextPotPos();
if (!closestDepoZone_)
{
status_ = MissionStatus::FAILED;
return;
}
closestDepoZonePoint_ = closestDepoZone_->ValidNextPotPos();
auto p = closestDepoZonePoint_.GetTakeBasePosition();
nav_->GoTo(p.x, p.y, p.theta);
if (nav_->CanGoTo(p.x, p.y, p.theta, false, Pathfinding::FREE | Pathfinding::WALL) != Pathfinding::FREE)
{
nav_->GoTo(p.x, p.y, p.theta, true, Pathfinding::FREE | Pathfinding::WALL);
}
else
{
nav_->CanGoTo(p.x, p.y, p.theta, false, Pathfinding::FREE | Pathfinding::WALL);
}
}
step_ = GO_TO_PLATFORM;
@@ -82,7 +115,7 @@ namespace Modelec
column_->setY(closestDepoZonePoint_.y);
column_->setTheta(closestDepoZonePoint_.theta);
column_->SetAtObjective(true);
nav_->getPathfinding()->AddObstacle(column_);
nav_->GetPathfinding()->AddObstacle(column_);
}
step_ = PLACE_PLATFORM;
@@ -91,7 +124,10 @@ namespace Modelec
case PLACE_PLATFORM:
{
auto p = closestDepoZonePoint_.GetTakeBasePosition();
nav_->GoTo(p.x, p.y, p.theta, true);
if (nav_->CanGoTo(p.x, p.y, p.theta) != Pathfinding::FREE)
{
nav_->GoTo(p.x, p.y, p.theta, true, Pathfinding::FREE | Pathfinding::WALL);
}
}
step_ = GO_BACK_2;

View File

@@ -27,7 +27,7 @@ namespace Modelec {
go_to_sub_ = node_->create_subscription<PosMsg>(
"nav/go_to", 10, [this](const PosMsg::SharedPtr msg) {
GoTo(msg);
GoTo(msg, false, Pathfinding::FREE | Pathfinding::WALL);
});
std::string deposite_zone_path = ament_index_cpp::get_package_share_directory("modelec_strat") + "/data/deposite_zone.xml";
@@ -37,12 +37,12 @@ namespace Modelec {
}
}
rclcpp::Node::SharedPtr NavigationHelper::getNode() const
rclcpp::Node::SharedPtr NavigationHelper::GetNode() const
{
return node_;
}
std::shared_ptr<Pathfinding> NavigationHelper::getPathfinding() const
std::shared_ptr<Pathfinding> NavigationHelper::GetPathfinding() const
{
return pathfinding_;
}
@@ -166,32 +166,54 @@ namespace Modelec {
return waypoints_.back().reached;
}
void NavigationHelper::GoTo(const PosMsg::SharedPtr& goal, bool isClose)
int NavigationHelper::GoTo(const PosMsg::SharedPtr& goal, bool isClose, int collisionMask)
{
auto [res, wl] = FindPath(goal, isClose, collisionMask);
if (wl.empty() || res != Pathfinding::FREE)
{
return res;
}
waypoints_.clear();
auto wml = FindPath(goal, isClose);
for (auto & w : wml)
for (auto & w : wl)
{
waypoints_.emplace_back(w);
}
SendWaypoint();
return res;
}
void NavigationHelper::GoTo(int x, int y, double theta, bool isClose)
int NavigationHelper::GoTo(int x, int y, double theta, bool isClose, int collisionMask)
{
PosMsg::SharedPtr goal = std::make_shared<PosMsg>();
goal->x = x;
goal->y = y;
goal->theta = theta;
GoTo(goal, isClose);
return GoTo(goal, isClose, collisionMask);
}
WaypointListMsg NavigationHelper::FindPath(const PosMsg::SharedPtr& goal, bool isClose)
int NavigationHelper::CanGoTo(const PosMsg::SharedPtr& goal, bool isClose, int collisionMask)
{
return pathfinding_->FindPath(current_pos_, goal, isClose);
return FindPath(goal, isClose, collisionMask).first;
}
int NavigationHelper::CanGoTo(int x, int y, double theta, bool isClose, int collisionMask)
{
PosMsg::SharedPtr goal = std::make_shared<PosMsg>();
goal->x = x;
goal->y = y;
goal->theta = theta;
return CanGoTo(goal, isClose, collisionMask);
}
std::pair<int, WaypointListMsg> NavigationHelper::FindPath(const PosMsg::SharedPtr& goal, bool isClose,
int collisionMask)
{
return pathfinding_->FindPath(current_pos_, goal, isClose, collisionMask);
}
PosMsg::SharedPtr NavigationHelper::GetCurrentPos() const
@@ -227,20 +249,20 @@ namespace Modelec {
return true;
}
std::shared_ptr<DepositeZone> NavigationHelper::GetClosestDepositeZone(const PosMsg::SharedPtr& pos, int teamId)
std::shared_ptr<DepositeZone> NavigationHelper::GetClosestDepositeZone(const PosMsg::SharedPtr& pos, int teamId, const std::vector<int>& blacklistedId)
{
std::shared_ptr<DepositeZone> closest_zone = nullptr;
double min_distance = std::numeric_limits<double>::max();
auto posPoint = Point(pos->x, pos->y, pos->theta);
for (const auto& zone : deposite_zones_)
for (const auto& [id, zone] : deposite_zones_)
{
if (zone.second->GetTeam() == teamId)
if (zone->GetTeam() == teamId && zone->RemainingPotPos() > 0 && blacklistedId.end() == std::find(blacklistedId.begin(), blacklistedId.end(), id))
{
double distance = Point::distance(posPoint, zone.second->GetPosition());
double distance = Point::distance(posPoint, zone->GetPosition());
if (distance < min_distance)
{
min_distance = distance;
closest_zone = zone.second;
closest_zone = zone;
}
}
}

View File

@@ -10,6 +10,13 @@ namespace Modelec
ColumnObstacle::ColumnObstacle(tinyxml2::XMLElement* obstacleElem)
: Obstacle(obstacleElem)
{
possible_angles_.push_back(theta_);
for (auto elem = obstacleElem->FirstChildElement("possible-angle");
elem;
elem = elem->NextSiblingElement("possible-angle"))
{
possible_angles_.push_back(elem->DoubleAttribute("theta"));
}
}
ColumnObstacle::ColumnObstacle(const modelec_interfaces::msg::Obstacle& msg) :
@@ -26,4 +33,38 @@ namespace Modelec
{
isAtObjective = atObjective;
}
Point ColumnObstacle::GetOptimizedGetPos(const modelec_interfaces::msg::OdometryPos::SharedPtr& msg) const
{
Point p = Point(msg->x, msg->y, msg->theta);
return GetOptimizedGetPos(p);
}
Point ColumnObstacle::GetOptimizedGetPos(const Point& currentPos) const
{
auto distance = std::numeric_limits<double>::max();
double optimizedAngle = 0;
for (const auto& angle : possible_angles_)
{
auto newPos = position().GetTakePosition(400, angle);
double dist = Point::distance(currentPos, newPos);
if (dist < distance)
{
distance = dist;
optimizedAngle = angle;
}
}
return Point(x_, y_, optimizedAngle);
}
std::vector<Point> ColumnObstacle::GetAllPossiblePositions() const
{
std::vector<Point> positions;
for (const auto & angle : possible_angles_)
{
positions.push_back(Point(x_, y_, angle));
}
return positions;
}
}

View File

@@ -3,13 +3,13 @@
#include <modelec_strat/obstacle/column.hpp>
#include <modelec_utils/config.hpp>
namespace Modelec {
namespace Modelec
{
struct AStarNode
{
int x, y;
double g = std::numeric_limits<double>::infinity(); // Cost from start
double f = std::numeric_limits<double>::infinity(); // g + heuristic
double g = std::numeric_limits<double>::infinity(); // Cost from start
double f = std::numeric_limits<double>::infinity(); // g + heuristic
int parent_x = -1;
int parent_y = -1;
@@ -21,7 +21,7 @@ namespace Modelec {
double heuristic(int x1, int y1, int x2, int y2)
{
return std::hypot(x1 - x2, y1 - y2); // Euclidean distance
return std::hypot(x1 - x2, y1 - y2); // Euclidean distance
}
Pathfinding::Pathfinding()
@@ -45,7 +45,8 @@ namespace Modelec {
grid_width_ = Config::get<int>("config.map.size.grid_width", 300);
grid_height_ = Config::get<int>("config.map.size.grid_height", 200);
std::string obstacles_path = ament_index_cpp::get_package_share_directory("modelec_strat") + "/data/obstacles.xml";
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");
@@ -53,7 +54,8 @@ namespace Modelec {
obstacle_add_sub_ = node_->create_subscription<modelec_interfaces::msg::Obstacle>(
"obstacle/add", 10,
[this](const modelec_interfaces::msg::Obstacle::SharedPtr msg) {
[this](const modelec_interfaces::msg::Obstacle::SharedPtr msg)
{
RCLCPP_INFO(node_->get_logger(), "Obstacle add request received");
AddObstacle(std::make_shared<Obstacle>(*msg));
});
@@ -63,7 +65,8 @@ namespace Modelec {
obstacle_remove_sub_ = node_->create_subscription<modelec_interfaces::msg::Obstacle>(
"obstacle/remove", 10,
[this](const modelec_interfaces::msg::Obstacle::SharedPtr msg) {
[this](const modelec_interfaces::msg::Obstacle::SharedPtr msg)
{
RCLCPP_INFO(node_->get_logger(), "Obstacle remove request received");
RemoveObstacle(msg->id);
});
@@ -74,7 +77,8 @@ namespace Modelec {
ask_obstacle_srv_ = node_->create_service<std_srvs::srv::Empty>(
"nav/ask_map_obstacle",
[this](const std::shared_ptr<std_srvs::srv::Empty::Request> request,
const std::shared_ptr<std_srvs::srv::Empty::Response> response) {
const std::shared_ptr<std_srvs::srv::Empty::Response> response)
{
RCLCPP_INFO(node_->get_logger(), "Ask obstacle request received");
HandleAskObstacleRequest(request, response);
});
@@ -82,7 +86,8 @@ namespace Modelec {
map_srv_ = node_->create_service<modelec_interfaces::srv::Map>(
"nav/map",
[this](const std::shared_ptr<modelec_interfaces::srv::Map::Request> request,
const std::shared_ptr<modelec_interfaces::srv::Map::Response> response) {
const std::shared_ptr<modelec_interfaces::srv::Map::Response> response)
{
RCLCPP_INFO(node_->get_logger(), "Map request received");
HandleMapRequest(request, response);
});
@@ -90,14 +95,16 @@ namespace Modelec {
map_size_srv_ = node_->create_service<modelec_interfaces::srv::MapSize>(
"nav/map_size",
[this](const std::shared_ptr<modelec_interfaces::srv::MapSize::Request> request,
const std::shared_ptr<modelec_interfaces::srv::MapSize::Response> response) {
const std::shared_ptr<modelec_interfaces::srv::MapSize::Response> response)
{
RCLCPP_INFO(node_->get_logger(), "Map size request received");
HandleMapSizeRequest(request, response);
});
enemy_pos_sub_ = node_->create_subscription<modelec_interfaces::msg::OdometryPos>(
"enemy/position", 10,
[this](const modelec_interfaces::msg::OdometryPos::SharedPtr msg) {
[this](const modelec_interfaces::msg::OdometryPos::SharedPtr msg)
{
OnEnemyPosition(msg);
});
@@ -139,12 +146,13 @@ namespace Modelec {
}
}*/
WaypointListMsg Pathfinding::FindPath(const PosMsg::SharedPtr& start, const PosMsg::SharedPtr& goal, bool isClose)
std::pair<int, WaypointListMsg> Pathfinding::FindPath(const PosMsg::SharedPtr& start, const PosMsg::SharedPtr& goal,
bool isClose, int collisionMask)
{
if (!start || !goal)
{
RCLCPP_WARN(node_->get_logger(), "Start or Goal position is null");
return WaypointListMsg();
return {-3, WaypointListMsg()};
}
WaypointListMsg waypoints;
@@ -161,7 +169,7 @@ namespace Modelec {
grid_.resize(grid_height_);
for (auto& row : grid_)
{
row.assign(grid_width_, 0); // 0 = free
row.assign(grid_width_, FREE);
}
if (has_enemy_pos_)
@@ -169,8 +177,10 @@ namespace Modelec {
int ex = (last_enemy_pos_.x / cell_size_mm_x);
int ey = ((map_height_mm_ - last_enemy_pos_.y) / cell_size_mm_y);
const int inflate_enemy_x = std::ceil((enemy_margin_mm_ + (enemy_width_mm_/2)) / cell_size_mm_x) + inflate_x;
const int inflate_enemy_y = std::ceil((enemy_margin_mm_ + (enemy_length_mm_/2)) / cell_size_mm_y) + inflate_y;
const int inflate_enemy_x = std::ceil((enemy_margin_mm_ + (enemy_width_mm_ / 2)) / cell_size_mm_x) +
inflate_x;
const int inflate_enemy_y = std::ceil((enemy_margin_mm_ + (enemy_length_mm_ / 2)) / cell_size_mm_y) +
inflate_y;
for (int y = ey - inflate_enemy_y; y <= ey + inflate_enemy_y; ++y)
{
@@ -178,7 +188,7 @@ namespace Modelec {
{
if (x >= 0 && y >= 0 && x < grid_width_ && y < grid_height_)
{
grid_[y][x] = 1;
grid_[y][x] |= ENEMY;
}
}
}
@@ -189,8 +199,8 @@ namespace Modelec {
{
for (int x = 0; x < inflate_x; ++x)
{
grid_[y][x] = 1; // bord gauche
grid_[y][grid_width_ - 1 - x] = 1; // bord droit
grid_[y][x] |= WALL;
grid_[y][grid_width_ - 1 - x] |= WALL;
}
}
@@ -199,18 +209,19 @@ namespace Modelec {
{
for (int y = 0; y < inflate_y; ++y)
{
grid_[y][x] = 1; // bord bas
grid_[grid_height_ - 1 - y][x] = 1; // bord haut
grid_[y][x] |= WALL;
grid_[grid_height_ - 1 - y][x] |= WALL;
}
}
// 2. Fill obstacles with inflation
// TODO some bug exist with the inflate
for (const auto& [id, obstacle] : obstacle_map_)
{
float cx = obstacle->x();
float cy = obstacle->y();
float width = obstacle->width() + inflate_x * cell_size_mm_x;
float height = obstacle->height() + inflate_y * cell_size_mm_y;
float width = obstacle->width() + inflate_x * 2 * cell_size_mm_x;
float height = obstacle->height() + inflate_y * 2 * cell_size_mm_y;
float theta = M_PI_2 - obstacle->theta();
float dx = width / 2.0f;
@@ -244,9 +255,9 @@ namespace Modelec {
}
int x_start = std::max(0, (int)(min_x / cell_size_mm_x));
int x_end = std::min(grid_width_ - 1, (int)(max_x / cell_size_mm_x));
int x_end = std::min(grid_width_ - 1, (int)(max_x / cell_size_mm_x));
int y_start = std::max(0, (int)(min_y / cell_size_mm_y));
int y_end = std::min(grid_height_ - 1, (int)(max_y / cell_size_mm_y));
int y_end = std::min(grid_height_ - 1, (int)(max_y / cell_size_mm_y));
// Mark cells that fall inside rotated rectangle
for (int y = y_start; y <= y_end; ++y)
@@ -269,7 +280,7 @@ namespace Modelec {
int gy = (grid_height_ - 1) - y;
if (x >= 0 && gy >= 0 && x < grid_width_ && gy < grid_height_)
{
grid_[gy][x] = 1;
grid_[gy][x] |= OBSTACLE;
}
}
}
@@ -287,34 +298,37 @@ namespace Modelec {
goal_x >= grid_width_ || goal_y >= grid_height_)
{
RCLCPP_WARN(node_->get_logger(), "Start or Goal out of bounds");
return waypoints;
return {-2, waypoints};
}
if (grid_[start_y][start_x] == 1 || grid_[goal_y][goal_x] == 1)
if (!TestCollision(start_x, start_y, collisionMask) || !TestCollision(goal_x, goal_y, collisionMask))
{
if (grid_[start_y][start_x] == 1)
if (!TestCollision(start_x, start_y, collisionMask))
{
RCLCPP_WARN(node_->get_logger(), "Start inside an obstacle");
return {grid_[start_y][start_x], waypoints};
}
else
{
RCLCPP_WARN(node_->get_logger(), "Goal inside an obstacle");
return {grid_[goal_y][goal_x], waypoints};
}
return waypoints;
}
// 4. A* algorithm
std::priority_queue<AStarNode, std::vector<AStarNode>, std::greater<AStarNode>> open_list;
std::unordered_map<int64_t, AStarNode> all_nodes;
auto hash = [](int x, int y) {
auto hash = [](int x, int y)
{
return (int64_t)x << 32 | (uint32_t)y;
};
auto neighbors = [](int x, int y) {
auto neighbors = [](int x, int y)
{
return std::vector<std::pair<int, int>>{
{x+1, y}, {x-1, y}, {x, y+1}, {x, y-1},
{x+1, y+1}, {x-1, y+1}, {x+1, y-1}, {x-1, y-1} // diagonals
{x + 1, y}, {x - 1, y}, {x, y + 1}, {x, y - 1},
{x + 1, y + 1}, {x - 1, y + 1}, {x + 1, y - 1}, {x - 1, y - 1} // diagonals
};
};
@@ -343,7 +357,7 @@ namespace Modelec {
if (nx < 0 || ny < 0 || ny >= (int)grid_.size() || nx >= (int)grid_[0].size())
continue;
if (grid_[ny][nx] == 1)
if (!TestCollision(nx, ny, collisionMask))
continue;
double tentative_g = current.g + heuristic(current.x, current.y, nx, ny);
@@ -367,7 +381,7 @@ namespace Modelec {
if (!path_found)
{
RCLCPP_WARN(node_->get_logger(), "No path found");
return waypoints;
return {-1, waypoints};
}
// 5. Reconstruct path
@@ -411,7 +425,7 @@ namespace Modelec {
while (true)
{
if (grid_[y][x] == 1)
if (!TestCollision(x, y, collisionMask))
{
clear = false;
break;
@@ -477,7 +491,7 @@ namespace Modelec {
waypoints.back().is_end = true;
}
return waypoints;
return {FREE, waypoints};
}
/*void Pathfinding::SetStartAndGoal(const PosMsg::SharedPtr& start, const PosMsg::SharedPtr& goal)
@@ -518,20 +532,28 @@ namespace Modelec {
obstacle_add_pub_->publish(msg);
}
std::shared_ptr<ColumnObstacle> Pathfinding::GetClosestColumn(const PosMsg::SharedPtr& pos)
std::shared_ptr<ColumnObstacle> Pathfinding::GetClosestColumn(const PosMsg::SharedPtr& pos,
const std::vector<int>& blacklistedId)
{
std::shared_ptr<ColumnObstacle> closest_obstacle = nullptr;
auto robotPos = Point(pos->x, pos->y, pos->theta);
float distance = std::numeric_limits<float>::max();
for (const auto& obstacle : obstacle_map_) {
if (auto obs = std::dynamic_pointer_cast<ColumnObstacle>(obstacle.second)) {
if (!obs->IsAtObjective())
for (const auto& [id, obstacle] : obstacle_map_)
{
if (auto obs = std::dynamic_pointer_cast<ColumnObstacle>(obstacle))
{
if (!obs->IsAtObjective() && std::find(blacklistedId.begin(), blacklistedId.end(), obs->id()) ==
blacklistedId.end())
{
auto dist = Point::distance(robotPos, obs->position());
if (dist < distance) {
distance = dist;
closest_obstacle = obs;
for (auto possiblePos : obs->GetAllPossiblePositions())
{
auto dist = Point::distance(robotPos, possiblePos);
if (dist < distance)
{
distance = dist;
closest_obstacle = obs;
}
}
}
}
@@ -556,16 +578,16 @@ namespace Modelec {
}
void Pathfinding::HandleMapSizeRequest(const std::shared_ptr<modelec_interfaces::srv::MapSize::Request>,
const std::shared_ptr<modelec_interfaces::srv::MapSize::Response> response)
const std::shared_ptr<modelec_interfaces::srv::MapSize::Response> response)
{
response->width = grid_width_;
response->height = grid_height_;
}
void Pathfinding::HandleAskObstacleRequest(const std::shared_ptr<std_srvs::srv::Empty::Request>,
const std::shared_ptr<std_srvs::srv::Empty::Response>)
const std::shared_ptr<std_srvs::srv::Empty::Response>)
{
for (auto & [index, obs] : obstacle_map_)
for (auto& [index, obs] : obstacle_map_)
{
obstacle_add_pub_->publish(obs->toMsg());
}
@@ -585,6 +607,18 @@ namespace Modelec {
}*/
}
bool Pathfinding::TestCollision(int x, int y, int collisionMask)
{
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 (grid_[y][x] & collisionMask) && !(grid_[y][x] & ~collisionMask);
}
bool Pathfinding::LoadObstaclesFromXML(const std::string& filename)
{
tinyxml2::XMLDocument doc;

View File

@@ -64,7 +64,6 @@ namespace Modelec
break;
case State::WAIT_START:
RCLCPP_INFO_ONCE(get_logger(), "State: WAIT_START");
if (started_)
{
match_start_time_ = now;
@@ -110,6 +109,11 @@ namespace Modelec
current_mission_.reset();
transition(State::SELECT_MISSION, "PrepareConcert finished");
}
else if (current_mission_->getStatus() == MissionStatus::FAILED)
{
current_mission_.reset();
transition(State::SELECT_MISSION, "PrepareConcert failed");
}
break;
case State::DO_PROMOTION:
@@ -124,6 +128,11 @@ namespace Modelec
current_mission_.reset();
transition(State::SELECT_MISSION, "Promotion finished");
}
else if (current_mission_->getStatus() == MissionStatus::FAILED)
{
current_mission_.reset();
transition(State::SELECT_MISSION, "Promotion failed");
}
break;
case State::DO_GO_HOME:

View File

@@ -12,7 +12,9 @@ namespace Modelec
static double distance(const Point& p1, const Point& p2);
Point GetTakeBasePosition();
Point GetTakeClosePosition();
Point GetTakePosition(int distance, double angle) const;
Point GetTakeBasePosition() const;
Point GetTakeClosePosition() const;
};
}

View File

@@ -8,22 +8,23 @@ namespace Modelec
return sqrt(std::pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2));
}
Point Point::GetTakeBasePosition()
Point Point::GetTakePosition(int distance, double angle) const
{
Point pos;
pos.x = x + 400 * cos(theta);
pos.y = y + 400 * sin(theta);
pos.theta = theta + M_PI;
pos.x = x + distance * cos(angle);
pos.y = y + distance * sin(angle);
pos.theta = angle + M_PI;
return pos;
}
Point Point::GetTakeClosePosition()
Point Point::GetTakeBasePosition() const
{
Point pos;
pos.x = x + 210 * cos(theta);
pos.y = y + 210 * sin(theta);
pos.theta = theta + M_PI;
return pos;
return GetTakePosition(300, theta);
}
Point Point::GetTakeClosePosition() const
{
return GetTakePosition(210, theta);
}
}