基于C#的机器学习--模糊逻辑-穿越障碍

模糊逻辑-穿越障碍

模糊逻辑。另一个我们经常听到的术语。但它的真正含义是什么?它是否意味着不止一件事?我们马上就会知道答案。

我们将使用模糊逻辑来帮助引导一辆自动驾驶汽车绕过障碍,如果我们做得正确,我们将避开沿途的障碍。我们的自动导航车辆(AGV)将在障碍物周围导航,感知其路径上的障碍物。它将使用一个推理系统来帮助引导它前进。你或者用户将能够创造障碍或通过的方式,AGV必须避开或通过。你可以观察跟踪光束的工作,以及跟踪AGV的路径沿其路线。AGV所采取的每一步都将在用户界面上进行更新,这样您就可以看到发生了什么。

在布尔逻辑中,事物或真或假,或黑或白。许多人不知道的是,还有一种被称为多值逻辑的东西,它的真实值介于1和0之间。模糊逻辑是处理部分真理的多值逻辑的概念实现。例如很多人也不知道的著名的sigmoid函数,它是一种模糊化的方法。

维基百科(建议大家多使用这个百科,百度百科真的只能用呵呵来形容)对此有很好的描述,如下:

维基百科:

"In this image, the meanings of the expressions cold, warm, and hot are represented by functions mapping a temperature scale. A point on that scale has three "truth values"-one for each of the three functions. The vertical line in the image represents a particular temperature that the three arrows (truth values) gauge. Since the red arrow points to zero,this temperature may be interpreted as "not hot". The orange arrow (pointing at 0.2) may describe it as "slightly warm" and the blue arrow (pointing at 0.8) "fairly cold"."

在这幅图中,cold(冷)、warm(暖)和hot(热)表达式的含义由映射一个温标的函数表示。这个尺度上的一个点有三个“真值”,三个函数各有一个。图像中的垂直线表示三个箭头(真值)测量的特定温度。由于红色箭头指向零,这个温度可以解释为“不热”。 橙色的箭头(指向0.2)可以描述为“轻微温暖”,蓝色的箭头(指向0.8)可以描述为“轻微温暖”或“很冷”。

我们为什么要展示这个?因为,这个图表和描述非常准确地描述了什么是模糊逻辑。我们将使用AForge.NET开源机器学习框架。这是一个很好的框架,它展示了使用推理引擎完成任务是多么容易。

在这一章中,我们将讨论:

  1. 模糊逻辑
  2. 避障与识别
  3. AGV

模糊逻辑

我们的应用程序将有两个简单的按钮,一个用于运行模糊集测试,另一个用于运行语言变量测试。下面是示例应用程序的快速快照:

创建此示例的代码相对较小且简单。这是当我们点击Run Fuzzy Set Test按钮时的样子。我们将创建两个模糊集(一个用于凉爽,一个用于温暖),并为每个模糊集添加一些成员数据值,然后绘制它们:

    #region 创建两个模糊集来表示凉和暖的温度
            #region 凉
            TrapezoidalFunction function1 = new TrapezoidalFunction(13, 18, 23, 28);
            FuzzySet fsCool = new FuzzySet("凉", function1);
            double[,] coolValues = new double[20, 2];
            for (int i = 10; i < 30; i++)
            {
                coolValues[i - 10, 0] = i;
                coolValues[i - 10, 1] = fsCool.GetMembership(i);
            }
            chart?.UpdateDataSeries("凉", coolValues);
            #endregion 凉
            #region 暖
            TrapezoidalFunction function2 = new TrapezoidalFunction(23, 28, 33, 38);
            FuzzySet fsWarm = new FuzzySet("暖", function2);
            double[,] warmValues = new double[20, 2];
            for (int i = 20; i < 40; i++)
            {
                warmValues[i - 20, 0] = i;
                warmValues[i - 20, 1] = fsWarm.GetMembership(i);
            }
            chart?.UpdateDataSeries("暖", warmValues);
            #endregion 暖
    #endregion 创建两个模糊集来表示凉和暖的温度

