42 #include <Eigen/Geometry>
47 #include <dynamic_reconfigure/server.h>
48 #include <geometry_msgs/Twist.h>
49 #include <safety_limiter_msgs/SafetyLimiterStatus.h>
50 #include <sensor_msgs/PointCloud.h>
51 #include <sensor_msgs/PointCloud2.h>
52 #include <std_msgs/Bool.h>
53 #include <std_msgs/Empty.h>
57 #include <pcl/common/transforms.h>
58 #include <pcl/filters/voxel_grid.h>
59 #include <pcl/kdtree/kdtree_flann.h>
60 #include <pcl/point_cloud.h>
61 #include <pcl/point_types.h>
67 #include <safety_limiter/SafetyLimiterConfig.h>
71 pcl::PointXYZ
operator-(
const pcl::PointXYZ& a,
const pcl::PointXYZ& b)
79 pcl::PointXYZ
operator+(
const pcl::PointXYZ& a,
const pcl::PointXYZ& b)
87 pcl::PointXYZ
operator*(
const pcl::PointXYZ& a,
const float& b)
173 pub_twist_ = neonavigation_common::compat::advertise<geometry_msgs::Twist>(
175 pnh_,
"cmd_vel_out", 1,
true);
182 nh_,
"disable_safety",
185 nh_,
"watchdog_reset",
188 int num_input_clouds;
189 pnh_.
param(
"num_input_clouds", num_input_clouds, 1);
190 if (num_input_clouds == 1)
198 for (
int i = 0; i < num_input_clouds; ++i)
206 ROS_WARN(
"safety_limiter: t_margin parameter is obsolated. Use d_margin and yaw_margin instead.");
209 double watchdog_interval_d;
210 pnh_.
param(
"watchdog_interval", watchdog_interval_d, 0.0);
222 ROS_FATAL(
"Footprint doesn't specified");
223 throw std::runtime_error(
"Footprint doesn't specified");
229 throw std::runtime_error(
"Invalid footprint");
232 for (
int i = 0; i < footprint_xml.
size(); i++)
238 throw std::runtime_error(
"Invalid footprint value");
242 v[0] =
static_cast<double>(footprint_xml[i][0]);
243 v[1] =
static_cast<double>(footprint_xml[i][1]);
246 const float dist = std::hypot(v[0], v[1]);
281 geometry_msgs::Twist cmd_vel;
296 geometry_msgs::Twist cmd_vel;
310 if (r_lim_current <
r_lim_)
313 if (r_lim_current < 1.0)
320 void cbParameter(
const SafetyLimiterConfig& config,
const uint32_t )
326 vel_[0] = config.lin_vel;
327 acc_[0] = config.lin_acc;
328 vel_[1] = config.ang_vel;
329 acc_[1] = config.ang_acc;
344 for (
int i = 0; i < 2; i++)
354 double predict(
const geometry_msgs::Twist& in)
372 geometry_msgs::TransformStamped fixed_to_base;
384 const Eigen::Affine3f fixed_to_base_eigen =
385 Eigen::Translation3f(
386 fixed_to_base.transform.translation.x,
387 fixed_to_base.transform.translation.y,
388 fixed_to_base.transform.translation.z) *
390 fixed_to_base.transform.rotation.w,
391 fixed_to_base.transform.rotation.x,
392 fixed_to_base.transform.rotation.y,
393 fixed_to_base.transform.rotation.z);
396 pcl::PointCloud<pcl::PointXYZ>::Ptr pc(
new pcl::PointCloud<pcl::PointXYZ>);
397 pcl::VoxelGrid<pcl::PointXYZ> ds;
402 auto filter_z = [
this](pcl::PointXYZ& p)
404 if (p.z < this->z_range_[0] || this->z_range_[1] < p.z)
409 pc->erase(std::remove_if(pc->points.begin(), pc->points.end(), filter_z),
422 pcl::KdTreeFLANN<pcl::PointXYZ> kdtree;
423 kdtree.setInputCloud(pc);
425 Eigen::Affine3f
move;
426 Eigen::Affine3f move_inv;
427 Eigen::Affine3f motion =
428 Eigen::AngleAxisf(-
twist_.angular.z *
dt_, Eigen::Vector3f::UnitZ()) *
429 Eigen::Translation3f(Eigen::Vector3f(-
twist_.linear.x *
dt_, -
twist_.linear.y *
dt_, 0.0));
430 Eigen::Affine3f motion_inv =
431 Eigen::Translation3f(Eigen::Vector3f(
twist_.linear.x *
dt_,
twist_.linear.y *
dt_, 0.0)) *
432 Eigen::AngleAxisf(
twist_.angular.z *
dt_, Eigen::Vector3f::UnitZ());
434 move_inv.setIdentity();
435 sensor_msgs::PointCloud col_points;
441 bool has_collision =
false;
442 float d_escape_remain = 0;
443 float yaw_escape_remain = 0;
445 const double linear_vel = std::hypot(
twist_.linear.x,
twist_.linear.y);
447 for (
float t = 0; t <
tmax_; t +=
dt_)
451 d_col += linear_vel *
dt_;
452 d_escape_remain -= linear_vel *
dt_;
454 yaw_escape_remain -= std::abs(
twist_.angular.z) *
dt_;
456 move_inv = move_inv * motion_inv;
459 pcl::PointXYZ center;
460 center = pcl::transformPoint(center, move_inv);
461 std::vector<int> indices;
462 std::vector<float> dist;
467 bool colliding =
false;
468 for (
auto& i : indices)
470 auto& p = pc->points[i];
471 auto point = pcl::transformPoint(p,
move);
472 vec v(point.x, point.y);
475 geometry_msgs::Point32 pos;
479 col_points.points.push_back(pos);
486 d_col -= linear_vel *
dt_;
496 if (d_escape_remain <= 0 || yaw_escape_remain <= 0)
504 has_collision =
true;
529 const float delay = 1.0 * (1.0 /
hz_) +
dt_;
530 const float acc_dtsq[2] =
532 static_cast<float>(
acc_[0] * std::pow(delay, 2)),
533 static_cast<float>(
acc_[1] * std::pow(delay, 2)),
536 d_col = std::max<float>(
538 std::abs(d_col) -
d_margin_ + acc_dtsq[0] -
539 std::sqrt(std::pow(acc_dtsq[0], 2) + 2 * acc_dtsq[0] * std::abs(d_col)));
540 yaw_col = std::max<float>(
543 std::sqrt(std::pow(acc_dtsq[1], 2) + 2 * acc_dtsq[1] * std::abs(yaw_col)));
546 std::sqrt(std::abs(2 *
acc_[0] * d_col)) / linear_vel;
548 std::sqrt(std::abs(2 *
acc_[1] * yaw_col)) / std::abs(
twist_.angular.z);
549 if (!std::isfinite(d_r))
551 if (!std::isfinite(yaw_r))
554 return std::min(d_r, yaw_r);
558 limit(
const geometry_msgs::Twist& in)
566 if (std::abs(in.linear.x - out.linear.x) >
EPSILON ||
567 std::abs(in.linear.y - out.linear.y) >
EPSILON ||
568 std::abs(in.angular.z - out.angular.z) >
EPSILON)
571 1.0,
"safety_limiter: (%0.2f, %0.2f, %0.2f)->(%0.2f, %0.2f, %0.2f)",
572 in.linear.x, in.linear.y, in.angular.z,
573 out.linear.x, out.linear.y, out.angular.z);
590 const double out_linear_vel = std::hypot(out.linear.x, out.linear.y);
593 const double vel_ratio =
max_values_[0] / out_linear_vel;
594 out.linear.x *= vel_ratio;
595 out.linear.y *= vel_ratio;
598 out.angular.z = (out.angular.z > 0) ?
609 vec(
const float x,
const float y)
637 return (*
this)[0] * a[1] - (*this)[1] * a[0];
641 return (*
this)[0] * a[0] + (*this)[1] * a[1];
645 return std::hypot((*
this)[0] - a[0], (*
this)[1] - a[1]);
649 return (b - a).cross((*
this) - a) / b.
dist(a);
653 if ((b - a).
dot((*
this) - a) <= 0)
654 return this->
dist(a);
655 if ((a - b).
dot((*
this) - b) <= 0)
656 return this->
dist(b);
664 void move(
const float& x,
const float& y,
const float& yaw)
666 const float cos_v = cosf(yaw);
667 const float sin_v = sinf(yaw);
671 p[0] = cos_v * tmp[0] - sin_v * tmp[1] + x;
672 p[1] = sin_v * tmp[0] + cos_v * tmp[1] + y;
678 for (
size_t i = 0; i <
v.size() - 1; i++)
682 if ((v1[1] <= a[1] && a[1] < v2[1]) ||
683 (v2[1] <= a[1] && a[1] < v1[1]))
686 lx = v1[0] + (v2[0] - v1[0]) * (a[1] - v1[1]) / (v2[1] - v1[1]);
691 return ((cn & 1) == 1);
695 float dist = std::numeric_limits<float>::max();
696 for (
size_t i = 0; i <
v.size() - 1; i++)
710 void cbTwist(
const geometry_msgs::Twist::ConstPtr& msg)
723 geometry_msgs::Twist cmd_vel;
736 void cbCloud(
const sensor_msgs::PointCloud2::ConstPtr& msg)
743 sensor_msgs::PointCloud2 cloud_msg_fixed;
746 const geometry_msgs::TransformStamped cloud_to_fixed =
756 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_fixed(
new pcl::PointCloud<pcl::PointXYZ>());
780 safety_limiter_msgs::SafetyLimiterStatus status_msg;
784 stat.
summary(diagnostic_msgs::DiagnosticStatus::ERROR,
"Stopped due to data timeout.");
788 stat.
summary(diagnostic_msgs::DiagnosticStatus::OK,
"OK");
792 stat.
summary(diagnostic_msgs::DiagnosticStatus::WARN,
794 "Cannot escape from collision." :
795 "Trying to avoid collision, but cannot move anymore.");
799 stat.
summary(diagnostic_msgs::DiagnosticStatus::OK,
801 "Escaping from collision." :
802 "Reducing velocity to avoid collision.");
804 stat.
addf(
"Velocity Limit Ratio",
"%.2f",
r_lim_);
805 stat.
add(
"Pointcloud Availability",
has_cloud_ ?
"true" :
"false");
808 status_msg.limit_ratio =
r_lim_;
819 int main(
int argc,
char** argv)