epson_imu_spi_ros_node.cpp
Go to the documentation of this file.
1 //==============================================================================
2 //
3 // epson_imu_spi_driver_node.cpp
4 // - ROS node for Epson IMU sensor evaluation
5 // - This program initializes the Epson IMU and publishes ROS messages in
6 // ROS topic /epson_imu as convention per [REP 145]
7 // (http://www.ros.org/reps/rep-0145.html).
8 // - If the IMU model supports quaternion output
9 // then sensor messages are published topic /epson_imu/data with
10 // angular_velocity, linear_acceleration, orientation fields updating
11 // - If the IMU model does not support quaternion output
12 // then sensor messages are published topic /epson_imu/data with only
13 // angular_velocity, and linear_acceleration fields updating
14 //
15 // [This software is BSD-3
16 // licensed.](http://opensource.org/licenses/BSD-3-Clause)
17 //
18 // Original Code Development:
19 // Copyright (c) 2019, Carnegie Mellon University. All rights reserved.
20 //
21 // Additional Code contributed:
22 // Copyright (c) 2019, 2024, Seiko Epson Corp. All rights reserved.
23 //
24 // Redistribution and use in source and binary forms, with or without
25 // modification, are permitted provided that the following conditions are met:
26 //
27 // 1. Redistributions of source code must retain the above copyright notice,
28 // this list of conditions and the following disclaimer.
29 //
30 // 2. Redistributions in binary form must reproduce the above copyright notice,
31 // this list of conditions and the following disclaimer in the documentation
32 // and/or other materials provided with the distribution.
33 //
34 // 3. Neither the name of the copyright holder nor the names of its
35 // contributors may be used to endorse or promote products derived from
36 // this software without specific prior written permission.
37 //
38 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
39 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
42 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
43 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
44 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
45 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
46 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
47 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
48 // THE POSSIBILITY OF SUCH DAMAGE.
49 //
50 //==============================================================================
51 
52 #include <geometry_msgs/Vector3Stamped.h>
53 #include <ros/ros.h>
54 #include <sensor_msgs/Imu.h>
55 #include <sensor_msgs/Temperature.h>
58 
59 #include <iostream>
60 #include <string>
61 
62 #include "hcl.h"
63 #include "hcl_gpio.h"
64 #include "hcl_spi.h"
65 #include "sensor_epsonSpi.h"
66 #include "sensor_epsonCommon.h"
67 
68 using namespace std;
69 
70 //=========================================================================
71 //------------------------ IMU Initialization -----------------------------
72 //=========================================================================
73 
74 bool init_imu(struct EpsonProperties* ptr_epson_sensor,
75  struct EpsonOptions* ptr_epson_options) {
76  char prod_id_[9]; // String to store Device Product ID
77  char ser_id_[9]; // String to store Device Serial ID
78 
79  ROS_INFO("Initializing HCL layer...");
80  if (!seInit()) {
81  ROS_ERROR(
82  "Error: could not initialize the Seiko Epson HCL layer. Exiting...");
83  return false;
84  }
85  std::cout << "...done." << std::endl;
86 
87  ROS_INFO("Initializing GPIO interface...");
88  if (!gpioInit()) {
89  ROS_ERROR("Error: could not initialize the GPIO layer. Exiting...");
90  seRelease();
91  return false;
92  }
93  std::cout << "...done." << std::endl;
94 
95  ROS_INFO("Initializing SPI interface...");
96  // The max SPI clock rate is 1MHz for current model Epson IMUs
97  if (!spiInit(SPI_MODE3, 1000000)) {
98  ROS_ERROR("Error: could not initialize SPI interface. Exiting...");
99  gpioRelease();
100  seRelease();
101  return false;
102  }
103  std::cout << "...done." << std::endl;
104 
105  // This is legacy dummy access when using NVIDIA Jetson TK1.
106  // This is precautionary incase, SPI interface pins glitch during board init
108  std::cout << "...done." << std::endl;
109 
110  ROS_INFO("Checking sensor NOT_READY status...");
111  if (!sensorPowerOn()) {
112  ROS_ERROR("Error: failed to power on Sensor. Exiting...");
113  spiRelease();
114  gpioRelease();
115  seRelease();
116  return false;
117  }
118  std::cout << "...done." << std::endl;
119 
120  ROS_INFO("Detecting sensor model...");
121  if (!sensorGetDeviceModel(ptr_epson_sensor, prod_id_, ser_id_)) {
122  ROS_ERROR("Error: failed to detect sensor model. Exiting...");
123  return false;
124  }
125  std::cout << "...done." << std::endl;
126 
127  ROS_INFO("Initializing Sensor...");
128  if (!sensorInitOptions(ptr_epson_sensor, ptr_epson_options)) {
129  ROS_ERROR("Error: could not initialize Epson Sensor. Exiting...");
130  spiRelease();
131  gpioRelease();
132  seRelease();
133  return false;
134  }
135  std::cout << "...done." << std::endl;
136 
137  ROS_INFO("Epson IMU initialized.\n");
138  return true;
139 }
140 
141 //=========================================================================
142 // Timestamp Correction
143 //
144 // Time correction makes use of the Epson IMU External Reset Counter function.
145 // This assumes that the ROS time is accurately sync'ed to GNSS (approx.
146 // within 100s of microsecs) and the GNSS 1PPS signal is sent to
147 // Epson IMU's GPIO2_EXT pin. The system latency for calling
148 // rclcpp::Clock().now() is assumed to be negligible. Otherwise the timestamp
149 // correction may not be reliable. The get_stamp() method attempts to return
150 // a timestamp based on the IMU reset count value to exclude time delays
151 // caused by latencies in the link between the host system and the IMU.
152 //=========================================================================
153 
155  private:
156  const int64_t ONE_SEC_NSEC = 1000000000;
157  const int64_t HALF_SEC_NSEC = 500000000;
158  // For Gen2 IMUs freq = 46875Hz, max_count = 65535/46875 * 1e9
159  const int64_t GEN2_MAX = 1398080000;
160  // For Gen3 IMUs freq = 62500Hz, max_count = 65535/62500 * 1e9
161  const int64_t GEN3_MAX = 1048560000;
162  int64_t max_count;
166  int64_t count_old;
167  int64_t count_diff;
169  int32_t time_sec_old;
171  bool rollover;
174 
175  public:
176  TimeCorrection();
177  void set_imu(int);
178  ros::Time get_stamp(int);
179 };
180 
181 // Constructor
183  max_count = GEN3_MAX;
184  almost_rollover = max_count;
185  count_corrected = 0;
186  count_old = 0;
187  count_diff = 0;
188  time_sec_current = 0;
189  time_sec_old = 0;
190  time_nsec_current = 0;
191  count_corrected_old = 0;
192  rollover = false;
193  flag_imu_lead = false;
194  is_gen2_imu = false;
195 }
196 
197 //=========================================================================
198 // TimeCorrection::set_imu
199 //
200 // Sets the count thresholds based on external counter reset frequencies
201 // which may vary depending on the Epson IMU model.
202 //=========================================================================
203 
204 void TimeCorrection::set_imu(int epson_model) {
205  // max_count depends on IMU model's reset counter freq
206  is_gen2_imu = ((epson_model == G320PDG0) || (epson_model == G320PDGN) ||
207  (epson_model == G354PDH0) || (epson_model == G364PDCA) ||
208  (epson_model == G364PDC0));
209 
210  max_count = (is_gen2_imu) ? GEN2_MAX : GEN3_MAX;
211 }
212 
213 //=========================================================================
214 // TimeCorrection::get_stamp
215 //
216 // Returns the timestamp based on time offset from most recent 1PPS signal.
217 // Epson IMU has a free-running up-counter that resets on active 1PPS signal.
218 // Counter value is embedded in the sensor data at the time of sampling.
219 // Time stamp is corrected based on reset counter retrieved from embedded
220 // sensor data.
221 //=========================================================================
223  time_sec_current = ros::Time::now().toSec();
224  time_nsec_current = ros::Time::now().nsec;
225 
226  // almost_rollover is arbitrarily set at ~96% of max_count
227  almost_rollover = max_count * 0.96;
228 
229  count_diff = count - count_old;
230  if (count > almost_rollover) {
231  rollover = true;
232  } else if (count_diff < 0) {
233  if (rollover) {
234  count_diff = count + (max_count - count_old);
235  ROS_WARN(
236  "Warning: time_correction enabled but IMU reset counter "
237  "rollover detected. If 1PPS not connected to IMU GPIO2/EXT "
238  "pin, disable time_correction.");
239  } else {
240  count_diff = count;
241  count_corrected = 0;
242  }
243  rollover = false;
244  }
245  count_corrected = (count_corrected + count_diff) % ONE_SEC_NSEC;
246  if ((time_sec_current != time_sec_old) && (count_corrected > HALF_SEC_NSEC)) {
247  time_sec_current = time_sec_current - 1;
248  } else if (((count_corrected - count_corrected_old) < 0) &&
249  (time_nsec_current > HALF_SEC_NSEC)) {
250  time_sec_current = time_sec_current + 1;
251  flag_imu_lead = true;
252  } else if ((flag_imu_lead) && (time_nsec_current > HALF_SEC_NSEC)) {
253  time_sec_current = time_sec_current + 1;
254  } else {
255  flag_imu_lead = false;
256  }
257  ros::Time time;
258  time.nsec = count_corrected;
259  time.sec = time_sec_current;
260  time_sec_old = time_sec_current;
261  count_old = count;
262  count_corrected_old = count_corrected;
263 
264  return time;
265 }
266 
267 //=========================================================================
268 //------------------------------ Main -------------------------------------
269 //=========================================================================
270 
271 int main(int argc, char** argv) {
272  ros::init(argc, argv, "ess_imu_driver_node");
273 
274  // Force flush of the stdout buffer, which ensures a sync of all console
275  // output even from a launch file.
276  setvbuf(stdout, nullptr, _IONBF, BUFSIZ);
277 
278  ros::NodeHandle nh;
279  ros::NodeHandle np("~");
280 
281  std::string frame_id = "imu_link";
282  std::string imu_topic = "epson_imu";
283  std::string temperature_topic = "epson_tempc";
284 
285  // IMU properties
286  struct EpsonProperties epson_sensor;
287 
288  // IMU configuration settings
289  struct EpsonOptions epson_options = {};
290 
291  // Buffer for scaled values of IMU read burst
292  struct EpsonData epson_data = {};
293 
294  // Time correction object
295  TimeCorrection tc;
296 
297  sensor_msgs::Temperature tempc_msg;
298  sensor_msgs::Imu imu_msg;
299 
300  int time_correction = false;
301 
302  // Recommend changing these parameters by .launch file instead of
303  // modifying source code below directly
304  np.param("ext_sel", epson_options.ext_sel, 1); // external reset counter
305  np.param("ext_pol", epson_options.ext_pol, 0);
306  np.param("drdy_on", epson_options.drdy_on, 1);
307  np.param("drdy_pol", epson_options.drdy_pol, 1);
308 
309  np.param("dout_rate", epson_options.dout_rate, CMD_RATE250);
310  np.param("filter_sel", epson_options.filter_sel, CMD_FLTAP32);
311 
312  np.param("flag_out", epson_options.flag_out, 1);
313  np.param("temp_out", epson_options.temp_out, 1);
314  np.param("gyro_out", epson_options.gyro_out, 1);
315  np.param("accel_out", epson_options.accel_out, 1);
316  np.param("qtn_out", epson_options.qtn_out, 1);
317  np.param("count_out", epson_options.count_out, 1);
318  np.param("checksum_out", epson_options.checksum_out, 1);
319 
320  np.param("temp_bit", epson_options.temp_bit, 1);
321  np.param("gyro_bit", epson_options.gyro_bit, 1);
322  np.param("accel_bit", epson_options.accel_bit, 1);
323  np.param("qtn_bit", epson_options.qtn_bit, 1);
324 
325  np.param("atti_mode", epson_options.atti_mode, 1);
326  np.param("atti_profile", epson_options.atti_profile, 0);
327 
328  np.param("time_correction", time_correction, 0);
329 
330  if (!init_imu(&epson_sensor, &epson_options)) return -1;
331 
332  // if quaternion output is enabled set topic to /epson_imu/data
333  // Otherwise set topic to /epson_imu/data_raw
334  imu_topic = (static_cast<bool>(epson_options.qtn_out) == false)
335  ? "/epson_imu/data_raw"
336  : "/epson_imu/data";
337 
338  // Initialize time correction thresholds if enabled
339  if (time_correction) {
340  tc.set_imu(epson_sensor.model);
341  }
342 
343  ros::Publisher tempc_pub =
344  nh.advertise<sensor_msgs::Temperature>(temperature_topic, 1);
345 
346  for (int i = 0; i < 9; i++) {
347  imu_msg.orientation_covariance[i] = 0;
348  imu_msg.angular_velocity_covariance[i] = 0;
349  imu_msg.linear_acceleration_covariance[i] = 0;
350  }
351  imu_msg.orientation_covariance[0] = -1;
352 
353  ros::Publisher imu_pub = nh.advertise<sensor_msgs::Imu>(imu_topic, 1);
354 
355  sensorStart();
356 
357  while (ros::ok()) {
358  if (sensorDataReadBurstNOptions(&epson_sensor, &epson_options,
359  &epson_data)) {
360  imu_msg.header.frame_id = frame_id;
361  if (!time_correction)
362  imu_msg.header.stamp = ros::Time::now();
363  else
364  imu_msg.header.stamp = tc.get_stamp(epson_data.count);
365  imu_msg.angular_velocity.x = epson_data.gyro_x;
366  imu_msg.angular_velocity.y = epson_data.gyro_y;
367  imu_msg.angular_velocity.z = epson_data.gyro_z;
368  imu_msg.linear_acceleration.x = epson_data.accel_x;
369  imu_msg.linear_acceleration.y = epson_data.accel_y;
370  imu_msg.linear_acceleration.z = epson_data.accel_z;
371 
372  // Publish quaternion orientation
373  imu_msg.orientation.x = epson_data.qtn1;
374  imu_msg.orientation.y = epson_data.qtn2;
375  imu_msg.orientation.z = epson_data.qtn3;
376  imu_msg.orientation.w = epson_data.qtn0;
377  imu_pub.publish(imu_msg);
378 
379  // Publish temperature
380  tempc_msg.header = imu_msg.header;
381  tempc_msg.temperature = epson_data.temperature;
382  tempc_pub.publish(tempc_msg);
383 
384  } else {
385  ROS_WARN(
386  "Warning: Checksum error or incorrect delimiter bytes in imu_msg "
387  "detected");
388  }
389  }
390  sensorStop();
391  seDelayMS(1000);
392  spiRelease();
393  gpioRelease();
394  seRelease();
395 
396  return 0;
397 }
spiRelease
int spiRelease(void)
Definition: hcl_spi_rpi.c:67
EpsonData::qtn1
double qtn1
Definition: sensor_epsonCommon.h:406
sensorInitOptions
int sensorInitOptions(const struct EpsonProperties *esensor, struct EpsonOptions *options)
Definition: sensor_epsonCommon.c:835
TimeCorrection::is_gen2_imu
bool is_gen2_imu
Definition: epson_imu_spi_ros_node.cpp:173
EpsonProperties
Definition: sensor_epsonCommon.h:314
EpsonData::accel_y
double accel_y
Definition: sensor_epsonCommon.h:403
sensor_epsonCommon.h
EpsonOptions::ext_pol
int ext_pol
Definition: sensor_epsonCommon.h:343
ros::Publisher
EpsonOptions::atti_mode
int atti_mode
Definition: sensor_epsonCommon.h:391
EpsonOptions::drdy_on
int drdy_on
Definition: sensor_epsonCommon.h:344
EpsonOptions::ext_sel
int ext_sel
Definition: sensor_epsonCommon.h:342
TimeCorrection
Definition: epson_imu_spi_ros_node.cpp:154
EpsonOptions::filter_sel
int filter_sel
Definition: sensor_epsonCommon.h:351
sensorDataReadBurstNOptions
int sensorDataReadBurstNOptions(const struct EpsonProperties *, const struct EpsonOptions *, struct EpsonData *)
Definition: sensor_epsonSpi.c:451
ros::init
ROSCPP_DECL void init(const M_string &remappings, const std::string &name, uint32_t options=0)
seDelayMS
void seDelayMS(uint32_t millis)
Definition: hcl_linux.c:47
ros.h
TimeCorrection::get_stamp
ros::Time get_stamp(int)
Definition: epson_imu_spi_ros_node.cpp:222
TimeCorrection::count_diff
int64_t count_diff
Definition: epson_imu_spi_ros_node.cpp:167
EpsonOptions::temp_bit
int temp_bit
Definition: sensor_epsonCommon.h:368
hcl_gpio.h
TimeBase< Time, Duration >::toSec
double toSec() const
TimeCorrection::flag_imu_lead
bool flag_imu_lead
Definition: epson_imu_spi_ros_node.cpp:172
TimeCorrection::TimeCorrection
TimeCorrection()
Definition: epson_imu_spi_ros_node.cpp:182
EpsonOptions::drdy_pol
int drdy_pol
Definition: sensor_epsonCommon.h:345
EpsonOptions
Definition: sensor_epsonCommon.h:340
gpioInit
int gpioInit(void)
Definition: hcl_gpio.c:32
ros::Publisher::publish
void publish(const boost::shared_ptr< M > &message) const
EpsonData::gyro_x
double gyro_x
Definition: sensor_epsonCommon.h:402
EpsonOptions::dout_rate
int dout_rate
Definition: sensor_epsonCommon.h:348
ros::NodeHandle::advertise
Publisher advertise(AdvertiseOptions &ops)
hcl.h
EpsonOptions::gyro_bit
int gyro_bit
Definition: sensor_epsonCommon.h:369
ros::ok
ROSCPP_DECL bool ok()
EpsonData::temperature
double temperature
Definition: sensor_epsonCommon.h:401
EpsonOptions::qtn_out
int qtn_out
Definition: sensor_epsonCommon.h:360
EpsonOptions::accel_out
int accel_out
Definition: sensor_epsonCommon.h:357
EpsonOptions::atti_profile
int atti_profile
Definition: sensor_epsonCommon.h:395
main
int main(int argc, char **argv)
Definition: epson_imu_spi_ros_node.cpp:271
EpsonOptions::count_out
int count_out
Definition: sensor_epsonCommon.h:364
CMD_FLTAP32
#define CMD_FLTAP32
Definition: sensor_epsonCommon.h:253
seRelease
int seRelease(void)
Definition: hcl_linux.c:39
EpsonData::accel_x
double accel_x
Definition: sensor_epsonCommon.h:403
EpsonOptions::flag_out
int flag_out
Definition: sensor_epsonCommon.h:354
Quaternion.h
EpsonOptions::checksum_out
int checksum_out
Definition: sensor_epsonCommon.h:365
TimeCorrection::max_count
int64_t max_count
Definition: epson_imu_spi_ros_node.cpp:162
EpsonData::gyro_z
double gyro_z
Definition: sensor_epsonCommon.h:402
EpsonData
Definition: sensor_epsonCommon.h:399
TimeCorrection::set_imu
void set_imu(int)
Definition: epson_imu_spi_ros_node.cpp:204
EpsonData::count
int count
Definition: sensor_epsonCommon.h:409
EpsonData::qtn3
double qtn3
Definition: sensor_epsonCommon.h:406
ROS_WARN
#define ROS_WARN(...)
TimeCorrection::almost_rollover
int64_t almost_rollover
Definition: epson_imu_spi_ros_node.cpp:163
gpioRelease
int gpioRelease(void)
Definition: hcl_gpio.c:40
init_imu
bool init_imu(struct EpsonProperties *ptr_epson_sensor, struct EpsonOptions *ptr_epson_options)
Definition: epson_imu_spi_ros_node.cpp:74
TimeBase< Time, Duration >::sec
uint32_t sec
G364PDC0
@ G364PDC0
Definition: sensor_epsonCommon.h:288
G320PDGN
@ G320PDGN
Definition: sensor_epsonCommon.h:285
TimeBase< Time, Duration >::nsec
uint32_t nsec
EpsonData::qtn2
double qtn2
Definition: sensor_epsonCommon.h:406
sensorStop
void sensorStop(void)
Definition: sensor_epsonCommon.c:504
hcl_spi.h
sensorPowerOn
int sensorPowerOn(void)
Definition: sensor_epsonCommon.c:445
TimeCorrection::count_corrected
int64_t count_corrected
Definition: epson_imu_spi_ros_node.cpp:164
ros::Time
std
TimeCorrection::rollover
bool rollover
Definition: epson_imu_spi_ros_node.cpp:171
EpsonData::qtn0
double qtn0
Definition: sensor_epsonCommon.h:406
SPI_MODE3
@ SPI_MODE3
Definition: hcl_spi.h:26
TimeCorrection::time_sec_old
int32_t time_sec_old
Definition: epson_imu_spi_ros_node.cpp:169
TimeCorrection::time_sec_current
int32_t time_sec_current
Definition: epson_imu_spi_ros_node.cpp:168
ROS_ERROR
#define ROS_ERROR(...)
sensorStart
void sensorStart(void)
Definition: sensor_epsonCommon.c:490
ros::NodeHandle::param
T param(const std::string &param_name, const T &default_val) const
EpsonData::gyro_y
double gyro_y
Definition: sensor_epsonCommon.h:402
EpsonOptions::gyro_out
int gyro_out
Definition: sensor_epsonCommon.h:356
TimeCorrection::time_nsec_current
int64_t time_nsec_current
Definition: epson_imu_spi_ros_node.cpp:170
TimeCorrection::count_old
int64_t count_old
Definition: epson_imu_spi_ros_node.cpp:166
EpsonOptions::temp_out
int temp_out
Definition: sensor_epsonCommon.h:355
EpsonOptions::accel_bit
int accel_bit
Definition: sensor_epsonCommon.h:370
EpsonData::accel_z
double accel_z
Definition: sensor_epsonCommon.h:403
CMD_RATE250
#define CMD_RATE250
Definition: sensor_epsonCommon.h:233
EpsonProperties::model
int model
Definition: sensor_epsonCommon.h:315
spiInit
int spiInit(uint8_t mode, uint32_t khzspeed)
Definition: hcl_spi_rpi.c:45
sensor_epsonSpi.h
TimeCorrection::count_corrected_old
int64_t count_corrected_old
Definition: epson_imu_spi_ros_node.cpp:165
ROS_INFO
#define ROS_INFO(...)
sensorDummyWrite
void sensorDummyWrite(void)
Definition: sensor_epsonCommon.c:766
Matrix3x3.h
EpsonOptions::qtn_bit
int qtn_bit
Definition: sensor_epsonCommon.h:373
seInit
int seInit(void)
Definition: hcl_linux.c:31
G364PDCA
@ G364PDCA
Definition: sensor_epsonCommon.h:287
ros::NodeHandle
G354PDH0
@ G354PDH0
Definition: sensor_epsonCommon.h:286
sensorGetDeviceModel
int sensorGetDeviceModel(struct EpsonProperties *esensor, char *prod_id, char *serial_id)
Definition: sensor_epsonCommon.c:1192
ros::Time::now
static Time now()
G320PDG0
@ G320PDG0
Definition: sensor_epsonCommon.h:284


ess_imu_driver
Author(s):
autogenerated on Wed Dec 11 2024 03:06:30