How can one input heading data from sources other than magnetometer?

I thank the ArduBoat group and all the greater ardupilot community for your exemplary intelligence-sharing and cooperative spirit!

Working with Pixhawk autopilots running APMrover2 aboard small Autonomous Surface Vehicles, my question is about the possibility of providing alternative heading data, for example from a dual-antenna phase-ambiguity-resolution GPS system, instead of using only on-board and external magnetometers.

I’ve made, and am continuing to make, an earnest effort to grasp the body of code that comprises APMrover2 and its many libraries and references, but so far it has been so formidable that I’ve taken the alternative tack of trying to simulate an external magnetometer (using an Arduino as a sort of UART NMEA translator and I2C imposter). By brute force I have learned a lot about I2C in general and about the HMC5883L magnetometer IC in particular, but have bogged down at the point of trying to figure out what the ardupilot code expects to get back from the external compass (in this case device “30”) after addressing register 10 (ID register A, the first of three).

I’d be interested to hear any thoughts on alternative heading input …or on my almost ridiculous exercise of trying to fool the Pixhawk and ardupilot by synthesis of phony X and Y magnetic vectors that always sum to the set declination, given ancillary heading input.

Hi Pete. Are you simply saying you would rather use the GPS for heading then the compass? If so just set all the COMPASS_USE# parameters to 0 and it will use the GPS instead of the compass. Just don’t try to drive backwards as it may get very confused.
Let us know how you go.

Thanks, Grant.

Hi Grant, it’s a pleasure to hear from you. No, what I’d like to be able to do is to use heading data from dual-antenna GPS systems such as this or this, which calculate heading by resolving carrier phase differences for all the visible space vehicles, rather than by using vectors of positions between antennas or positions between fixes.

I guess I’m interested more generally in the possibility of inputting heading data via any UART port (perhaps even NMEA HDT from a gyro) instead of using heading data from magnetometers, whether they be the 303H (I guess it is) aboard the Pixhawk, or the HMC5883L that is in the uBlox GPS/Compass that I am fiddling with.

Kind thanks to you,

I think your making it harder for yourself then it needs to be. Your talking L1/L2 GPS receivers which we already support in ArduPilot and by doing what I described above you will take advantage of their more accurate heading information. If you search for the make/model your planning to use along with ArduPilot you will probably find how to connect one to ArduPilot.

However, if you need to write your own driver as you’ve asked have a look at ardupilot/libraries/AP_GPS where all the GPS driver code is. Just copy one of those and modified as needed.

Alternatively you can always set GPS_TYPE to 5 which is NMEA and assuming you have the baud rate settings correct plug your advanced GPS into that port although this would incur the limitations inherent with the NMEA protocol.

Let us know how you go.

Thanks, Grant.

You’re probably right about me making it harder for myself than it needs to be, ha ha!

But I did start down the path that you suggested quite a while back. In fact I perused AP_GPS_xxxx library code (albeit not with C++ expert eye) and came to the conclusion that only NMEA VTG or RMC sentences were parsed for “heading” data, not HDT sentences.

If I am correct about that, the problem is that VTG (“Vessel Track Good”, I think that stands for) and RMC contain only direction vector data derived from successive position fixes, NOT true heading data derived from phase ambiguity resolution solutions calculated using signals from two separated antennas.

Dual-antenna GPS (or GNSS) equipment that I am familiar with, for example the Hemisphere VS101 that friends have lent me to experiment with, typically output NMEA HDT (along with GGA, VTG, and RMC). HDT was, I think, originally supposed to be a ship gyro sentence; in any case it is the NMEA sentence containing the true heading data that I would like to use for autopilot navigation.

But you’ve given me another odd work-around idea (since I’m still working up to reasonable ardupilot code modification or addition, and since my efforts to emulate an external I2C magnetometer have not been successful): the idea is to “translate” my dual-antenna GPS data before input to Pixhawk, putting the HDT true heading data into the vessel-track field of a sort of imposter VTG sentence.

I’ve asked our GPS guru to provide you some feedback.
Thanks, Grant.

So there are a couple of things with this. This is generally referred to as moving baseline, and is definitely on the future roadmap at some point. However what you are really getting out of this is the heading not the track over the ground. (That information is of course available as well, but not really what you are looking for here).

