几种常用温控算法的比较与总结

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了几种常用温控算法的比较与总结相关的知识,希望对你有一定的参考价值。

参考技术A         最近在做一个有关大气VOCs实时监测的项目,由于该项目要求控温精度在0.1度之内,所以就研究了一下有关温控的算法,我们知道对于一些大惯性的系统,比如加热炉、智能小车中都会用到PID(比例、积分和微分)算法,而PID算法分为二值式、位置式、增量式和分段式,当然也有模糊式等。现根据在实际项目中的应用情况将其总结如下:

        (1)二值式

        二值式温控算法只存在两个状态,不是开,就是关。常用在一些控温精度不高的场合。

        (2)位置式

        位置式PID算法由于计算量比较大,降低了单片机的运行速度,需要单片机比较大的内存,所以在实际应用中应用的比较少,除非有特除要求的场合。

        (3)增量式

        增量式PID算法相比二值式控温精度比较高,相比位置式计算量减少了许多,提高了单片机的运行速度,也增大了单片机的选择余地(内存要求降低)。为了提高温控的速度,减少温控所需要的时间,所以该增加式PID算法常与BangBang算法、大林算法相结合使用。BangBang算法和大林算法即是全功率加热,比如BangBang-PID算法通过会有一个阈值,一旦采用BangBang或大林算法升温到阈值时,就会自动切换到增量式PID算法进行控温。另外该阈值的选择是个难点,阈值小了,升温时间比较长,阈值大了,过冲量比较大,所以说该阈值的选择需要从以下两个方面去确定:升温速率、距离设定值的差值大小等方面。

        (4)分段式

        分段式PID算法虽然比模糊PID算法差一些,但是模糊PID控制大多数还停留在理论阶段,应用到实际系统的还比较少,控制效果如何还不是很确定。分段式PID算法在某些方面与模糊式PID算法有很多相近的地方,也是对信号进行阈值的划分,然后在不同的阈值阶段采用不同的控制参数。分段PID优于模糊PID的地方在于我们现有的工控机在编辑控制算法时是数字式的,模糊PID算法要想实现其功能除了要进行数据的离散化外,其用到的数据参数也比较多导致统计起来比较麻烦,经过以上对比分析,从系统的可实现性方面考虑,还是采用分段式PID算法的比较多些。

        根据项目的实际控制结果表明单纯的采用单一的PID参数进行调节要想达到较为理想的控制效果是不容易的。所以可以根据控制对象的实际情况及偏差的大小,在不同的控制阶段给定不同的PID调节参数,这样可以在偏差大的时候加大比例调节,降低积分作用,偏差小的时候减少比例作用,加大积分作用。这样既可以增加响应速度,超调量也不会太大,这就是分段PID的控制思想。  下面对普通PID与分段PID在同一控制变量下做出的反应做一下对比,他们的输出曲线如下图:

        在上图输出曲线中可以看出在目标值情况相同的情况下,分段PID的响应速度更快,达到目标值时分段PID比普通PID所用的时间少一半,所用控制系统的快速性被分段PID明显提高了。采用分段PID即是将一个控制过程进行分段控制,可以避免采用单一PID控制时对误差积累较多的缺点(采用单一PID算法时,刚开始启动时目标值与实际值的差值会很大,如果有积分变量的话,积分变量大了会导致较大的积累偏差,导致消除困难,造成系统较大的系统超调;积分变量小了会导致精差消除较慢。),这样在每一阶段都对误差进行消除,最后误差结果会小很多。分段PID算法的实现步骤:这里假定阈值a为偏差的50%,阈值b为偏差的30%。

        a、根据工程需要设置阈值a>b>0;

        b、当偏差较大,且偏差大于等于a时,采用PD控制,可加快系统响应;

        c、当偏差较小,且大于b,小于a时采用PI控制;

        d、当偏差小于b时,采用PID控制(P设的小些,I设的大些),可减少系统精差。

        以上是对几种常用PID算法的比较和总结,在实际的项目中用的比较多的是增量式PID算法和分段式PID算法,分段式PID算比单一的增量式PID算法控温速度快,精度更高,虽然分段PID算法参数整定比较繁琐些,但鉴于它的控制速度快、精度高,还是推荐使用分段PID算法应用于温度控制、电机控制等领域或项目中。

常用排序算法总结

排序算法

1. 排序算法概述

1.1 什么是排序算法?

