MWC飞控增加声纳定高的方法

2015.12.17 更新:经过2个周末的上机测试,该算法效果很好,在低空超声锁高之后离地高度非常稳定,现在已经成功应用在低空航拍上了。

现状

MWC开源飞控已经很有点年头了,现在好多新的穿越机改用CC3D、航拍机改用APM,商业化的飞控也很多了。但是MWC作为基于Arduino的开源飞控,可以说非常成熟而且代码简单易懂,便宜,效果也不错,所以我的四轴平台依然采用了MWC飞控。MWC2.4相对前代的改进,主要是加入了对串口GPS的支持,修复了声纳崩溃问题,以及改进了计算实时性。但是声纳定高依然没有得到官方支持(包括I2C声纳)。新加入的GPS支持,由于占用内存过高,实际上在Mega之外的Arduino上无法使用。一个没有GPS定点和声纳定高的飞控,就相当于一个简单的增稳飞控而已,没有一定的技术的话生存能力不是太高。个人觉得MWC有点走偏了。

所以我的思路是在MWC2.4的基础上进一步实现声纳定高和GPS定点,至于GPS RTB、WP等功能等生存能力提高之后再考虑。声纳定高和GPS定点应该都不会对内存造成过大的压力。这次首先介绍声纳定高的方法。

传统的MWC为了支持GPS和声纳,一般是使用I2C_GPS_Nav这个项目,外置一片Arduino作为I2C GPS和声纳板。另外网上有些高手自行给MWC打了补丁,可以在低空用声纳数据覆盖气压计数据,从而利用气压定高代码。

思路

我的声纳定高基本思路也是利用气压定高代码。

但是气压计的高度读数与飞机本身姿态无关,而超声波必须考虑机身倾斜程度,将测得的距离向Z轴投影,才是真实的高度。

网上对于MWC Baro模式的描述往往就是一句气压定高,具体的使用方法并没有讲得很清楚。阅读代码发现,Baro模式的具体行为是,油门不变,MWC会维持在进入Baro时的高度上;以进入Baro模式时为基准,增减油门飞机会升降,其速率为油门每增减100点,速度相应的增减30cm/s左右;油门回到基准则维持当前高度。

用Baro模式辅助降落应该是比较有用的,飞机降到声纳有效范围之后,开启Baro模式,稍微收油,飞机缓缓降落。

实现

首先是需要把I2C_GPS_Nav中对于Pingpong型声纳的代码移植到MWC2.4的Sensor.cpp中。

我的MWC上A1和A2是空出来的,所以就用这两个作为声纳的信号引脚。

// ************************************************************************************************************
// Pingpong Sonar
// ************************************************************************************************************
// 2015.11.29 by XD : ported from I2C_GPS_NAV_v2_2
#if defined(PINGPONG_SONAR)
volatile static uint32_t Sonar_starTime = ;
volatile static uint32_t Sonar_echoTime = ;
volatile static uint16_t Sonar_waiting_echo = ; void Sonar_init()
{
// Pin change interrupt control register - enables interrupt vectors
PCICR |= (<<PCIE1); // Port C // Pin change mask registers decide which pins are enabled as triggers
PCMSK1 |= (<<PCINT10); // echo, arduino pin A2, PC2 DDRC |= 0x02; // trigger, arduino pin A1, triggerpin PC1 as output
Sonar_update();
} ISR(PCINT1_vect) {
//uint8_t pin = PINC;
if (PINC & <<PCINT10) { //indicates if the bit 0 of the arduino port [B0-B7] is at a high state
Sonar_starTime = micros();
}
else {
Sonar_echoTime = micros() - Sonar_starTime; // Echo time in microseconds if (Sonar_echoTime <= *) { // valid distance
sonarAlt = Sonar_echoTime / ;
}
else
{
// No valid data
sonarAlt = -;
}
Sonar_waiting_echo = ;
}
} void Sonar_update()
{
if (Sonar_waiting_echo == )
{
// Send 2ms LOW pulse to ensure we get a nice clean pulse
// PORTC &= ~(0x08);//PC3 low
PORTC &= ~(0x02);//PC1 low
delayMicroseconds(); // send 10 microsecond pulse
// PORTC |= (0x08);//PC3 high
PORTC |= (0x02);//PC1 high
// wait 10 microseconds before turning off
delayMicroseconds();
// stop sending the pulse
// PORTC &= ~(0x08);//PC3 low
PORTC &= ~(0x02);//PC1 low
Sonar_waiting_echo = ;
}
}
#endif // #if defined(PINGPONG_SONAR)