运行语言变量测试的代码如下。同样,我们创建模糊集,但这次我们创建4个而不是2个。与我们的第一个测试一样,我们首先添加成员数据,然后绘图:

       LinguisticVariable lvTemperature = new LinguisticVariable("温度", 0, 80);
            TrapezoidalFunction function1 = new TrapezoidalFunction(10, 15, TrapezoidalFunction.EdgeType.Right);
            FuzzySet fsCold = new FuzzySet("冷", function1);
            lvTemperature.AddLabel(fsCold);
            TrapezoidalFunction function2 = new TrapezoidalFunction(10, 15, 20, 25);
            FuzzySet fsCool = new FuzzySet("凉", function2);
            lvTemperature.AddLabel(fsCool);
            TrapezoidalFunction function3 = new TrapezoidalFunction(20, 25, 30, 35);
            FuzzySet fsWarm = new FuzzySet("暖", function3);
            lvTemperature.AddLabel(fsWarm);
            TrapezoidalFunction function4 = new TrapezoidalFunction(30, 35, TrapezoidalFunction.EdgeType.Left);
            FuzzySet fsHot = new FuzzySet("热", function4);
            lvTemperature.AddLabel(fsHot);
            double[][,] chartValues = new double[4][,];
            for (int i = 0; i < 4; i++)
                chartValues[i] = new double[160, 2]; 

最后我们画出这些值:

        #region 语言变量的形状——它的标签从开始到结束的形状
            int j = 0;
            for (float x = 0; x < 80; x += 0.5f, j++)
            {
                double y1 = lvTemperature.GetLabelMembership("冷", x);
                double y2 = lvTemperature.GetLabelMembership("凉", x);
                double y3 = lvTemperature.GetLabelMembership("暖", x);
                double y4 = lvTemperature.GetLabelMembership("热", x);

                chartValues[0][j, 0] = x;
                chartValues[0][j, 1] = y1;
                chartValues[1][j, 0] = x;
                chartValues[1][j, 1] = y2;
                chartValues[2][j, 0] = x;
                chartValues[2][j, 1] = y3;
                chartValues[3][j, 0] = x;
                chartValues[3][j, 1] = y4;
            }
            chart.UpdateDataSeries("冷", chartValues[0]);
            chart.UpdateDataSeries("凉", chartValues[1]);
            chart.UpdateDataSeries("暖", chartValues[2]);
            chart.UpdateDataSeries("热", chartValues[3]);
         #endregion 语言变量的形状——它的标签从开始到结束的形状 

语言变量的形状:

正如所看到的,我们能够很容易地展示出维基百科定义所展示的视觉定义。

模糊的自主移动小车

在我们继续之前,先看一下我们的应用程序将是什么样子的,然后对推理引擎进行简要的解释:

尽管AForge.NET使得我们很容易和透明的创建一个InferenceSystem(推理系统)对象。

让我先来解释下什么是模糊推理系统。模糊推理系统是一种能够进行模糊计算的模型。这是使用数据库、语言变量和规则库完成的,所有这些都可以存储在内存中。

模糊推理系统的典型操作如下:

  1. 获取数字输入
  2. 结合激活规则的结果得到一个模糊输出
  3. 验证输入激活了来自规则库的哪些规则
  4. 利用带有语言变量的数据库获取每个数字输入的语言意义

对于我们来讲,大部分工作将在初始化我们的模糊逻辑系统时进行。让我们将其分解为前面概述的各个步骤。

首先,我们需要准备语言标签(模糊集)组成的距离。他们分别是Near(近)、Medium(中)和Far(远)。

            #region 构成这些距离的语言标签(模糊集)

            FuzzySet fsNear = new FuzzySet("Near", new TrapezoidalFunction(15, 50, TrapezoidalFunction.EdgeType.Right));

            FuzzySet fsMedium = new FuzzySet("Medium", new TrapezoidalFunction(15, 50, 60, 100));

            FuzzySet fsFar = new FuzzySet("Far", new TrapezoidalFunction(60, 100, TrapezoidalFunction.EdgeType.Left));

            #endregion 构成这些距离的语言标签(模糊集)        

接下来,我们初始化所需的语言变量。第一个是lvRight,它将是右侧测量距离的变量:

            #region 右侧测量距离(输入)

            LinguisticVariable lvRight = new LinguisticVariable("RightDistance", 0, 120);

            lvRight.AddLabel(fsNear);

            lvRight.AddLabel(fsMedium);

            lvRight.AddLabel(fsFar);

            #endregion 右侧测量距离(输入)     