对一序列对象根据某个关键字,按照某种规则进行排序

1.2、排序术语

  • 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面
  • 不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面
  • 内排序:所有排序操作都在内存中完成
  • 外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行
  • 时间复杂度: 一个算法执行所耗费的时间。
    空间复杂度:运行完一个程序所需内存的大小

1.3 算法总结

技术图片

  • (注意:n指数据规模;k指“桶”的个数;In-place指占用常数内存,不占用额外内存;Out-place指占用额外内存)

1.4 算法分类

技术图片

1.5 比较排序与非比较排序

1.5.1 比较排序

  • 常见的快速排序、归并排序、堆排序、冒泡排序等属于比较排序。在排序的最终结果里,元素之间的次序依赖于它们之间的比较。每个数都必须和其他数进行比较,才能确定自己的位置。在冒泡排序之类的排序中,问题规模为n,又因为需要比较n次,所以平均时间复杂度为(O(n2))。在归并排序、快速排序之类的排序中,问题规模通过分治法消减为(log n)次,所以时间复杂度平均(O(nlogn))。比较排序的优势是,适用于各种规模的数据,也不在乎数据的分布,都能进行排序。可以说,比较排序适用于一切需要排序的情况。

1.5.2 非比较排序

  • 计数排序、基数排序、桶排序则属于非比较排序。非比较排序是通过确定每个元素之前,应该有多少个元素来排序。针对数组arr,计算arr[i]之前有多少个元素,则唯一确定了arr[i]在排序后数组中的位置。非比较排序只要确定每个元素之前的已有的元素个数即可,所有一次遍历即可解决。算法时间复杂度(O(n))。非比较排序时间复杂度底,但由于非比较排序需要占用空间来确定唯一位置。所以对数据规模和数据分布有一定的要求

2. 排序算法具体实现

2.1 冒泡排序

2.1.1 算法思想

  • 冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

2.1.2 算法描述

  1. 比较相邻的元素。如果第一个比第二个大,就交换它们两个

  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;

  3. 针对所有的元素重复以上的步骤,除了最后一个;

  4. 重复步骤1~3,直到排序完成。

    从后往前将最小的排到前面
    技术图片
    从前往后将最大的拍到后面
    技术图片

2.1.3 算法实现

(1)数组线性表
/**
 * 冒泡排序-数组线性表排序算法
 */
public void bubbleSort(){
    if(this.size <= 1){
        return ;
    }
    for(int i = 0;i<this.size;i++){
        boolean didSwap = false;
        for(int j = 0;j<this.size-i-1;j++){
            if(((Comparable<E>)objects[j+1]).compareTo((E)objects[j])<0){
                Comparable<E> temp =(Comparable<E>) objects[j+1];
                objects[j+1] = objects[j];
                objects[j] = temp;
                didSwap = true;
            }
        }
        if(!didSwap){
            return ;
        }
    }
}
(2)链式线性表
/**
 * 冒泡排序-链式表排序算法
 */
public void bubbleSort(){
    if(this.head.next == null||this.head.next.next==null){
        return ;
    }
    //后面已经浮上来的(排好序的)头结点
    Node<E> sortedHead = null;
    //第一个存储元素的结点
    Node<E> first = head.next;
    for(Node<E> outCur = head.next;outCur!=null;outCur = outCur.next){
        Node<E> cur = first;
        for(Node<E> afterCur = first.next;afterCur!=sortedHead;afterCur = afterCur.next,cur = cur.next){
            if(cur.elem.compareTo(afterCur.elem)>0){
                Node.swapElem(cur, afterCur);
            }
        }
        sortedHead = cur;
    }
}

2.1.4 算法分析

  • 最佳情况:(T(n) = O(n))
    • 此时数组为正序排列,如果第一次检测没有发生交换,则可以认为为正序排列,则可以直接退出
  • 最差情况:(T(n) = O(n^2))
    • 逆序排列,对于每个位置都需要进行交换
  • 平均情况:(T(n) = O(n^2))

2.2排序

2.2.1 算法思想

  • 选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

2.2.2 算法描述

n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下:

  1. 初始状态:无序区为(R[1..n]),有序区为空;

  2. 第i趟排序((i=1,2,3…n-1))开始时,当前有序区和无序区分别为(R[1..i-1])(R(i..n))。该趟排序从当前无序区中-选出关键字最小的记录 (R[k]),将它与无序区的第1个记录R交换,使(R[1..i])(R[i+1..n))分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;

  3. n-1趟结束,数组有序化了。

