complementary_filter_ros.cpp
Go to the documentation of this file.
1 /*
2  @author Roberto G. Valenti <robertogl.valenti@gmail.com>
3 
4  @section LICENSE
5  Copyright (c) 2015, City University of New York
6  CCNY Robotics Lab <http://robotics.ccny.cuny.edu>
7  All rights reserved.
8 
9  Redistribution and use in source and binary forms, with or without
10  modification, are permitted provided that the following conditions are met:
11  1. Redistributions of source code must retain the above copyright
12  notice, this list of conditions and the following disclaimer.
13  2. Redistributions in binary form must reproduce the above copyright
14  notice, this list of conditions and the following disclaimer in the
15  documentation and/or other materials provided with the distribution.
16  3. Neither the name of the City College of New York nor the
17  names of its contributors may be used to endorse or promote products
18  derived from this software without specific prior written permission.
19 
20  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23  DISCLAIMED. IN NO EVENT SHALL the CCNY ROBOTICS LAB BE LIABLE FOR ANY
24  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31 
33 
34 #include <std_msgs/Float64.h>
35 #include <std_msgs/Bool.h>
36 
37 namespace imu_tools {
38 
40  const ros::NodeHandle& nh,
41  const ros::NodeHandle& nh_private):
42  nh_(nh),
43  nh_private_(nh_private),
44  initialized_filter_(false)
45 {
46  ROS_INFO("Starting ComplementaryFilterROS");
48 
49  int queue_size = 5;
50 
51  // Register publishers:
52  imu_publisher_ = nh_.advertise<sensor_msgs::Imu>(ros::names::resolve("imu") + "/data", queue_size);
53 
55  {
56  rpy_publisher_ = nh_.advertise<geometry_msgs::Vector3Stamped>(
57  ros::names::resolve("imu") + "/rpy/filtered", queue_size);
58 
60  {
61  state_publisher_ = nh_.advertise<std_msgs::Bool>(
62  ros::names::resolve("imu") + "/steady_state", queue_size);
63  }
64  }
65 
66  // Register IMU raw data subscriber.
67  imu_subscriber_.reset(new ImuSubscriber(nh_, ros::names::resolve("imu") + "/data_raw", queue_size));
68 
69  // Register magnetic data subscriber.
70  if (use_mag_)
71  {
72  mag_subscriber_.reset(new MagSubscriber(nh_, ros::names::resolve("imu") + "/mag", queue_size));
73 
74  sync_.reset(new Synchronizer(
75  SyncPolicy(queue_size), *imu_subscriber_, *mag_subscriber_));
76  sync_->registerCallback(
77  boost::bind(&ComplementaryFilterROS::imuMagCallback, this, _1, _2));
78  }
79  else
80  {
81  imu_subscriber_->registerCallback(
83  }
84 }
85 
87 {
88  ROS_INFO("Destroying ComplementaryFilterROS");
89 }
90 
92 {
93  double gain_acc;
94  double gain_mag;
95  bool do_bias_estimation;
96  double bias_alpha;
97  bool do_adaptive_gain;
98 
99  if (!nh_private_.getParam ("fixed_frame", fixed_frame_))
100  fixed_frame_ = "odom";
101  if (!nh_private_.getParam ("use_mag", use_mag_))
102  use_mag_ = false;
103  if (!nh_private_.getParam ("publish_tf", publish_tf_))
104  publish_tf_ = false;
105  if (!nh_private_.getParam ("reverse_tf", reverse_tf_))
106  reverse_tf_ = false;
107  if (!nh_private_.getParam ("constant_dt", constant_dt_))
108  constant_dt_ = 0.0;
109  if (!nh_private_.getParam ("publish_debug_topics", publish_debug_topics_))
110  publish_debug_topics_ = false;
111  if (!nh_private_.getParam ("gain_acc", gain_acc))
112  gain_acc = 0.01;
113  if (!nh_private_.getParam ("gain_mag", gain_mag))
114  gain_mag = 0.01;
115  if (!nh_private_.getParam ("do_bias_estimation", do_bias_estimation))
116  do_bias_estimation = true;
117  if (!nh_private_.getParam ("bias_alpha", bias_alpha))
118  bias_alpha = 0.01;
119  if (!nh_private_.getParam ("do_adaptive_gain", do_adaptive_gain))
120  do_adaptive_gain = true;
121 
122  double orientation_stddev;
123  if (!nh_private_.getParam ("orientation_stddev", orientation_stddev))
124  orientation_stddev = 0.0;
125 
126  orientation_variance_ = orientation_stddev * orientation_stddev;
127 
128  filter_.setDoBiasEstimation(do_bias_estimation);
129  filter_.setDoAdaptiveGain(do_adaptive_gain);
130 
131  if(!filter_.setGainAcc(gain_acc))
132  ROS_WARN("Invalid gain_acc passed to ComplementaryFilter.");
133  if (use_mag_)
134  {
135  if(!filter_.setGainMag(gain_mag))
136  ROS_WARN("Invalid gain_mag passed to ComplementaryFilter.");
137  }
138  if (do_bias_estimation)
139  {
140  if(!filter_.setBiasAlpha(bias_alpha))
141  ROS_WARN("Invalid bias_alpha passed to ComplementaryFilter.");
142  }
143 
144  // check for illegal constant_dt values
145  if (constant_dt_ < 0.0)
146  {
147  // if constant_dt_ is 0.0 (default), use IMU timestamp to determine dt
148  // otherwise, it will be constant
149  ROS_WARN("constant_dt parameter is %f, must be >= 0.0. Setting to 0.0", constant_dt_);
150  constant_dt_ = 0.0;
151  }
152 }
153 
154 void ComplementaryFilterROS::imuCallback(const ImuMsg::ConstPtr& imu_msg_raw)
155 {
156  const geometry_msgs::Vector3& a = imu_msg_raw->linear_acceleration;
157  const geometry_msgs::Vector3& w = imu_msg_raw->angular_velocity;
158  const ros::Time& time = imu_msg_raw->header.stamp;
159 
160  // Initialize.
161  if (!initialized_filter_)
162  {
163  time_prev_ = time;
164  initialized_filter_ = true;
165  return;
166  }
167 
168  // determine dt: either constant, or from IMU timestamp
169  double dt;
170  if (constant_dt_ > 0.0)
171  dt = constant_dt_;
172  else
173  dt = (time - time_prev_).toSec();
174 
175  time_prev_ = time;
176 
177  // Update the filter.
178  filter_.update(a.x, a.y, a.z, w.x, w.y, w.z, dt);
179 
180  // Publish state.
181  publish(imu_msg_raw);
182 }
183 
184 void ComplementaryFilterROS::imuMagCallback(const ImuMsg::ConstPtr& imu_msg_raw,
185  const MagMsg::ConstPtr& mag_msg)
186 {
187  const geometry_msgs::Vector3& a = imu_msg_raw->linear_acceleration;
188  const geometry_msgs::Vector3& w = imu_msg_raw->angular_velocity;
189  const geometry_msgs::Vector3& m = mag_msg->magnetic_field;
190  const ros::Time& time = imu_msg_raw->header.stamp;
191 
192  // Initialize.
193  if (!initialized_filter_)
194  {
195  time_prev_ = time;
196  initialized_filter_ = true;
197  return;
198  }
199 
200  // Calculate dt.
201  double dt = (time - time_prev_).toSec();
202  time_prev_ = time;
203  //ros::Time t_in, t_out;
204  //t_in = ros::Time::now();
205  // Update the filter.
206  if (std::isnan(m.x) || std::isnan(m.y) || std::isnan(m.z))
207  filter_.update(a.x, a.y, a.z, w.x, w.y, w.z, dt);
208  else
209  filter_.update(a.x, a.y, a.z, w.x, w.y, w.z, m.x, m.y, m.z, dt);
210 
211  //t_out = ros::Time::now();
212  //float dt_tot = (t_out - t_in).toSec() * 1000.0; // In msec.
213  //printf("%.6f\n", dt_tot);
214  // Publish state.
215  publish(imu_msg_raw);
216 }
217 
219  double q0, double q1, double q2, double q3) const
220 {
221  // ROS uses the Hamilton quaternion convention (q0 is the scalar). However,
222  // the ROS quaternion is in the form [x, y, z, w], with w as the scalar.
223  return tf::Quaternion(q1, q2, q3, q0);
224 }
225 
227  const sensor_msgs::Imu::ConstPtr& imu_msg_raw)
228 {
229  // Get the orientation:
230  double q0, q1, q2, q3;
231  filter_.getOrientation(q0, q1, q2, q3);
232  tf::Quaternion q = hamiltonToTFQuaternion(q0, q1, q2, q3);
233 
234  // Create and publish fitlered IMU message.
236  boost::make_shared<sensor_msgs::Imu>(*imu_msg_raw);
237  tf::quaternionTFToMsg(q, imu_msg->orientation);
238 
239  imu_msg->orientation_covariance[0] = orientation_variance_;
240  imu_msg->orientation_covariance[1] = 0.0;
241  imu_msg->orientation_covariance[2] = 0.0;
242  imu_msg->orientation_covariance[3] = 0.0;
243  imu_msg->orientation_covariance[4] = orientation_variance_;
244  imu_msg->orientation_covariance[5] = 0.0;
245  imu_msg->orientation_covariance[6] = 0.0;
246  imu_msg->orientation_covariance[7] = 0.0;
247  imu_msg->orientation_covariance[8] = orientation_variance_;
248 
249  // Account for biases.
251  {
252  imu_msg->angular_velocity.x -= filter_.getAngularVelocityBiasX();
253  imu_msg->angular_velocity.y -= filter_.getAngularVelocityBiasY();
254  imu_msg->angular_velocity.z -= filter_.getAngularVelocityBiasZ();
255  }
256 
257  imu_publisher_.publish(imu_msg);
258 
260  {
261  // Create and publish roll, pitch, yaw angles
262  geometry_msgs::Vector3Stamped rpy;
263  rpy.header = imu_msg_raw->header;
264 
265  tf::Matrix3x3 M;
266  M.setRotation(q);
267  M.getRPY(rpy.vector.x, rpy.vector.y, rpy.vector.z);
268  rpy_publisher_.publish(rpy);
269 
270  // Publish whether we are in the steady state, when doing bias estimation
272  {
273  std_msgs::Bool state_msg;
274  state_msg.data = filter_.getSteadyState();
275  state_publisher_.publish(state_msg);
276  }
277  }
278 
279  if (publish_tf_)
280  {
281  // Create and publish the ROS tf.
282  tf::Transform transform;
283  transform.setOrigin(tf::Vector3(0.0, 0.0, 0.0));
284  transform.setRotation(q);
285 
286  if (reverse_tf_)
287  {
289  tf::StampedTransform(transform.inverse(),
290  imu_msg_raw->header.stamp,
291  imu_msg_raw->header.frame_id,
292  fixed_frame_));
293  }
294  else
295  {
297  tf::StampedTransform(transform,
298  imu_msg_raw->header.stamp,
299  fixed_frame_,
300  imu_msg_raw->header.frame_id));
301  }
302  }
303 }
304 
305 } // namespace imu_tools
TFSIMD_FORCE_INLINE void setRotation(const Quaternion &q)
void publish(const boost::shared_ptr< M > &message) const
void setDoAdaptiveGain(bool do_adaptive_gain)
boost::shared_ptr< ImuSubscriber > imu_subscriber_
boost::shared_ptr< MagSubscriber > mag_subscriber_
ROSCPP_DECL std::string resolve(const std::string &name, bool remap=true)
bool setBiasAlpha(double bias_alpha)
boost::shared_ptr< Synchronizer > sync_
void imuCallback(const ImuMsg::ConstPtr &imu_msg_raw)
#define ROS_WARN(...)
void setRotation(const Quaternion &q)
message_filters::sync_policies::ApproximateTime< ImuMsg, MagMsg > SyncPolicy
void getOrientation(double &q0, double &q1, double &q2, double &q3) const
message_filters::Subscriber< MagMsg > MagSubscriber
void setDoBiasEstimation(bool do_bias_estimation)
tf::Quaternion hamiltonToTFQuaternion(double q0, double q1, double q2, double q3) const
void getRPY(tfScalar &roll, tfScalar &pitch, tfScalar &yaw, unsigned int solution_number=1) const
#define ROS_INFO(...)
ComplementaryFilterROS(const ros::NodeHandle &nh, const ros::NodeHandle &nh_private)
static void quaternionTFToMsg(const Quaternion &bt, geometry_msgs::Quaternion &msg)
void sendTransform(const StampedTransform &transform)
Transform inverse() const
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
TFSIMD_FORCE_INLINE const tfScalar & w() const
void update(double ax, double ay, double az, double wx, double wy, double wz, double dt)
bool getParam(const std::string &key, std::string &s) const
void imuMagCallback(const ImuMsg::ConstPtr &imu_msg_raw, const MagMsg::ConstPtr &mav_msg)
message_filters::Synchronizer< SyncPolicy > Synchronizer
void publish(const sensor_msgs::Imu::ConstPtr &imu_msg_raw)
message_filters::Subscriber< ImuMsg > ImuSubscriber
TFSIMD_FORCE_INLINE void setOrigin(const Vector3 &origin)


imu_complementary_filter
Author(s): Roberto G. Valenti
autogenerated on Thu Apr 15 2021 05:05:57