Autonomous Racing  1
f1tenth Project Group of Technical University Dortmund, Germany
wall_following.cpp
Go to the documentation of this file.
1 #include "wall_following.h"
2 #include <boost/algorithm/clamp.hpp>
3 
5  : m_debug_geometry(this->m_node_handle, TOPIC_VISUALIZATION, LIDAR_FRAME)
6 {
7  this->m_lidar_subscriber =
8  m_node_handle.subscribe<sensor_msgs::LaserScan>(TOPIC_LASER_SCAN, 1, &WallFollowing::lidarCallback, this);
9  this->m_drive_parameter_publisher = m_node_handle.advertise<drive_msgs::drive_param>(TOPIC_DRIVE_PARAMETERS, 1);
10 
11  this->updateDynamicConfig();
12 
13  m_dyn_cfg_server.setCallback([&](wallfollowing1::wallfollowing1Config& cfg, uint32_t) {
14  m_min_range = cfg.min_range;
15  m_max_range = cfg.max_range;
16 
17  m_fallback_range = cfg.fallback_range;
18 
19  m_sample_angle_1 = static_cast<float>(cfg.sample_angle_1) * DEG_TO_RAD;
20  m_sample_angle_2 = static_cast<float>(cfg.sample_angle_2) * DEG_TO_RAD;
21 
22  m_wall_following_max_speed = cfg.wall_following_max_speed;
23  m_wall_following_min_speed = cfg.wall_following_min_speed;
24 
25  m_prediction_distance = cfg.prediction_distance;
26 
27  m_target_wall_distance = cfg.target_wall_distance;
28 
29  m_time_between_scans = cfg.time_between_scans;
30  });
31 }
32 
33 // map value in range [in_lower, in_upper] to the corresponding number in range [out_lower, out_upper]
34 float map(float in_lower, float in_upper, float out_lower, float out_upper, float value)
35 {
36  return out_lower + ((out_upper - out_lower) * (value - in_lower) / (in_upper - in_lower));
37 }
38 
39 float WallFollowing::getRangeAtDegree(const sensor_msgs::LaserScan::ConstPtr& lidar, float angle)
40 {
41  int sampleCount = (lidar->angle_max - lidar->angle_min) / lidar->angle_increment;
42  int index = map(lidar->angle_min, lidar->angle_max, 0, sampleCount, angle);
43 
44  // clang-format off
45  if (index < 0
46  || index >= sampleCount
47  || lidar->ranges[index] < m_min_range
48  || lidar->ranges[index] > m_max_range) {
49  ROS_INFO_STREAM("Could not sample lidar, using fallback value");
50  return m_fallback_range;
51  }
52  // clang-format on
53 
54  return lidar->ranges[index];
55 }
56 
57 Wall WallFollowing::getWall(const sensor_msgs::LaserScan::ConstPtr& lidar, bool right_wall)
58 {
59  float leftRightSign = right_wall ? -1 : 1;
60 
61  float angle1 = m_sample_angle_1 * leftRightSign;
62  float angle2 = m_sample_angle_2 * leftRightSign;
63  float range1 = this->getRangeAtDegree(lidar, angle1);
64  float range2 = this->getRangeAtDegree(lidar, angle2);
65 
66  return Wall(angle1, angle2, range1, range2);
67 }
68 
78 void WallFollowing::followSingleWall(const sensor_msgs::LaserScan::ConstPtr& lidar, bool right_wall)
79 {
80  float leftRightSign = right_wall ? -1 : 1;
81 
82  Wall wall = this->getWall(lidar, right_wall);
83  float predictedWallDistance = wall.predictDistance(m_prediction_distance);
84 
85  float error = m_target_wall_distance - predictedWallDistance;
86  float correction = this->m_pid_controller.updateAndGetCorrection(error, m_time_between_scans);
87 
88  float steeringAngle = atan(leftRightSign * correction) * 2 / M_PI;
89  float velocity = m_wall_following_max_speed * (1 - std::abs(steeringAngle));
90  velocity = boost::algorithm::clamp(velocity, m_wall_following_min_speed, m_wall_following_max_speed);
91 
92  wall.draw(this->m_debug_geometry, 0, createColor(0, 0, 1, 1));
93  this->m_debug_geometry.drawLine(1, createPoint(m_prediction_distance, 0, 0),
94  createPoint(m_prediction_distance, -error * leftRightSign, 0),
95  createColor(1, 0, 0, 1), 0.03);
96  float wallAngle = wall.getAngle();
97  this->m_debug_geometry.drawLine(2, createPoint(m_prediction_distance, -error * leftRightSign, 0),
98  createPoint(m_prediction_distance + std::cos(wallAngle) * 2,
99  (-error + std::sin(wallAngle) * 2) * leftRightSign, 0),
100  createColor(0, 1, 1, 1), 0.03);
101 
102  this->publishDriveParameters(velocity, steeringAngle);
103 }
104 
105 void WallFollowing::followWalls(const sensor_msgs::LaserScan::ConstPtr& lidar)
106 {
107  Wall leftWall = this->getWall(lidar, false);
108  Wall rightWall = this->getWall(lidar, true);
109 
110  float error =
111  (rightWall.predictDistance(m_prediction_distance) - leftWall.predictDistance(m_prediction_distance)) / 2;
112  float correction = this->m_pid_controller.updateAndGetCorrection(error, m_time_between_scans);
113 
114  float steeringAngle = atan(correction) * 2 / M_PI;
115  float velocity = m_wall_following_max_speed * (1 - std::max(0.0f, std::abs(steeringAngle) - 0.15f));
116  velocity = boost::algorithm::clamp(velocity, m_wall_following_min_speed, m_wall_following_max_speed);
117 
118  leftWall.draw(this->m_debug_geometry, 0, createColor(0, 0, 1, 1));
119  rightWall.draw(this->m_debug_geometry, 1, createColor(0, 0, 1, 1));
120  this->m_debug_geometry.drawLine(2, createPoint(m_prediction_distance, 0, 0),
121  createPoint(m_prediction_distance, -error, 0), createColor(1, 0, 0, 1), 0.03);
122  float distance2 = m_prediction_distance + 2;
123  this->m_debug_geometry.drawLine(
124  3, createPoint(m_prediction_distance, -error, 0),
125  createPoint(distance2, -(rightWall.predictDistance(distance2) - leftWall.predictDistance(distance2)) / 2, 0),
126  createColor(0, 1, 1, 1), 0.03);
127 
128  this->publishDriveParameters(velocity, steeringAngle);
129 }
130 
131 void WallFollowing::publishDriveParameters(float velocity, float angle)
132 {
133  drive_msgs::drive_param drive_parameters;
134  drive_parameters.velocity = velocity;
135  drive_parameters.angle = angle;
136  this->m_drive_parameter_publisher.publish(drive_parameters);
137 }
138 
139 void WallFollowing::lidarCallback(const sensor_msgs::LaserScan::ConstPtr& lidar)
140 {
141  this->followWalls(lidar);
142 }
143 
144 void WallFollowing::updateDynamicConfig()
145 {
146  wallfollowing1::wallfollowing1Config cfg;
147  {
148  cfg.min_range = m_min_range;
149  cfg.max_range = m_max_range;
150 
151  cfg.fallback_range = m_fallback_range;
152 
153  cfg.sample_angle_1 = m_sample_angle_1 / DEG_TO_RAD;
154  cfg.sample_angle_2 = m_sample_angle_2 / DEG_TO_RAD;
155 
156  cfg.wall_following_max_speed = m_wall_following_max_speed;
157  cfg.wall_following_min_speed = m_wall_following_min_speed;
158 
159  cfg.prediction_distance = m_prediction_distance;
160 
161  cfg.target_wall_distance = m_target_wall_distance;
162 
163  cfg.time_between_scans = m_time_between_scans;
164  }
165  m_dyn_cfg_server.updateConfig(cfg);
166 }
167 
168 int main(int argc, char** argv)
169 {
170  ros::init(argc, argv, "wall_following");
171  WallFollowing wall_following;
172  ros::spin();
173  return EXIT_SUCCESS;
174 }
float updateAndGetCorrection(float error, float deltaTime)
constexpr const char * TOPIC_LASER_SCAN
constexpr const char * LIDAR_FRAME
std_msgs::ColorRGBA createColor(double r, double g, double b, double a)
Definition: wall.h:7
int main(int argc, char **argv)
float getAngle()
Definition: wall.cpp:11
constexpr const char * TOPIC_VISUALIZATION
float predictDistance(float distancce_to_current_position)
Definition: wall.cpp:17
void draw(RvizGeometryPublisher &geometry, int id, std_msgs::ColorRGBA color)
Definition: wall.cpp:24
constexpr float DEG_TO_RAD
void drawLine(int id, geometry_msgs::Point point1, geometry_msgs::Point point2, std_msgs::ColorRGBA color, float width)
geometry_msgs::Point createPoint(float x, float y, float z)
float map(float in_lower, float in_upper, float out_lower, float out_upper, float value)