技术图片

技术图片

2.2.3 算法实现

(1)数组线性表

public class SelectionSorter<E extends Comparable<E>> implements Sorter<E> {
    @Override
    public void sort(E[] elemArr){
        for(int i = 0;i < elemArr.length;i++){
            int minIndex = i;
            for(int j = i;j<elemArr.length;j++){
                if(elemArr[j].compareTo(elemArr[minIndex])<0){
                    minIndex = j;
                }
            }
            E temp = elemArr[i];
            elemArr[i] = elemArr[minIndex];
            elemArr[minIndex] = temp;
        }
    }
}

(2)链式线性表

/**
 * 插入排序
 */
public void selectionSort(){
    if(this.head.next==null||this.head.next.next==null){
        return ;
    }

    for(Node<E> outCur = head.next;outCur!=null;outCur = outCur.next){

        Node<E> innerCur = outCur;
        Node<E> afterInnerCur = innerCur.next;
        Node<E> minNode = outCur;
        for(innerCur = outCur;afterInnerCur!=null;innerCur = innerCur.next,afterInnerCur = afterInnerCur.next){
            if(innerCur.elem.compareTo(minNode.elem)<0){
                minNode = innerCur;
            }
        }
        Node.swapElem(outCur,minNode);
    }
}

2.3 插入排序

2.3.1 算法思想

  • 插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。
  • 它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入
  • 插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

2.3.2 算法描述

  1. 从第一个元素开始,该元素可以认为已经被排序

  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描

  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置

  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置

  5. 将新元素插入到该位置后

  6. 重复步骤2~5

    技术图片

    技术图片

2.3.3 算法实现

public class InsertSorter<E extends Comparable<E>> implements Sorter<E> {
    @Override
    public void sort(E[] arr) {
        for (int i = 1; i < arr.length; i++) {
            for (int j = i;j > 0;--j) {
                if (arr[j].compareTo(arr[j - 1]) < 0) {
                    E temp = arr[j];
                    arr[j] = arr[j - 1];
                    arr[j - 1] = temp;
                } else {
                    break;
                }
            }
        }
    }
}

2.3.4 算法分析

  • 最佳情况:(T(n) = O(n)) 序列本身有序(每次选取的第一个无序点都大于有序序列的最大值)
  • 最差情况:(T(n) = O(n^2)) 序列为逆序(每次选取的无序序列都需要移动到有序序列最前面(1+2+...+(n-1)=frac{(n-1)n}{2})
  • 平均情况:(T(n) = O(n^2))

2.4 希尔排序

2.4.1 算法思想

  • 希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破(O(n^2))的第一批算法之一。
  • 它与插入排序的不同之处在于,它会优先比较距离较远的元素。
  • 希尔排序又叫缩小增量排序。希尔排序是把记录按下表的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止
  • 该方法实质上是一种分组插入方法
  • 增量序列的最后一个增量值必须等于1

2.4.2 算法描述

  1. 选择增量(gap=length/2),缩小增量继续以(gap = gap/2)的方式,这种增量选择我们可以用一个序列来表示,(T={n/2,(n/2)/2,...,1}),称为增量序列。
    • 采用上述增量序列因为增量序列难以通过数学证明求出,所以选择一个常用的增量序列 ,上述增量序列也被称为希尔增量。
      • 但其实这个增量序列不是最优的
  2. 按增量序列个数k,对序列进行k 趟排序
  3. 每趟排序,根据对应的增量ti,将待排序列分成若干长度为m 的子序列,分别对各子表进行直接插入排序。
  4. 仅增量因子为1时,整个序列作为一个表来处理,表长度即为整个序列的长度
    技术图片
  5. 技术图片

2.4.3 算法实现

package com.sort;


/**
 * @author Ni187
 */
public class ShellSorter<E extends Comparable<E>> implements Sorter<E> {
    @Override
    public void sort(E[] arr) {
        for (int gap = arr.length / 2; gap > 0; gap /= 2){
            for(int i=gap; i<arr.length; i++){
                for(int j=i-gap; j>=0&&arr[j].compareTo(arr[j+gap])>0; j=j-gap){
                    E temp = arr[j];
                    arr[j] = arr[j+gap];
                    arr[j+gap] = temp;
                }
            }
        }
    }
}


2.4.4 算法分析

  • 最佳情况:(T(n)=O(n^2))
  • 最坏情况:(T(n)=O(nlog(2n)))
  • 平均情况:(T(n)=O(nlog(2n)))

2.5 归并排序

2.5.1 算法思想

  • 归并排序是建立在归并操作上的一种有效的排序算法。
  • 该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
  • 归并排序是一种稳定的排序方法。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并

2.5.2 算法描述

  1. 把长度为n的输入序列分成两个长度为n/2的子序列

  2. 对这两个子序列分别采用归并排序

  3. 将两个排序好的子序列合并成一个最终的排序序列
    技术图片

    技术图片

2.5.3 算法实现

package com.sort;

import java.util.Arrays;

/**
 * @author Ni187
 */
public class MergeSorter<E extends Comparable<E>> implements Sorter<E> {

    @Override
    public void sort(E[] arr) {
        Object[] mergedArr = new Object[arr.length];
        sort(arr, 0, arr.length-1,mergedArr);
    }

    public void sort(E[] arr, int left, int right,Object[] mergedArr) {
        if (right<=left) {
            return;
        }
        int mid = (left + right) / 2;
        sort(arr, left, mid,mergedArr);
        sort(arr, mid+1 , right,mergedArr);
        merge(arr, left, mid, right,mergedArr);
    }

    private void merge(E[] arr, int left, int mid, int right,Object[] mergedArr) {
        int i = left;
        int j = mid + 1;
        int index = 0;
        while (i <= mid && j <= right) {
            if (arr[i].compareTo(arr[j]) <= 0) {
                mergedArr[index++] = arr[i++];
            } else {
                mergedArr[index++] = arr[j++];
            }
        }
        while (i <= mid) {
            mergedArr[index++] = arr[i++];
        }
        while (j <= right) {
            mergedArr[index++] = arr[j++];
        }
        index = 0;
        while(left <= right){
            arr[left++] = (E)mergedArr[index++];
        }
    }

}

2.5.5 算法分析

  • 每次合并操作的平均时间复杂度为(O(n)),而完全二叉树的深度为(logn)。总的平均时间复杂度为(O(nlogn))
  • 归并排序的最好,最坏,平均时间复杂度均为(O(nlogn))
  • 由于使用辅助数组来存储归并的数组,空间复杂度为(O(n))

2.6 快速排序

2.6.1 算法思想

  • 通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序

2.6.2 算法描述

  1. 从数列中挑出一个元素,称为 “基准”(pivot);
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作
    • 挖坑法
    • 交换法
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序
    技术图片

2.6.3 算法实现

  • 此处使用交换法实现
package com.sort;

import java.util.Random;

/**
 * @author Ni187
 */
public class QuickSorter<E extends Comparable<E>> implements Sorter<E> {
    @Override
    public void sort(E[] arr) {
        QuickSort(arr, 0, arr.length-1);
    }

    public  void QuickSort(E[] array, int start, int end) {
        if (start > end) {
            return ;
        }
        int smallIndex = partition(array, start, end);
        QuickSort(array, start, smallIndex - 1);
        QuickSort(array, smallIndex + 1, end);
    }

    public int partition(E[] array, int left, int right) {
        int pivot = (int) (left + Math.random() * (right - left));
        E basic = array[pivot];
        while(left!=right){
            while(left < right && array[right].compareTo(basic)>=0){
                right--;
            }
            while(left < right && array[left].compareTo(basic)<=0){
                left++;
            }
            swap(array, left, right);
        }
        array[left]=basic;
        return left;
    }

    public void swap(E[] array, int i, int j) {
        E temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}

2.5.4 算法分析

  • 最好情况:每次选择的基数恰好把两侧分开(左侧小于基数,右侧大于基数),每次扫描n,共分成logn,时间复杂度为(O(logn))
  • 最坏情况:每次选择的基数恰好位于两侧之一,每次扫描n,共分成n,时间复杂度为(O(n^2))
  • 平均:复杂度(O(nlog n))

以上是关于几种常用温控算法的比较与总结的主要内容,如果未能解决你的问题,请参考以下文章

js 常用的比较排序算法总结

几种常用加密算法比较

常用编程思想与算法

对比几种在ROS中常用的几种SLAM算法

几种常用加密算法比较 -- 转 achejq的专栏

温控算法的实现:四