现在,我们对左侧测量距离的变量做同样的操作:

        #region 左侧测量距离(输入)

            LinguisticVariable lvLeft = new LinguisticVariable("LeftDistance", 0, 120);

            lvLeft.AddLabel(fsNear);

            lvLeft.AddLabel(fsMedium);

            lvLeft.AddLabel(fsFar);

        #endregion 左侧测量距离(输入)

最后一个语言变量是前方测量距离:

        #region 前方测量距离(输入)

            LinguisticVariable lvFront = new LinguisticVariable("FrontalDistance", 0, 120);

            lvFront.AddLabel(fsNear);

            lvFront.AddLabel(fsMedium);

            lvFront.AddLabel(fsFar);

        #endregion 前方测量距离(输入) 

现在我们关注组成这个角度的语言标签(模糊集)。我们需要完成这一步,这样才能创建最终的语言变量:

      #region 组成角度的语言标签(模糊集)

            FuzzySet fsVN = new FuzzySet("VeryNegative", new TrapezoidalFunction(-40, -35, TrapezoidalFunction.EdgeType.Right));

            FuzzySet fsN = new FuzzySet("Negative", new TrapezoidalFunction(-40, -35, -25, -20));

            FuzzySet fsLN = new FuzzySet("LittleNegative", new TrapezoidalFunction(-25, -20, -10, -5));

            FuzzySet fsZero = new FuzzySet("Zero", new TrapezoidalFunction(-10, 5, 5, 10));

            FuzzySet fsLP = new FuzzySet("LittlePositive", new TrapezoidalFunction(5, 10, 20, 25));

            FuzzySet fsP = new FuzzySet("Positive", new TrapezoidalFunction(20, 25, 35, 40));

            FuzzySet fsVP = new FuzzySet("VeryPositive", new TrapezoidalFunction(35, 40, TrapezoidalFunction.EdgeType.Left));

        #endregion 组成角度的语言标签(模糊集) 

现在我们可以创建角度的最终语言变量:

        #region 角

            LinguisticVariable lvAngle = new LinguisticVariable("Angle", -50, 50);

            lvAngle.AddLabel(fsVN);

            lvAngle.AddLabel(fsN);

            lvAngle.AddLabel(fsLN);

            lvAngle.AddLabel(fsZero);

            lvAngle.AddLabel(fsLP);

            lvAngle.AddLabel(fsP);

            lvAngle.AddLabel(fsVP);

        #endregion 角 

现在我们可以继续创建我们的模糊数据库。对于我们的应用程序,这是一个语言变量的内存字典,如果您愿意的话可以将其实现为SQL、NoSQL或任何其他类型的具体数据库。

        #region 数据库

            Database fuzzyDB = new Database();

            fuzzyDB.AddVariable(lvFront);

            fuzzyDB.AddVariable(lvLeft);

            fuzzyDB.AddVariable(lvRight);

            fuzzyDB.AddVariable(lvAngle);

        #endregion 数据库 

接下来,我们将创建主推理引擎。下一行代码中最有趣的是CentroidDifuzzifier。在推理过程的最后,我们需要一个数值来控制过程的其他部分。为了得到这个数字,我们采用了一种去模糊化的方法。

我们的模糊推理系统的输出是一组点火强度大于零的规则。这种点火强度对随后的模糊规则集施加了约束。当我们把这些模糊集合放在一起时,它们会形成一个形状,这就是语言输出的意义。重心法将计算我们的形状面积的中心,以获得输出的数值表示。它使用近似数,所以会选择几个区间进行计算。随着时间间隔的增加,输出的精度也会增加:

// 创建推理系统
IS = new InferenceSystem(fuzzyDB, new CentroidDefuzzifier(1000));

