10

I have been working on a small compass app the past couple of days, and have gotten the code up and running, but it seems that the compass reading is not accurate. After calibrating both phones, my first test that i found this in what that i simply held the phone against and flat surface looked at the reading then flipped it horizontally and put it against the same flat surface (a 180* turn) and the value did not change 180* it was closer to 240*.

I then tested the reading vs a compass, at times the reading seemed to be close but at other points it was more than 50* off. I even tried to put my phone and compass on the floor to keep the compasses away from any magnetic interference with the same results (note i also keep the compass and phone apart keeping them in the same direction by lining up with edges of a book).

I then put the sample application on another phone (first was nexus S, second was Motorola droid 1). Between the two phones the difference ranges from being equal at some points but at most points being between 50 and 15 degrees off.

I have looked through the documentation and also looked through many different forum posts and I do not see anyone with the same results. There may be some small error in my code that is causing my reading to come out incorrectly, or some documented bug that I am not seeing.

Any insight or suggestions would be greatly appreciated!!

Heres my on sensor changed code in my SensorEventListener class

public void onSensorChanged(SensorEvent event)
    {
        // If the sensor data is unreliable return
        if (event.accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE)
        {
            Toast.makeText(main.this, "Sensor Status Unreliable",Toast.LENGTH_SHORT).show();
        }





        // Gets the value of the sensor that has been changed
        switch (event.sensor.getType())
        {
        case Sensor.TYPE_ACCELEROMETER:
            m_vfgravity = event.values.clone();
            break;
        case Sensor.TYPE_MAGNETIC_FIELD:
            m_vfgeomag = event.values.clone();
            break;
        }

        if (m_vfgravity != null && m_vfgeomag != null)
        {
            if(SensorManager.getRotationMatrix(m_vfinR, m_vfI, m_vfgravity, m_vfgeomag))
            {
                SensorManager.getOrientation(m_vfinR, m_vforientVals);

                m_fCompBearing = (float) Math.round((Math.toDegrees(m_vforientVals[0])) *2)/2;

                //convert to 0-360 from -180-180
                if(m_fCompBearing < 0.0)
                {
                    m_fCompBearing = 360 + m_fCompBearing;
                }


                mCompHead.setText("" + (int)m_fCompBearing);
            }

            calcOffset();   
            rotateCmp();
        }
    }

And code in on create of my activity

    mSMngr = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
    mSListener = new cSensorListener();

    mSMngr.registerListener(mSListener,
            mSMngr.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
            SensorManager.SENSOR_DELAY_UI);
    mSMngr.registerListener(mSListener,
            mSMngr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
            SensorManager.SENSOR_DELAY_UI);

Thanks in advance!

Edit: also tried it with the droid X with the worst results yet... When the phone is rolled around 45 degrees (rotated around z axis a computer coordinate system) the compass heading returned can change over 180 degrees, in fact the heading value goes in the opposite direction of the other phones when spun in the same direction. This is the only phone that produces this result even after calibrating in the settings. Also their is a compass live wallpaper i test against that doesnt have the same issue. So I would assume there would be something i can do in software to avoid this.

4

3 回答 3

34

Even though the author answered his own question I have to chime in here just to reinforce the almost utter uselessness of any kind of compass functionality on the Android platform.

I have come to the conclusion that anyone that depends on an Android compass application is asking for trouble, none of them work reliably and it is not the fault of the developer.

Google and the mfg's simply have not provided a way to get reliable accuracy from these devices, or even to determine if the accuracy is reliable which is even worse because sometimes they are and many times they are not and if someone trusts these devices for real orienteering God help them.

The reason the author probably thinks he is getting better results is that they use a pretty good noise filter on the deprecated orientation sensor ( why they could not do this on the newer method is beyond me ) and in limited testing on a single device after calibration this will appear to work, but in the field using many devices I have found that for the most part the reliability is always in question.

First the noise generated from the magnetic and orientation sensors are horrible, yes this can be overcome with proper DSP techniques, and with 2.3 and gyro enabled phones it will get better overall, but shame on Google and the Mfg's for wasting so much developer time with shoddy implementations of hardware and software outputs.

Second, I have tested at least 18 phones with proper DSP filtering in place and while that cleans up the noise it does not help with accuracy, even the same model of phones have different outputs (though some models seem better than others)

