【CurieNano】对于此板指南针的个人研究
我一直都想把curie的6-axis跟 板上的compass融合一下,问题是这个板上的compass太多bug了。所以我把我对此板compass的研究说一下,希望一些想9轴融合的老哥指点指点。DFRobot的官方放出来的library是给HMC5883L用的,大家用的时候,出来的数字一定基本上是怪怪的,如果我没猜错的话(至少我是这样)。
经过多番研究,我终于发现了一个东西,我的板上的compass有几个小字:M883。然后经过我多番research,在某个网站上看到的是,如果compass上面是M883的是HMC5883(注意没L), 如果L883的才是HMC5883L (我暂时没法证明这个说法是对还是错,不过我M883的跟着没L的说明书来弄能运行)。
我的板是CurieNano V2.0, 所以有这批的老哥想用compass的话请注意你的compass是写着M883还是L883。你们可以下载看看datasheets:
(1) HMC5883跟HMC5883L的功能差不多,I2C的地址也一样,但是在Configuration Register A(datasheets的p12)的时候有些不一样。话不多说,放图:
对于HMC5883来说, CRA6跟CRA5需要是0才能正常工作。对于HMC5883L的话,CRA6跟CRA5可以是00,01,10,11 。所以一旦老哥们用的library是setSamples这个东西的,可能会导致HMC5883没L不工作。
(2) 对于Configuration Register B的时候(datasheets的p13),有L跟没L的CRB7 to CRB0位置都一样,但是他们的range跟gain不一样。
但是这个Gain是给人看的,后面校准的时候我会解释为什么这个gain没卵用,我们自己测gain。
(3)对于Mode Register的话(datasheets的p14), 两个差不多,一般都是写入0x00(Continuous-Measurement Mode)。HMC5883L的MR7是给High Speed的I2C用的,所以一般都是关掉的,所以一般有L没L都是写入0x00 。
问题分析完了,那么我就开始使用HMC5883没L了。对于这个CRB这个+/-1.9 Gauss这个range,因为我试了0.9跟1.2这两个range,我的板会返回-4096这些奇怪的数字,一看就知道这个不可以用。从1.9以后的range都没有这样的问题。对于这个问题,我暂时无法解释,datasheet 15页说overflow就是 -4096了。作为老哥,我们需要稳住。
我的setup是这样搞:
#include<Wire.h>
const uint8_t HMC5883 = 0x1E; //有L没L都是这个地址了
uint8_t outputData; //这个一定要是 unsigned的,有LSB会装在里面,这是一个buffer
void setup() {
Serial.begin(9600);
Wire.begin();
Wire.beginTransmission(HMC5883);
Wire.write(0x00); //Configuration Resgister A
Wire.write(0b00010100); //30HZ
Wire.endTransmission();
Wire.beginTransmission(HMC5883);
Wire.write(0x01); //Configuration Resgister B
Wire.write(0b01000000); // +/- 1.9Ga Range
Wire.endTransmission();
Wire.beginTransmission(HMC5883);
Wire.write(0x02); //Mode Register
Wire.write(0x00); // Continuous-Measurement Mode
Wire.endTransmission();
}
void loop() {
int16_t x,y,z; //这里不能直接int。一般int是16位的但是intel这个int好像是32位的,所以用int会把MSB的第一个正负号bit变成数字,这里只能short或int16_t 。
Wire.beginTransmission(HMC5883);
Wire.write(0x03); // 从0x03开始读,参考datasheet p11
Wire.endTransmission();
delay(30);
Wire.requestFrom(HMC5883, 6);
if(6 <= Wire.available()) // If the number of bytes available for reading be <=6.
{
for(int i=0;i<6;i++)
{
outputData=Wire.read();//Store the data in outputData buffer
}
}
Wire.endTransmission();
//下面这里看p11啦
x=outputData << 8 | outputData; //Combine MSB and LSB of X Data output register
z=outputData << 8 | outputData; //Combine MSB and LSB of Z Data output register
y=outputData << 8 | outputData; //Combine MSB and LSB of Y Data output register
Serial.print(x);
Serial.print(" ");
Serial.print(y);
Serial.print(" ");
Serial.println(z);
}
上面这个program,我们需要各个方位乱动去尽量把x,y,z的raw data的最大最小拿到(xyz跟你地方的地磁线重叠就可以比较容易拿到,后面会说到不同地方的地磁线)。但是拿到raw data我们怎样用呢。话不多说,先上图:
这个是compass 的x跟y的plot,如果我plot了z,那么这个就是个球体/椭圆。我们可以看到我这个HMC5883的offset还是比较大的。
那么我们需要做的就是把这个球的圆心放到中间,所以 x_offset = (最大的x+最小的x)/2,y_offset = (最大的y+最小的y)/2,z_offset = (最大的z+最小的z)/2
然后我们把得到的raw data依次减去这些offset就好。
然后我们还需要把这些applied offset的raw data normalized了,因为减去offset以后,这个球/椭圆的圆心在origin了。正常情况下,我xyz轴跟地球磁线正向或反向重叠,我最大x需要跟最大y和最大z是一样得到的数才能说的过去,最小的同理。这些就是我最近一次得到的最大最小xyz (没有减去offset):
#define MAG0MAX 575
#define MAG0MIN -265
#define MAG1MAX 586
#define MAG1MIN -265
#define MAG2MAX 378
#define MAG2MIN -393
可见z的peak-to-peak是771(约等于5883没L的datasheet p13给出的Gain:768),但x跟y的peak-to-peak是800多,所以说datasheet给出的gain没卵用,实际上gain就是xyz三轴的peak-to-peak。所以我要做这样的计算把它们normalize了:
float magOffset = { (MAG0MAX + MAG0MIN) / 2, (MAG1MAX + MAG1MIN) / 2, (MAG2MAX + MAG2MIN) / 2 };
mx = (x-magOffset)/(MAG0MAX - MAG0MIN);
my = (y-magOffset)/(MAG1MAX - MAG1MIN);
mz = (z-magOffset)/(MAG2MAX - MAG2MIN);
mx,my,mz就是normalize之后的以gauss为单位的值(这些值不一定是准确的值,但是他们三个是normalize之后的值,就是它们相除的时候会得到准确的比例,yaw就是跟x和y的比例依赖性很高)。
我们得到mx,my,mz,基本上就大功告成啦。
这里要说一下地球的地磁线,因为这东西对compass的影响很大:
地磁线就是上图那个蓝色的线,Declination只是一个真北跟磁北的区别,我们用compass的原因是我们希望得到一个yaw(heading)的Reference,所以无论是真北或者磁北对heading的References影响不大,因为如果想得到真北,只需要减去declination就好。
但是Inclination对于compass的影响,我可以说是比较大的。 因为当compass的Z轴跟这个field正反向重叠的时候,z的值会去到最大或者最小。这个时候x跟y的值会变的很小很小,然后会在0上下浮动。如果x是正,y是负,那么heading就在270-360度之间;如果x是负,y是正,那么heading就是90-180度之间。这就说明指南针失灵了。这个情况就像你在地磁南极或者北极,inclination是+/- 90度,你拿着个指南针平衡于地面,指南针就找不到北了。
Inclination是跟纬度的关系比较大,在赤道,大概是0度,然后广州大概是向下34度。越高纬度越向下。
https://www.ngdc.noaa.gov/geomag-web/#igrfgrid
这个网站能根据你当地的经纬给出declination跟inclination,在magnetic components那里选,然后最后calculate他会给你一个xml文件,里面有D跟I。
在calibration的时候,应该避免compass靠近高电流的电线,因为那个磁场会影响compass的reading。
Last: 对于老哥想用9轴的数据去得到一个很好的AHRS,如果用Madgwick的filter的话,记得把指南针的的坐标跟curie的坐标对齐(mx变成my,my变成-mx),然后在把他们放进 function。
这个是我对curie的Reference Axes的猜测(望老哥指正,curie里面的是BMI160),HMC5883的Reference Axes是对的(根据datasheet):
9轴的Madgwick或者是Mahony filter的code我就不放上来了,用update(9个东西) 取代 updateIMU(6个东西)。
其实arduino里面的库可以搜索AHRS,它会显示三个result。三个src我都看过,adafruit那个比较完善,里面有Madgwick跟Mahony,然后这个Mahony还可以设置sample的frequency (在库里直接搜索ahrs得到的mahony不能设置frequency,直接默认512hz)。你们还可以试试两个filter有什么不同。
这个放个code给老哥们得到最大最小的xyz,不要靠近高电流的地方,这个程序会不断记录你知道现在为止最大或最小的xyz:
#include<Wire.h>
const uint8_t HMC5883 = 0x1E;
uint8_t outputData;
int16_t maxmagx = 0;
int16_t minmagx = 0;
int16_t maxmagy = 0;
int16_t minmagy = 0;
int16_t maxmagz = 0;
int16_t minmagz = 0;
void setup() {
Serial.begin(9600);
Wire.begin();
Wire.beginTransmission(HMC5883);
Wire.write(0x00); //Configuration Resgister A
Wire.write(0b00010100); //30HZ
Wire.endTransmission();
Wire.beginTransmission(HMC5883);
Wire.write(0x01);
Wire.write(0b01000000);
Wire.endTransmission();
Wire.beginTransmission(HMC5883);
Wire.write(0x02);
Wire.write(0x00);
Wire.endTransmission();
}
void loop() {
int16_t x,y,z;
Wire.beginTransmission(HMC5883);
Wire.write(0x03);
Wire.endTransmission();
delay(30);
Wire.requestFrom(HMC5883, 6);
if(6 <= Wire.available()) // If the number of bytes available for reading be <=6.
{
for(int i=0;i<6;i++)
{
<i style="background-color: initial;"> outputData=Wire.read();//Store the data in outputData buffer
}
}
Wire.endTransmission();
x=outputData << 8 | outputData; //Combine MSB and LSB of X Data output register
z=outputData << 8 | outputData; //Combine MSB and LSB of Z Data output register
y=outputData << 8 | outputData; //Combine MSB and LSB of Y Data output register
if (x > maxmagx) maxmagx = x;
else if (x < minmagx) minmagx = x;
if (y > maxmagy) maxmagy = y;
else if (y < minmagy) minmagy = y;
if (z > maxmagz) maxmagz = z;
else if (z < minmagz) minmagz = z;
Serial.print("Orientation: ");
Serial.print(maxmagx);
Serial.print(" ");
Serial.print(minmagx);
Serial.print(" ");
Serial.print(maxmagy);
Serial.print(" ");
Serial.print(minmagy);
Serial.print(" ");
Serial.print(maxmagz);
Serial.print(" ");
Serial.println(minmagz);
}</i>
我看论坛有个老哥搞蓝牙跟6轴的,然后想搞9轴,我希望我这个文章可以帮到你。对于您抖动的情况, 那个setgyrorate跟setAccelxxxxxRate可以把它弄到50,filter.begin(50) 然后下面microsPerReading = 1000000 / 50。 如果你想快速roll跟pitch,可以把那个setxxxxrange调高, 下图。不过在convertRaw的方程里面你要把相应的数字放进去。
专业贴,必须赞一个。 非常棒的文章啊!!!!赞 好帖顶一个 老哥,这波稳,服! 感谢分享 确实是883
厉害, 挖得有点深! 422234 发表于 2017-6-12 15:04
确实是883
我的是M883,按道理是HMC5883
如果是L883, 应该才是HMC5883L
(我看到外arduino官方的论坛有个老哥是这样说的,根据我的经历,好像也是这样)
老哥你可以试试我贴上的第一堆code, 那个是直接输出 xyz三轴的raw data。
如果数字一直有变的话就是用我的code能动,因为http://www.eeboard.com/bbs/thread-95424-1-1.html这个老哥用了DFRobot在github上找的hmc5883L的code(DFRobot给的library里的示例)出现这样的问题。
我也有这样的问题,所以我才做了一系列的研究才写自己的code来运行。 Lingo 发表于 2017-6-12 15:59
我的是M883,按道理是HMC5883
如果是L883, 应该才是HMC5883L
(我看到外arduino官方的论坛有个老哥是这 ...
好的,非常感谢!!!回头我试试 稳! 赞一个!! 好厉害 厉害了 厉害了 厉害。。。。。。。。。。。 牛牛牛牛牛
{:6_214:}。。。。 厉害厉害 不错不错 厉害厉害 赞赞赞!
页:
[1]