AHRS: pure Python attitude estimators from Madgwick to unscented Kalman
Attitude and Heading Reference Systems in Python
At a glance
- What is it?
- AHRS is a pure Python package of attitude and heading estimation algorithms that turns accelerometer, gyroscope and magnetometer readings into quaternions, covering eighteen estimators from Madgwick and Mahony to TRIAD and an unscented Kalman filter. Its author states plainly that performance is not the goal, and the package depends on nothing but NumPy.
- Who is it for?
- AHRS suits a robotics or wearable engineer who needs a readable, dependency-light reference implementation of an attitude estimator, a student who wants to follow the original equations alongside the code, or a test harness that needs synthetic MARG sensor data.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 48 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Eighteen estimators behind one call signature
AHRS is a collection of functions and algorithms in pure Python used to estimate the orientation of mobile systems. The name comes from the aircraft instrument, a set of orthogonal sensors giving attitude information, and the readme notes that the field has expanded well beyond aircraft to wearables, automated transportation and anything else in motion.
The estimator table is the core of the package. Eighteen algorithms are implemented, and the table records exactly which sensors each one needs: AQUA, Complementary, FQA, Madgwick, Mahony, Tilt and UKF all take the gyroscope with an optional magnetometer, while Davenport's, FAMC, FLAE, Fourati, OLEQ, QUEST, ROLEQ, SAAM and TRIAD use the accelerometer and magnetometer without a gyroscope. Integration takes the gyroscope alone. The EKF, FKF and ROLEQ rows take all three sensors.
That table is the first thing to read when choosing an estimator, because it encodes a real physical constraint. A magnetometer is optional in several estimators, and an estimator without magnetometer input cannot correct for heading in the way the three-sensor ones can.
The usage is uniform. You pass the data to the estimator and it produces quaternions:
>>> attitude = ahrs.filters.Madgwick(acc=acc_data, gyr=gyro_data)
>>> attitude.Q.shape
(6959, 4)The .Q attribute is the quaternion array, and the shape reflects the number of samples and the four components of a quaternion. Some algorithms accept extra parameters for finer tuning, and the documentation for each one describes them.
Wahba's problem and dead reckoning are the two foundations
The estimators are grouped under two underlying solutions, and knowing which one a given algorithm uses tells you how it behaves.
Wahba's problem is the attitude determination problem, which finds a rotation matrix between two coordinate systems. The way to think about it, as the readme puts it, is that measurement vectors are compared against reference vectors and the difference is the rotation. The solution compares accelerometers and magnetometers against the gravitational and geomagnetic vectors respectively, so it is a purely geometric approach that does not integrate anything over time.
Dead reckoning is the other foundation. It integrates the measured local angular velocity to increasingly estimate the angular position of the sensor, which is an open loop integration and therefore drifts over time.
Most real estimators mix the two. A gyroscope integration provides smooth short term attitude, while a magnetometer or accelerometer measurement supplies the absolute reference that stops the integration from wandering. An estimator that uses only a gyroscope, such as Integration, drifts without bound, and one that uses only accelerometers and magnetometers, such as TRIAD or QUEST, is noisier but does not accumulate error.
The implementation note matters for anyone using this to learn: all estimators are refactored to be consistent with the corresponding articles describing them, and they carry in-code references to the equations, so you can follow the original papers alongside the code.
Installing needs NumPy and nothing else
The dependency story is a deliberate design decision and the readme explains it. AHRS depends merely on NumPy, and more packages are avoided to reduce its third-party dependency.
For a stable release, the install is a single command:
pip install ahrsFor the latest version straight from the repository, which the readme calls the most recommended method, clone and install locally:
git clone https://github.com/Mayitzin/ahrs.git
cd ahrs
python -m pip install .The package is compatible with Python 3.9 and newer, and the project metadata lists support for 3.9 through 3.12, with classifiers for developers, education and science research audiences and topics covering physics, GIS and embedded systems.
The build itself is Hatch. The pyproject.toml uses the hatchling backend and takes the version from the package source with a pattern that reads the version assignment out of ahrs/__init__.py, so the version lives in one place rather than in the project file. Bumping it is a single command:
hatch version <major, minor, patch>The development extras are pytest, flake8 and black, the docs extras are sphinx with the bibtex extension and the pydata theme, and there is a build extra for hatch and hatchling.
The top-level layout shows a normal research package: ahrs/ for the code, tests/, docs/, notebooks/, tools/, a CHANGELOG.md, a CITATION.cff for citation metadata and a .readthedocs.yml for the documentation build.
Version 0.4 reorganised the package and changed the core types
The 0.4 release is a restructuring rather than a feature drop, and it is worth understanding before reading older code or examples.
The headline changes are the new attitude estimators, with an Unscented Kalman Filter and a Fast Kalman Filter added, each with documentation and unit tests, and the claim that all estimators are now tested. The new Sensors class generates synthetic MARG data from any given orientation, which is the piece that makes the package useful as a test harness rather than only as an estimator.
Two type changes affect user code. The Quaternion and QuaternionArray classes are now derived from numpy.ndarray, so they can be used as regular NumPy arrays with all their methods and properties, and the metrics were improved to compute faster when vectorization is possible. Every function and object is type hinted, and a batch of new constant values is accessible from the top level of the package.
The quaternion class also gained methods that fill gaps and derive new quantities: slerp_nan() fills NaNs with interpolated values, angular_velocities() computes angular velocities from a sequence of quaternions, from_DCM() and the DCM parameter take a direction cosine matrix, from_rpy() produces quaternions from roll, pitch and yaw angles, and to_array() converts to a regular NumPy array.
The geodesy work moved into its own submodule, which now holds the world models, transformations and conversions between coordinate systems, along with the geodetic calculation tools. The CHANGELOG.md at the root carries the full list of changes.
The release cadence here is worth noting. v0.4.0 was published on 2025-10-13, while v0.3.1 dates from 2021-09-29 and v0.3.0 from 2021-02-03. The project was quiet for four years and then reorganised, and the last push was on 2026-09-06.
Prototyping is the goal, and the readme says so plainly
The package states its own priorities without hedging. The focus is fast prototyping, education, testing and modularity, and performance is explicitly not the main goal. The readme points to endless resources in C, C++ or Fortran for optimised implementations, and to my own judgement that a user who needs speed should not be reading this page.
That honesty makes the trade-off easy to place. In pure Python with NumPy, an estimator is a loop over samples doing small matrix operations, and on a robot with an inertial unit at 1000 Hz that is not free. But the alternative libraries are opaque, and the AHRS estimators are short enough to read in one sitting, with equations in the comments.
For testing and simulation, the value is higher still. The Sensors class produces gyroscope, accelerometer and magnetometer readings plus the true quaternion for any orientation you specify, which means you can generate a trajectory, run it through an estimator and compare the output against ground truth. That is how you find out whether an estimator suits your sensor noise, and you cannot easily do that with a black box.
The synthetic data also exposes the estimator differences in a way a table cannot. Feed a magnetometer-free estimator a trajectory with yaw and watch it diverge, feed a magnetometer-equipped one and watch it hold heading. The package gives you the means to run that experiment, which is the strongest argument for it.
For the maths itself, the topics attached to the repository name the pieces the code deals with: quaternions, orientations, gravity, geomagnetism, WGS84 and the world magnetic model, alongside Madgwick and the attitude estimators themselves.
Editorial conclusion
AHRS suits a robotics or wearable engineer who needs a readable, dependency-light reference implementation of an attitude estimator, a student who wants to follow the original equations alongside the code, or a test harness that needs synthetic MARG sensor data. It does not suit a production robot that needs to fuse a thousand inertial units per second, because the package states that performance is not the main goal and points to C, C++ or Fortran for optimised implementations. Before building on it, read the CHANGELOG for the 0.4 restructuring, check whether the estimator you need accepts the magnetometer data you actually have, and pin the version, since 0.3.1 and 0.4.0 differ substantially in layout.
Frequently asked questions
What is AHRS and what is it for?
AHRS is a collection of functions and algorithms in pure Python for estimating the orientation of mobile systems from accelerometer, gyroscope and magnetometer data, producing quaternions. The package names fast prototyping, education, testing and modularity as its focus, and states that performance is not the main goal.
How do I install AHRS?
For a stable release use pip install ahrs, or for the latest version clone the repository and run python -m pip install . inside the checkout. Python 3.9 or newer is required, and NumPy is the only runtime dependency.
Which attitude estimators does AHRS include?
Eighteen are implemented, including AQUA, Complementary, Davenport's, EKF, FAMC, FKF, FLAE, Fourati, FQA, Integration, Madgwick, Mahony, OLEQ, QUEST, ROLEQ, SAAM, Tilt, TRIAD and UKF. The table in the documentation records which of the gyroscope, accelerometer and magnetometer each one requires.
How do I estimate orientation with AHRS?
Pass the sensor data to the estimator you want and read the quaternion output, for example attitude = ahrs.filters.Madgwick(acc=acc_data, gyr=gyro_data) with the result in the Q attribute. Some estimators accept further parameters for finer tuning, which their own documentation describes.
What changed in AHRS version 0.4?
Version 0.4 added the Unscented Kalman Filter and Fast Kalman Filter estimators, a Sensors class for synthetic MARG data, a geodesy submodule, and new quaternion methods including slerp_nan, angular_velocities, from_DCM and from_rpy. The Quaternion and QuaternionArray classes are now derived from numpy.ndarray, and all functions are type hinted.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mayitzin-ahrs)Was this useful?