Third you have little in the way of determining if the sensors are calibrated, even doing the spastic figure 8 motion may or may not calibrate the phone and the user never really knows if it's working or not, unless you have a compass to verify, which kind of defeats the point doesn't it?

NOTE: you can multiply and sum the magnetic sensors with each other and take the square root of that sqrt(x*x+y*y+z*z) and make sure it's between 25 and 65 or so, this is one indicator you can use to detect anomalous fields but it's not completely reliable, better than nothing I guess.

Fourth, many phones are completely unreliable, calibrated or not, which is not limited to model types but possibly shoddy QA on the part of the mfg, I really don't know why but I can say that 3 HTC ARIA's produced wildly different results (one 30 degrees off, the other 50, and a third almost spot on) same thing with incredible, nexus, etc.

I tested 18 phones and many were pretty close to accurate IF you could calibrate properly, but many of those took 2-10 try's ( we verified after each calibration attempt with a high precision compass ) and more than a few times they would simply NOT calibrate at all.

NOTE: you do have to account for declination for a true north offset, which you can do with the API in android if you have access to the current GPS coordinates, altitude, time of day etc. the problem was NOT declination and if you are comparing to a compass that's not an issue anyway since it will also be effected by local magnetic fields.

Fith, cold starts always require the calibration step on every phone we tested, which includes the X, the incredible, the Aria, the Nexus, and the Thunderbolt. In other words the first time you start sensor listening 95% of the time it will require a calibration step (even a broken clock is right twice a day) so if you insist on adding this functionality I would just tell your user to do it at the start of each listener event.

If you leave the senors running (bad for battery) then you may or may not have to re-calibrate depending on the fields it's encountered) the above method works ok for that.

The bottom line is that when they work they seem cool, but you can NEVER currently be sure of the accuracy of the azimuth, which makes them pretty unreliable and useless for any real work.

Personally I would use a GPS bearing while moving and then a rotation vector method if possible, it might not be perfect but it would be a heck of a lot better than the craptacular implementation that you have in the current line of phones for azimuth.

Sorry about the long winded reply but I have wasted almost a month on trying to get this to work with the help of an expert DSP engineer and we have pretty much written of the android platform as useful in this respect.

A "Sometimes this works, other times it won't, you can never be sure unless you have a real compass" disclaimer should be put in every compass application in my opinion.

于 2011-06-14T19:01:58.713 回答
3

Well after much testing, and debugging. I came to the conclusion that yes as some of you mentioned the differences my droid 1 and Nexus S were purely hardware difference and magnetic interference.

However the Droid X was a different issue, whatever i tried i could not get the correct readings out of the recommended way with getRotationMatrix and getOrientation, even when added re-map coordinates function. So after some tinkering with no success i figured id give the Orientation sensor way a shot.

Google says that this way is deprecated and they recommend doing it the way i started off with, however i tried all types of combinations with that way with no success. So I went ahead and ignored thier warning and used the orientation sensor ... and it worked. Why ? i have no clue, the droid x is newer os than my droid 1 so it shouldn't have to do with using legacy code. However it does make sense why compass apps wrote to target 1.6 would work while my app doing the "recommended way" was not working.

If anyone has any better way to do this let me know, or if you know a way to make it work with getRotationMatrix and getOrientation also do tell.

Otherwise for anyone else who hits this brick wall as hard as I did heres the code that ended up working for me.

my on sensor changed

        switch (event.sensor.getType())
        {
        case Sensor.TYPE_ORIENTATION:
            m_vforientVals = event.values.clone();
            break;
        }

        if(m_vforientVals != null)
        {
            m_fCompBearing = m_vforientVals[0];             


            mCompHead.setText("" + (int)m_fCompBearing);

            calcOffset();   
            rotateCmp();
        }

and initialize the sensor listener

    mSMngr.registerListener(mSListener,mSMngr.getDefaultSensor(Sensor.TYPE_ORIENTATION),
                            SensorManager.SENSOR_DELAY_NORMAL);
于 2011-06-08T18:37:40.977 回答
2

Your code looks fine to me, if there would be an error in your code I am pretty sure both devices would suffer from that. I think it's the hardware in the devices that's causing the differences. Have you "calibrated" both compasses by moving the phone in figure eight shapes? Many compass apps suggest that, including the map software that comes with symbian devices. That could work

于 2011-06-06T19:59:33.830 回答