LaserSafetyFilter: scan-based emergency brake + evasive turn before cmd publish
This commit is contained in:
@@ -27,6 +27,7 @@ add_executable(nav_lite_node
|
|||||||
src/bt_executor.cpp
|
src/bt_executor.cpp
|
||||||
src/session_mapper.cpp
|
src/session_mapper.cpp
|
||||||
src/cone_updater.cpp
|
src/cone_updater.cpp
|
||||||
|
src/laser_safety.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(nav_lite_node PRIVATE
|
target_include_directories(nav_lite_node PRIVATE
|
||||||
|
|||||||
20
include/car_nav_lite/laser_safety.hpp
Normal file
20
include/car_nav_lite/laser_safety.hpp
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "car_nav_lite/types.hpp"
|
||||||
|
|
||||||
|
namespace car_nav_lite {
|
||||||
|
|
||||||
|
// Fast scan-based safety filter. Runs every frame before publishing cmd_vel.
|
||||||
|
// Checks if the planned command would hit anything in the latest scan.
|
||||||
|
struct LaserSafetyFilter {
|
||||||
|
bool enable = true;
|
||||||
|
double front_width = 0.30; // half-width of front safety zone (m)
|
||||||
|
double stop_dist = 0.35; // brake distance (m)
|
||||||
|
double slow_dist = 0.70; // slow-down distance (m)
|
||||||
|
double turn_wz = 0.8; // evasive turn rate (rad/s)
|
||||||
|
double max_range = 2.0; // max lookahead (m)
|
||||||
|
|
||||||
|
Twist filter(const Twist& cmd, const LaserScan& scan) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace car_nav_lite
|
||||||
50
src/laser_safety.cpp
Normal file
50
src/laser_safety.cpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#include "car_nav_lite/laser_safety.hpp"
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace car_nav_lite {
|
||||||
|
|
||||||
|
Twist LaserSafetyFilter::filter(const Twist& cmd, const LaserScan& scan) const {
|
||||||
|
Twist out = cmd;
|
||||||
|
if (!enable || scan.size() < 10) return out;
|
||||||
|
|
||||||
|
double front_min = 999.0;
|
||||||
|
double left_sum = 0, right_sum = 0;
|
||||||
|
int left_n = 0, right_n = 0;
|
||||||
|
double half_w = front_width;
|
||||||
|
double stop = stop_dist + std::max(0.0, cmd.vx) * 0.5;
|
||||||
|
double slow = slow_dist + std::max(0.0, cmd.vx) * 0.5;
|
||||||
|
|
||||||
|
for (int i = 0; i < scan.size(); ++i) {
|
||||||
|
double r = scan.ranges[i];
|
||||||
|
if (r < 0.10 || r > max_range) continue;
|
||||||
|
double a = scan.angle_min + i * scan.angle_increment;
|
||||||
|
double x = r * std::cos(a); // body-frame
|
||||||
|
double y = r * std::sin(a);
|
||||||
|
if (x <= 0) continue; // behind car
|
||||||
|
|
||||||
|
// Front safety zone
|
||||||
|
if (std::abs(y) < half_w && x < front_min) front_min = x;
|
||||||
|
|
||||||
|
// Side clearance for evasive turn
|
||||||
|
if (x > 0 && x < 1.0) {
|
||||||
|
if (y > 0) { left_sum += r; left_n++; }
|
||||||
|
else { right_sum += r; right_n++; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double left_avg = left_n > 0 ? left_sum / left_n : 0;
|
||||||
|
double right_avg = right_n > 0 ? right_sum / right_n : 0;
|
||||||
|
|
||||||
|
if (front_min < stop) {
|
||||||
|
// Emergency: stop and turn away
|
||||||
|
out.vx = 0.0;
|
||||||
|
out.wz = (left_avg > right_avg) ? turn_wz : -turn_wz;
|
||||||
|
} else if (front_min < slow) {
|
||||||
|
out.vx *= 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace car_nav_lite
|
||||||
14
src/main.cpp
14
src/main.cpp
@@ -22,6 +22,7 @@
|
|||||||
#include "car_nav_lite/fence_model.hpp"
|
#include "car_nav_lite/fence_model.hpp"
|
||||||
#include "car_nav_lite/session_mapper.hpp"
|
#include "car_nav_lite/session_mapper.hpp"
|
||||||
#include "car_nav_lite/cone_updater.hpp"
|
#include "car_nav_lite/cone_updater.hpp"
|
||||||
|
#include "car_nav_lite/laser_safety.hpp"
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
using namespace car_nav_lite;
|
using namespace car_nav_lite;
|
||||||
@@ -149,7 +150,9 @@ private:
|
|||||||
SessionMapper session_mapper_;
|
SessionMapper session_mapper_;
|
||||||
FenceModel fence_model_;
|
FenceModel fence_model_;
|
||||||
ConeUpdater cone_updater_;
|
ConeUpdater cone_updater_;
|
||||||
|
LaserSafetyFilter safety_filter_;
|
||||||
std::vector<Eigen::Vector2d> known_cones_;
|
std::vector<Eigen::Vector2d> known_cones_;
|
||||||
|
rclcpp::Time last_cone_tick_{0,0,RCL_ROS_TIME};
|
||||||
enum SessionState { CALIBRATING, READY };
|
enum SessionState { CALIBRATING, READY };
|
||||||
SessionState session_state_ = CALIBRATING;
|
SessionState session_state_ = CALIBRATING;
|
||||||
int calib_cnt_ = 0;
|
int calib_cnt_ = 0;
|
||||||
@@ -261,9 +264,11 @@ private:
|
|||||||
pub_cmd_->publish(geometry_msgs::msg::Twist());
|
pub_cmd_->publish(geometry_msgs::msg::Twist());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// READY: online cone discovery + optional dynamic obstacles
|
// READY: online cone discovery (3Hz) + optional dynamic obstacles
|
||||||
if (has_scan_) {
|
if (has_scan_) {
|
||||||
if (online_cone_enable_) {
|
auto now = rclcpp::Clock().now();
|
||||||
|
if (online_cone_enable_ && (now - last_cone_tick_).seconds() > 0.3) {
|
||||||
|
last_cone_tick_ = now;
|
||||||
cone_updater_.update(latest_scan_, pose, fence_model_, known_cones_);
|
cone_updater_.update(latest_scan_, pose, fence_model_, known_cones_);
|
||||||
auto new_cones = cone_updater_.popConfirmed();
|
auto new_cones = cone_updater_.popConfirmed();
|
||||||
for (auto& c : new_cones) {
|
for (auto& c : new_cones) {
|
||||||
@@ -295,6 +300,11 @@ private:
|
|||||||
// Slow down if localization confidence is low
|
// Slow down if localization confidence is low
|
||||||
if (localizer_.loc_conf_ < 0.3) { cmd.vx *= 0.4; cmd.wz *= 0.4; }
|
if (localizer_.loc_conf_ < 0.3) { cmd.vx *= 0.4; cmd.wz *= 0.4; }
|
||||||
|
|
||||||
|
// Laser safety filter: last-moment scan check
|
||||||
|
if (has_scan_ && session_state_ == READY) {
|
||||||
|
Twist safe = safety_filter_.filter({(float)cmd.vx, (float)cmd.wz}, latest_scan_);
|
||||||
|
cmd.vx = safe.vx; cmd.wz = safe.wz;
|
||||||
|
}
|
||||||
auto tw=geometry_msgs::msg::Twist();tw.linear.x=cmd.vx;tw.angular.z=cmd.wz;
|
auto tw=geometry_msgs::msg::Twist();tw.linear.x=cmd.vx;tw.angular.z=cmd.wz;
|
||||||
pub_cmd_->publish(tw);
|
pub_cmd_->publish(tw);
|
||||||
if(!bt_.currentPath().empty()){
|
if(!bt_.currentPath().empty()){
|
||||||
|
|||||||
Reference in New Issue
Block a user