接下来,我们可以开始向我们的推理系统添加规则:

            // 直走
            IS.NewRule("规则 1", "IF FrontalDistance IS Far THEN Angle IS Zero");

            // 直走(如果可以走到任何地方)
            IS.NewRule("规则 2", "IF FrontalDistance IS Far AND RightDistance IS Far AND LeftDistance IS Far THEN Angle IS Zero");

            // 右侧有墙
            IS.NewRule("规则 3", "IF RightDistance IS Near AND LeftDistance IS Not Near THEN Angle IS LittleNegative");

            // 左侧有墙
            IS.NewRule("规则 4", "IF RightDistance IS Not Near AND LeftDistance IS Near THEN Angle IS LittlePositive");

            // 前方有墙 - 房间在右侧
            IS.NewRule("规则 5", "IF RightDistance IS Far AND FrontalDistance IS Near THEN Angle IS Positive");

            // 前方有墙 - 房间在左侧
            IS.NewRule("规则 6", "IF LeftDistance IS Far AND FrontalDistance IS Near THEN Angle IS Negative");

            // 前方有墙 -两边都是房间 - 向右走
            IS.NewRule("规则 7", "IF RightDistance IS Far AND LeftDistance IS Far AND FrontalDistance IS Near THEN Angle IS Positive");

经过所有这些工作,我们的推理系统就已经准备好了!

            if (FirstInference)
                GetMeasures();

            try
            {
                DoInference();

                MoveAGV();

                GetMeasures();
            }

            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            } 

应用程序的主代码循环如下所示。我们将详细描述每个功能:

让我们快速看一下GetMeasures函数。获取当前地图以及AGV的位置后,调用HandleAGVOnWall函数,用于处理AGV碰到墙壁无法移动的情况。在这之后,DrawAGV在地图中绘制AGV。最后,RefreshTerrain 刷新地图:

        /// <summary>
        /// 得到传感器的测量结果
        /// </summary>
        private void GetMeasures()

        {
            #region 获得自主移动小车的位置

            pbTerrain.Image = CopyImage(OriginalMap);

            Bitmap b = (Bitmap)pbTerrain.Image;

            Point pPos = new Point(pbRobot.Left - pbTerrain.Left + 5, pbRobot.Top - pbTerrain.Top + 5);

            #endregion 获得自主移动小车的位置

            HandleAGVOnWall(b, pPos);

            DrawAGV(pPos, b);

            RefreshTerrain();
        } 

DrawAGV向左和向右遇到任何障碍时,如果选中Show beam复选框,就会看到前面、左边和右边的避束检测器显示:

        /// <summary>
        /// 绘制AGV
        /// </summary>
        /// <param name="pPos">坐标</param>
        /// <param name="b">位图</param>
        private void DrawAGV(Point pPos, Bitmap b)
        {
            Point pFrontObstacle = GetObstacle(pPos, b, -1, 0);

            Point pLeftObstacle = GetObstacle(pPos, b, 1, 90);

            Point pRightObstacle = GetObstacle(pPos, b, 1, -90);

            #region 显示线束

            Graphics g = Graphics.FromImage(b);

            if (cbLasers.Checked)
            {
                g.DrawLine(new Pen(Color.Red, 1), pFrontObstacle, pPos);

                g.DrawLine(new Pen(Color.Red, 1), pLeftObstacle, pPos);

                g.DrawLine(new Pen(Color.Red, 1), pRightObstacle, pPos);
            }

            #endregion 显示线束

            #region 绘制AGV

            if (btnRun.Text != RunLabel)
            {
                g.FillEllipse(new SolidBrush(Color.Blue), pPos.X - 5, pPos.Y - 5, 10, 10);
            }

            g.DrawImage(b, 0, 0);

            g.Dispose();

            #endregion 绘制AGV

            #region 更新显示的距离

            txtFront.Text = GetDistance(pPos, pFrontObstacle).ToString();

            txtLeft.Text = GetDistance(pPos, pLeftObstacle).ToString();

            txtRight.Text = GetDistance(pPos, pRightObstacle).ToString();

            #endregion 更新显示的距离
    } 

