Skip to content

Commit 15ad430

Browse files
committed
Final alignements
Signed-off-by: Francisco Martín Rico <fmrico@gmail.com>
1 parent 732bf07 commit 15ad430

3 files changed

Lines changed: 84 additions & 11 deletions

File tree

controllers/easynav_mpc_controller/include/easynav_mpc_controller/MPCController.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ class MPCController : public ControllerMethodBase
7373
double max_ang_vel_{1.5}; ///< Maximum angular velocity for MPC.
7474
bool verbose_{false}; ///< Value to debug on terminal
7575

76+
// Fallback goal tolerances if GoalManager does not publish them
77+
double fallback_goal_pos_tol_{0.05}; ///< Default positional tolerance (meters).
78+
double fallback_goal_yaw_tol_{0.05}; ///< Default angular tolerance (radians).
79+
7680
std::unique_ptr<MPCOptimizer> optimizer_; ///< MPC optimizer
7781

7882
rclcpp::Publisher<nav_msgs::msg::Path>::SharedPtr mpc_path_pub_; ///< Publisher for MPC path.

controllers/easynav_mpc_controller/src/easynav_mpc_controller/MPCController.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,19 @@ MPCController::on_initialize()
4343
node->declare_parameter<double>(plugin_name + ".max_angular_velocity", max_ang_vel_);
4444
node->declare_parameter<bool>(plugin_name + ".verbose", verbose_);
4545

46+
node->declare_parameter<double>(plugin_name + ".fallback_goal_pos_tol", fallback_goal_pos_tol_);
47+
node->declare_parameter<double>(plugin_name + ".fallback_goal_yaw_tol", fallback_goal_yaw_tol_);
48+
4649
node->get_parameter<int>(plugin_name + ".horizon_steps", horizon_steps_);
4750
node->get_parameter<double>(plugin_name + ".dt", dt_);
4851
node->get_parameter<double>(plugin_name + ".safety_radius", safety_radius_);
4952
node->get_parameter<double>(plugin_name + ".max_linear_velocity", max_lin_vel_);
5053
node->get_parameter<double>(plugin_name + ".max_angular_velocity", max_ang_vel_);
5154
node->get_parameter<bool>(plugin_name + ".verbose", verbose_);
5255

56+
node->get_parameter<double>(plugin_name + ".fallback_goal_pos_tol", fallback_goal_pos_tol_);
57+
node->get_parameter<double>(plugin_name + ".fallback_goal_yaw_tol", fallback_goal_yaw_tol_);
58+
5359
optimizer_ = std::make_unique<MPCOptimizer>();
5460

5561
mpc_path_pub_ =
@@ -267,6 +273,62 @@ MPCController::update_rt(NavState & nav_state)
267273

268274
collision_checker(&params, u);
269275

276+
// Final alignment phase with hysteresis on distance:
277+
// - Enter when dist_to_goal <= 0.5 * pos_tol
278+
// - Stay in this phase (even if dist grows slightly) until dist_to_goal > pos_tol
279+
{
280+
const auto & goal_pose = path.poses.back().pose;
281+
282+
double pos_tol = fallback_goal_pos_tol_;
283+
double yaw_tol = fallback_goal_yaw_tol_;
284+
285+
if (nav_state.has("goal_tolerance.position")) {
286+
pos_tol = nav_state.get<double>("goal_tolerance.position");
287+
}
288+
if (nav_state.has("goal_tolerance.yaw")) {
289+
yaw_tol = nav_state.get<double>("goal_tolerance.yaw");
290+
}
291+
292+
const double dx_g = goal_pose.position.x - pose.position.x;
293+
const double dy_g = goal_pose.position.y - pose.position.y;
294+
const double dist_to_goal = std::hypot(dx_g, dy_g);
295+
296+
const double yaw_goal = std::atan2(
297+
2.0 * (goal_pose.orientation.w * goal_pose.orientation.z +
298+
goal_pose.orientation.x * goal_pose.orientation.y),
299+
1.0 - 2.0 * (goal_pose.orientation.y * goal_pose.orientation.y +
300+
goal_pose.orientation.z * goal_pose.orientation.z));
301+
double e_theta_goal = std::atan2(std::sin(yaw_ - yaw_goal), std::cos(yaw_ - yaw_goal));
302+
303+
const double enter_dist = 0.5 * pos_tol;
304+
305+
// Hysteresis based on distance to goal: start aligning when we are
306+
// well inside the goal radius (enter_dist) and keep aligning until
307+
// we move clearly outside (dist_to_goal > pos_tol).
308+
const bool inside_hysteresis_band = (dist_to_goal <= pos_tol);
309+
const bool should_enter_alignment = (dist_to_goal <= enter_dist);
310+
311+
if ((inside_hysteresis_band && std::fabs(e_theta_goal) > yaw_tol) ||
312+
should_enter_alignment)
313+
{
314+
// Stay in place and rotate towards the goal orientation using a simple P controller
315+
const double k_align = 1.0;
316+
double vlin = 0.0;
317+
double vrot = -k_align * e_theta_goal;
318+
319+
vrot = std::clamp(vrot, -max_ang_vel_, max_ang_vel_);
320+
321+
cmd_vel_.header.frame_id = path.header.frame_id;
322+
cmd_vel_.header.stamp = get_node()->now();
323+
cmd_vel_.twist.linear.x = vlin;
324+
cmd_vel_.twist.angular.z = vrot;
325+
326+
nav_state.set("cmd_vel", cmd_vel_);
327+
publish_mpc_path(&params, u, path);
328+
return;
329+
}
330+
}
331+
270332
// Publish the computed velocity command
271333
cmd_vel_.header.frame_id = path.header.frame_id;
272334
cmd_vel_.header.stamp = get_node()->now();

controllers/easynav_serest_controller/src/easynav_serest_controller/SerestController.cpp

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <algorithm>
2424
#include <limits>
2525
#include <cmath>
26+
#include <numbers>
2627

2728
#include "tf2/utils.hpp"
2829
#include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
@@ -401,17 +402,16 @@ SerestController::compute_goal_zone(
401402
double & stop_r, double & slow_r, double & gamma_slow,
402403
Vec2 & goal_xy, double & yaw_goal) const
403404
{
404-
const double PI = 3.14159265358979323846;
405-
const double goal_yaw_tol = goal_yaw_tol_deg_ * PI / 180.0; // (no se devuelve, pero útil si quieres)
406-
(void)goal_yaw_tol;
407-
408405
const auto & pgoal = path.poses.back().pose.position;
409406
yaw_goal = tf2::getYaw(path.poses.back().pose.orientation);
410407
goal_xy = Vec2{pgoal.x, pgoal.y};
411408

412409
dist_xy_goal = std::hypot(robot_xy.x - goal_xy.x, robot_xy.y - goal_xy.y);
413410
e_theta_goal = std::atan2(std::sin(robot_yaw - yaw_goal), std::cos(robot_yaw - yaw_goal));
414411

412+
// Use externally provided goal position tolerance when available (via update_rt),
413+
// which stores it in goal_pos_tol_. Here we only shape the radial zone using
414+
// the current value of goal_pos_tol_ as stop radius.
415415
stop_r = std::max(0.0, goal_pos_tol_);
416416
slow_r = std::max(slow_radius_, stop_r + 0.05);
417417

@@ -482,8 +482,7 @@ SerestController::should_turn_in_place(
482482
{
483483
// Mantenemos compatibilidad con la firma, pero ignoramos turn_in_place_thr
484484
// y usamos dos umbrales internos sin exponer parámetros.
485-
const double PI = 3.14159265358979323846;
486-
const double thr_enter = 60.0 * PI / 180.0; // entra a girar si |e_theta| > 60°
485+
const double thr_enter = 60.0 * std::numbers::pi / 180.0; // entra a girar si |e_theta| > 60°
487486
// const double thr_exit = 35.0 * PI / 180.0; // sale de girar si |e_theta| < 35°
488487

489488
// No permitimos “atajo” marcha atrás en esta decisión: si no permites reverse,
@@ -506,8 +505,7 @@ SerestController::maybe_final_align_and_publish(
506505
double dist_xy_goal, double stop_r, double e_theta_goal,
507506
double gamma_slow, double dt)
508507
{
509-
const double PI = 3.14159265358979323846;
510-
const double goal_yaw_tol = goal_yaw_tol_deg_ * PI / 180.0;
508+
const double goal_yaw_tol = goal_yaw_tol_deg_ * std::numbers::pi / 180.0;
511509

512510
if (dist_xy_goal > stop_r) {
513511
return false;
@@ -620,6 +618,16 @@ SerestController::update_rt(NavState & nav_state)
620618
nav_msgs::msg::Odometry odom;
621619
if (!fetch_required_inputs(nav_state, path, odom)) {return;}
622620

621+
// 1.5) Goal tolerances: prefer shared GoalManager values, fallback to local params
622+
double goal_pos_tol = goal_pos_tol_;
623+
double goal_yaw_tol = goal_yaw_tol_deg_ * (std::numbers::pi / 180.0);
624+
if (nav_state.has("goal_tolerance.position")) {
625+
goal_pos_tol = nav_state.get<double>("goal_tolerance.position");
626+
}
627+
if (nav_state.has("goal_tolerance.yaw")) {
628+
goal_yaw_tol = nav_state.get<double>("goal_tolerance.yaw");
629+
}
630+
623631
// 2) Robot state (position + yaw)
624632
Vec2 robot_xy; double yaw = 0.0;
625633
robot_state_from_odom(odom, robot_xy, yaw);
@@ -714,8 +722,7 @@ SerestController::update_rt(NavState & nav_state)
714722
double v_prog_ref = v_prog_ref_free * gamma_slow;
715723

716724
// Maintain a small cruising speed when roughly aligned and outside the stop zone (no reverse)
717-
const double PI = 3.14159265358979323846;
718-
const double align_thr = 30.0 * PI / 180.0;
725+
const double align_thr = 30.0 * std::numbers::pi / 180.0;
719726
if (!allow_reverse_ && (dist_xy_goal > stop_r) && std::fabs(e_theta) < align_thr) {
720727
v_prog_ref = std::max(v_prog_ref, std::min(slow_min_speed_, v_prog_ref_free));
721728
}
@@ -766,7 +773,7 @@ SerestController::update_rt(NavState & nav_state)
766773

767774
// Optional classic TiP safeguard using the same angular threshold
768775
{
769-
const double turn_in_place_thr = (60.0 * PI / 180.0);
776+
const double turn_in_place_thr = (60.0 * std::numbers::pi / 180.0);
770777
const double s_total = pd.s_acc.back();
771778
const double dist_to_end = s_total - prj.s_star;
772779

0 commit comments

Comments
 (0)