2017-6-10 16:27:55 [显示全部楼层]
13220浏览
查看: 13220|回复: 20

[讨论] 【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:

【CurieNano】对于此板指南针的个人研究图2

【CurieNano】对于此板指南针的个人研究图2


(1) HMC5883跟HMC5883L的功能差不多,I2C的地址也一样,但是在Configuration Register A(datasheets的p12)的时候有些不一样。话不多说,放图:
【CurieNano】对于此板指南针的个人研究图1 【CurieNano】对于此板指南针的个人研究图2

对于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是这样搞:

  1. #include<Wire.h>
  2. const uint8_t HMC5883 = 0x1E;               //有L没L都是这个地址了
  3. uint8_t outputData[6];           //这个一定要是 unsigned的,有LSB会装在里面,这是一个buffer
  4. void setup() {
  5.   Serial.begin(9600);
  6.   Wire.begin();
  7.   Wire.beginTransmission(HMC5883);
  8.   Wire.write(0x00);       //Configuration Resgister A
  9.   Wire.write(0b00010100); //30HZ
  10.   Wire.endTransmission();
  11.   Wire.beginTransmission(HMC5883);
  12.   Wire.write(0x01);     //Configuration Resgister B
  13.   Wire.write(0b01000000);       // +/- 1.9Ga Range
  14.   Wire.endTransmission();
  15.   Wire.beginTransmission(HMC5883);
  16.   Wire.write(0x02);     //Mode Register
  17.   Wire.write(0x00);     // Continuous-Measurement Mode
  18.   Wire.endTransmission();   
  19. }
  20. void loop() {
  21.   int16_t x,y,z;           //这里不能直接int。一般int是16位的但是intel这个int好像是32位的,所以用int会把MSB的第一个正负号bit变成数字,这里只能short或int16_t 。
  22.   Wire.beginTransmission(HMC5883);
  23.   Wire.write(0x03);         // 从0x03开始读,参考datasheet p11
  24.   Wire.endTransmission();   
  25.   delay(30);
  26.   Wire.requestFrom(HMC5883, 6);
  27.   if(6 <= Wire.available()) // If the number of bytes available for reading be <=6.
  28.     {
  29.         for(int i=0;i<6;i++)
  30.         {
  31.             outputData=Wire.read();  //Store the data in outputData buffer
  32.         }
  33.     }
  34.     Wire.endTransmission();
  35.     //下面这里看p11啦
  36.     x=outputData[0] << 8 | outputData[1]; //Combine MSB and LSB of X Data output register
  37.     z=outputData[2] << 8 | outputData[3]; //Combine MSB and LSB of Z Data output register
  38.     y=outputData[4] << 8 | outputData[5]; //Combine MSB and LSB of Y Data output register
  39.     Serial.print(x);
  40.     Serial.print(" ");
  41.     Serial.print(y);
  42.     Serial.print(" ");
  43.     Serial.println(z);
  44. }
复制代码



上面这个program,我们需要各个方位乱动去尽量把x,y,z的raw data的最大最小拿到(xyz跟你地方的地磁线重叠就可以比较容易拿到,后面会说到不同地方的地磁线)。但是拿到raw data我们怎样用呢。话不多说,先上图:
【CurieNano】对于此板指南针的个人研究图5


这个是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):
  1. #define MAG0MAX 575
  2. #define MAG0MIN -265
  3. #define MAG1MAX 586
  4. #define MAG1MIN -265
  5. #define MAG2MAX 378
  6. #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了:

  1. float magOffset[3] = { (MAG0MAX + MAG0MIN) / 2, (MAG1MAX + MAG1MIN) / 2, (MAG2MAX + MAG2MIN) / 2 };
  2. mx = (x-magOffset[0])/(MAG0MAX - MAG0MIN);
  3. my = (y-magOffset[1])/(MAG1MAX - MAG1MIN);
  4. mz = (z-magOffset[2])/(MAG2MAX - MAG2MIN);
复制代码

mx,my,mz就是normalize之后的以gauss为单位的值(这些值不一定是准确的值,但是他们三个是normalize之后的值,就是它们相除的时候会得到准确的比例,yaw就是跟x和y的比例依赖性很高)。

我们得到mx,my,mz,基本上就大功告成啦。

这里要说一下地球的地磁线,因为这东西对compass的影响很大:
【CurieNano】对于此板指南针的个人研究图6


