psm_node.cpp
Go to the documentation of this file.
1 /*
2 * Polar Scan Matcher
3 * Copyright (C) 2010, CCNY Robotics Lab
4 * Ivan Dryanovski <ivan.dryanovski@gmail.com>
5 * William Morris <morris@ee.ccny.cuny.edu>
6 * http://robotics.ccny.cuny.edu
7 * Modified 2014, Daniel Axtens <daniel@axtens.net>
8 * whilst a student at the Australian National University
9 *
10 * This program is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 *
23 * This is a wrapper around Polar Scan Matcher [1], written by
24 * Albert Diosi
25 *
26 * [1] A. Diosi and L. Kleeman, "Laser Scan Matching in Polar Coordinates with
27 * Application to SLAM " Proceedings of 2005 IEEE/RSJ International Conference
28 * on Intelligent Robots and Systems, August, 2005, Edmonton, Canada
29 */
30 
32 
33 int main (int argc, char** argv)
34 {
35  ros::init(argc, argv, "PolarScanMatching Node");
36  PSMNode psmNode;
37  ros::spin();
38 }
39 
41 {
42  ROS_INFO("Creating PolarScanMatching node");
43 
44  ros::NodeHandle nh;
45 
46  initialized_ = false;
47  totalDuration_ = 0.0;
48  scansCount_ = 0;
49 
51 
52  getParams();
53 
56  posePublisher_ = nh.advertise<geometry_msgs::Pose2D>(poseTopic_, 10);
57 }
58 
60 {
61  ROS_INFO("Destroying PolarScanMatching node");
62 }
63 
65 {
66  ros::NodeHandle nh_private("~");
67 
68  std::string odometryType;
69 
70  // **** wrapper parameters
71 
72  if (!nh_private.getParam ("world_frame", worldFrame_))
73  worldFrame_ = "world";
74  if (!nh_private.getParam ("base_frame", baseFrame_))
75  baseFrame_ = "base_link";
76  if (!nh_private.getParam ("publish_tf", publishTf_))
77  publishTf_ = true;
78  if (!nh_private.getParam ("publish_pose", publishPose_))
79  publishPose_ = true;
80  if (!nh_private.getParam ("odometry_type", odometryType))
81  odometryType = "none";
82 
83  if (odometryType.compare("none") == 0)
84  {
85  useTfOdometry_ = false;
86  useImuOdometry_ = false;
87  }
88  else if (odometryType.compare("tf") == 0)
89  {
90  useTfOdometry_ = true;
91  useImuOdometry_ = false;
92  }
93  else if (odometryType.compare("imu") == 0)
94  {
95  useTfOdometry_ = false;
96  useImuOdometry_ = true;
97  }
98  else
99  {
100  ROS_WARN("Unknown value of odometry_type parameter passed to psm_node. \
101  Using default value (\"none\")");
102  useTfOdometry_ = false;
103  useImuOdometry_ = false;
104  }
105 
106  // **** PSM parameters
107 
108  if (!nh_private.getParam ("min_valid_points", minValidPoints_))
109  minValidPoints_ = 200;
110  if (!nh_private.getParam ("search_window", searchWindow_))
111  searchWindow_ = 40;
112  if (!nh_private.getParam ("max_error", maxError_))
113  maxError_ = 0.20;
114  if (!nh_private.getParam ("max_iterations", maxIterations_))
115  maxIterations_ = 20;
116  if (!nh_private.getParam ("stop_condition", stopCondition_))
117  stopCondition_ = 0.01;
118 }
119 
120 bool PSMNode::initialize(const sensor_msgs::LaserScan& scan)
121 {
122  laserFrame_ = scan.header.frame_id;
123 
124  // **** get base to laser tf
125 
126  tf::StampedTransform baseToLaserTf;
127  try
128  {
129  tfListener_.waitForTransform(baseFrame_, scan.header.frame_id, scan.header.stamp, ros::Duration(1.0));
130  tfListener_.lookupTransform (baseFrame_, scan.header.frame_id, scan.header.stamp, baseToLaserTf);
131  }
132  catch (tf::TransformException ex)
133  {
134  ROS_WARN("ScanMatcherNode: Could get initial laser transform, skipping scan (%s)", ex.what());
135  return false;
136  }
137  baseToLaser_ = baseToLaserTf;
139 
140  // **** pass parameters to matcher and initialise
141 
142  matcher_.PM_L_POINTS = scan.ranges.size();
143 
144  matcher_.PM_FOV = (scan.angle_max - scan.angle_min) * 180.0 / M_PI;
145  matcher_.PM_MAX_RANGE = scan.range_max * ROS_TO_PM;
146 
147  matcher_.PM_TIME_DELAY = 0.00;
148 
152 
157 
158  matcher_.pm_init();
159 
160  // **** get the initial worldToBase tf
161 
163 
164  // **** create the first pm scan from the laser scan message
165 
166  tf::Transform t;
167  t.setIdentity();
168  prevPMScan_ = new PMScan(scan.ranges.size());
169  rosToPMScan(scan, t, prevPMScan_);
170 
171  return true;
172 }
173 
174 void PSMNode::imuCallback (const sensor_msgs::Imu& imuMsg)
175 {
176  imuMutex_.lock();
177  tf::Quaternion q(imuMsg.orientation.x, imuMsg.orientation.y, imuMsg.orientation.z, imuMsg.orientation.w);
178  tf::Matrix3x3 m(q);
179  double temp;
180  m.getRPY(temp, temp, currImuAngle_);
181  imuMutex_.unlock();
182 }
183 
184 void PSMNode::scanCallback(const sensor_msgs::LaserScan& scan)
185 {
186  ROS_DEBUG("Received scan");
187  scansCount_++;
188 
189  struct timeval start, end;
190  gettimeofday(&start, NULL);
191 
192  // **** if this is the first scan, initialize and leave the function here
193 
194  if (!initialized_)
195  {
196  initialized_ = initialize(scan);
197  if (initialized_) ROS_INFO("Matcher initialized");
198  return;
199  }
200 
201  // **** attmempt to match the two scans
202 
203  // PM scan matcher is used in the following way:
204  // The reference scan (prevPMScan_) always has a pose of 0
205  // The new scan (currPMScan) has a pose equal to the movement
206  // of the laser in the world frame since the last scan (tf::Transform change)
207  // The computed correction is then propagated using the tf machinery
208 
209  prevPMScan_->rx = 0;
210  prevPMScan_->ry = 0;
211  prevPMScan_->th = 0;
212 
213  tf::Transform currWorldToBase;
214  tf::Transform change;
215  change.setIdentity();
216 
217  // what odometry model to use
218  if (useTfOdometry_)
219  {
220  // get the current position of the base in the world frame
221  // if no transofrm is available, we'll use the last known transform
222 
223  getCurrentEstimatedPose(currWorldToBase, scan);
224  change = laserToBase_ * prevWorldToBase_.inverse() * currWorldToBase * baseToLaser_;
225  }
226  else if (useImuOdometry_)
227  {
228  imuMutex_.lock();
229  double dTheta = currImuAngle_ - prevImuAngle_;
230  prevImuAngle_ = currImuAngle_;
231  change.getRotation().setRPY(0.0, 0.0, dTheta);
232  imuMutex_.unlock();
233  }
234 
235  PMScan * currPMScan = new PMScan(scan.ranges.size());
236  rosToPMScan(scan, change, currPMScan);
237 
238  try
239  {
240  matcher_.pm_psm(prevPMScan_, currPMScan);
241  }
242  catch(int err)
243  {
244  ROS_WARN("Error in scan matching");
245  delete prevPMScan_;
246  prevPMScan_ = currPMScan;
247  return;
248  };
249 
250  // **** calculate change in position
251 
252  // rotate by -90 degrees, since polar scan matcher assumes different laser frame
253  // and scale down by 100
254  double dx = currPMScan->ry / ROS_TO_PM;
255  double dy = -currPMScan->rx / ROS_TO_PM;
256  double da = currPMScan->th;
257 
258  // change = scan match result for how much laser moved between scans,
259  // in the world frame
260  change.setOrigin(tf::Vector3(dx, dy, 0.0));
261  tf::Quaternion q;
262  q.setRPY(0, 0, da);
263  change.setRotation(q);
264 
265  // **** publish the new estimated pose as a tf
266 
267  currWorldToBase = prevWorldToBase_ * baseToLaser_ * change * laserToBase_;
268 
269  if (publishTf_ ) publishTf (currWorldToBase, scan.header.stamp);
270  if (publishPose_) publishPose(currWorldToBase);
271 
272  // **** swap old and new
273 
274  delete prevPMScan_;
275  prevPMScan_ = currPMScan;
276  prevWorldToBase_ = currWorldToBase;
277 
278  // **** timing information - needed for profiling only
279 
280  gettimeofday(&end, NULL);
281  double dur = ((end.tv_sec * 1000000 + end.tv_usec ) -
282  (start.tv_sec * 1000000 + start.tv_usec)) / 1000.0;
283  totalDuration_ += dur;
284  double ave = totalDuration_/scansCount_;
285 
286  ROS_INFO("dur:\t %.3f ms \t ave:\t %.3f ms", dur, ave);
287 }
288 
289 void PSMNode::publishTf(const tf::Transform& transform,
290  const ros::Time& time)
291 {
292  tf::StampedTransform transformMsg (transform, time, worldFrame_, baseFrame_);
293  tfBroadcaster_.sendTransform (transformMsg);
294 }
295 
296 void PSMNode::publishPose(const tf::Transform& transform)
297 {
298  geometry_msgs::Pose2D pose;
299  tfToPose2D(transform, pose);
300 
301  posePublisher_.publish(pose);
302 }
303 
304 void PSMNode::rosToPMScan(const sensor_msgs::LaserScan& scan,
305  const tf::Transform& change,
306  PMScan* pmScan)
307 {
308  geometry_msgs::Pose2D pose;
309  tfToPose2D(change, pose);
310 
311  // FIXME: rotate x & y by 90 degree?
312 
313  pmScan->rx = pose.x * ROS_TO_PM;
314  pmScan->ry = pose.y * ROS_TO_PM;
315  pmScan->th = pose.theta;
316 
317  for (int i = 0; i < scan.ranges.size(); ++i)
318  {
319  if (scan.ranges[i] == 0)
320  {
321  pmScan->r[i] = 99999; // hokuyo uses 0 for out of range reading
322  }
323  else
324  {
325  pmScan->r[i] = scan.ranges[i] * ROS_TO_PM;
326  pmScan->x[i] = (pmScan->r[i]) * matcher_.pm_co[i];
327  pmScan->y[i] = (pmScan->r[i]) * matcher_.pm_si[i];
328  pmScan->bad[i] = 0;
329  }
330 
331  pmScan->bad[i] = 0;
332  }
333 
334  matcher_.pm_median_filter (pmScan);
336  matcher_.pm_segment_scan (pmScan);
337 }
338 
340  const sensor_msgs::LaserScan& scanMsg)
341 {
342  tf::StampedTransform worldToBaseTf;
343  try
344  {
345  tfListener_.lookupTransform (worldFrame_, baseFrame_, scanMsg.header.stamp, worldToBaseTf);
346  }
347  catch (tf::TransformException ex)
348  {
349  // transform unavailable - use the pose from our last estimation
350  ROS_WARN("Transform unavailable, using last estimated pose (%s)", ex.what());
351  worldToBase = prevWorldToBase_;
352  return;
353  }
354  worldToBase = worldToBaseTf;
355 }
356 
357 void PSMNode::pose2DToTf(const geometry_msgs::Pose2D& pose, tf::Transform& t)
358 {
359  t.setOrigin(tf::Vector3(pose.x, pose.y, 0.0));
360  tf::Quaternion q;
361  q.setRPY(0, 0, pose.theta);
362  t.setRotation(q);
363 }
364 
365 void PSMNode::tfToPose2D(const tf::Transform& t, geometry_msgs::Pose2D& pose)
366 {
367  tf::Matrix3x3 m(t.getRotation());
368  double roll, pitch, yaw;
369  m.getRPY(roll, pitch, yaw);
370 
371  pose.x = t.getOrigin().getX();
372  pose.y = t.getOrigin().getY();
373  pose.theta = yaw;
374 }
int main(int argc, char **argv)
Definition: psm_node.cpp:33
PM_TYPE pm_psm(PMScan *lsr, PMScan *lsa)
int PM_MAX_ITER
stopping condition; the pose change has to be smaller than ...
Definition: polar_match.h:152
TFSIMD_FORCE_INLINE void setRotation(const Quaternion &q)
ros::Subscriber scanSubscriber_
Definition: psm_node.h:55
void pm_init()
Initialises internar variables.
Definition: polar_match.cpp:72
void pm_median_filter(PMScan *ls)
Definition: polar_match.cpp:96
boost::mutex imuMutex_
Definition: psm_node.h:72
double PM_FOV
Definition: polar_match.h:143
int scansCount_
Definition: psm_node.h:67
void publish(const boost::shared_ptr< M > &message) const
std::string worldFrame_
Definition: psm_node.h:89
tf::Transform baseToLaser_
Definition: psm_node.h:62
std::vector< int > bad
Definition: polar_match.h:87
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
void pm_segment_scan(PMScan *ls)
double currImuAngle_
Definition: psm_node.h:74
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
double PM_STOP_COND
Definition: polar_match.h:151
TFSIMD_FORCE_INLINE const tfScalar & getY() const
void tfToPose2D(const tf::Transform &t, geometry_msgs::Pose2D &pose)
Definition: psm_node.cpp:365
int PM_STOP_COND_ICP
maximum number of iterations for ICP,MBICP
Definition: polar_match.h:154
#define ROS_WARN(...)
PM_TYPE ry
Definition: polar_match.h:82
std::vector< PM_TYPE > x
Definition: polar_match.h:85
void rosToPMScan(const sensor_msgs::LaserScan &scan, const tf::Transform &change, PMScan *pmScan)
Definition: psm_node.cpp:304
ROSCPP_DECL void spin(Spinner &spinner)
void setIdentity()
int PM_SEARCH_WINDOW
minimum number of valid points for scanmatching
Definition: polar_match.h:146
PSMNode()
Definition: psm_node.cpp:40
const double ROS_TO_PM
Definition: psm_node.h:49
bool waitForTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration &timeout, const ros::Duration &polling_sleep_duration=ros::Duration(0.01), std::string *error_msg=NULL) const
void pm_find_far_points(PMScan *ls)
PolarMatcher matcher_
Definition: psm_node.h:69
virtual ~PSMNode()
Definition: psm_node.cpp:59
tf::TransformBroadcaster tfBroadcaster_
Definition: psm_node.h:59
std::vector< PM_TYPE > r
Definition: polar_match.h:84
std::vector< PM_TYPE > y
Definition: polar_match.h:86
double maxError_
Definition: psm_node.h:85
std::string baseFrame_
Definition: psm_node.h:90
bool useImuOdometry_
Definition: psm_node.h:81
tf::Transform prevWorldToBase_
Definition: psm_node.h:61
void setRPY(const tfScalar &roll, const tfScalar &pitch, const tfScalar &yaw)
void getRPY(tfScalar &roll, tfScalar &pitch, tfScalar &yaw, unsigned int solution_number=1) const
#define ROS_INFO(...)
void pose2DToTf(const geometry_msgs::Pose2D &pose, tf::Transform &t)
Definition: psm_node.cpp:357
double PM_TIME_DELAY
half window size which is searched for correct orientation
Definition: polar_match.h:148
void sendTransform(const StampedTransform &transform)
double PM_MAX_ERROR
Definition: polar_match.h:150
ros::Publisher posePublisher_
Definition: psm_node.h:57
Transform inverse() const
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
void publishTf(const tf::Transform &transform, const ros::Time &time)
Definition: psm_node.cpp:289
bool publishPose_
Definition: psm_node.h:79
TFSIMD_FORCE_INLINE Vector3 & getOrigin()
void lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, StampedTransform &transform) const
std::vector< PM_TYPE > pm_si
Definition: polar_match.h:140
PM_TYPE rx
Definition: polar_match.h:81
bool publishTf_
Definition: psm_node.h:78
tf::TransformListener tfListener_
Definition: psm_node.h:60
bool useTfOdometry_
Definition: psm_node.h:80
tf::Transform laserToBase_
Definition: psm_node.h:63
bool initialized_
Definition: psm_node.h:65
const std::string scanTopic_
Definition: psm_node.h:46
Quaternion getRotation() const
void scanCallback(const sensor_msgs::LaserScan &scan)
Definition: psm_node.cpp:184
const std::string poseTopic_
Definition: psm_node.h:47
bool getParam(const std::string &key, std::string &s) const
PM_TYPE th
Definition: polar_match.h:83
int PM_MAX_ITER_ICP
maximum number of iterations for PSM.PSM_C
Definition: polar_match.h:153
TFSIMD_FORCE_INLINE const tfScalar & getX() const
void publishPose(const tf::Transform &transform)
Definition: psm_node.cpp:296
std::string laserFrame_
Definition: psm_node.h:91
double PM_MAX_RANGE
field of view of the laser range finder in degrees
Definition: polar_match.h:144
int maxIterations_
Definition: psm_node.h:86
void getCurrentEstimatedPose(tf::Transform &worldToBase, const sensor_msgs::LaserScan &scanMsg)
Definition: psm_node.cpp:339
PMScan * prevPMScan_
Definition: psm_node.h:70
int PM_MIN_VALID_POINTS
[cm] max valid laser range (set this to about 400 for the Hokuyo URG)
Definition: polar_match.h:145
std::vector< PM_TYPE > pm_co
Definition: polar_match.h:141
double totalDuration_
Definition: psm_node.h:66
void imuCallback(const sensor_msgs::Imu &imuMsg)
Definition: psm_node.cpp:174
double prevImuAngle_
Definition: psm_node.h:73
TFSIMD_FORCE_INLINE void setOrigin(const Vector3 &origin)
int minValidPoints_
Definition: psm_node.h:83
double stopCondition_
Definition: psm_node.h:87
int searchWindow_
Definition: psm_node.h:84
void getParams()
Definition: psm_node.cpp:64
bool initialize(const sensor_msgs::LaserScan &scan)
Definition: psm_node.cpp:120
ros::Subscriber imuSubscriber_
Definition: psm_node.h:56
#define ROS_DEBUG(...)
const std::string imuTopic_
Definition: psm_node.h:45


polar_scan_matcher
Author(s): Ivan Dryanovski
autogenerated on Mon Jun 10 2019 15:08:51