├── CMakeLists.txt
├── README.md
├── Screenshot from 2017-07-29 10-49-16.png
├── cfg
├── MyStuff.cfg
└── MyStuff2.cfg
├── package.xml
└── src
├── Madgwick_filter.cpp
├── Mahony_filter.cpp
└── bias_calculator.cpp
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8.3)
2 | project(filter)
3 |
4 | ## Compile as C++11, supported in ROS Kinetic and newer
5 | # add_compile_options(-std=c++11)
6 |
7 | ## Find catkin macros and libraries
8 | ## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
9 | ## is used, also find other catkin packages
10 |
11 | find_package(catkin REQUIRED COMPONENTS
12 | roscpp
13 | rospy
14 | sensor_msgs geometry_msgs tf dynamic_reconfigure
15 | )
16 |
17 | generate_dynamic_reconfigure_options(
18 | cfg/MyStuff.cfg
19 | cfg/MyStuff2.cfg
20 | )
21 |
22 |
23 | catkin_package(
24 | # INCLUDE_DIRS include
25 | # LIBRARIES temp
26 | # CATKIN_DEPENDS roscpp
27 | # DEPENDS system_lib
28 | )
29 |
30 | include_directories(
31 | # include
32 | ${catkin_INCLUDE_DIRS}
33 | )
34 |
35 |
36 |
37 |
38 | add_executable(Mahony_filter src/Mahony_filter.cpp)
39 | add_dependencies(Mahony_filter ${PROJECT_NAME}_gencfg)
40 | target_link_libraries(Mahony_filter ${catkin_LIBRARIES} )
41 |
42 |
43 | add_executable(Madgwick_filter src/Madgwick_filter.cpp)
44 | add_dependencies(Madgwick_filter ${PROJECT_NAME}_gencfg)
45 | target_link_libraries(Madgwick_filter ${catkin_LIBRARIES} )
46 |
47 | add_executable(bias_calculator src/bias_calculator.cpp)
48 | add_dependencies(bias_calculator ${PROJECT_NAME}_gencfg)
49 | target_link_libraries(bias_calculator ${catkin_LIBRARIES} )
50 |
51 |
52 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # imu_filter
2 |
3 | This is a project that uses IMU filter algorithm in ROS(Robot Operating System). The IMU I use is ICM 20602, consisting of a tri-axis gyroscope and a tri-axis accelerometer.
4 |
5 | ## ROS API
6 | ### Subscribed Topics
7 | Message containing raw IMU data, including angular velocities and linear accelerations.
8 | `imu0`(sensor_msgs/Imu)
9 | ### Published Topics
10 | The fused pose representation.
11 | `quaternion`(geometry_msg/QuaternionStamped)
12 | `ypr`(geometry_msgs/Vector3Stamped)
13 | ## Parameters
14 | ### Dynamically Reconfigurable Parameters
15 | (Mahony_filter) `twoKp` `twoKi`
16 | (Madgwick_filter) `beta`
17 | To tune the dynamic parameters after runnig the node, input
18 |
19 | rosrun rqt_configure rqt_reconfigure
20 | According to Madgwick's thesis, the suggested beta = sqrt(3.0f / 4.0f) * gyroMeasError. ICM20602's gyroscope
21 | sensitivity erroris ±1%. Thus the default beta is setting to 0.1088. As the specific application, the value
22 | can be tuned accoring to response and requriments.
23 | ### Non-dynamically Reconfigurable Parameters
24 | `sampleFreq`(float, default: 400.0)
25 | You can change its value at the beginning, Such as:
26 |
27 | rosrun filter Madgwick_filter _sampleFreq:=200
28 | ## provied tf Transforms
29 | odom -> imu
30 | So you can open rviz and set the fixed frame "odom", add TF then you can use it to verify the effect directly.
31 | 
32 | ## Result
33 | The Madgwick_filter node's effect is acceptable, but the Mahony_filter is disappointing.
34 |
35 |
--------------------------------------------------------------------------------
/Screenshot from 2017-07-29 10-49-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marooncn/imu_filter/05a3c074285b4d25215c04ea1d23506e15dbc168/Screenshot from 2017-07-29 10-49-16.png
--------------------------------------------------------------------------------
/cfg/MyStuff.cfg:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 | PACKAGE = 'filter'
4 |
5 |
6 | from dynamic_reconfigure.parameter_generator_catkin import *
7 |
8 | gen = ParameterGenerator()
9 |
10 | gen.add("twoKp", 'double', 0, "2* accelerometer proportional gain", 0.01, 0, 0.1);
11 | gen.add("twoKi", 'double', 0, "2* integral gain", 0.002, 0, 0.01);
12 |
13 | exit(gen.generate(PACKAGE, "filter", "MyStuff"))
14 |
15 |
--------------------------------------------------------------------------------
/cfg/MyStuff2.cfg:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 | PACKAGE = 'filter'
4 |
5 |
6 | from dynamic_reconfigure.parameter_generator_catkin import *
7 |
8 | gen = ParameterGenerator()
9 |
10 | gen.add("beta", 'double', 0, "2* accelerometer proportional gain", 0.1088, 0, 0.5);
11 |
12 |
13 | exit(gen.generate(PACKAGE, "filter", "MyStuff2"))
14 |
15 |
--------------------------------------------------------------------------------
/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | filter
4 | 0.0.0
5 | The filter package
6 |
7 |
8 |
9 |
10 | maroon
11 |
12 |
13 |
14 |
15 |
16 | TODO
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | catkin
43 | roscpp
44 | rospy
45 | sensor_msgs
46 | geometry_msgs
47 | tf
48 | dynamic_reconfigure
49 | roscpp
50 | rospy
51 | sensor_msgs
52 | geometry_msgs
53 | tf
54 | dynamic_reconfigure
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/src/Madgwick_filter.cpp:
--------------------------------------------------------------------------------
1 | #include "ros/ros.h"
2 | #include "std_msgs/Float64.h"
3 | #include "std_msgs/Header.h"
4 | #include "geometry_msgs/Quaternion.h"
5 | #include "geometry_msgs/QuaternionStamped.h"
6 | #include "geometry_msgs/Vector3Stamped.h"
7 | #include "geometry_msgs/TransformStamped.h"
8 | #include "sensor_msgs/Imu.h"
9 | #include "tf/transform_broadcaster.h"
10 | #include "tf/transform_datatypes.h"
11 | #include "tf/transform_listener.h"
12 | #include "math.h"
13 | #include "ros/console.h"
14 | #include "dynamic_reconfigure/server.h"
15 | #include "filter/MyStuff2Config.h"
16 |
17 |
18 | using namespace std_msgs;
19 |
20 | geometry_msgs::QuaternionStamped q;
21 | geometry_msgs::Vector3Stamped v;
22 | geometry_msgs::TransformStamped q_trans;
23 | float sampleFreq;
24 | double beta;
25 | float q0=1.0, q1=0.0, q2=0.0, q3=0.0;
26 | Header header;
27 | float ax, ay, az, gx, gy, gz;
28 | ros::Duration dtime;
29 | float dt;
30 |
31 | float invSqrt(float );
32 | void qua2Euler(geometry_msgs::QuaternionStamped );
33 | void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az);
34 |
35 | void callback(filter::MyStuff2Config &config, uint32_t level) {
36 | beta = config.beta;
37 |
38 | }
39 |
40 |
41 | void filter_function(const sensor_msgs::Imu& msg)
42 | {
43 | // ros::Time start_time = ros::Time::now();
44 |
45 |
46 | header = msg.header;
47 | // q.linear_acceleration = msg.linear_acceleration;
48 | // q.angular_velocity = msg.angular_velocity;
49 | ax = msg.linear_acceleration.x;
50 | ay = msg.linear_acceleration.y;
51 | az = msg.linear_acceleration.z;
52 | gx = msg.angular_velocity.x;
53 | gy = msg.angular_velocity.y;
54 | gz = msg.angular_velocity.z;
55 |
56 | /* ros::Time this_time = header.stamp;
57 | static ros::Time last_time = header.stamp;
58 | dtime = this_time - last_time;
59 | dt = dtime.toNSec();
60 | ROS_INFO(" this_time = %f", this_time.toSec());
61 | ROS_INFO(" last_time = %f", last_time.toSec());
62 | dt = dt/1.0e9;
63 | // dt = 0.0025;
64 | ROS_INFO(" dt = %f", dt);
65 | last_time = this_time; */
66 |
67 | // since we are using 1000 degrees/seconds range and the register is 16 bits
68 | // -1000 maps to a raw value of -32768
69 | // +1000 maps to a raw value of 32767
70 | gx = gx*1000.0/32768.0;
71 | gy = gy*1000.0/32768.0;
72 | gz = gz*1000.0/32768.0;
73 |
74 | //change Gyroscope units to radians/second, accelerometer units are irrelevant as the vector is normalised.
75 | // Convert gyroscope degrees/sec to radians/sec
76 | gx *= 0.0174533f;
77 | gy *= 0.0174533f;
78 | gz *= 0.0174533f;
79 |
80 | ROS_INFO("The original gx=%f, gy=%f, gz=%f ", gx, gy, gz);
81 | ROS_INFO("beta=%f", beta);
82 |
83 | MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az) ;
84 |
85 | q.header = header;
86 | q.quaternion.w = q0;
87 | q.quaternion.x = q1;
88 | q.quaternion.y = q2;
89 | q.quaternion.z = q3;
90 |
91 | ROS_INFO(" q0=%f, q1=%f, q2=%f, q3=%f ", q0, q1, q2, q3);
92 | qua2Euler(q);
93 | // ros::Time end_time = ros::Time::now();
94 | // double interval_time = (end_time - start_time).toNSec();
95 | // ROS_INFO("the interval time is %lf ns" , interval_time);
96 | }
97 |
98 |
99 | int main(int argc, char **argv)
100 | {
101 | ros::init(argc, argv, "Mahony_filter");
102 | ros::NodeHandle n;
103 | ros::Publisher pub1 = n.advertise("quaternion", 1);
104 | ros::Publisher pub2 = n.advertise("ypr", 1);
105 | tf::TransformBroadcaster q_broadcaster;
106 | ros::Subscriber sub = n.subscribe("imu0", 10, filter_function);
107 | if(!n.getParam("sampleFreq", sampleFreq))
108 | sampleFreq = 400.0;
109 | dynamic_reconfigure::Server server;
110 | dynamic_reconfigure::Server::CallbackType f;
111 | f = boost::bind(&callback, _1, _2);
112 | server.setCallback(f);
113 |
114 | ros::Rate r(sampleFreq);
115 | while(ros::ok())
116 | {
117 | pub1.publish(q);
118 | pub2.publish(v);
119 | ros::spinOnce();
120 |
121 | q_trans.header.stamp = header.stamp;
122 | q_trans.header.frame_id = "/odom";
123 | q_trans.child_frame_id = "/imu";
124 | /* q_trans.transform.rotation.w = q.quaternion.w;
125 | q_trans.transform.rotation.x = q.quaternion.x;
126 | q_trans.transform.rotation.y = q.quaternion.y;
127 | q_trans.transform.rotation.z = q.quaternion.z; */
128 | q_trans.transform.rotation = tf::createQuaternionMsgFromRollPitchYaw(v.vector.z ,v.vector.y, v.vector.x);
129 |
130 | q_broadcaster.sendTransform(q_trans);
131 | r.sleep();
132 | }
133 | return 0;
134 | }
135 |
136 | //Fast inverse square root
137 | //reference: https://en.wikipedia.org/wiki/Fast_inverse_square_root
138 | float invSqrt(float x)
139 | {
140 | float xhalf = 0.5f * x;
141 | union
142 | {
143 | float x;
144 | int i;
145 | } u;
146 | u.x = x;
147 | u.i = 0x5f3759df - (u.i >> 1);
148 | /* The next line can be repeated any number of times to increase accuracy */
149 | u.x = u.x * (1.5f - xhalf * u.x * u.x);
150 | return u.x;
151 | }
152 |
153 | //reference: https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
154 | void qua2Euler(geometry_msgs::QuaternionStamped q) {
155 |
156 | float x,y,z,w;
157 |
158 | x = q.quaternion.x;
159 | y = q.quaternion.y;
160 | z = q.quaternion.z;
161 | w = q.quaternion.w;
162 |
163 | v.header = q.header;
164 |
165 | //yaw (z-axis rotation)
166 | float t0 = 2.0*(w*z+x*y);
167 | float t1 = 1.0-2.0*(y*y+z*z);
168 | v.vector.x = atan2(t0,t1); //the unit is radians
169 | //pitch (y-axis rotation)
170 | float t2 = 2.0*(w*y-z*x);
171 | t2 = (t2 > 1.0) ? 1.0 : t2;
172 | t2 = (t2 < -1.0)? -1.0 : t2;
173 | v.vector.y = asin(t2);
174 | //roll (x-axis rotation)
175 | float t3 = 2.0*(w*x+y*z);
176 | float t4 = 1.0-2.0*(x*x+y*y);
177 | v.vector.z = atan2(t3, t4);
178 | }
179 |
180 |
181 | //Implementation of Madgwick's IMU and AHRS algorithms.
182 | //reference: An efficient orientation filter for inertial and inertial/magnetic sensor arrays. (This thesis's appendix has more detailed code and illutration)
183 | //reference: http://x-io.co.uk/open-source-imu-and-ahrs-algorithms/
184 |
185 | void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az) {
186 | float recipNorm;
187 | float s0, s1, s2, s3; // estimated direction of the gyroscope error
188 | float qDot1, qDot2, qDot3, qDot4; // quaternion derrivative from gyroscopes elements
189 | float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3; //Axulirary variables to avoid reapeated calcualtions
190 |
191 | // Rate of change of quaternion from gyroscope
192 | qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz);
193 | qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy);
194 | qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx);
195 | qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx);
196 |
197 | // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
198 | if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {
199 |
200 | // Normalise accelerometer measurement
201 | recipNorm = invSqrt(ax * ax + ay * ay + az * az);
202 | ax *= recipNorm;
203 | ay *= recipNorm;
204 | az *= recipNorm;
205 |
206 | // Auxiliary variables to avoid repeated arithmetic
207 | _2q0 = 2.0f * q0;
208 | _2q1 = 2.0f * q1;
209 | _2q2 = 2.0f * q2;
210 | _2q3 = 2.0f * q3;
211 | _4q0 = 4.0f * q0;
212 | _4q1 = 4.0f * q1;
213 | _4q2 = 4.0f * q2;
214 | _8q1 = 8.0f * q1;
215 | _8q2 = 8.0f * q2;
216 | q0q0 = q0 * q0;
217 | q1q1 = q1 * q1;
218 | q2q2 = q2 * q2;
219 | q3q3 = q3 * q3;
220 |
221 | // Gradient decent algorithm corrective step
222 | s0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay;
223 | s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * q1 - _2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az;
224 | s2 = 4.0f * q0q0 * q2 + _2q0 * ax + _4q2 * q3q3 - _2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az;
225 | s3 = 4.0f * q1q1 * q3 - _2q1 * ax + 4.0f * q2q2 * q3 - _2q2 * ay;
226 |
227 | recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // Normalise the gradient
228 | s0 *= recipNorm;
229 | s1 *= recipNorm;
230 | s2 *= recipNorm;
231 | s3 *= recipNorm;
232 |
233 |
234 | // Apply feedback step
235 | qDot1 -= beta * s0;
236 | qDot2 -= beta * s1;
237 | qDot3 -= beta * s2;
238 | qDot4 -= beta * s3;
239 | }
240 |
241 | // Integrate rate of change of quaternion to yield quaternion
242 | q0 += qDot1 * (1.0f / sampleFreq);
243 | q1 += qDot2 * (1.0f / sampleFreq);
244 | q2 += qDot3 * (1.0f / sampleFreq);
245 | q3 += qDot4 * (1.0f / sampleFreq);
246 |
247 | // Normalise quaternion
248 | recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
249 | q0 *= recipNorm;
250 | q1 *= recipNorm;
251 | q2 *= recipNorm;
252 | q3 *= recipNorm;
253 | }
254 |
255 |
256 |
257 |
258 |
259 |
260 |
--------------------------------------------------------------------------------
/src/Mahony_filter.cpp:
--------------------------------------------------------------------------------
1 | #include "ros/ros.h"
2 | #include "std_msgs/Float64.h"
3 | #include "std_msgs/Header.h"
4 | //#include "std_msgs/Float64MultiArray.h"
5 | #include "geometry_msgs/Quaternion.h"
6 | #include "geometry_msgs/QuaternionStamped.h"
7 | #include "geometry_msgs/Vector3Stamped.h"
8 | #include "geometry_msgs/TransformStamped.h"
9 | #include "sensor_msgs/Imu.h"
10 | #include "tf/transform_broadcaster.h"
11 | #include "tf/transform_datatypes.h"
12 | #include "tf/transform_listener.h"
13 | #include "math.h"
14 | #include "ros/console.h"
15 | #include "dynamic_reconfigure/server.h"
16 | #include "filter/MyStuffConfig.h"
17 |
18 |
19 | using namespace std_msgs;
20 |
21 | geometry_msgs::QuaternionStamped q;
22 | geometry_msgs::Vector3Stamped v;
23 | geometry_msgs::TransformStamped q_trans;
24 | double twoKp; //2* accelerometer proportional gain
25 | double twoKi; //2* integral gain
26 | float sampleFreq;
27 | float q0=1.0, q1=0.0, q2=0.0, q3=0.0;
28 | float ex_int=0.0, ey_int=0.0, ez_int=0.0;
29 | Header header;
30 | float ax, ay, az, gx, gy, gz;
31 | ros::Duration dtime;
32 | float dt;
33 |
34 |
35 | float invSqrt(float );
36 | void qua2Euler(geometry_msgs::QuaternionStamped );
37 | void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az);
38 |
39 | void callback(filter::MyStuffConfig &config, uint32_t level) {
40 | twoKp = config.twoKp;
41 | twoKi = config.twoKi;
42 | ROS_INFO("twoKi=%f, twoKp=%f", twoKi, twoKp);
43 | }
44 |
45 |
46 | //Implementation of Mahony&Madgwick's IMU and AHRS algorithms.
47 | //reference: http://x-io.co.uk/open-source-imu-and-ahrs-algorithms/
48 | void filter_function(const sensor_msgs::Imu& msg)
49 | {
50 | // ros::Time start_time = ros::Time::now();
51 |
52 | header = msg.header;
53 | // q.linear_acceleration = msg.linear_acceleration;
54 | // q.angular_velocity = msg.angular_velocity;
55 | ax = msg.linear_acceleration.x;
56 | ay = msg.linear_acceleration.y;
57 | az = msg.linear_acceleration.z;
58 | gx = msg.angular_velocity.x;
59 | gy = msg.angular_velocity.y;
60 | gz = msg.angular_velocity.z;
61 |
62 | /* ros::Time this_time = header.stamp;
63 | static ros::Time last_time = header.stamp;
64 | dtime = this_time - last_time;
65 | dt = dtime.toNSec();
66 | ROS_INFO(" this_time = %f", this_time.toSec());
67 | ROS_INFO(" last_time = %f", last_time.toSec());
68 | dt = dt/1.0e9;
69 | // dt = 0.0025;
70 | ROS_INFO(" dt = %f", dt);
71 | last_time = this_time; */
72 |
73 | // since we are using 1000 degrees/seconds range and the register is 16 bits
74 | // -1000 maps to a raw value of -32768
75 | // +1000 maps to a raw value of 32767
76 | gx = gx*1000.0/32768.0;
77 | gy = gy*1000.0/32768.0;
78 | gz = gz*1000.0/32768.0;
79 |
80 | //change Gyroscope units to radians/second, accelerometer units are irrelevant as the vector is normalised.
81 | // Convert gyroscope degrees/sec to radians/sec
82 | gx *= 0.0174533f;
83 | gy *= 0.0174533f;
84 | gz *= 0.0174533f;
85 |
86 | ROS_INFO("The original gx=%f, gy=%f, gz=%f ", gx, gy, gz);
87 |
88 | MahonyAHRSupdateIMU(gx, gy, gz, ax, ay, az);
89 |
90 | q.header = header;
91 | q.quaternion.w = q0;
92 | q.quaternion.x = q1;
93 | q.quaternion.y = q2;
94 | q.quaternion.z = q3;
95 |
96 | ROS_INFO(" q0=%f, q1=%f, q2=%f, q3=%f ", q0, q1, q2, q3);
97 | qua2Euler(q);
98 | // ros::Time end_time = ros::Time::now();
99 | // double interval_time = (end_time - start_time).toNSec();
100 | // ROS_INFO("the interval time is %lf ns" , interval_time);
101 | }
102 |
103 |
104 | int main(int argc, char **argv)
105 | {
106 | ros::init(argc, argv, "Mahony_filter");
107 | ros::NodeHandle n;
108 | ros::Publisher pub1 = n.advertise("quaternion", 1);
109 | ros::Publisher pub2 = n.advertise("ypr", 1);
110 | // ros::Publisher odom_pub = n.advertise("odom", 50);
111 | tf::TransformBroadcaster q_broadcaster;
112 | //ros::Publisher pub3 = n.advertise("DCM",20);
113 | //setFullSacleGyroRange(ICM20602_GYRO_RANGE_1000);
114 | ros::Subscriber sub = n.subscribe("imu0", 10, filter_function);
115 | if(!n.getParam("sampleFreq", sampleFreq))
116 | sampleFreq = 400.0;
117 | dt = 1.0/sampleFreq;
118 |
119 | dynamic_reconfigure::Server server;
120 | dynamic_reconfigure::Server::CallbackType f;
121 | f = boost::bind(&callback, _1, _2);
122 | server.setCallback(f);
123 |
124 | ros::Rate r(sampleFreq);
125 | while(ros::ok())
126 | {
127 | pub1.publish(q);
128 | pub2.publish(v);
129 | ros::spinOnce();
130 |
131 | q_trans.header.stamp = header.stamp;
132 | q_trans.header.frame_id = "/odom";
133 | q_trans.child_frame_id = "/imu";
134 | /* q_trans.transform.rotation.w = q.quaternion.w;
135 | q_trans.transform.rotation.x = q.quaternion.x;
136 | q_trans.transform.rotation.y = q.quaternion.y;
137 | q_trans.transform.rotation.z = q.quaternion.z; */
138 | q_trans.transform.rotation = tf::createQuaternionMsgFromRollPitchYaw(v.vector.z ,v.vector.y, v.vector.x);
139 |
140 | q_broadcaster.sendTransform(q_trans);
141 | r.sleep();
142 | }
143 | return 0;
144 | }
145 |
146 | //Fast inverse square root
147 | //reference: https://en.wikipedia.org/wiki/Fast_inverse_square_root
148 | float invSqrt(float x)
149 | {
150 | float xhalf = 0.5f * x;
151 | union
152 | {
153 | float x;
154 | int i;
155 | } u;
156 | u.x = x;
157 | u.i = 0x5f3759df - (u.i >> 1);
158 | /* The next line can be repeated any number of times to increase accuracy */
159 | u.x = u.x * (1.5f - xhalf * u.x * u.x);
160 | return u.x;
161 | }
162 |
163 | //reference: https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
164 | void qua2Euler(geometry_msgs::QuaternionStamped q) {
165 |
166 | float x,y,z,w;
167 |
168 | x = q.quaternion.x;
169 | y = q.quaternion.y;
170 | z = q.quaternion.z;
171 | w = q.quaternion.w;
172 |
173 | v.header = q.header;
174 |
175 | //yaw (z-axis rotation)
176 | float t0 = 2.0*(w*z+x*y);
177 | float t1 = 1.0-2.0*(y*y+z*z);
178 | v.vector.x = atan2(t0,t1)*57.29578; //the unit is degree
179 | //pitch (y-axis rotation)
180 | float t2 = 2.0*(w*y-z*x);
181 | t2 = (t2 > 1.0) ? 1.0 : t2;
182 | t2 = (t2 < -1.0)? -1.0 : t2;
183 | v.vector.y = asin(t2)*57.29578;
184 | //roll (x-axis rotation)
185 | float t3 = 2.0*(w*x+y*z);
186 | float t4 = 1.0-2.0*(x*x+y*y);
187 | v.vector.z = atan2(t3, t4)*57.29578;
188 |
189 | }
190 |
191 | void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az) {
192 |
193 | float recipNorm;
194 | float halfvx, halfvy, halfvz;
195 | float halfex, halfey, halfez;
196 | float thetax, thetay, thetaz;
197 | float dq0, dq1, dq2, dq3;
198 |
199 | // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
200 | if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {
201 | //normalize the accelerometer vector
202 | recipNorm = invSqrt(ax*ax+ay*ay+az*az);
203 | ax *= recipNorm;
204 | ay *= recipNorm;
205 | az *= recipNorm;
206 |
207 | ROS_INFO(" ax=%f, ay=%f, az=%f ", ax, ay, az);
208 |
209 | //calculate the direction of gravity according to quaternion
210 | halfvx = q1*q3 - q0*q2;
211 | halfvy = q0*q1 + q2*q3;
212 | // vz = q0*q0 - q1*q1 - q2*q2 + q3*q3;
213 | halfvz = q0*q0 -0.5f + q3*q3;
214 |
215 | ROS_INFO("The halfvx=%f, halfvy=%f, halfvz=%f", halfvx, halfvy, halfvz);
216 |
217 |
218 |
219 | //calculate the error which is cross product between estimated and measured direction of gravity
220 | halfex = ay*halfvz - az*halfvy;
221 | halfey = az*halfvx - ax*halfvz;
222 | halfez = ax*halfvy - ay*halfvx;
223 |
224 | ROS_INFO("The halfex=%f, halfey=%f, halfez=%f", halfex, halfey, halfez);
225 |
226 | if (twoKi > 0.0f) {
227 | //integral the error
228 | ex_int += twoKi*halfex*dt;
229 | ey_int += twoKi*halfey*dt;
230 | ez_int += twoKi*halfez*dt;
231 |
232 | ROS_INFO("The ex_int=%f, ey_int=%f, ez_int=%f", ex_int, ey_int, ez_int);
233 |
234 | //apply the integral feedback
235 | gx += ex_int;
236 | gy += ey_int;
237 | gz += ez_int; }
238 |
239 | else {
240 | ex_int = 0.0f;
241 | ey_int = 0.0f;
242 | ez_int = 0.0f;
243 | }
244 |
245 | //apply the proportional feedback
246 | gx += twoKp*halfex;
247 | gy += twoKp*halfey;
248 | gz += twoKp*halfez;
249 |
250 | ROS_INFO("The revised gx=%f, gy=%f, gz=%f ", gx, gy, gz); }
251 |
252 | //calculate the rotary angle
253 | thetax = 0.5*gx*dt;
254 | thetay = 0.5*gy*dt;
255 | thetaz = 0.5*gz*dt;
256 |
257 | //update quaternion using Runge Kutta algorithms.
258 | //explanation: http://blog.csdn.net/aasdsadad/article/details/73080832
259 | dq0 = -q1*thetax-q2*thetay-q3*thetaz;
260 | dq1 = q0*thetax+q2*thetaz-q3*thetay;
261 | dq2 = q0*thetay-q1*thetaz+q3*thetax;
262 | dq3 = q0*thetaz+q1*thetay-q2*thetax;
263 |
264 | q0 += dq0;
265 | q1 += dq1;
266 | q2 += dq2;
267 | q3 += dq3;
268 |
269 | //normalize quaternion
270 | recipNorm = invSqrt(q0*q0+q1*q1+q2*q2+q3*q3);
271 | q0 *= recipNorm;
272 | q1 *= recipNorm;
273 | q2 *= recipNorm;
274 | q3 *= recipNorm;
275 |
276 | }
277 |
278 |
279 |
--------------------------------------------------------------------------------
/src/bias_calculator.cpp:
--------------------------------------------------------------------------------
1 | #include "ros/ros.h"
2 | #include "geometry_msgs/Vector3.h"
3 | #include "sensor_msgs/Imu.h"
4 |
5 | double x = 0, y = 0, z = 0;
6 | double count = 0.0;
7 | geometry_msgs::Vector3 v;
8 |
9 | void calculator(sensor_msgs::Imu imu_msg) {
10 | x += imu_msg.angular_velocity.x;
11 | y += imu_msg.angular_velocity.y;
12 | z += imu_msg.angular_velocity.z;
13 | count++;
14 | }
15 |
16 | int main(int argc, char** argv) {
17 | ros::init(argc, argv, "bias_calculator");
18 | ROS_INFO("Please keep your IMU still for 10 secs");
19 | ros::NodeHandle n;
20 | ros::Subscriber sub = n.subscribe("imu0", 1000, calculator);
21 | ros::Publisher pub = n.advertise< geometry_msgs::Vector3>("bias", 1);
22 |
23 | while(ros::ok()) {
24 | for(int i=0; i<10; i++)
25 | {ros::Duration(1).sleep();
26 | ROS_INFO("%ds left", 10-i);
27 | ros::spinOnce(); }
28 | if(count == 0)
29 | { ROS_WARN("can't receive imu0 data. Please try again.");
30 | break; }
31 | v.x = x /count;
32 | v.y = y /count;
33 | v.z = z /count;
34 | pub.publish(v);
35 | ROS_INFO("Calculate ends.");
36 | ROS_INFO("gyro's initial data offest is %f, y is %f, z is %f", v.x, v.y, v.z);
37 | break;
38 | }
39 | }
40 |
41 |
--------------------------------------------------------------------------------