我用的声纳就是X宝上买的10多块号称比较准的超声波模块,拔掉背后的跳线帽就是Pingpong模式(不然是串口模式),这种模块还可以输出温度值用于距离补偿,但是读取温度需要额外占用10多us,而且绝对距离在飞控上意义不大,所以暂时不考虑。这种Pingpong声纳在读取距离的时候会占用10多us,相当于100多个时钟周期,软件开销还是有点的。不过好在便宜,I2C的声纳要100多软妹币,这个只要10多块。

官方I2C声纳的代码最后,如果所有的I2C声纳都没有定义,则会把Sonar_init和Sonar_update定义为空函数。这里(2.4版本Sensor.cpp:1576)需要进行一点修改,避免重复定义Sonar_init和Sonar_update:

#if !defined(PINGPONG_SONAR) // 2015.11.29 by XD : check for pingpong
inline void Sonar_init() {}
void Sonar_update() {}
#endif

在Config.h中开启Pingpong声纳:

#define PINGPONG_SONAR 

要注意在def.h中添加对Pingpong声纳的支持:

#if defined(SRF02) || defined(SRF08) || defined(SRF10) || defined(SRC235) || defined(I2C_GPS_SONAR) || defined(PINGPONG_SONAR) // 2015.11.29 by XD : ported from I2C_GPS_NAV_v2_2
#define SONAR 1
#else
#define SONAR 0
#endif

编译通过并且正确连接声纳(Trigger接A1,Echo接A2)的话,可以看到MWC GUI上Sonar的标签变绿了。

但是现在声纳数据还没有被使用,需要进一步添加代码使之与气压计数据结合。修改IMU.cpp中getEstimatedAltitude()函数,在BaroEstAlt LPF之后进行融合:

  BaroEstAlt = (BaroEstAlt *  + BaroAlt ) >> ; // additional LPF to reduce baro noise (faster by 30 µs)

  // 2015.11.29 by XD, if sonar reads less than 4.2m (sensor limit 4.5m - safety margin 0.3m), use sonar
// this maybe unsafe...
if ((sonarAlt > && sonarAlt < ) ||
((att.angle[ROLL] > - && att.angle[ROLL] < ) && (att.angle[PITCH] > - && att.angle[PITCH] < )))
{
// actual alt = sonarAlt * cos(att.angle[ROLL]) * cos(att.angle[PITCH])
int32_t actualAlt = abs((int32_t)sonarAlt * (_cos10(att.angle[ROLL]) * _cos10(att.angle[PITCH]) >> ));
alt.EstAlt = actualAlt >> ;
}
else
alt.EstAlt = BaroEstAlt;

上面这段代码中,声纳的测量值乘以Roll和Pitch的余弦就是向Z轴的投影,最终高度结果是以cm为单位。_cos10()是用于计算余弦的函数。

其实这个算法个人感觉有可能不安全,当高度超过声纳阈值的时候会突然切换到气压计高度,中间的误差可能导致飞机猛烈晃动。可以考虑设置一个过渡区,根据飞机的高度对声纳/气压数据进行权重加和,权重根据高度自动调节。

Arduino库中的cos函数开销太大,经过测试,使用原装cos函数,MWC较难在2.8ms内跑完一个周期(计算周期可以在GUI的Cycle Time查看,2.4版本大部分时候都在2800us,略有跳动)。

所以需要自己编写快速余弦函数:

// 2015.11.30 by XD, x is in 0.1 deg, returns cos * (1 << 10)
// when x approaches zero, cos(x) = 1 - x ^ 2 / 2, x is in radians
// https://en.wikipedia.org/wiki/Small-angle_approximation
// rad = deg / 180 * PI
int32_t _cos10(int16_t x)
{
// x within [-1800, 1800]
int32_t radTemp = (int32_t)x * ; // rad = x * ((PI / 1800) << 16), rad within [-205200, 205200]
int32_t rad = radTemp >> ; // rad ^ 2 within [-657922500, 657922500] int32_t cos20 = ((uint32_t) << ) - ((rad * rad) >> );
int32_t result = cos20 >> ; return result;
}

使用的算法和MWC IMU差不多,在小角度的条件下计算三角函数的级数展开式前两项。对于cos来说:cos(x) = 1 - x ^ 2 / 2,x是弧度单位。

由于Arduino没有浮点单元和硬件除法器,需要尽量避免这两种运算,这里直接手工计算PI / 1800并扩大65536倍(1 << 16)。

在求平方的时候,为了保证计算不越界,需要预先进行移位操作。

计算完成后将结果右移,最终保持10位2进制有效数字,差不多相当于4位10进制有效数字。最终结果相当于余弦乘以1024。

在前面的数据融合代码中,我们也可以看到移位操作,就是用来避免计算越界,以及把余弦多乘的1024除回来。

最后在四轴上实际测试的效果不错,Cycle Time没有明显变化,算出来的高度还是很准的,准备周末去实地飞行了。

