Gorgon_Meducer 发表于 2006-4-25 16:23:31

[古董贴]OurRobotV1 用户手册 附录A——《循线算法原理与实践(Not Only For OurRobot

[免责声明]

   虽然,那一天,笔者答应过ArmOk同志,在“曼哈顿”计划完成之前,严格保密。但是,有一句诗说得好——将财由盗守,将肉置狗头——手中拿着Beta版的OurRobotV1,怎么不让我激动呢?为了对的起大家,爱show的我决定,一定要泄露一部分与大家共享,反正将在外君令有所不受——何况,网络这个东西,你一但放出风声来,就如洪水猛兽一般难以阻挡了——而且是这等的天才文章呢?(不要打我,吹一下乃人之常情)

   其实,这次的文章的部分章节,曾经在别的板块出现过,但是,当时缺乏一个统一的硬件平台,技术实在不好详细讲解,更没有办法提供详细的代码,感觉就像是站着讲话不腰疼……

   另外,嵌入式商业开发中有一个重要的概念就是 “即时上市” ,为了抢先即时上市,获得最好的商机,需要“足够好的软件”。然而,阿莫找我可能找错了人,我是出了名的懒,上一次写一个关于LCD12864的东西托了n九,这次要给OurRobotV1写软件系统和驱动库,肯定不能马上完成啦。所以,本人擅自决定,动用大家的力量,以其来完成OurRobotV1的范例,利用群众的力量开发OSS(开放元代码软件开发)。

   那么,OurRobotV1到底有哪些资源呢?这个我不敢说了,只能告诉你,能循线,哈哈哈……(废话,不然这篇文章怎么写?)



   以上就是我的免责说明。也欢迎大家写上您的《无责任书评》,很有可能真的会出现在最终的用户手册PDF中哦!至少是鸣谢哈!

Gorgon_Meducer 发表于 2006-4-25 16:34:33

[硬件基本构架]

    对于机器人的循线,为了获得场地上白线(黑线)的信息,硬件结构一般有如下几种种类。

    1、红外对管阵列。采取这种方式的机器人比较多,尤其在各种机器人竞赛中,几乎是标准配置。但是这种技术有一个致命的弱点,就是对于场地光线的干扰特别敏感,而且也很难把红色和白线区别开来,所以使用受到一定的限制。一般解决这类问题的方法是在红外光上加载一个调制波,通过检测这个调制波来消除场地光线的干扰,至于如何解决红色和白色的区别问题,那就几乎是五花八门了。



    2、光纤传感器阵列。采用这种传感器阵列的原因是,光纤非常细,在单位面积内可以安装更多的传感器,从而获得更精确地场地信息。当然,钱也也花得更多。



    3、线性CCD。这种硬件方法几乎是一种对场地信息分辨率的BT追求,如果说红外对管阵列还是离散信息的话,那么线性CCD就是线性的连续数据。当然驱动它也不是一件容易的事情,对于单片机也有更高的速度要求。



    4、视觉。视觉技术相对复杂,对硬件设备的要求对我们来说就好比是 平民要迎娶公主一样 天价……没有个10万8万不要过问——当然,大款斗不过公_款,在实验室耍的各位不在以上说明之列。需要知识:ARM类32位单片机系统FPGA类数字电路系统MOS/CCD类光学传感系统 计算机图形学 接口_技术(USB等等) etc.



    OurRobotV1是一款面向广大初学者的DIY类机器人系统,肯定从最基础最“原理”的部分入手了哈——成本也是考虑因素之一。硬件系统使用4051驱动 5个红外管,通过AD来采集,在程序中需要使用以下的配置来驱动该硬件系统:



/*-----------红外循线模块初始化--------------*/

# define IRSB_A      _PB2                                 //3个4051通道

# define IRSB_B      _PB1

# define IRSB_C      _PB0                                 

# define IRSB_COM    0                                    //使用AD0通道

# define IRSB_SENSOR_COUNT    5                           //说明管子的数目,默认为8个

# define IRSB_SENSOR_SENSITIVITY 50                     //对外界环境的灵敏度

# define IRSB_SENSOR_BLACK_LINE                           //白底黑线,默认黑底白线

# include <RD_UseInfraredScanBoard.h>                     //头文件




-----此内容被Gorgon Meducer于2006-04-25,16:36:03编辑过

Gorgon_Meducer 发表于 2006-4-25 17:27:19

[基本原理]

    所谓循线,就是通过一定的传感器探测地面色调迥异的两种色彩从而获得引导线位置,修正机器人运动路径的一种技术。就红外传感器来说,就是通过对主动发射的红外线进行接受,检测反射光的强度来获取地面色彩信息的。常识告诉我们,黑色几乎不反射,所以回来的光强最弱,白色反射很强,所以很容易被从黑色的背景中检测到——说的太拗口了。不说太多理论的东西,我们就从基于红外对管阵列的循线技术来说起。

    假设,我们使用的是黑底白线的场地。红外对管阵列由3个红外对管1字摆开组成。白线的宽度略小于或等于红外对管阵列的宽度。



1、数据的采集。

    对于机器人来说,通过传感器感知周围事物的信息,利用这些信息并不作太多智能上的计算而直接通过一定的转换,指导机器人的运动——这种形式在人工智能(AI)上叫做机器人的“反应范式”。我们要想让我们的机器人能够寻着我们给定的轨迹线运动,第一步就必须让他感知到轨迹线的存在。一般的做法就是通过AD采样,获得红外对管(传感器)反馈回来的电压信息。然而,这样获得的电压值信息是无法直接指导运动的,必须把他们转化为二值(也就是二进制信息,1表示线存在,0表示线不存在)信息,然后扫描每一个管子反馈回来的二值信息来判断白线的位置。



    >>技术点A

    AD信号的阀值化。(你可以参考其它的算法,获得比较详尽的技术,我这里只是举例一二)

    所谓阀值化,就是通过一定的“范围把握”,把线性数据转化为离散数据的一种变换。简单的说,就是通过分段函数的方法,将数据分类。我们这个应用中,需要使AD采集回来的电压信息阵列变换为一个恰恰能够较为准确表示白线位置信息的二进制信息阵列;假定1代表白线存在,0代表白线不存在。由于白色和黑色在电压差异上非常之巨大,所以可以简单的通过一个标志线来区分它们,当电压值高于这个标志线了,就把他标志为1,否则就标志为0,算法描述为:

    if (AdValue > MarkLing)

    {

      LineInfor = 1;

    }

    else

    {

      LineInfor = 0;

    }

这样做非常简单,适合于比较标准的场地,然而对于那些模糊了的场地或者是非标准场地,即便人的肉眼能够看出来,对于机器人来说,看到的就是花白的一片或者是黑色的夜幕——当标志线值过高时,机器人能看到的只是那些特别明显的白线,其他则是黑色的夜幕,很容易丢失轨迹线;当标志线值过低时,机器人眼中就是白茫茫的一片毛刺。总而言之,这种方法对场地的适应性非常差。解决方法是,利用斯密特触发特性,通过设定两个标志线来标定轨迹线信息,当AD值高于某一值且原本状态位标志该位置为0时,标志1;当AD值低于另外某一值且原本状态位显示该位置为1时,标定0。算法描述为:



/***********************************************************

*函数说明:白线状态计算函数                              *

*输入:    结果变量的地址                              *

*输出:    探测到白线的传感器数量                        *

***********************************************************/

char IRSBgetLineState(char *LastState)

{

    char n = 0;

                char Start = 0;

                char TGTCounter = 0;

    #ifdef IRSB_SENSOR_COUNT_EVEN                           //传感器为偶数时的情形

                Start = (8 - IRSB_SENSOR_COUNT)>>1;

                #else                                                   //传感器为奇数时的情形

                Start = (7 - IRSB_SENSOR_COUNT)>>1;

                #endif

               

       for (n = 0;n<IRSB_SENSOR_COUNT;n++)

                {

                  #ifndef IRSB_SENSOR_BLACK_LINE

                  if ((*LastState) << (7 - Start - n)>>7)            //从上向下跳变

                        {

                          if (IRSB_SENSOR(n) < IRSBJudgeLine)

                                {

                                  (*LastState) &= ~(1 << (Start + n));

                                }

                                else

                                {

                                  TGTCounter ++;

                                }

                        }

                        else                                                //从下向上跳变

                        {

                          if (IRSB_SENSOR(n) > IRSBJudgeLine)

                                {

                                  (*LastState) |= 1 << (Start + n);

                                        TGTCounter ++;

                                }

                        }

                        #else

                        if ((*LastState) << (7 - Start - n)>>7)             //从上向下跳变

                        {

                          if (IRSB_SENSOR(n) > IRSBJudgeLine)

                                {

                                  (*LastState) &= ~(1 << (Start + n));

                                }

                                else

                                {

                                  TGTCounter ++;

                                }

                        }

                        else                                                //从下向上跳变

                        {

                          if (IRSB_SENSOR(n) < IRSBJudgeLine)

                                {

                                  (*LastState) |= 1 << (Start + n);

                                        TGTCounter ++;

                                }

                        }

                        #endif

                }       

               

                return TGTCounter;

}

很显然,通过调节两条线之间的距离就可以控制算法的灵敏度,一种极限的情况,当该值为0时就是前面提到过的那种简单算法了。





    >>技术点 B 动态预值。(你可以参考其它的算法,获得比较详尽的技术,我这里只是举例一二)



当然,这种算法在简单的机器人循线中不是很常用。比较常见,适应性强的方法是,首先从AD值中找到一个中间值作为MarkLine,然后从MarkLine稍小的值开始向上或向下搜索,(或者可以从AD值中找那些比较接近最大值和最小值之差的0.618倍的数值),然后再标记,寻找一个合理的值,比方说恰好在这种状态下找到的白线数量少于4个。这样的算法叫做动态预值。

/***********************************************************

*函数说明:传感结果处理函数                              *

***********************************************************/

char IRSBproccessAdsValue(char TempLineState)

{

    unsigned int MaxValue = 0;

                unsigned int MinValue = 0xffff;

                unsigned int TempValue = 0;

                char n = 0;

               

                if (!IfGetAllSensorADCValue)                            //采样是否完成

                {

                  return TempLineState;

                }



                for (n = 0;n<IRSB_SENSOR_COUNT;n++)                     //获取采样跨度

                {

                  TempValue = IRSB_SENSOR(n);

                  if (MaxValue < TempValue)

                        {

                          MaxValue = TempValue;

                        }

                        if (MinValue > TempValue)

                        {

                          MinValue = TempValue;

                        }

                }

               

                if (MaxValue - MinValue > 200)                        //开始动态设定阀值

                {

                     if (((MaxValue + MinValue) >> 1) > (IRSB_SENSOR_SENSITIVITY >> 1))

                       {

                             TempValue = ((MaxValue + MinValue) >> 1) - (IRSB_SENSOR_SENSITIVITY >> 1);   

                       }

                       else

                       {

                             TempValue = 0;

                       }

                       

                       while(TempValue < 650)                           //测试循环体

                       {

                             IRSBJudgeLine = TempValue;

                               IRSBJudgeLine = TempValue + IRSB_SENSOR_SENSITIVITY;

                               if (IRSBgetLineState(&TempLineState) < 4)

                               {

                                     break;

                               }

                               TempValue += 20;

                       }

                }

                else

                {

                     #ifndef IRSB_SENSOR_BLACK_LINE

                     if (MaxValue > 700)                              //上限

                       {

                             TempLineState = 0xff;

                       }   

                       else

                       {

                             TempLineState = 0x00;

                       }

                       #else

                       if (MaxValue > 700)                              //上限

                       {

                             TempLineState = 0x00;

                       }   

                       else

                       {

                             TempLineState = 0xff;

                       }

                       #endif

                }

   

                IfGetAllSensorADCValue = False;                         //设定操作标志位False

               

                return TempLineState;

}

Gorgon_Meducer 发表于 2006-4-25 17:38:53

2、数据的简单加工——第一个循线程序。

    到目前为止,我们已经把AD的值的数组转变为了一个表示白线位置的二进制位的数组——我们不妨直接把他用一个字节表示哈。那么,这个字节的状态就表示了当前白线的位置信息。再假设,我们已经写好了几个函数用来分别控制小车的左右运动。那么我们就可以通过以下的简单方式来实现循线了。



//用字节的高三位表示三个管子检测到的白线信息。

switch (LineInforByte)

{

    case 0b11100000:         //全部在白线上

      Motor_Left_GoFront(FullSpeed);

      Motor_Right_GoFront(FullSpeed);

      break;

    case 0b01100000:         //明显车子向左偏了哈

      Motor_Left_GoFront(FullSpeed);

      Motor_Right_GoFront(NormalSpeed);

      break;

    case 0b00100000:

      Motor_Left_GoFront(FullSpeed);

      Motor_Right_GoFront(LowSpeed);

      break;

    ……

         

    //其他情况仿照上面自己写了哈。

    default:

      Motor_Left_GoFront(StopNow);

      Motor_Right_GoFront(StopNow);

       break

}



    呵呵,这样就完成了一个循线小车的程序了哈。简单吧。

    顺便说明一下下,Motor_Left_GoFront()函数是一个控制电机PWM输出的函数。FullSpeed NormalSpeed LowSpeed StopNow StopFree 是一些控制PWM的宏定义,你可以修改这些宏定义的值来实现以上的功能。我想,你看了这个程序应该已经对循线的基本原理了然于胸了吧。哈哈哈哈哈哈哈哈。

    作为伪码,实际情形往往不是这样简单,比方说OurRobotV1的循线程序使用的是下面我们要提到的方法。

Gorgon_Meducer 发表于 2006-4-25 17:46:53

3、数据的高级加工——复杂地面情况的模糊识别算法。



   以上的算法的确可以应付规范场地下的情况了,但是由于其类似查表式的数据处理方式,一旦出现真值表中没有的情况——哪怕是很明显的直线存在——机器人都没有办法处理了。典型的就是在地上有大块的白色斑点,导致机器人对白线视而不见。



   解决以上问题的方法还要从人眼识别白线的原理上说起。在破坏严重的场地上,人类的眼睛仍然可以识别出原先的白线,这是为什么呢?通过重心。人类的眼睛通过捕捉白线的重心确立白线的大体轨迹,从而辨认出白线的位置。从概率的角度上说,在破坏严重的场地上,出现在白线两边的浅色干扰的概率是一样的,即使不同,由于白线本身的存在,其重心至少是不会偏离白线很远的,所以,只要简单的获得地面浅色标志的重心,就可以大体确立白线的所在。我们可以利用物理学上质心的算法获得这一信息。忘了说一点,要想机器人增强对环境的适应力,就需要增加传感器的数目。我们不妨用8个红外管作为传感器。这样通过处理后获得的场地信息就整整1个字节了。假设1个光电管的1拥有1单位的重量,八个光电管的坐标分别为 -7 -5 -3 -11 3 5 7,其间距都是2个单位,通过质心公式很容易计算出质心的坐标,通过这个坐标和0的绝对值,就可以知道当前机器人偏离白线的多少,而这个偏离值则可以通过简单的比例直接指导运动函数。——对于OurRobotV1 5个红外传感器的情况,我们采用丢弃字节的高位,余下的7位关于中心对称,坐标编号改为-6 -4 -2 0 2 4 6的组织形式。典型实例如下:



/***********************************************************

*函数说明:获取偏离轨迹线的数值                        *

*输入:    表明寻线状态的字节                            *

*输出:    白线质心位置                                  *

*[说明]                                                *

*         通过类质心算法获取当前机器人偏离轨迹线的量   *

*         - 表示偏左 + 表示偏右                        *

***********************************************************/

signed char IRSBgetCG_X(unsigned char AdcValues)

{

   signed char a = 0;

        signed char Temp = 0;

        signed char Totals = 0;

       

        #ifdef IRSB_SENSOR_COUNT_EVEN                            //传感器为偶数的情形

        for (a = 0;a<8;a++)

        {

          if (AdcValues & (1<<(7-a)))

          {

                Temp += ((-7)+ (a<<1));

                     Totals++;

          }

        }

        #else                                                    //传感器为奇数的情形

        for (a = 0;a<7;a++)

        {

          if (AdcValues & (1<<(7-a)))

                   {

                     Temp += ((-6) + (a<<1));

                           Totals ++;

                   }

        }

        #endif

        if (Totals ==0)

        {

          return 0;

        }

        else

        {        

          return (Temp / Totals);

        }

}



#endif



函数调用GetCG_X函数,用来获取CG_X,CG_X直接在PWM输出函数里面指导机器人的运动。



以上方法的好处是,提供了一个比例调节循线动作的可能。支持多传感器的情况,尤其适合线性CCD类的线性数据的处理。为机器人提供了一个相对完整的视觉,不可能出现无法识别的情况。

Gorgon_Meducer 发表于 2006-4-25 17:52:57

4、循线位置闭环控制中的PD算法



   从上面各章中,我们已经初步掌握了获取机器人位置信息的方法,并且通过质心公式拥有了利用简单的P调解来指导机器人运动的可能——不知不觉间,我们已经进入了控制学中关于位置环的闭环控制的问题了。



   我们通过传感器获得白线的位置信息,并且通过计算获得了白线的质心距离车体中心的偏移量。控制车体中心位于白线的质心——这就是循线中位置环所要控制的东西。



   首先,我们来说说如果不用PD算法,我们会如何控制这一位置环。我们可以简单的利用如果质心偏左就向左边全速运动,质心偏右就向右边全速运动来实现。这样的确可以完成循线的操作,但是效果显而易见,车子就像喝醉了酒一样,走着蛇形的路线。如果我们通过偏移量的多少乘上一个比例K来指导向左或者向右运动的幅度,那么我们的s形运动的幅度就会越来越小,最终到达水平运动。



   这就是P算法。在P算法中k值的确立非常关键,如果K值过大,仍然会出现明显的S形运动——这个被称为超调;如果K值过小,那么就不能有效地指导循线,机器人就像对白线没有反应一样,很容易就超出了合理的运动范围;如果K值不合适,那么至少也会导致机器人不能以最快的速度到达位置环的平衡位置甚至是震荡。如何获得一个合适的K值理论上是可以通过计算获得的,但往往经验值和测试得来的数值会比较有效。



   当使用P算法时,到达平衡位置中心后由于场地的原因,机器人出现频繁的调节位置的动作,就会大大限制住机器人移动的平均速度。这个时候,就需要一个和回复位置环速度有关系的量,这个就是我们通常称之为D的量,通常这个也称之为和动量有关的修正量——大家可以查阅相关的书籍获得D调节的详细内容。一般情况下,循线中仅仅是P调节就已经足够了满足一般要求了。

Gorgon_Meducer 发表于 2006-4-25 17:53:33



-----------------------------------------

未完待续……

chenbin0011 发表于 2006-4-26 09:21:23

armok一直在筹划网站的转型呀,看来今年会有大动作。

JAMESKING 发表于 2006-4-26 10:55:59

嘿。。。程序结构是个好东西。。。。顶了!!!

SKYdai 发表于 2006-4-26 22:40:46

顶!!

pcl001 发表于 2006-5-8 08:26:45

好文!!!!!!!!!!!!!!

高手就是不一样 ,



怎么好久没更新那哦!

期待下文

flfihc 发表于 2006-5-8 15:14:05

傻孩子太帅了,很喜欢你的文章,以后要多写点呀。觉得你有写教材的能力了,有空出本书吧!呵呵!

FlashNuk 发表于 2006-5-8 19:40:34

个人觉得“傻孩子”喜欢把简单的事情复杂化!

flfihc 发表于 2006-5-8 21:36:37

是呀,个人觉得,学习就是把复杂的问题简单化,然后在把简单的问题复杂化,刚开始觉得复杂说明你不懂,要好好学习,学会了也就简单了,然后就是举一反三之类的,把简单的问题又复杂化了,对知识又会有更深入的了解,这时知识就被你完全消化了。

zy851001 发表于 2008-6-9 13:06:27

傻孩子太帅了

myownsky8445 发表于 2008-9-1 13:30:45

顶!mark

guolinjie007 发表于 2008-10-18 09:25:58

哈哈

对即将要玩的智能车有帮助

谢谢啦

guolinjie007 发表于 2008-11-24 17:36:01

mark!

qaybgogo 发表于 2008-11-26 14:41:15

高手太牛了

zhoujie 发表于 2008-11-26 16:16:40

好~~学习中

jiangweng1234 发表于 2009-3-12 20:47:41

学习一下!

fy024 发表于 2009-4-7 22:25:01

我也觉得是是把简单问题复杂化了,不过有些东西很值得学习,收藏了。

weilan2200 发表于 2009-8-24 14:49:25

好文!

tiantiantian 发表于 2009-8-25 09:38:57

牛贴~自然顶~

sting 发表于 2009-8-25 15:50:49

谢谢啊,正好想学习这个

qilin3 发表于 2009-10-2 23:02:24

mark 先

w48720770 发表于 2009-12-20 10:39:02

先顶一下
再看看啊~~

lcy19870 发表于 2009-12-22 16:29:14

思想不错,顶顶一下

armok 发表于 2010-2-14 18:42:40

以下蓝色文字由坛主:armok 于:2010-02-14,18:42:40 加入。<font color=black>请发贴人注意:本贴放在这分区不合适,即将移走
原来分区:大学生电子设计竞赛及其嵌入式系统专题邀请赛
即将移去的分区:开源雕刻机DIY活动(及机械分论坛)
移动执行时间:自本贴发表0小时后
任何的疑问或咨询,请可随时联系站长。谢谢你的支持!</font>

armok 发表于 2010-2-14 18:42:47

论坛重新进行规划。此帖子移到更适合的技术论坛。

xky183 发表于 2010-2-14 21:58:32

顶学习了

xiaolei0428 发表于 2010-2-15 21:24:22

顶一个

damoplus 发表于 2010-2-16 01:09:23

有意思顶一下

Gorgon_Meducer 发表于 2011-4-20 20:14:44

终于找到这个古董了……重新整理一下……奇怪……我没有权限移动这个帖子……
阿莫,帮我个忙,放到我的专区去吧。帮学弟学妹找的时候,我费了好大力气……

wpnx 发表于 2011-4-22 00:26:48

mark

lrbdh 发表于 2011-5-24 13:07:09

mark

pcbddd 发表于 2011-9-5 22:22:02

好,不错

majesty 发表于 2011-9-7 02:38:55

看成了循钱大法。。

avrwoo 发表于 2011-9-7 06:37:06

mark

ssxuan 发表于 2011-9-12 12:53:38

看不懂这个/
页: [1]
查看完整版本: [古董贴]OurRobotV1 用户手册 附录A——《循线算法原理与实践(Not Only For OurRobot