3030#include " sensor_msgs/msg/range.hpp"
3131#include " sensor_msgs/point_cloud2_iterator.hpp"
3232#include " geometry_msgs/msg/twist.hpp"
33+ #include " geometry_msgs/msg/polygon_stamped.hpp"
3334
3435#include " tf2_ros/transform_broadcaster.h"
3536
3839
3940using namespace std ::chrono_literals;
4041
41- static constexpr double EPSILON = std::numeric_limits< float >::epsilon() ;
42+ static constexpr double EPSILON = 1e-5 ;
4243
4344static const char BASE_FRAME_ID[]{" base_link" };
4445static const char SOURCE_FRAME_ID[]{" base_source" };
4546static const char ODOM_FRAME_ID[]{" odom" };
4647static const char CMD_VEL_IN_TOPIC[]{" cmd_vel_in" };
4748static const char CMD_VEL_OUT_TOPIC[]{" cmd_vel_out" };
49+ static const char FOOTPRINT_TOPIC[]{" footprint" };
4850static const char SCAN_NAME[]{" Scan" };
4951static const char POINTCLOUD_NAME[]{" PointCloud" };
5052static const char RANGE_NAME[]{" Range" };
@@ -132,6 +134,9 @@ class Tester : public ::testing::Test
132134 // Setting TF chains
133135 void sendTransforms (const rclcpp::Time & stamp);
134136
137+ // Publish robot footprint
138+ void publishFootprint (const double radius, const rclcpp::Time & stamp);
139+
135140 // Main topic/data working routines
136141 void publishScan (const double dist, const rclcpp::Time & stamp);
137142 void publishPointCloud (const double dist, const rclcpp::Time & stamp);
@@ -149,6 +154,9 @@ class Tester : public ::testing::Test
149154 // CollisionMonitor node
150155 std::shared_ptr<CollisionMonitorWrapper> cm_;
151156
157+ // Footprint publisher
158+ rclcpp::Publisher<geometry_msgs::msg::PolygonStamped>::SharedPtr footprint_pub_;
159+
152160 // Data source publishers
153161 rclcpp::Publisher<sensor_msgs::msg::LaserScan>::SharedPtr scan_pub_;
154162 rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pointcloud_pub_;
@@ -165,6 +173,9 @@ Tester::Tester()
165173{
166174 cm_ = std::make_shared<CollisionMonitorWrapper>();
167175
176+ footprint_pub_ = cm_->create_publisher <geometry_msgs::msg::PolygonStamped>(
177+ FOOTPRINT_TOPIC, rclcpp::QoS (rclcpp::KeepLast (1 )).transient_local ().reliable ());
178+
168179 scan_pub_ = cm_->create_publisher <sensor_msgs::msg::LaserScan>(
169180 SCAN_NAME, rclcpp::QoS (rclcpp::KeepLast (1 )).transient_local ().reliable ());
170181 pointcloud_pub_ = cm_->create_publisher <sensor_msgs::msg::PointCloud2>(
@@ -181,6 +192,8 @@ Tester::Tester()
181192
182193Tester::~Tester ()
183194{
195+ footprint_pub_.reset ();
196+
184197 scan_pub_.reset ();
185198 pointcloud_pub_.reset ();
186199 range_pub_.reset ();
@@ -236,12 +249,19 @@ void Tester::addPolygon(
236249 cm_->set_parameter (
237250 rclcpp::Parameter (polygon_name + " .type" , " polygon" ));
238251
239- const std::vector<double > points {
240- size, size, size, -size, -size, -size, -size, size};
241- cm_->declare_parameter (
242- polygon_name + " .points" , rclcpp::ParameterValue (points));
243- cm_->set_parameter (
244- rclcpp::Parameter (polygon_name + " .points" , points));
252+ if (at != " approach" ) {
253+ const std::vector<double > points {
254+ size, size, size, -size, -size, -size, -size, size};
255+ cm_->declare_parameter (
256+ polygon_name + " .points" , rclcpp::ParameterValue (points));
257+ cm_->set_parameter (
258+ rclcpp::Parameter (polygon_name + " .points" , points));
259+ } else { // at == "approach"
260+ cm_->declare_parameter (
261+ polygon_name + " .footprint_topic" , rclcpp::ParameterValue (FOOTPRINT_TOPIC));
262+ cm_->set_parameter (
263+ rclcpp::Parameter (polygon_name + " .footprint_topic" , FOOTPRINT_TOPIC));
264+ }
245265 } else if (type == CIRCLE) {
246266 cm_->declare_parameter (
247267 polygon_name + " .type" , rclcpp::ParameterValue (" circle" ));
@@ -379,6 +399,31 @@ void Tester::sendTransforms(const rclcpp::Time & stamp)
379399 }
380400}
381401
402+ void Tester::publishFootprint (const double radius, const rclcpp::Time & stamp)
403+ {
404+ std::unique_ptr<geometry_msgs::msg::PolygonStamped> msg =
405+ std::make_unique<geometry_msgs::msg::PolygonStamped>();
406+
407+ msg->header .frame_id = BASE_FRAME_ID;
408+ msg->header .stamp = stamp;
409+
410+ geometry_msgs::msg::Point32 p;
411+ p.x = radius;
412+ p.y = radius;
413+ msg->polygon .points .push_back (p);
414+ p.x = radius;
415+ p.y = -radius;
416+ msg->polygon .points .push_back (p);
417+ p.x = -radius;
418+ p.y = -radius;
419+ msg->polygon .points .push_back (p);
420+ p.x = -radius;
421+ p.y = radius;
422+ msg->polygon .points .push_back (p);
423+
424+ footprint_pub_->publish (std::move (msg));
425+ }
426+
382427void Tester::publishScan (const double dist, const rclcpp::Time & stamp)
383428{
384429 std::unique_ptr<sensor_msgs::msg::LaserScan> msg =
@@ -544,7 +589,7 @@ TEST_F(Tester, testProcessStopSlowdown)
544589 ASSERT_NEAR (cmd_vel_out_->linear .y , 0.0 , EPSILON);
545590 ASSERT_NEAR (cmd_vel_out_->angular .z , 0.0 , EPSILON);
546591
547- // 4. Restorint back normal operation
592+ // 4. Restoring back normal operation
548593 publishScan (3.0 , curr_time);
549594 ASSERT_TRUE (waitData (3.0 , 500ms, curr_time));
550595 publishCmdVel (0.5 , 0.2 , 0.1 );
@@ -606,7 +651,7 @@ TEST_F(Tester, testProcessApproach)
606651 ASSERT_NEAR (cmd_vel_out_->linear .y , 0.0 , EPSILON);
607652 ASSERT_NEAR (cmd_vel_out_->angular .z , 0.0 , EPSILON);
608653
609- // 4. Restorint back normal operation
654+ // 4. Restoring back normal operation
610655 publishScan (3.0 , curr_time);
611656 ASSERT_TRUE (waitData (3.0 , 500ms, curr_time));
612657 publishCmdVel (0.5 , 0.2 , 0.0 );
@@ -619,6 +664,71 @@ TEST_F(Tester, testProcessApproach)
619664 cm_->stop ();
620665}
621666
667+ TEST_F (Tester, testProcessApproachRotation)
668+ {
669+ rclcpp::Time curr_time = cm_->now ();
670+
671+ // Set Collision Monitor parameters.
672+ // Making one circle footprint for approach.
673+ setCommonParameters ();
674+ addPolygon (" Approach" , POLYGON, 1.0 , " approach" );
675+ addSource (RANGE_NAME, RANGE);
676+ setVectors ({" Approach" }, {RANGE_NAME});
677+
678+ // Start Collision Monitor node
679+ cm_->start ();
680+
681+ // Publish robot footprint
682+ publishFootprint (1.0 , curr_time);
683+
684+ // Share TF
685+ sendTransforms (curr_time);
686+
687+ // 1. Obstacle is far away from robot
688+ publishRange (2.0 , curr_time);
689+ ASSERT_TRUE (waitData (2.0 , 500ms, curr_time));
690+ publishCmdVel (0.0 , 0.0 , M_PI / 4 );
691+ ASSERT_TRUE (waitCmdVel (500ms));
692+ ASSERT_NEAR (cmd_vel_out_->linear .x , 0.0 , EPSILON);
693+ ASSERT_NEAR (cmd_vel_out_->linear .y , 0.0 , EPSILON);
694+ ASSERT_NEAR (cmd_vel_out_->angular .z , M_PI / 4 , EPSILON);
695+
696+ // 2. Approaching rotation to obstacle ( M_PI / 4 - M_PI / 20 ahead from robot)
697+ publishRange (1.4 , curr_time);
698+ ASSERT_TRUE (waitData (1.4 , 500ms, curr_time));
699+ publishCmdVel (0.0 , 0.0 , M_PI / 4 );
700+ ASSERT_TRUE (waitCmdVel (500ms));
701+ ASSERT_NEAR (
702+ cmd_vel_out_->linear .x , 0.0 , EPSILON);
703+ ASSERT_NEAR (
704+ cmd_vel_out_->linear .y , 0.0 , EPSILON);
705+ ASSERT_NEAR (
706+ cmd_vel_out_->angular .z ,
707+ M_PI / 5 ,
708+ (M_PI / 4 ) * (SIMULATION_TIME_STEP / TIME_BEFORE_COLLISION));
709+
710+ // 3. Obstacle is inside robot footprint
711+ publishRange (0.5 , curr_time);
712+ ASSERT_TRUE (waitData (0.5 , 500ms, curr_time));
713+ publishCmdVel (0.0 , 0.0 , M_PI / 4 );
714+ ASSERT_TRUE (waitCmdVel (500ms));
715+ ASSERT_NEAR (cmd_vel_out_->linear .x , 0.0 , EPSILON);
716+ ASSERT_NEAR (cmd_vel_out_->linear .y , 0.0 , EPSILON);
717+ ASSERT_NEAR (cmd_vel_out_->angular .z , 0.0 , EPSILON);
718+
719+ // 4. Restoring back normal operation
720+ publishRange (2.5 , curr_time);
721+ ASSERT_TRUE (waitData (2.5 , 500ms, curr_time));
722+ publishCmdVel (0.0 , 0.0 , M_PI / 4 );
723+ ASSERT_TRUE (waitCmdVel (500ms));
724+ ASSERT_NEAR (cmd_vel_out_->linear .x , 0.0 , EPSILON);
725+ ASSERT_NEAR (cmd_vel_out_->linear .y , 0.0 , EPSILON);
726+ ASSERT_NEAR (cmd_vel_out_->angular .z , M_PI / 4 , EPSILON);
727+
728+ // Stop Collision Monitor node
729+ cm_->stop ();
730+ }
731+
622732TEST_F (Tester, testCrossOver)
623733{
624734 rclcpp::Time curr_time = cm_->now ();
0 commit comments