DoInference函数运行我们的模糊推理系统的一个历元(实例、生成等等)。最终,它负责确定AGV的下一个角度。

        /// <summary>
        /// 运行模糊推理系统的一个纪元
        /// </summary>
        private void DoInference()
        {
            // 输入设置
            IS?.SetInput("RightDistance", Convert.ToSingle(txtRight.Text));

            IS?.SetInput("LeftDistance", Convert.ToSingle(txtLeft.Text));

            IS?.SetInput("FrontalDistance", Convert.ToSingle(txtFront.Text));

            // 输出设置
            try
            {
                double NewAngle = IS.Evaluate("Angle");

                txtAngle.Text = NewAngle.ToString("##0.#0");

                Angle += NewAngle;
            }
            catch (Exception)
            {
            }
       } 

MoveAGV函数负责将AGV移动一步。如果您检查了跟踪路径的话,会发现这个函数中大约50%的代码时用于绘制AGV的历史轨迹的。

        /// <summary>
        /// 移动AGV
        /// </summary>
        private void MoveAGV()
        {
            double rad = ((Angle + 90) * Math.PI) / 180;

            int Offset = 0;

            int Inc = -4;

            Offset += Inc;

            int IncX = Convert.ToInt32(Offset * Math.Cos(rad));

            int IncY = Convert.ToInt32(Offset * Math.Sin(rad));

            if (cbTrajeto.Checked)
            {
                Graphics g = Graphics.FromImage(OriginalMap);

                Point p1 = new Point(pbRobot.Left - pbTerrain.Left + pbRobot.Width / 2, pbRobot.Top - pbTerrain.Top + pbRobot.Height / 2);

                Point p2 = new Point(p1.X + IncX, p1.Y + IncY);

                g.DrawLine(new Pen(new SolidBrush(Color.Green)), p1, p2);

                g.DrawImage(OriginalMap, 0, 0);

                g.Dispose();
            }

            pbRobot.Top = pbRobot.Top + IncY;

            pbRobot.Left = pbRobot.Left + IncX;
       } 

主要应用与显示光束的选择:

随着应用程序的运行,AGV成功导航障碍物,显示路径和光束。角度是AGV当前所面对的角度,传感器读数与前、左、右波束传感器有关:

我们的AGV成功地完成了穿越障碍,并继续运行:

轨迹路径和显示光束可单独选择:

总结

在这一章中,我们学习了各种类型的模糊逻辑实现,并看到了使用AForge.NET将这种逻辑添加到我们的应用程序中是多么的容易。在我们的下一章中,我们将开始深入研究自组织地图,将我们的机器学习技能提升到一个新的层次。如果你还记得小学时上的美术课,这一章一定会给你带来回忆。

原文地址:https://www.cnblogs.com/wangzhenyao1994/p/10263467.html

时间: 2024-08-12 10:49:45

基于C#的机器学习--模糊逻辑-穿越障碍的相关文章

[转]基于图的机器学习技术:谷歌众多产品和服务背后的智能

近来机器学习领域实现了很多重大的进展,这些进展让计算机系统具备了解决复杂的真实世界问题的能力.其中,谷歌的机器学习又是怎样的 ? 近来机器学习领域实现了很多重大的进展,这些进展让计算机系统具备了解决复杂的真实世界问题的能力.其中之一是谷歌的大规模的.基于图(graph-based)的机器学习平台,该平台由Google Research的Expander团队打造. 基于图的机器学习支持着你可能每天都在使用的谷歌产品和功能,这项技术是一种强大的工具,可以被用于驱动Inbox的提醒功能和Allo的智能

基于JavaScript的机器学习系统

我喜欢基于web应用来工作.实现一个从任何地方.任何设备都可运行的应用,是十分有吸引力.在过去的几个月,我一直在尝试获取一些在Javascript上运行的基础轻量级机器学习算法,然后用它们构建”智能的”web应用.随着Node的出现,在服务器端训练模型进而用这些模型在客户端做预测已经成为可能.我研究了一些类库(工具),它们确实有助于实现基于Javascript的机器学习.现在我们看看都有哪些类库: Brain.js 神经网络类库 svmjs  支持向量机类库 Forest.js 实现随机树Jav

UVA 1600 Patrol Robot(机器人穿越障碍最短路线BFS)

UVA 1600 Patrol Robot Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Description A robot has to patrol around a rectangular area which is in a form of mxn grid (m rows and n columns). The rows are labeled from 1 to m. The colu

走在大数据的边缘 基于Spark的机器学习-智能客户系统项目实战(项目实战)

