32 #include <sensor_msgs/PointCloud2.h> 33 #include <sensor_msgs/PointCloud.h> 34 #include <geometry_msgs/Twist.h> 35 #include <std_msgs/Bool.h> 36 #include <std_msgs/Empty.h> 41 #include <Eigen/Geometry> 43 #include <pcl/common/transforms.h> 44 #include <pcl/filters/voxel_grid.h> 45 #include <pcl/kdtree/kdtree_flann.h> 46 #include <pcl/point_cloud.h> 47 #include <pcl/point_types.h> 60 pcl::PointXYZ
operator-(
const pcl::PointXYZ& a,
const pcl::PointXYZ& b)
68 pcl::PointXYZ
operator+(
const pcl::PointXYZ& a,
const pcl::PointXYZ& b)
76 pcl::PointXYZ
operator*(
const pcl::PointXYZ& a,
const float& b)
108 pcl::PointCloud<pcl::PointXYZ>::Ptr
cloud_;
147 , cloud_(new
pcl::PointCloud<
pcl::PointXYZ>)
148 , cloud_clear_(false)
149 , last_disable_cmd_(0)
150 , watchdog_stop_(false)
153 , has_collision_at_now_(false)
156 pub_twist_ = neonavigation_common::compat::advertise<geometry_msgs::Twist>(
158 pnh_,
"cmd_vel_out", 1,
true);
159 pub_cloud_ = nh_.advertise<sensor_msgs::PointCloud>(
"collision", 1,
true);
160 pub_debug_ = nh_.advertise<sensor_msgs::PointCloud>(
"debug", 1,
true);
165 nh_,
"disable_safety",
168 nh_,
"watchdog_reset",
171 int num_input_clouds;
172 pnh_.param(
"num_input_clouds", num_input_clouds, 1);
173 if (num_input_clouds == 1)
181 for (
int i = 0; i < num_input_clouds; ++i)
183 sub_clouds_.push_back(nh_.subscribe(
188 pnh_.param(
"freq", hz_, 6.0);
189 pnh_.param(
"cloud_timeout", timeout_, 0.8);
190 pnh_.param(
"disable_timeout", disable_timeout_, 0.1);
191 pnh_.param(
"lin_vel", vel_[0], 0.5);
192 pnh_.param(
"lin_acc", acc_[0], 1.0);
193 pnh_.param(
"ang_vel", vel_[1], 0.8);
194 pnh_.param(
"ang_acc", acc_[1], 1.6);
195 pnh_.param(
"z_range_min", z_range_[0], 0.0);
196 pnh_.param(
"z_range_max", z_range_[1], 0.5);
197 pnh_.param(
"dt", dt_, 0.1);
198 if (pnh_.hasParam(
"t_margin"))
199 ROS_WARN(
"safety_limiter: t_margin parameter is obsolated. Use d_margin and yaw_margin instead.");
200 pnh_.param(
"d_margin", d_margin_, 0.2);
201 pnh_.param(
"d_escape", d_escape_, 0.05);
202 pnh_.param(
"yaw_margin", yaw_margin_, 0.2);
203 pnh_.param(
"yaw_escape", yaw_escape_, 0.05);
204 pnh_.param(
"downsample_grid", downsample_grid_, 0.05);
205 pnh_.param(
"frame_id", frame_id_, std::string(
"base_link"));
207 pnh_.param(
"hold", hold_d, 0.0);
209 double watchdog_interval_d;
210 pnh_.param(
"watchdog_interval", watchdog_interval_d, 0.0);
212 pnh_.param(
"allow_empty_cloud", allow_empty_cloud_,
false);
215 for (
int i = 0; i < 2; i++)
217 auto t = vel_[i] / acc_[i];
222 tmax_ += std::max(d_margin_ / vel_[0], yaw_margin_ / vel_[1]);
226 if (!pnh_.hasParam(
"footprint"))
228 ROS_FATAL(
"Footprint doesn't specified");
229 throw std::runtime_error(
"Footprint doesn't specified");
231 pnh_.getParam(
"footprint", footprint_xml);
235 throw std::runtime_error(
"Invalid footprint");
237 footprint_radius_ = 0;
238 for (
int i = 0; i < footprint_xml.
size(); i++)
244 throw std::runtime_error(
"Invalid footprint value");
248 v[0] =
static_cast<double>(footprint_xml[i][0]);
249 v[1] =
static_cast<double>(footprint_xml[i][1]);
252 const float dist = hypotf(v[0], v[1]);
253 if (dist > footprint_radius_)
254 footprint_radius_ = dist;
257 ROS_INFO(
"footprint radius: %0.3f", footprint_radius_);
279 watchdog_timer_.
setPeriod(watchdog_interval_,
true);
280 watchdog_stop_ =
false;
285 watchdog_stop_ =
true;
286 geometry_msgs::Twist cmd_vel;
299 geometry_msgs::Twist cmd_vel;
302 cloud_.reset(
new pcl::PointCloud<pcl::PointXYZ>);
307 const double r_lim_current =
predict(twist_);
309 if (r_lim_current < r_lim_)
310 r_lim_ = r_lim_current;
312 if (r_lim_current < 1.0)
313 hold_off_ = now +
hold_;
319 double predict(
const geometry_msgs::Twist& in)
321 pcl::PointCloud<pcl::PointXYZ>::Ptr pc(
new pcl::PointCloud<pcl::PointXYZ>);
322 pcl::VoxelGrid<pcl::PointXYZ> ds;
323 ds.setInputCloud(cloud_);
324 ds.setLeafSize(downsample_grid_, downsample_grid_, downsample_grid_);
327 auto filter_z = [
this](pcl::PointXYZ& p)
329 if (p.z < this->z_range_[0] || this->z_range_[1] < p.z)
334 pc->erase(std::remove_if(pc->points.begin(), pc->points.end(), filter_z),
339 if (allow_empty_cloud_)
347 pcl::KdTreeFLANN<pcl::PointXYZ> kdtree;
348 kdtree.setInputCloud(pc);
350 Eigen::Affine3f move;
351 Eigen::Affine3f move_inv;
352 Eigen::Affine3f motion =
353 Eigen::AngleAxisf(-twist_.angular.z * dt_, Eigen::Vector3f::UnitZ()) *
354 Eigen::Translation3f(Eigen::Vector3f(-twist_.linear.x * dt_, 0.0, 0.0));
355 Eigen::Affine3f motion_inv =
356 Eigen::Translation3f(Eigen::Vector3f(twist_.linear.x * dt_, 0.0, 0.0)) *
357 Eigen::AngleAxisf(twist_.angular.z * dt_, Eigen::Vector3f::UnitZ());
359 move_inv.setIdentity();
360 sensor_msgs::PointCloud col_points;
361 sensor_msgs::PointCloud debug_points;
364 debug_points.header = col_points.header;
368 bool has_collision =
false;
369 float d_escape_remain = 0;
370 float yaw_escape_remain = 0;
371 has_collision_at_now_ =
false;
373 for (
float t = 0; t <
tmax_; t +=
dt_)
377 d_col += twist_.linear.x *
dt_;
378 d_escape_remain -= std::abs(twist_.linear.x) *
dt_;
379 yaw_col += twist_.angular.z *
dt_;
380 yaw_escape_remain -= std::abs(twist_.angular.z) *
dt_;
381 move = move * motion;
382 move_inv = move_inv * motion_inv;
385 pcl::PointXYZ center;
386 center = pcl::transformPoint(center, move_inv);
387 geometry_msgs::Point32 p;
391 debug_points.points.push_back(p);
393 std::vector<int> indices;
394 std::vector<float> dist;
395 const int num = kdtree.radiusSearch(center, footprint_radius_, indices, dist);
399 bool colliding =
false;
400 for (
auto& i : indices)
402 auto& p = pc->points[i];
403 auto point = pcl::transformPoint(p, move);
404 vec v(point.x, point.y);
407 geometry_msgs::Point32 pos;
411 col_points.points.push_back(pos);
424 has_collision_at_now_ =
true;
426 if (d_escape_remain <= 0 || yaw_escape_remain <= 0)
428 if (has_collision_at_now_)
434 has_collision =
true;
439 pub_debug_.
publish(debug_points);
440 pub_cloud_.
publish(col_points);
449 const float delay = 1.0 * (1.0 /
hz_) + dt_;
450 const float acc_dtsq[2] =
452 static_cast<float>(acc_[0] * std::pow(delay, 2)),
453 static_cast<float>(acc_[1] * std::pow(delay, 2)),
456 d_col = std::max<float>(
458 std::abs(d_col) - d_margin_ + acc_dtsq[0] -
459 std::sqrt(std::pow(acc_dtsq[0], 2) + 2 * acc_dtsq[0] * std::abs(d_col)));
460 yaw_col = std::max<float>(
462 std::abs(yaw_col) - yaw_margin_ + acc_dtsq[1] -
463 std::sqrt(std::pow(acc_dtsq[1], 2) + 2 * acc_dtsq[1] * std::abs(yaw_col)));
466 (std::sqrt(std::abs(2 * acc_[0] * d_col)) +
EPSILON) / std::abs(twist_.linear.x);
468 (std::sqrt(std::abs(2 * acc_[1] * yaw_col)) +
EPSILON) / std::abs(twist_.angular.z);
469 if (!std::isfinite(d_r))
471 if (!std::isfinite(yaw_r))
474 return std::min(d_r, yaw_r);
478 limit(
const geometry_msgs::Twist& in)
481 if (r_lim_ < 1.0 - EPSILON)
485 if (std::abs(in.linear.x - out.linear.x) > EPSILON ||
486 std::abs(in.angular.z - out.angular.z) > EPSILON)
489 1.0,
"safety_limiter: (%0.2f, %0.2f)->(%0.2f, %0.2f)",
490 in.linear.x, in.angular.z,
491 out.linear.x, out.angular.z);
501 vec(
const float x,
const float y)
529 return (*
this)[0] * a[1] - (*this)[1] * a[0];
533 return (*
this)[0] * a[0] + (*this)[1] * a[1];
537 return hypotf((*
this)[0] - a[0], (*
this)[1] - a[1]);
541 return (b - a).cross((*
this) - a) / b.
dist(a);
545 if ((b - a).
dot((*
this) - a) <= 0)
546 return this->
dist(a);
547 if ((a - b).
dot((*
this) - b) <= 0)
548 return this->
dist(b);
556 void move(
const float& x,
const float& y,
const float& yaw)
558 const float cos_v = cosf(yaw);
559 const float sin_v = sinf(yaw);
563 p[0] = cos_v * tmp[0] - sin_v * tmp[1] + x;
564 p[1] = sin_v * tmp[0] + cos_v * tmp[1] + y;
570 for (
size_t i = 0; i < v.size() - 1; i++)
574 if ((v1[1] <= a[1] && a[1] < v2[1]) ||
575 (v2[1] <= a[1] && a[1] < v1[1]))
578 lx = v1[0] + (v2[0] - v1[0]) * (a[1] - v1[1]) / (v2[1] - v1[1]);
583 return ((cn & 1) == 1);
587 float dist = FLT_MAX;
588 for (
size_t i = 0; i < v.size() - 1; i++)
602 void cbTwist(
const geometry_msgs::Twist::ConstPtr& msg)
609 if (now - last_disable_cmd_ <
ros::Duration(disable_timeout_))
615 geometry_msgs::Twist cmd_vel;
620 geometry_msgs::Twist cmd_vel =
limit(twist_);
627 void cbCloud(
const sensor_msgs::PointCloud2::ConstPtr& msg)
629 pcl::PointCloud<pcl::PointXYZ>::Ptr pc(
new pcl::PointCloud<pcl::PointXYZ>());
633 sensor_msgs::PointCloud2 pc2_tmp;
635 frame_id_, msg->header.frame_id, msg->header.stamp,
ros::Duration(0.1));
647 cloud_clear_ =
false;
648 cloud_.reset(
new pcl::PointCloud<pcl::PointXYZ>);
651 last_cloud_stamp_ = msg->header.stamp;
666 stat.
summary(diagnostic_msgs::DiagnosticStatus::OK,
"OK");
668 else if (r_lim_ == 0.0)
670 stat.
summary(diagnostic_msgs::DiagnosticStatus::WARN,
671 (has_collision_at_now_) ?
672 "Cannot escape from collision." :
673 "Trying to avoid collision, but cannot move anymore.");
677 stat.
summary(diagnostic_msgs::DiagnosticStatus::OK,
678 (has_collision_at_now_) ?
679 "Escaping from collision." :
680 "Reducing velocity to avoid collision.");
682 stat.
addf(
"Velocity Limit Ratio",
"%.2f", r_lim_);
686 int main(
int argc,
char** argv)
std::vector< ros::Subscriber > sub_clouds_
double predict(const geometry_msgs::Twist &in)
static constexpr float EPSILON
pcl::PointCloud< pcl::PointXYZ >::Ptr cloud_
int main(int argc, char **argv)
float cross(const vec &a) const
void publish(const boost::shared_ptr< M > &message) const
geometry_msgs::Twist twist_
float dist(const vec &a) const
void cbCloud(const sensor_msgs::PointCloud2::ConstPtr &msg)
void summary(unsigned char lvl, const std::string s)
diagnostic_updater::Updater diag_updater_
void cbWatchdogReset(const std_msgs::Empty::ConstPtr &msg)
void setHardwareID(const std::string &hwid)
void cbDisable(const std_msgs::Bool::ConstPtr &msg)
bool inside(const vec &a) const
virtual geometry_msgs::TransformStamped lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration timeout) const
void setPeriod(const Duration &period, bool reset=true)
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
void add(const std::string &name, TaskFunction f)
void fromROSMsg(const sensor_msgs::PointCloud2 &cloud, pcl::PointCloud< T > &pcl_cloud)
pcl::PointXYZ operator*(const pcl::PointXYZ &a, const float &b)
ros::Subscriber subscribe(ros::NodeHandle &nh_new, const std::string &topic_new, ros::NodeHandle &nh_old, const std::string &topic_old, uint32_t queue_size, void(*fp)(M), const ros::TransportHints &transport_hints=ros::TransportHints())
void cbWatchdogTimer(const ros::TimerEvent &event)
tf2_ros::TransformListener tfl_
void doTransform(const T &data_in, T &data_out, const geometry_msgs::TransformStamped &transform)
Type const & getType() const
ros::Duration watchdog_interval_
void addf(const std::string &key, const char *format,...)
float dist_line(const vec &a, const vec &b) const
void move(const float &x, const float &y, const float &yaw)
ROSCPP_DECL void spin(Spinner &spinner)
ros::Time last_cloud_stamp_
float dist_linestrip(const vec &a, const vec &b) const
#define ROS_WARN_THROTTLE(period,...)
ros::Publisher pub_debug_
float dist(const vec &a) const
bool has_collision_at_now_
pcl::PointXYZ operator+(const pcl::PointXYZ &a, const pcl::PointXYZ &b)
void cbTwist(const geometry_msgs::Twist::ConstPtr &msg)
Timer createTimer(Rate r, Handler h, Obj o, bool oneshot=false, bool autostart=true) const
float dot(const vec &a) const
const float & operator[](const int &i) const
ros::Timer watchdog_timer_
ros::Publisher pub_cloud_
geometry_msgs::Twist limit(const geometry_msgs::Twist &in)
ros::Time last_disable_cmd_
void diagnoseCollision(diagnostic_updater::DiagnosticStatusWrapper &stat)
vec operator-(const vec &a) const
vec(const float x, const float y)
float & operator[](const int &i)
void cbPredictTimer(const ros::TimerEvent &event)
ros::Publisher pub_twist_
ros::Subscriber sub_disable_
bool XmlRpc_isNumber(XmlRpc::XmlRpcValue &value)
ros::Subscriber sub_watchdog_
ros::Subscriber sub_twist_
pcl::PointXYZ operator-(const pcl::PointXYZ &a, const pcl::PointXYZ &b)