MWC飞控增加声纳定高的方法的更多相关文章

  1. MWC飞控增加声纳定高的方法(转)

    源: MWC飞控增加声纳定高的方法

  2. mysql优化连接数防止访问量过高的方法

    这篇文章主要介绍了mysql优化连接数防止访问量过高的方法,需要的朋友可以参考下 很多开发人员都会遇见”MySQL: ERROR 1040: Too many connections”的异常情况,造成 ...

  3. 离场定高转弯DF与CF的对比

    也许是刚学会CAD的缘故,配合风螺旋插件,画图的感觉真是蛮爽的,忍不住画了一张又一张. 接着昨天的离场保护区,我们来聊一下PBN指定高度转弯保护区的画法.指定高度转弯的计算本身没有太多复杂的地方,真正 ...

  4. 数组的高阶方法map filter reduce的使用

    数组中常用的高阶方法: foreach    map    filter    reduce    some    every 在这些方法中都是对数组中每一个元素进行遍历操作,只有foreach是没有 ...

  5. Linux下java进程CPU占用率高分析方法

    Linux下java进程CPU占用率高分析方法 在工作当中,肯定会遇到由代码所导致的高CPU耗用以及内存溢出的情况.这种情况发生时,我们怎么去找出原因并解决. 一般解决方法是通过top命令找出消耗资源 ...

  6. js获取隐藏元素宽高的方法

    网上有一些js获取隐藏元素宽高的方法,但是可能会存在某些情况获取不了. 例如: <!DOCTYPE html> <html lang="en"> <h ...

  7. JS快速获取图片宽高的方法

    快速获取图片的宽高其实是为了预先做好排版样式布局做准备,通过快速获取图片宽高的方法比onload方法要节省很多时间,甚至一分钟以上都有可能,并且这种方法适用主流浏览器包括IE低版本浏览器. 我们一步一 ...

  8. 动态布局--动态修改RelativeLayout宽高的方法

    本文实例讲述了Android编程动态修改RelativeLayout宽高的方法.分享给大家供大家参考,具体如下: 我们经常会动态修改RelativeLayout的宽高,这样的代码,比较简单,就是修改R ...

  9. 转载:JS快速获取图片宽高的方法

    快速获取图片的宽高其实是为了预先做好排版样式布局做准备,通过快速获取图片宽高的方法比onload方法要节省很多时间,甚至一分钟以上都有可能,并且这种方法适用主流浏览器包括IE低版本浏览器. 我们一步一 ...

随机推荐

  1. 单源最短路径——dijkstra算法

    dijkstra算法与prim算法的区别   1.先说说prim算法的思想: 众所周知,prim算法是一个最小生成树算法,它运用的是贪心原理(在这里不再证明),设置两个点集合,一个集合为要求的生成树的 ...

  2. windows7修改双系统启动项名称、先后顺序、等待时间

    一.进入BCDEdit.exe  正常启动Windows 7 系统,点击“开始” -> “所有程序” -> “附件”,右击“命令提示符” -> “以管理员身份运行”(需要将操作当前用 ...

  3. 单实例Singleton

    单实例Singleton设计模式可能是被讨论和使用的最广泛的一个设计模式了,这可能也是面试中问得最多的一个设计模式了.这个设计模式主要目的是想在 整个系统中只能出现一个类的实例.这样做当然是有必然的, ...

  4. 使用jackson进行json数据格式转换

    private static final JsonFactory factory = new JsonFactory(); StringWriter jsonOut = new StringWrite ...

  5. jsonp跨域请求的问题

    今天发现json取数据的时候有时会报如下的错误: No 'Access-Control-Allow-Origin' header is present on the requested resourc ...

  6. 设置presentVC跟PushVC一样的效果即从右到左的动画

    SettingViewController *VC = [[SettingViewControlleralloc]init]; VC.view.backgroundColor = [UIColorwh ...

  7. ice介绍 z

    什么是ICE(Internet Communications Engine)呢,它是由Zeroc公司开 发的一套开源中间件系统,与DCOM,CORBA,WEB SERVICEDcom类似,支持RPC( ...

  8. Intellisense for Xrm.Page in CRM 2011

    Intellisense for Xrm.Page in CRM 2011 In CRM 2011 javascripts for crm forms can be stored externally ...

  9. Dephi的同一个线程支持累次Execute吗

    Suspend放到循环里------解决方案--------------------执行完不结束只休眠.另外还需要线程池.------解决方案--------------------    while ...

  10. C++ DLL中导出函数的声明的方法

    定义: TESTDLLEXPORT_API int fnTestDllExport(void); TESTDLLEXPORT_API int fnTestCall(void); TESTDLLEXPO ...