38套大数据,云计算,架构,数据分析师,Hadoop,Spark,Storm,Kafka,人工智能,机器学习,深度学习,项目实战视频教程 视频课程包含: 38套大数据和人工智能精品高级课包含:大数据,云计算,架构,数据挖掘实战,实时推荐系统实战,电视收视率项目实战,实时流统计项目实战,离线电商分析项目实战,Spark大型项目实战用户分析,智能客户系统项目实战,Linux基础,Hadoop,Spark,Storm,Docker,Mapreduce,Kafka,Flume,OpenStack,Hiv

基于Spark的机器学习实践 (九) - 聚类算法

0 相关源码 1 k-平均算法(k-means clustering)概述 1.1 回顾无监督学习 ◆ 分类.回归都属于监督学习 ◆ 无监督学习是不需要用户去指定标签的 ◆ 而我们看到的分类.回归算法都需要用户输入的训练数据集中给定一个个明确的y值 1.2 k-平均算法与无监督学习 ◆ k-平均算法是无监督学习的一种 ◆ 它不需要人为指定一个因变量,即标签y ,而是由程序自己发现,给出类别y ◆ 除此之外,无监督算法还有PCA,GMM等 源于信号处理中的一种向量量化方法,现在则更多地作为一种聚类

基于Apache Spark机器学习的客户流失预测

流失预测是个重要的业务,通过预测哪些客户可能取消对服务的订阅来最大限度地减少客户流失.虽然最初在电信行业使用,但它已经成为银行,互联网服务提供商,保险公司和其他垂直行业的通用业务. 预测过程是大规模数据的驱动,并且经常结合使用先进的机器学习技术.在本篇文章中,我们将看到通常使用的哪些类型客户数据,对数据进行一些初步分析,并生成流失预测模型 - 所有这些都是通过Spark及其机器学习框架来完成的. 使用数据科学更好地理解和预测客户行为是一个迭代过程,其中涉及: 1.发现和模型创建: 分析历史数据.

基于C#的机器学习--机器学习的基本知识

机器学习的基本知识 作为一个终生的微软开发人员,我经常看到开发人员努力寻找解决日常问题所需的资源.让我们面对现实吧,我们没有人有时间按照自己喜欢的方式做事,我们中很少有人有幸在真正的研发部门工作.虽然这些年来我们已经走过了相当长的一段旅程,还记得曾经我们通过桌上的C程序员参考资料和其他50本书中翻找资料,到现在能够在谷歌中快速搜索并得到我们想要的东西.但现在人工智能时代已经到来,情况有些不同了. 作为c#开发人员,当涉及到机器学习时,谷歌搜索并不总是我们最好的朋友,因为几乎所有正在使用的东西都是

基于C#的机器学习--贝叶斯定理-执行数据分析解决肇事逃逸之谜

贝叶斯定理-执行数据分析解决肇事逃逸之谜 ? 在这一章中,我们将: 应用著名的贝叶斯定理来解决计算机科学中的一个非常著名的问题. 向您展示如何使用贝叶斯定理和朴素贝叶斯来绘制数据,从真值表中发现异常值等等 贝叶斯定理概况 当我们使用贝叶斯定理的时候,我们是在测量一件事发生的概论程度: ? 上式表示在给定事件B的情况下事件A发生的概率. 概率通常被量化为0和1之间的一个数,包括这两者;0表示不可能,1表示绝对肯定.概率越大,确定性越大.掷骰子得到6的概率和掷硬币得到正面的概率这两个例子你们肯定很熟

基于C#的机器学习--c# .NET中直观的深度学习

在本章中,将会学到: l  如何使用Kelp.Net来执行自己的测试 l  如何编写测试 l  如何对函数进行基准测试 Kelp.Net是一个用c#编写的深度学习库.由于能够将函数链到函数堆栈中,它在一个非常灵活和直观的平台中提供了惊人的功能.它还充分利用OpenCL语言平台,在支持cpu和gpu的设备上实现无缝操作.深度学习是一个非常强大的工具,对Caffe和Chainer模型加载的本机支持使这个平台更加强大.您将看到,只需几行代码就可以创建一个100万个隐藏层的深度学习网络. Kelp.Ne