As far as ingesting the data, there isn’t any option for this at the moment. There aren’t any places to input the external heading information like this, and still fuse it with the onboard sensors. Unfortunately your proposal to use the VTG message won’t work as that message will simply be decoded as course over ground, rather then heading. This can start to lead to bad fusion of the data if the onboard sensors are measuring a different course because of a different in heading. (This would also give you exactly wrong information if you were ever drifting backwards).

At the moment I think you would need to get advice from @priseborough or @tridge to look at how to integrate the heading information. I’d say that adding a driver or modifying the NMEA one to understand the additional sentence is probably the easiest part, its handling the data at the AHRS level that needs to be addressed before making the data available is of use.

Thank you WS, and Grant, for your helpful attention to this thread.

Yes, I see the problem with injecting true heading (HDT) data in the place of course made good (VTG). Am I correct that, if you set COMPASS_USE = 0, so that the autopilot uses only GPS, then there is no “heading” entity, per se, used in the autopilot algorithm at all?

I still wonder why you could not use true heading data in the same way as magnetic compass heading, as long as you hacked it correctly, so to speak. (I got bogged down in I2C slave simulation when I went down that path.)

The question of VTG vis-à-vis HDT actually is the tip of a larger niggling question that I’ve had about autopilot algorithms: how importantly does heading information, as contrasted to course-made-good information, figure in an autopilot algorithm? After all, an autopilot program is by its nature an adaptive algorithm (quintessential cybernetics by the root of the word), meaning to say that it constantly corrects its response (outputs including steering and throttle) according to their effect (inputs including position, VTG or HDT, etc.). It would seem that the prescribed vehicle track vector is the ultimate aim and that to some extent it shouldn’t matter which direction, exactly, the autopilot thinks the vehicle is pointing, as long as it turns in the right direction to get back on the right track.

In fact I’ve wondered whether an aquatic rover autopilot algorithm needs to be significantly different than a land rover autopilot algorithm, in light of the greater possibility of crabbing due to current or wind.

Experimentally I’ve found that the autopilot adapts fairly well to wind and current except in turns, but I’ve found that if the compasses are not well-calibrated the autopilot can scarcely follow a mission course. Just to pursue the academic question of the importance of accurate heading, maybe I will try setting the local magnetic declination off by increments, to get a sense of autopilot adaptability…

As an advance apology for tardy acknowledgement of any feedback, my wife and I are going “walkabout” in the nearby mountains for a week.

On-water trials in September using the aforementioned Hemisphere VS-101 for position (but not for heading), while alternatively using Pixhawk on-board magnetometer for heading or using no heading at all (COMPASS_USE = 0) convinced me that the autopilot does not do well with GPS-derived CMG alone; heading data seems to be essential.

At the same time, trials alternatively enabling or disabling the Kalman filter (EK2_ENABLE = 0; I didn’t know to try AHRS_EKF_TYPE = 0) convinced me that, in the case of my small catamaran, the Extended Kalman Filter does little to improve tracking. This could perhaps be in part because the GPS used for position incorporates its own filter for its transmitted position (as well as for its transmitted heading and roll or pitch). But I wonder if the multi-parameter EKF/EK2 is really appropriate for a relatively slow-moving boat, differing from a ground-surface rover or a 3-D aircraft? Maybe there ought to be a simpler arduboat code fork…

Anyway, I’ve undertaken the daunting challenge of learning enough C++ in general and ardupilot code in particular to at least “ingest” HDT data instead of the 3-D magnetic field vector data.

I’m trying to work in a Windows environment using Eclipse and the px4 toolchain. Since the “PX4 Software Download” part of the toolchain does not seem to fetch all of the essential source code, I assembled a resource tree like this, but when I attempt to build (as yet unmodified) APMrover2 px4-v2, it fails quite quickly with this compiler/linker error report.

I guess I’m drifting out of arduboat-specific topics into developer questions, but wanted to lay out “the rest of the story” as it presently stands for me, on the topic of improved heading input options for water-borne Autonomous Surface Vehicles), with hopes of helpful feedback!

I use Linux, not windows but looks like you haven’t checked out your git repositories correctly. Did you do the following?

git clone
cd ardupilot
git submodule update --init --recursive

Lots of info here:

Thanks, Grant.

@gmorph, kind thanks to you, Grant: your suggestions led to a bit of a breakthrough for me; for the first time I succeeded in building APMrover2.px4 from source.

