IMU Integration with GY-85 for Pitch/Roll Correction in Target Coordinates: PLFM-RADAR Deep Dive
The PLFM-RADAR system streams pitch and roll measurements from a GY-85 IMU over USB CDC and subtracts the platform attitude offset from raw radar angles in GUI_V5.py before plotting georeferenced targets on the map.
The NawfalMotii79/PLFM_RADAR repository implements a complete attitude-compensation pipeline that fuses GY-85 inertial measurements with phased-array radar detections. By integrating the IMU via I²C on the STM32 firmware and forwarding telemetry through a USB CDC interface, the PC-side Python GUI can apply real-time pitch and roll corrections to raw target coordinates. This article walks through the end-to-end data flow, the key source files, and the trigonometric correction algorithms used to align the antenna frame with the world frame.
How GY-85 IMU Data Flows Through the PLFM-RADAR Stack
The system moves attitude data from the physical sensor to the corrected map display through five distinct layers:
- Measurement — The STM32 MCU queries the GY-85 accelerometer, gyroscope, and magnetometer over I²C and computes pitch and roll in degrees.
- Transport — The STM32 packages the IMU readings into a binary USB CDC telemetry frame alongside GPS data.
- Acquisition — On the PC, the
STM32USBInterfaceclass in9_Firmware/9_3_GUI/v7/hardware.pyenumerates devices withlist_devices, opens the selected port withopen_device, and continuously callsread_datato ingest the binary stream. - Parsing — A background thread in
9_Firmware/9_3_GUI/GUI_V5.py(often referred to asgps_thread) invokes the telemetry parser defined in9_Firmware/9_3_GUI/radar_protocol.pyto extract fields such asimu_pitch,imu_roll,gps_lat, andgps_lon. - Correction & Display — The GUI applies
apply_pitch_correctionto the radar elevation angle and can apply an analogous roll correction to azimuth before forwarding the results to9_Firmware/9_3_GUI/v7/map_widget.py.
This chain ensures that every target plotted on the map reflects the true physical orientation of the antenna array.
Acquiring GY-85 Telemetry via USB CDC
The Python layer relies on hardware.py to manage the USB connection. Once the device is opened, a background thread in GUI_V5.py polls the interface for 64-byte packets. The following snippet illustrates how the thread retrieves the current pitch and roll values:
def process_gps_data(self):
"""Continuously read GPS + IMU telemetry from the STM32."""
while True:
raw = self.stm32_usb_interface.read_data(64) # 64-byte packet
if raw:
# Payload layout (binary, defined in STM32 firmware):
# [gps_lat, gps_lon, gps_alt, imu_pitch, imu_roll, ...]
packet = self.telemetry_parser.parse(raw)
self.current_pitch = packet.imu_pitch # degrees
self.current_roll = packet.imu_roll # degrees
# GPS fields are also stored for map centering
self.current_lat = packet.gps_lat
self.current_lon = packet.gps_lon
The telemetry_parser.parse(raw) call delegates the binary decoding to the packet format defined in radar_protocol.py, isolating the IMU fields from the GPS payload.
IMU-Based Pitch and Roll Correction for Target Coordinates
With fresh pitch and roll values available as instance variables, the GUI corrects each incoming radar frame before visualization.
Pitch Correction on Elevation
The currently implemented routine, apply_pitch_correction in GUI_V5.py, converts both the raw elevation angle and the IMU pitch to radians, subtracts the platform tilt, normalises the result to the [0, 180) degree range, and returns the corrected elevation:
def apply_pitch_correction(self, raw_elevation: float, pitch_angle: float) -> float:
"""
Adjust the measured elevation angle by the platform pitch.
Parameters
----------
raw_elevation : float
Elevation returned by the radar processing pipeline (degrees).
pitch_angle : float
Pitch reported by the GY-85 IMU (degrees, positive when the nose is up).
Returns
-------
float
Corrected elevation angle (degrees, wrapped to [0, 180)).
"""
raw_rad = math.radians(raw_elevation)
pitch_rad = math.radians(pitch_angle)
corrected_rad = raw_rad - pitch_rad
corrected_deg = math.degrees(corrected_rad) % 180
if corrected_deg < 0:
corrected_deg += 180
return corrected_deg
Roll Correction on Azimuth
Although the V5 GUI currently implements pitch correction only, extending the logic to roll follows the identical pattern. Replacing elevation with azimuth and pitch with roll produces apply_roll_correction, which normalises to [0, 360):
def apply_roll_correction(self, raw_azimuth: float, roll_angle: float) -> float:
"""
Adjust the measured azimuth angle by the platform roll.
Parameters
----------
raw_azimuth : float
Azimuth returned by the radar processing pipeline (degrees).
roll_angle : float
Roll reported by the GY-85 IMU (degrees, positive when right wing down).
Returns
-------
float
Corrected azimuth angle (degrees, wrapped to [0, 360)).
"""
raw_rad = math.radians(raw_azimuth)
roll_rad = math.radians(roll_angle)
corrected_rad = raw_rad - roll_rad
corrected_deg = math.degrees(corrected_rad) % 360
if corrected_deg < 0:
corrected_deg += 360
return corrected_deg
Integration in the Rendering Pipeline
The corrected angles feed directly into the map widget. Inside the radar-frame processing loop, the GUI calls both routines before updating the target plot:
elev_raw = radar_frame.elevation # degrees from FPGA processing
azim_raw = radar_frame.azimuth # degrees from FPGA processing
# Apply IMU-based attitude compensation
elev_corr = self.apply_pitch_correction(elev_raw, self.current_pitch)
azim_corr = self.apply_roll_correction(azim_raw, self.current_roll)
# Feed corrected angles to the map widget
self.map_widget.update_target(elev_corr, azim_corr,
lat=self.current_lat,
lon=self.current_lon)
Why Pitch and Roll Compensation Matters for Phased-Array Radar
Without real-time attitude correction, the PLFM-RADAR pipeline suffers from three systematic error sources:
- Beam-steering errors — The phased-array beam is defined in the antenna frame; any tilt of the platform introduces angular offsets that skew the perceived elevation and azimuth.
- Geolocation drift — Uncorrected attitude propagates into GPS-tagged target positions, producing larger errors for high-elevation or low-SNR detections.
- Dynamic platform instability — Mobile deployments such as UAVs or ground vehicles experience rapid pitch and roll changes; continuous compensation keeps the world-frame mapping consistent across frames.
Key Source Files for IMU Integration
Attitude compensation in PLFM-RADAR spans both firmware and PC-side Python code. The files responsible for the IMU pipeline include:
README.md— Describes the hardware architecture, including the GY-85 IMU and GPS integration strategy.9_Firmware/9_3_GUI/v7/hardware.py— Implements theSTM32USBInterfaceclass that enumerates, opens, and reads the USB CDC stream from the STM32.9_Firmware/9_3_GUI/GUI_V5.py— Hostsapply_pitch_correction, the background GPS/IMU thread, and the logic that integrates corrected angles into the map view.9_Firmware/9_3_GUI/radar_protocol.py— Defines the binary packet format carrying GPS and IMU data from the STM32 to the PC.9_Firmware/9_3_GUI/v7/map_widget.py— Renders targets on a geographical map using the corrected elevation, azimuth, and GPS fix.docs/architecture.html— Provides the system-architecture diagram illustrating the end-to-end IMU data path.
Summary
- The GY-85 IMU measures pitch and roll over I²C on the STM32 and forwards them inside a USB CDC telemetry frame.
hardware.pyexposes theSTM32USBInterface.read_data()method that delivers 64-byte binary packets to the PC.GUI_V5.pyparses the stream and storescurrent_pitchandcurrent_rollfor every incoming radar frame.apply_pitch_correctionsubtracts the platform pitch from the raw elevation angle and wraps the result to[0, 180).- Roll correction follows the same trigonometric pattern on the azimuth axis, wrapping to
[0, 360). - Corrected coordinates are passed to
map_widget.pyfor georeferenced display that accounts for real-time platform attitude.
Frequently Asked Questions
What IMU sensor does the PLFM-RADAR system use for attitude sensing?
The system uses the GY-85, an I²C-based module that combines an accelerometer, gyroscope, and magnetometer. The STM32 firmware reads this sensor to compute real-time pitch and roll angles that are streamed to the PC-side GUI.
How does IMU data travel from the STM32 to the Python application?
The STM32 packages IMU and GPS readings into a binary telemetry frame and transmits them over a USB CDC connection. On the PC, the STM32USBInterface class in hardware.py enumerates the device and continuously calls read_data(64) to ingest the stream.
Which function in the PLFM-RADAR codebase corrects the radar elevation for platform pitch?
The apply_pitch_correction method inside 9_Firmware/9_3_GUI/GUI_V5.py performs the correction. It converts the raw elevation and IMU pitch to radians, subtracts the pitch offset, and normalises the output to the [0, 180) degree range.
Can the existing code correct for roll in addition to pitch?
Yes. While the V5 GUI currently implements pitch correction only, the repository demonstrates that roll correction can be added analogously. The apply_roll_correction routine uses the same subtraction logic on the azimuth angle and wraps the result to [0, 360), making the extension straightforward.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →