Port of http://dev.qu.tu-berlin.de/projects/sf-razor-9dof-ahrs to an mbed, tested with a 9DOF Sensor Stick, SEN-10724
Razor_AHRS.cpp
00001 /* This file is part of the Razor AHRS Firmware */ 00002 #include "Razor_AHRS.h" 00003 #include <math.h> 00004 00005 void IMU::read_sensors() { 00006 Read_Gyro(); // Read gyroscope 00007 Read_Accel(); // Read accelerometer 00008 Read_Magn(); // Read magnetometer 00009 } 00010 00011 // Read every sensor and record a time stamp 00012 // Init DCM with unfiltered orientation 00013 // TODO re-init global vars? 00014 void IMU::reset_sensor_fusion() { 00015 float temp0[3] = { accel[0], accel[1], accel[2] }; 00016 float temp1[3]; 00017 float temp2[3]; 00018 float xAxis[] = {1.0f, 0.0f, 0.0f}; 00019 00020 read_sensors(); 00021 timestamp = timer.read_ms(); 00022 00023 // GET PITCH 00024 // Using y-z-plane-component/x-component of gravity vector 00025 pitch = -atan2(accel[0], sqrt((double)(accel[1] * accel[1] + accel[2] * accel[2]))); 00026 00027 // GET ROLL 00028 // Compensate pitch of gravity vector 00029 00030 Vector_Cross_Product(temp1, temp0, xAxis); 00031 Vector_Cross_Product(temp2, xAxis, temp1); 00032 // Normally using x-z-plane-component/y-component of compensated gravity vector 00033 // roll = atan2(temp2[1], sqrt(temp2[0] * temp2[0] + temp2[2] * temp2[2])); 00034 // Since we compensated for pitch, x-z-plane-component equals z-component: 00035 roll = atan2(temp2[1], temp2[2]); 00036 00037 // GET YAW 00038 Compass_Heading(); 00039 yaw = MAG_Heading; 00040 00041 // Init rotation matrix 00042 init_rotation_matrix(DCM_Matrix, yaw, pitch, roll); 00043 } 00044 00045 // Apply calibration to raw sensor readings 00046 void IMU::compensate_sensor_errors() { 00047 // Compensate accelerometer error 00048 accel[0] = (accel[0] - ACCEL_X_OFFSET) * ACCEL_X_SCALE; 00049 accel[1] = (accel[1] - ACCEL_Y_OFFSET) * ACCEL_Y_SCALE; 00050 accel[2] = (accel[2] - ACCEL_Z_OFFSET) * ACCEL_Z_SCALE; 00051 00052 // Compensate magnetometer error 00053 magnetom[0] = (magnetom[0] - MAGN_X_OFFSET) * MAGN_X_SCALE; 00054 magnetom[1] = (magnetom[1] - MAGN_Y_OFFSET) * MAGN_Y_SCALE; 00055 magnetom[2] = (magnetom[2] - MAGN_Z_OFFSET) * MAGN_Z_SCALE; 00056 00057 // Compensate gyroscope error 00058 gyro[0] -= GYRO_AVERAGE_OFFSET_X; 00059 gyro[1] -= GYRO_AVERAGE_OFFSET_Y; 00060 gyro[2] -= GYRO_AVERAGE_OFFSET_Z; 00061 } 00062 00063 // Reset calibration session if reset_calibration_session_flag is set 00064 void IMU::check_reset_calibration_session() { 00065 // Raw sensor values have to be read already, but no error compensation applied 00066 00067 // Reset this calibration session? 00068 if (!reset_calibration_session_flag) return; 00069 00070 // Reset acc and mag calibration variables 00071 for (int i = 0; i < 3; i++) { 00072 accel_min[i] = accel_max[i] = accel[i]; 00073 magnetom_min[i] = magnetom_max[i] = magnetom[i]; 00074 } 00075 00076 // Reset gyro calibration variables 00077 gyro_num_samples = 0; // Reset gyro calibration averaging 00078 gyro_average[0] = gyro_average[1] = gyro_average[2] = 0; 00079 00080 reset_calibration_session_flag = false; 00081 } 00082 00083 void IMU::turn_output_stream_on() { 00084 output_stream_on = true; 00085 statusLed = 1; 00086 } 00087 00088 void IMU::turn_output_stream_off() { 00089 output_stream_on = false; 00090 statusLed = 0; 00091 } 00092 00093 // Blocks until another byte is available on serial port 00094 char IMU::readChar() { 00095 while (pc.rxBufferGetCount() < 1) { } // Block 00096 return pc.getc(); 00097 } 00098 00099 void IMU::readInput() { 00100 // Read incoming control messages 00101 if (pc.rxBufferGetCount() >= 2) { 00102 if (pc.getc() == '#') { // Start of new control message 00103 int command = pc.getc(); // Commands 00104 if (command == 'f') // request one output _f_rame 00105 output_single_on = true; 00106 else if (command == 's') { // _s_ynch request 00107 // Read ID 00108 char id[2]; 00109 id[0] = readChar(); 00110 id[1] = readChar(); 00111 00112 // Reply with synch message 00113 pc.printf("#SYNCH"); 00114 pc.putc(id[0]); 00115 pc.putc(id[1]); 00116 pc.printf(NEW_LINE); 00117 } else if (command == 'o') { // Set _o_utput mode 00118 char output_param = readChar(); 00119 if (output_param == 'n') { // Calibrate _n_ext sensor 00120 curr_calibration_sensor = (curr_calibration_sensor + 1) % 3; 00121 reset_calibration_session_flag = true; 00122 } else if (output_param == 't') // Output angles as _t_ext 00123 output_mode = OUTPUT__MODE_ANGLES_TEXT; 00124 else if (output_param == 'b') // Output angles in _b_inary form 00125 output_mode = OUTPUT__MODE_ANGLES_BINARY; 00126 else if (output_param == 'c') { // Go to _c_alibration mode 00127 output_mode = OUTPUT__MODE_CALIBRATE_SENSORS; 00128 reset_calibration_session_flag = true; 00129 } else if (output_param == 's') // Output _s_ensor values as text 00130 output_mode = OUTPUT__MODE_SENSORS_TEXT; 00131 else if (output_param == '0') { // Disable continuous streaming output 00132 turn_output_stream_off(); 00133 reset_calibration_session_flag = true; 00134 } else if (output_param == '1') { // Enable continuous streaming output 00135 reset_calibration_session_flag = true; 00136 turn_output_stream_on(); 00137 } else if (output_param == 'e') { // _e_rror output settings 00138 char error_param = readChar(); 00139 if (error_param == '0') output_errors = false; 00140 else if (error_param == '1') output_errors = true; 00141 else if (error_param == 'c') { // get error count 00142 pc.printf("#AMG-ERR:%d,%d,%d" NEW_LINE,num_accel_errors,num_magn_errors,num_gyro_errors); 00143 } 00144 } 00145 } 00146 #if OUTPUT__HAS_RN_BLUETOOTH == true 00147 // Read messages from bluetooth module 00148 // For this to work, the connect/disconnect message prefix of the module has to be set to "#". 00149 else if (command == 'C') // Bluetooth "#CONNECT" message (does the same as "#o1") 00150 turn_output_stream_on(); 00151 else if (command == 'D') // Bluetooth "#DISCONNECT" message (does the same as "#o0") 00152 turn_output_stream_off(); 00153 #endif // OUTPUT__HAS_RN_BLUETOOTH == true 00154 } else { } // Skip character 00155 } 00156 } 00157 00158 IMU::IMU() 00159 : gyro_num_samples(0) 00160 , yaw(0) 00161 , pitch(0) 00162 , roll(0) 00163 , MAG_Heading(0) 00164 , timestamp(0) 00165 , timestamp_old(0) 00166 , G_Dt(0) 00167 , output_mode(-1) // Select your startup output mode here!// Select your startup output mode here! 00168 , output_stream_on(false) 00169 , output_single_on(true) 00170 , curr_calibration_sensor(0) 00171 , reset_calibration_session_flag(true) 00172 , num_accel_errors(0) 00173 , num_magn_errors(0) 00174 , num_gyro_errors(0) 00175 , output_errors(true) 00176 , statusLed(LED1) 00177 , pc(USBTX, USBRX) 00178 , Wire(p28, p27) { 00179 00180 accel[0] = accel_min[0] = accel_max[0] = magnetom[0] = magnetom_min[0] = magnetom_max[0] = gyro[0] = gyro_average[0] = 0; 00181 accel[1] = accel_min[1] = accel_max[1] = magnetom[1] = magnetom_min[1] = magnetom_max[1] = gyro[1] = gyro_average[1] = 0; 00182 accel[2] = accel_min[2] = accel_max[2] = magnetom[2] = magnetom_min[2] = magnetom_max[2] = gyro[2] = gyro_average[2] = 0; 00183 00184 Accel_Vector[0] = Gyro_Vector[0] = Omega_Vector[0] = Omega_P[0] = Omega_I[0] = Omega[0] = errorRollPitch[0] = errorYaw[0] = 0; 00185 Accel_Vector[1] = Gyro_Vector[1] = Omega_Vector[1] = Omega_P[1] = Omega_I[1] = Omega[1] = errorRollPitch[1] = errorYaw[1] = 0; 00186 Accel_Vector[2] = Gyro_Vector[2] = Omega_Vector[2] = Omega_P[2] = Omega_I[2] = Omega[2] = errorRollPitch[2] = errorYaw[2] = 0; 00187 00188 DCM_Matrix[0][0] = 1; 00189 DCM_Matrix[0][1] = 0; 00190 DCM_Matrix[0][2] = 0; 00191 DCM_Matrix[1][0] = 0; 00192 DCM_Matrix[1][1] = 1; 00193 DCM_Matrix[1][2] = 0; 00194 DCM_Matrix[2][0] = 0; 00195 DCM_Matrix[2][1] = 0; 00196 DCM_Matrix[2][2] = 1; 00197 00198 Update_Matrix[0][0] = 0; 00199 Update_Matrix[0][1] = 1; 00200 Update_Matrix[0][2] = 2; 00201 Update_Matrix[1][0] = 3; 00202 Update_Matrix[1][1] = 4; 00203 Update_Matrix[1][2] = 5; 00204 Update_Matrix[2][0] = 6; 00205 Update_Matrix[2][1] = 7; 00206 Update_Matrix[2][2] = 8; 00207 00208 Temporary_Matrix[0][0] = 0; 00209 Temporary_Matrix[0][1] = 0; 00210 Temporary_Matrix[0][2] = 0; 00211 Temporary_Matrix[1][0] = 0; 00212 Temporary_Matrix[1][1] = 0; 00213 Temporary_Matrix[1][2] = 0; 00214 Temporary_Matrix[2][0] = 0; 00215 Temporary_Matrix[2][1] = 0; 00216 Temporary_Matrix[2][2] = 0; 00217 00218 timer.start(); 00219 // Init serial output 00220 pc.baud(OUTPUT__BAUD_RATE); 00221 00222 // Init status LED 00223 statusLed = 0; 00224 00225 // Init sensors 00226 wait_ms(50); // Give sensors enough time to start 00227 I2C_Init(); 00228 Accel_Init(); 00229 Magn_Init(); 00230 Gyro_Init(); 00231 00232 // Read sensors, init DCM algorithm 00233 wait_ms(20); // Give sensors enough time to collect data 00234 reset_sensor_fusion(); 00235 00236 // Init output 00237 #if (OUTPUT__HAS_RN_BLUETOOTH == true) || (OUTPUT__STARTUP_STREAM_ON == false) 00238 turn_output_stream_off(); 00239 #else 00240 turn_output_stream_on(); 00241 #endif 00242 } 00243 00244 // Main loop 00245 void IMU::loop() { 00246 timestamp_old = timestamp; 00247 timestamp = timer.read_ms(); 00248 if (timestamp > timestamp_old) 00249 G_Dt = (float) (timestamp - timestamp_old) / 1000.0f; // Real time of loop run. We use this on the DCM algorithm (gyro integration time) 00250 else 00251 G_Dt = 0; 00252 00253 // Update sensor readings 00254 read_sensors(); 00255 00256 if (output_mode == OUTPUT__MODE_CALIBRATE_SENSORS) { // We're in calibration mode 00257 check_reset_calibration_session(); // Check if this session needs a reset 00258 if (output_stream_on || output_single_on) 00259 output_calibration(curr_calibration_sensor); 00260 } else if (output_mode == OUTPUT__MODE_SENSORS_TEXT) { 00261 // Apply sensor calibration 00262 compensate_sensor_errors(); 00263 00264 if (output_stream_on || output_single_on) 00265 output_sensors(); 00266 } else if (output_mode == OUTPUT__MODE_ANGLES_TEXT || output_mode == OUTPUT__MODE_ANGLES_BINARY) { 00267 // Apply sensor calibration 00268 compensate_sensor_errors(); 00269 00270 // Run DCM algorithm 00271 Compass_Heading(); // Calculate magnetic heading 00272 Matrix_update(); 00273 Normalize(); 00274 Drift_correction(); 00275 Euler_angles(); 00276 00277 if (output_stream_on || output_single_on) 00278 output_angles(); 00279 } 00280 00281 output_single_on = false; 00282 00283 #if DEBUG__PRINT_LOOP_TIME == true 00284 pc.printf("loop time (ms) = %f" NEW_LINE, timer.read_ms() - timestamp); 00285 #endif 00286 } 00287 00288 int main() { 00289 IMU imu; 00290 00291 Ticker looper; 00292 looper.attach(&imu, &IMU::loop, (float)OUTPUT__DATA_INTERVAL / 1000.0f); // 50Hz update 00293 00294 while (1) 00295 { 00296 imu.readInput(); 00297 wait_ms(5); 00298 } 00299 00300 return 0; 00301 }
Generated on Sun Jul 17 2022 22:09:51 by 1.7.2