I did study (again) the two helpful pages that you just suggested, but essentially the “git clone” command, issued in a MINGW32 console window from my c:/pixhawk_toolchain/Firmware directory, evidently was key to obtaining the required resources in the right places so that “git submodule update --init --recursive” then completed the process successfully enough that “make px4-v2” (in the MINGW32 console from AMProver2 directory) carried through to produce the APMrover2-v2.px4 executable. When uploaded to the Pixhawk via Mission Planner, it ran as expected.

I started a sort of fork discussion in the “General” category on this general topic since I regressed from the original question concerning alternative heading input.

As said in the other discussion, I’m convinced to take up Linux as well, but for completeness I’ll say that what I did so far was in a Windows XP OS within the aforementioned MINGW32 “PX4 Console” that comes as part of the Pixhawk toolchain for Windows (whose “PX4 Software Download” tool appears not to accomplish what your three lines do accomplish!)

Now I have a Rover question that may be an Eclipse question in disguise:

With an ardupilot project set up properly, why would one see the untouched source code riddled with Eclipse IDE - detected “bugs”, for example line 317 of APMrover2.cpp,

mavlink_system.sysid = g.sysid_this_mav;

for which Eclipse says “Field ‘sysid’ could not be resolved”, or line 732 of AP_Compass.cpp,


for which the function ‘radians’ could not be resolved?

Many thanks,

Is that error happening when you compile in Eclipse or is Eclipse being helpful and pointing out those errors. If its the former it will be something to do with the linking I’m gunna guess. mavlink_system is defined as an extern in GCS_Mavlink library so if the linker can’t find it your going to have that kind of error. So when you build waf knows where everything is but Eclipse does not.

Thanks, Grant.

Thank you again, Grant. I’d say the answer is the latter: Eclipse is being helpful and pointing out what it reckons are errors in the project code. The given example “bug” messages appeared on mouse-over certain Eclipse-perceived problem spots in the source code, but the specific examples with line numbers are probably unhelpful since ardupilot code is ever-changing, and I now have a whole new set of Eclipse-perceived problem spots throughout the ardupilot project, having fetched all the latest and greatest since a couple days ago sigh

In my past (limited) code-writing, I’d used simple text editors, but I decided to “get with the program”, learn to use Eclipse and avail myself of its potentially helpful capabilities, such as that of displaying the external origin of an expression on mouse-over. But if Eclipse doesn’t know where everything is, I’m kind of stuck with manual searching, trying to construct a mental map of the formidable body of code that comprises ardupilot.

Incidentally, the “CTD Build Console” pane in Eclipse indicates that it uses “make px4-v2” to initiate build, just as I had previously done (with success) from the bare MINGW32 console window. I don’t see that waf is invoked at all, but I understand little about this, even as I attentively watch verbose build progress messages!

Near the end of Eclipse build messages, I see:

fatal: not a git repository: c:/pixhawk_toolchain/Firmware/ardupilot/modules/PX4NuttX/nuttx//…/…/.git/modules/modules/PX4NuttX
Failed to get nuttx hash

I don’t even understand the odd double slashes, or multiple jumps back up, in the path expression …yet it must not have been fatal in the bigger picture, for the final message is the following:

PX4 APMrover2 Firmware is in APMrover2-v2.px4

Anyway, I guess I’ll carry on with Eclipse, with the original aim to accommodate NMEA HDT true heading input to be used instead of magnetometer for rover steering.


@gmorph and all interested,

I did take up trying to work with ardupilot code using Linux (and Notepad++ instead of Eclipse, via the “wine” virtual Windows environment), but alas, breadth of effort has not really afforded me greater depth of understanding.

Still wishing to do without magnetometer compass for heading, and to use heading as NMEA HDT from a dual-antenna GPS, I wonder what approach would be most in conformance with present ardupilot code development:

  • creation of a new class for such a “sensor”, even though, in the cases I would be working with, the data would be provided by the same device as that which provides position?

  • amending Compass class?

  • amending AP_GPS class?

To be honest I’m about to start from scratch because I’m drowning in the sea of ardupilot code for all possible platforms, while a boat that is equipped with a survey-grade position- and heading instrument can probably do without EKF, not to mention a host of sensors pertinent to 3-D flight with rapid 6-degree-of-freedom motion/attitude that might affect autopilot course.