地磁线就是上图那个蓝色的线,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):
【CurieNano】对于此板指南针的个人研究图7


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:

  1. #include<Wire.h>
  2. const uint8_t HMC5883 = 0x1E;
  3. uint8_t outputData[6];
  4. int16_t maxmagx = 0;
  5. int16_t minmagx = 0;
  6. int16_t maxmagy = 0;
  7. int16_t minmagy = 0;
  8. int16_t maxmagz = 0;
  9. int16_t minmagz = 0;
  10. void setup() {
  11.   Serial.begin(9600);
  12.   Wire.begin();
  13.   Wire.beginTransmission(HMC5883);
  14.   Wire.write(0x00);       //Configuration Resgister A
  15.   Wire.write(0b00010100); //30HZ
  16.   Wire.endTransmission();
  17.   Wire.beginTransmission(HMC5883);
  18.   Wire.write(0x01);
  19.   Wire.write(0b01000000);
  20.   Wire.endTransmission();
  21.   Wire.beginTransmission(HMC5883);
  22.   Wire.write(0x02);
  23.   Wire.write(0x00);
  24.   Wire.endTransmission();   
  25. }
  26. void loop() {
  27.   int16_t x,y,z;
  28.   Wire.beginTransmission(HMC5883);
  29.   Wire.write(0x03);
  30.   Wire.endTransmission();   
  31.   delay(30);
  32.   Wire.requestFrom(HMC5883, 6);
  33.   if(6 <= Wire.available()) // If the number of bytes available for reading be <=6.
  34.     {
  35.         for(int i=0;i<6;i++)
  36.         {
  37. <i style="background-color: initial;">            outputData=Wire.read();  //Store the data in outputData buffer
  38.         }
  39.     }
  40.     Wire.endTransmission();
  41.     x=outputData[0] << 8 | outputData[1]; //Combine MSB and LSB of X Data output register
  42.     z=outputData[2] << 8 | outputData[3]; //Combine MSB and LSB of Z Data output register
  43.     y=outputData[4] << 8 | outputData[5]; //Combine MSB and LSB of Y Data output register
  44.     if (x > maxmagx) maxmagx = x;
  45.     else if (x < minmagx) minmagx = x;
  46.     if (y > maxmagy) maxmagy = y;
  47.     else if (y < minmagy) minmagy = y;
  48.     if (z > maxmagz) maxmagz = z;
  49.     else if (z < minmagz) minmagz = z;
  50.     Serial.print("Orientation: ");
  51.     Serial.print(maxmagx);
  52.     Serial.print(" ");
  53.     Serial.print(minmagx);
  54.     Serial.print(" ");
  55.     Serial.print(maxmagy);
  56.     Serial.print(" ");
  57.     Serial.print(minmagy);
  58.     Serial.print(" ");
  59.     Serial.print(maxmagz);
  60.     Serial.print(" ");
  61.     Serial.println(minmagz);
  62. }</i>
复制代码

我看论坛有个老哥搞蓝牙跟6轴的,然后想搞9轴,我希望我这个文章可以帮到你。对于您抖动的情况, 那个setgyrorate跟setAccelxxxxxRate可以把它弄到50,filter.begin(50) 然后下面microsPerReading = 1000000 / 50。 如果你想快速roll跟pitch,可以把那个setxxxxrange调高, 下图。不过在convertRaw的方程里面你要把相应的数字放进去。
【CurieNano】对于此板指南针的个人研究图8【CurieNano】对于此板指南针的个人研究图9

Ricky  NPC

发表于 2017-6-10 23:13:23

专业贴,必须赞一个。
回复

使用道具 举报

422234  高级技师

发表于 2017-6-11 20:27:41

非常棒的文章啊!!!!赞
回复

使用道具 举报

O_oYYQ  高级技师

发表于 2017-6-12 11:06:04

好帖  顶一个
回复

使用道具 举报

Grey  中级技匠

发表于 2017-6-12 12:19:28

老哥,这波稳,服!
回复

使用道具 举报

pATAq  版主 来自手机

发表于 2017-6-12 14:45:39

感谢分享
回复

使用道具 举报

422234  高级技师

发表于 2017-6-12 15:04:52

确实是883
【CurieNano】对于此板指南针的个人研究图1

回复

使用道具 举报

Angelo  初级技匠

发表于 2017-6-12 15:37:23

厉害, 挖得有点深!
回复

使用道具 举报

Lingo  初级技师
 楼主|

发表于 2017-6-12 15:59:44


我的是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来运行。
回复

使用道具 举报

422234  高级技师

发表于 2017-6-12 16:12:11

Lingo 发表于 2017-6-12 15:59
我的是M883,按道理是HMC5883
如果是L883, 应该才是HMC5883L
(我看到外arduino官方的论坛有个老哥是这 ...

好的,非常感谢!!!回头我试试
回复

使用道具 举报

nicho  中级技匠

发表于 2017-6-12 17:07:16

稳! 赞一个!!
回复

使用道具 举报

Lay  学徒 来自手机

发表于 2017-6-12 21:03:45

好厉害
回复

使用道具 举报

hnyzcj  版主

发表于 2017-6-14 11:10:38

厉害了
回复

使用道具 举报

xhuaihe  见习技师

发表于 2017-7-31 08:49:36

厉害了
回复

使用道具 举报

gray6666  初级技神

发表于 2017-9-5 10:39:41

厉害。。。。。。。。。。。
回复

使用道具 举报

派大星ym  初级技匠

发表于 2022-8-5 09:56:39

牛牛牛牛牛
回复

使用道具 举报

派大星ym  初级技匠

发表于 2022-8-5 10:11:07

。。。。
回复

使用道具 举报

花生编程  中级技匠

发表于 2023-8-16 14:15:33

厉害厉害
回复

使用道具 举报

花生编程  中级技匠

发表于 2023-8-16 14:16:37

不错不错
回复

使用道具 举报

三春牛-创客  初级技神

发表于 2023-8-17 12:05:56

厉害厉害
回复

使用道具 举报

三春牛-创客  初级技神

发表于 2023-8-17 12:09:33

赞赞赞!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
关于楼主
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail