I am using MPU9250 from adafruit (https://learn.adafruit.com/adafruit-tdk-invensense-icm-20948-9-dof-imu) This sensor is outputting raw data of magnetometer. I was able to calibrate data from to where I get calibration number by x offset = (max_x+min_x)/2 y offset = (max_y+min_y)/2 z offset = (max_z+min_z)/2 and I subtract offset to raw data. However, I used calculation from (https://cdn-shop.adafruit.com/datasheets/AN203_Compass_Heading_Using_Magnetometers.pdf) Direction (y>0) = 90 - [arcTAN(x/y)]*180/PI Direction (y<0) = 270 - [arcTAN(x/y)]*180/PI Direction (y=0, x<0) = 180.0 Direction (y=0, x>0) = 0.0 However, direction is alway off and incorrect. for example, when I amd heaind 0 degree (directly to the north) x 15.749999999999998 y -0.9749999999999996 z 47.4 and calculation returns 40.44 degrees. When I am heading 90 degree (East) x 4.049999999999999 y -14.475 z 46.949999999999996 heading is 84.906 When I am heading 270 degree (West) x 1.3499999999999996 y 7.125 z 45.0 Heading is 75.774 Degree When I am heading 180 degree (to South) x -11.55 y -2.9250000000000007 z 46.5 and calculation result is 128.99 degrees. Does my methodology to calculate heading is wrong? Or does my calibration need more steps? [Edit] Magnetometer calibration: def calibrationDataCollection(): i2c = board.I2C() # uses board.SCL and board.SDA icm = adafruit_icm20x.ICM20948(i2c) lx = kbhit.lxTerm() lx.start() x=[] y=[] z=[] while True: #print(icm.magnetic) if lx.kbhit(): c = lx.getch() c_ord = ord(c) if c_ord == 32: # Spacebar print("\nStop") break x.append(icm.magnetic[0]) y.append(icm.magnetic[1]) z.append(icm.magnetic[2]) print((max(x)+min(x))/2) print((max(y)+min(y))/2) print((max(z)+min(z))/2) print() time.sleep(0.1) comp_df = pd.DataFrame({"x": x, "y": y, "z": z}) comp_df["offset_x"] = (comp_df.x.max()+comp_df.x.min())/2 comp_df["offset_y"] = (comp_df.y.max()+comp_df.y.min())/2 comp_df["offset_z"] = (comp_df.z.max()+comp_df.z.min())/2 toCSV(analysisPath, "Calibration_2.csv", comp_df) I calibrate the sensor by moving 8 shape in 3D (like infinite loop shape) Then, whenever offset values become steady, I stop calibration and convert it to csv file. Then, use offset value, I am getting calibrated value def Compass_2(): i2c = board.I2C() # uses board.SCL and board.SDA icm = adafruit_icm20x.ICM20948(i2c) while True: x = icm.magnetic[0]- 12.15 #(these are my calibration offsets) y = -icm.magnetic[1]+ 6.225 z = icm.magnetic[2]+ 14.4 print("x ", x) print("y ", y) print("z ", z) heading = magToHeading2([x,y,z]) print("heading: ", heading) time.sleep(0.5) The heading(magToHeading2) is calculated by def magToHeading2(magnetic): heading = -1 if magnetic[1] > 0: heading = 90-math.atan(magnetic[0]/magnetic[1]) * 180 / math.pi elif magnetic[1] < 0: heading = 270-math.atan(magnetic[0]/magnetic[1]) * 180 / math.pi elif magnetic[1] == 0: if magnetic[0] < 0: heading = 180.0 else: heading = 0.0 return round(heading,3) Continue reading...