Anyway, I invite fresh thought and suggestions!

ArduPilot already supports running without a compass. Make sure your using the lastest Rover beta or running from master. If you set all the COMPASS_USE parameters to 0 it will still log that compass information but will only use the GPS for heading.

NOW, unless you have a very expensive multi-antenna GPS which can do phase calculations to determine YAW when stationary the Rover will have no idea of its orientation when it first starts. It assumes its facing north when it first starts and then once it starts moving it eventually can figure out its yaw from the GPS and start navigating much better.

If your trying to integrate one of these fancy GPS units you want to create a new AP_GPS driver for it and have it have an API that says it supports stationary yaw and then the EKF can interrogate this and incorporate it into its calculation.

Hope that helps a bit.

Thanks, Grant.

Thank you again, Grant. I had done some trials in early September with all COMPASS-USE parameters set to zero and observed that ardupilot did not work well at all for my skid-steer catamaran arduboat using GPS alone.

I am indeed specifically interested in integrating heading data NOT from magnetic compass. I have (lent to me) a Hemisphere VS101 as mentioned earlier in the thread, which does calculate a static heading based on phase-ambiguity resolution with a known antenna separation and orientation with respect to vessel head. It has internal sensors and filters such that I’d like the autopilot to use its position and heading data (serial NMEA GGA and HDT) more or less directly.

Thanks for the thoughts along the line of creating a new AP_GPS driver and the part about it having an API that says it supports stationary yaw. I had been worried that it might be impractical to sever the connection between “yaw” and “compass” in the established ardupilot framework.


Hi all,

It may be that this proposition belongs in the “Development Team” category of discussion, but it is sequential to this thread.

I’d like to propose some sponsorship of development of the desired capability to use NMEA HDT heading input instead of magnetometer heading input. As elaborated previously, this would be for an “ArduBoat” (small skid-steer Autonomous Surface Vehicle) equipped with dual-antenna GPS heading- and position instrumentation with its own INS- and filtering aspects. Sad to say, I haven’t been able to catch up with the ArduPilot code complexity enough to be able to enjoy experimenting with substantial desired modifications myself.


1 Like

Hi Pete. I’ve sent you a message to discuss this further.

Thanks, Grant.

Hi Guys,

I am also interested in this topic. If you can include me in the discussion.


Hi all,

As said way back in this thread, I’m very interested in the question of the essential role of heading data in the whole autopilot algorithm, as well as the question of the essential role of EKF for the case of a water-borne rover which might employ position and heading data that are already Kalman-filtered.

So I did some test runs using 3.2.0-dev firmware, for the four cases:

  1. Using EK3 and compass (Pixhawk internal magnetometer);
  2. Using compass but no EK3;
  3. Using neither compass nor EK3;
  4. Using EK3 but no compass.

Position input was from uBlox GPS/compass but its magnetometer was not enabled.

I used 3.2.0-dev firmware because I found that there was apparently no “target heading” for the autopilot with EKF disabled in 3.1.1 firmware!

To disable EKF I would set AHRS_EKF_USE=0 as well as EK3_ENABLE=0. To disable compass I would set COMPASS_USE=0.

Coarsely-viewed, the autopilot performed remarkably well without EKF and/or without compass, as illustrated by the tracks for

  1. EK3 and Compass enabled.
  2. EK3 disabled, Compass enabled;
  3. EK3 disabled, Compass disabled;
  4. EK3 enabled, Compass disabled.

Graphs of RCout for the port (ch1) and stbd (ch2) thruster motors of this skid-steer ArduBoat better illustrate differences in the smoothness of control:

  1. EK3 and Compass enabled;
  2. EK3 disabled, Compass enabled;
  3. EK3 disabled, Compass disabled;
  4. EK3 enabled, Compass disabled.

It seems noteworthy that RCout is smoothest when both EK3 and Compass are enabled

Log files for the four cases are posted:

  1. EK3 and Compass enabled;
  2. EK3 disabled, Compass enabled;
  3. EK3 disabled, Compass disabled;
  4. EK3 enabled, Compass disabled.

I welcome any thoughts on what is essential vs. what is superfluous for this vehicle, or any thoughts on what may be happening “behind the scenes” (since the source code is very difficult for me to grasp as a whole).

Thanks to all,