imu_ros_i.cpp
Go to the documentation of this file.
1 #include <boost/make_shared.hpp>
3 
4 namespace phidgets {
5 
7  Imu(),
8  nh_(nh),
9  nh_private_(nh_private),
10  is_connected_(false),
11  serial_number_(-1),
12  error_number_(0),
13  target_publish_freq_(0.0)
14 {
15  ROS_INFO ("Starting Phidgets IMU");
16 
17  // **** get parameters
18 
19  if (!nh_private_.getParam ("period", period_))
20  period_ = 8; // 8 ms
21  if (!nh_private_.getParam ("frame_id", frame_id_))
22  frame_id_ = "imu";
23  if (!nh_private_.getParam ("angular_velocity_stdev", angular_velocity_stdev_))
24  angular_velocity_stdev_ = 0.02 * (M_PI / 180.0); // 0.02 deg/s resolution, as per manual
25  if (!nh_private_.getParam ("linear_acceleration_stdev", linear_acceleration_stdev_))
26  linear_acceleration_stdev_ = 300.0 * 1e-6 * G; // 300 ug as per manual
27  if (!nh_private_.getParam ("magnetic_field_stdev", magnetic_field_stdev_))
28  magnetic_field_stdev_ = 0.095 * (M_PI / 180.0); // 0.095°/s as per manual
29  if (nh_private_.getParam ("serial_number", serial_number_)) // optional param serial_number, default is -1
30  ROS_INFO_STREAM("Searching for device with serial number: " << serial_number_);
31 
32  nh_private_.param("use_imu_time", use_imu_time_, true);
33 
34  bool has_compass_params =
35  nh_private_.getParam ("cc_mag_field", cc_mag_field_)
36  && nh_private_.getParam ("cc_offset0", cc_offset0_)
37  && nh_private_.getParam ("cc_offset1", cc_offset1_)
38  && nh_private_.getParam ("cc_offset2", cc_offset2_)
39  && nh_private_.getParam ("cc_gain0", cc_gain0_)
40  && nh_private_.getParam ("cc_gain1", cc_gain1_)
41  && nh_private_.getParam ("cc_gain2", cc_gain2_)
42  && nh_private_.getParam ("cc_t0", cc_T0_)
43  && nh_private_.getParam ("cc_t1", cc_T1_)
44  && nh_private_.getParam ("cc_t2", cc_T2_)
45  && nh_private_.getParam ("cc_t3", cc_T3_)
46  && nh_private_.getParam ("cc_t4", cc_T4_)
47  && nh_private_.getParam ("cc_t5", cc_T5_);
48 
49  // **** advertise topics
50 
52  "imu/data_raw", 5);
54  "imu/mag", 5);
55  cal_publisher_ = nh_.advertise<std_msgs::Bool>(
56  "imu/is_calibrated", 5);
57 
58  // Set up the topic publisher diagnostics monitor for imu/data_raw
59  // 1. The frequency status component monitors if imu data is published
60  // within 10% tolerance of the desired frequency of 1.0 / period
61  // 2. The timstamp status component monitors the delay between
62  // the header.stamp of the imu message and the real (ros) time
63  // the maximum tolerable drift is +- 100ms
64  target_publish_freq_ = 1000.0 / static_cast<double>(period_);
65  imu_publisher_diag_ptr_ = boost::make_shared<diagnostic_updater::TopicDiagnostic>(
66  "imu/data_raw",
67  boost::ref(diag_updater_),
68  diagnostic_updater::FrequencyStatusParam(&target_publish_freq_, &target_publish_freq_, 0.1, 5),
70  );
71 
72  // **** advertise services
73 
75  "imu/calibrate", &ImuRosI::calibrateService, this);
76 
77  // **** initialize variables and device
78 
79  // ---- IMU message
80 
81  imu_msg_.header.frame_id = frame_id_;
82 
83  // build covariance matrices
84 
85  double ang_vel_var = angular_velocity_stdev_ * angular_velocity_stdev_;
87 
88  for (int i = 0; i < 3; ++i)
89  for (int j = 0; j < 3; ++j)
90  {
91  int idx = j*3 +i;
92 
93  if (i == j)
94  {
95  imu_msg_.angular_velocity_covariance[idx] = ang_vel_var;
96  imu_msg_.linear_acceleration_covariance[idx] = lin_acc_var;
97  }
98  else
99  {
100  imu_msg_.angular_velocity_covariance[idx] = 0.0;
101  imu_msg_.linear_acceleration_covariance[idx] = 0.0;
102  }
103  }
104 
105  // ---- magnetic field message
106 
107  mag_msg_.header.frame_id = frame_id_;
108 
109  // build covariance matrix
110 
111  double mag_field_var = magnetic_field_stdev_ * magnetic_field_stdev_;
112 
113  for (int i = 0; i < 3; ++i)
114  for (int j = 0; j < 3; ++j)
115  {
116  int idx = j * 3 + i;
117 
118  if (i == j)
119  {
120  mag_msg_.magnetic_field_covariance[idx] = mag_field_var;
121  }
122  else
123  {
124  mag_msg_.magnetic_field_covariance[idx] = 0.0;
125  }
126  }
127 
128  // init diagnostics, we set the hardware id properly when the device is connected
130  diag_updater_.add("IMU Driver Status", this, &phidgets::ImuRosI::phidgetsDiagnostics);
131 
132  initDevice();
133 
134  if (has_compass_params)
135  {
136  int result = CPhidgetSpatial_setCompassCorrectionParameters(imu_handle_, cc_mag_field_,
139  if (result)
140  {
141  const char *err;
142  CPhidget_getErrorDescription(result, &err);
143  ROS_ERROR("Problem while trying to set compass correction params: '%s'.", err);
144  }
145  }
146  else
147  {
148  ROS_INFO("No compass correction params found.");
149  }
150 }
151 
153 {
154  ROS_INFO_STREAM("Opening device");
155 
156  open(serial_number_); // optional param serial_number, default is -1
157 
158  ROS_INFO("Waiting for IMU to be attached...");
159  int result = waitForAttachment(10000);
160  if(result)
161  {
162  is_connected_ = false;
163  error_number_ = result;
165  const char *err;
166  CPhidget_getErrorDescription(result, &err);
167  ROS_FATAL("Problem waiting for IMU attachment: %s Make sure the USB cable is connected and you have executed the phidgets_api/share/setup-udev.sh script.", err);
168  }
169 
170  // calibrate on startup
171  calibrate();
172 
173  // set the hardware id for diagnostics
175  getDeviceName().c_str(),
177 }
178 
179 bool ImuRosI::calibrateService(std_srvs::Empty::Request &req,
180  std_srvs::Empty::Response &res)
181 {
182  calibrate();
183  return true;
184 }
185 
187 {
188  ROS_INFO("Calibrating IMU...");
189  zero();
190  ROS_INFO("Calibrating IMU done.");
191 
193 
194  // publish message
195  std_msgs::Bool is_calibrated_msg;
196  is_calibrated_msg.data = true;
197  cal_publisher_.publish(is_calibrated_msg);
198 }
199 
200 void ImuRosI::processImuData(CPhidgetSpatial_SpatialEventDataHandle* data, int i)
201 {
202  // **** calculate time from timestamp
203  ros::Duration time_imu(data[i]->timestamp.seconds +
204  data[i]->timestamp.microseconds * 1e-6);
205 
206  ros::Time time_now = time_zero_ + time_imu;
207 
208  if (use_imu_time_) {
209  double timediff = time_now.toSec() - ros::Time::now().toSec();
210  if (fabs(timediff) > MAX_TIMEDIFF_SECONDS) {
211  ROS_WARN("IMU time lags behind by %f seconds, resetting IMU time offset!", timediff);
212  time_zero_ = ros::Time::now() - time_imu;
213  time_now = ros::Time::now();
214  }
215 
216  // Ensure that we only publish strictly ordered timestamps,
217  // also in case a time reset happened.
218  if (time_now <= last_published_time_) {
219  ROS_WARN_THROTTLE(1.0, "Ignoring data with out-of-order time.");
220  return;
221  }
222  } else {
223  time_now = ros::Time::now();
224  }
225 
226  // **** create and publish imu message
227 
228  boost::shared_ptr<ImuMsg> imu_msg =
229  boost::make_shared<ImuMsg>(imu_msg_);
230 
231  imu_msg->header.stamp = time_now;
232 
233  // set linear acceleration
234  imu_msg->linear_acceleration.x = - data[i]->acceleration[0] * G;
235  imu_msg->linear_acceleration.y = - data[i]->acceleration[1] * G;
236  imu_msg->linear_acceleration.z = - data[i]->acceleration[2] * G;
237 
238  // set angular velocities
239  imu_msg->angular_velocity.x = data[i]->angularRate[0] * (M_PI / 180.0);
240  imu_msg->angular_velocity.y = data[i]->angularRate[1] * (M_PI / 180.0);
241  imu_msg->angular_velocity.z = data[i]->angularRate[2] * (M_PI / 180.0);
242 
243  imu_publisher_.publish(imu_msg);
244  imu_publisher_diag_ptr_->tick(time_now);
245 
246  // **** create and publish magnetic field message
247 
248  boost::shared_ptr<MagMsg> mag_msg =
249  boost::make_shared<MagMsg>(mag_msg_);
250 
251  mag_msg->header.stamp = time_now;
252 
253  if (data[i]->magneticField[0] != PUNK_DBL)
254  {
255  // device reports data in Gauss, multiply by 1e-4 to convert to Tesla
256  mag_msg->magnetic_field.x = data[i]->magneticField[0] * 1e-4;
257  mag_msg->magnetic_field.y = data[i]->magneticField[1] * 1e-4;
258  mag_msg->magnetic_field.z = data[i]->magneticField[2] * 1e-4;
259  }
260  else
261  {
262  double nan = std::numeric_limits<double>::quiet_NaN();
263 
264  mag_msg->magnetic_field.x = nan;
265  mag_msg->magnetic_field.y = nan;
266  mag_msg->magnetic_field.z = nan;
267  }
268 
269  mag_publisher_.publish(mag_msg);
270 
271  // diagnostics
273 
274  last_published_time_ = time_now;
275 }
276 
277 void ImuRosI::dataHandler(CPhidgetSpatial_SpatialEventDataHandle *data, int count)
278 {
279  for(int i = 0; i < count; i++)
280  processImuData(data, i);
281 }
282 
284 {
286  is_connected_ = true;
287  // Reset error number to no error if the prev error was disconnect
288  if (error_number_ == 13) error_number_ = 0;
290 
291  // Set device params. This is in attachHandler(), since it has to be repeated on reattachment.
293 }
294 
296 {
298  is_connected_ = false;
300 }
301 
302 void ImuRosI::errorHandler(int error)
303 {
304  Imu::errorHandler(error);
305  error_number_ = error;
307 }
308 
310 {
311  if (is_connected_)
312  {
313  stat.summary(diagnostic_msgs::DiagnosticStatus::OK, "The Phidget is connected.");
314  stat.add("Device Serial Number", getDeviceSerialNumber());
315  stat.add("Device Name", getDeviceName());
316  stat.add("Device Type", getDeviceType());
317  }
318  else
319  {
320  stat.summary(diagnostic_msgs::DiagnosticStatus::ERROR, "The Phidget is not connected. Check the USB.");
321  }
322 
323  if (error_number_ != 0)
324  {
325  stat.summary(diagnostic_msgs::DiagnosticStatus::ERROR, "The Phidget reports error.");
326  stat.add("Error Number", error_number_);
327  stat.add("Error message", getErrorDescription(error_number_));
328  }
329 }
330 
331 } // namespace phidgets
332 
ros::ServiceServer cal_srv_
Definition: imu_ros_i.h:43
std::string frame_id_
Definition: imu_ros_i.h:66
#define ROS_FATAL(...)
void publish(const boost::shared_ptr< M > &message) const
double cc_offset2_
Definition: imu_ros_i.h:78
void setDataRate(int rate)
void summary(unsigned char lvl, const std::string s)
void setHardwareID(const std::string &hwid)
const float MAX_TIMEDIFF_SECONDS
Definition: imu_ros_i.h:22
ImuRosI(ros::NodeHandle nh, ros::NodeHandle nh_private)
Definition: imu_ros_i.cpp:6
void add(const std::string &name, TaskFunction f)
ros::NodeHandle nh_
Definition: imu_ros_i.h:38
int getDeviceSerialNumber()
virtual void detachHandler()
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
int open(int serial_number)
double target_publish_freq_
Definition: imu_ros_i.h:53
ros::Time time_zero_
Definition: imu_ros_i.h:62
#define ROS_WARN(...)
double cc_gain0_
Definition: imu_ros_i.h:79
ros::Publisher imu_publisher_
Definition: imu_ros_i.h:40
double linear_acceleration_stdev_
Definition: imu_ros_i.h:70
int waitForAttachment(int timeout)
static std::string getErrorDescription(int errorCode)
boost::shared_ptr< diagnostic_updater::TopicDiagnostic > imu_publisher_diag_ptr_
Definition: imu_ros_i.h:48
virtual void errorHandler(int error)
#define ROS_WARN_THROTTLE(period,...)
sensor_msgs::MagneticField MagMsg
Definition: imu_ros_i.h:27
sensor_msgs::Imu ImuMsg
Definition: imu_ros_i.h:26
#define ROS_INFO(...)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
void processImuData(CPhidgetSpatial_SpatialEventDataHandle *data, int i)
Definition: imu_ros_i.cpp:200
virtual void attachHandler()
std::string getDeviceType()
void errorHandler(int error)
Definition: imu_ros_i.cpp:302
bool calibrateService(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res)
Definition: imu_ros_i.cpp:179
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
double cc_gain2_
Definition: imu_ros_i.h:81
ros::Time last_published_time_
Definition: imu_ros_i.h:56
double magnetic_field_stdev_
Definition: imu_ros_i.h:71
const float G
Definition: imu_ros_i.h:21
double angular_velocity_stdev_
Definition: imu_ros_i.h:69
void phidgetsDiagnostics(diagnostic_updater::DiagnosticStatusWrapper &stat)
Main diagnostic method that takes care of collecting diagnostic data.
Definition: imu_ros_i.cpp:309
ros::NodeHandle nh_private_
Definition: imu_ros_i.h:39
#define ROS_INFO_STREAM(args)
double cc_offset1_
Definition: imu_ros_i.h:77
bool getParam(const std::string &key, std::string &s) const
double cc_offset0_
Definition: imu_ros_i.h:76
static Time now()
ros::Publisher cal_publisher_
Definition: imu_ros_i.h:42
double cc_mag_field_
Definition: imu_ros_i.h:75
void add(const std::string &key, const T &val)
void setHardwareIDf(const char *format,...)
CPhidgetSpatialHandle imu_handle_
ros::Publisher mag_publisher_
Definition: imu_ros_i.h:41
diagnostic_updater::Updater diag_updater_
updater object of class Update. Used to add diagnostic tasks, set ID etc. refer package API...
Definition: imu_ros_i.h:47
#define ROS_ERROR(...)
double cc_gain1_
Definition: imu_ros_i.h:80
void dataHandler(CPhidgetSpatial_SpatialEventDataHandle *data, int count)
Definition: imu_ros_i.cpp:277
std::string getDeviceName()


phidgets_imu
Author(s): Ivan Dryanovski
autogenerated on Tue May 7 2019 03:19:31