Author Archives: Yunfei

如何构建和管理自己的知识系统

1. 背景

三年前,我在做硕士毕业设计的时候第一次使用Evernote(印象笔记)收集资料,之后就一直使用Evernote记录一些备忘和生活琐事,但也没有形成相对完整的系统。开始读博士之后,我习惯随手在论文上记批注和笔记,觉得这样个人观点和原文的耦合度会比较高。但是等到要检索和引用资料时,脑中对这些知识点有些印象,可很多资料就是找不到出处,这时又要重新谷歌或者去翻文件夹。这让我思考是否需要将笔记做成电子形式,这样检索和使用的时候会比较方便,知识也能线性积累。去纸化的过程是比较痛苦的,因为我们从小到大习惯了纸质笔记这种记录方式。我们的工具十分简单(纸和笔),却可以记录各种类别的信息(文字、结构图、流程图、批注)。然而到了电子时代,我们可用的工具很多,但却很难形成和纸质笔记相比的“灵活性”。当然,相比传统的笔记方式,电子笔记可以多次修改、任意排序和整理、发布和分享、互相引用与检索,但有的时候却不如传统笔记那么“直观”和“简单”(想想用pdf做标注 v.s. 在书上直接做批注 或是 用画图软件画一个流程图 v.s. 在纸上随手画一个流程图)。在过去的一年时间里,我一直在思考和实践电子化笔记系统的可行性,以及如何凸显和最大程度发挥电子笔记的优势。而我发现,电子笔记不仅有上述提到的几个优势,还可以成为构建和强化知识系统的有力工具。

这里,我将一切广义上可以提高人对某一事物或领域的认知或记忆的信息(以文字为主)称为知识,而将通过计算机、平板、手机等手段记录个性化知识的方式称为电子笔记。此文分享了我对于知识和记笔记的理解,以及通过电子化工具构建知识系统的思考和经验。

2. 为什么需要记笔记?

上一节聊了我为什么需要将笔记电子化的动机,现在简单说一下我们为什么需要记笔记。每个人都有这样的经历:和别人聊天时,总是有一些感觉在嘴边的东西,大脑却无法回忆起来;或者某天去超市购物时,明明觉得需要买什么,却怎么也想不起来。不管我们认不认同,人脑的潜力不是无限的。理解我们自身局限性的其中之一,就是认识到人脑是有局限性的,并且记忆的容量是有限的。所以构建一个笔记/知识信息系统,就是要补偿人脑的这种局限性,将笔记系统作为我们的第二大脑。这个观点已经得到了主流大众的认可。我们知道记笔记需要花费时间,这种时间实际上是一种对未来的投资。我们一般记录事情的动机有两个:一是为了增强对摄取信息的认知;二是为了日后需要的时候可以回想起来。大部分人也应该可以认同记笔记是强化知识的重要手段。除此之外,记笔记的核心价值是“使用”。也就是说,一个好的笔记应该是有使用价值的。一个shopping list可以让你在逛超市的时候买齐所有的东西;一篇日记让你可以回忆过去自己经历的事情;一篇论文笔记让人在需要的时候可以快速回忆起论文的核心观点;一个programming reference可以让你在编程的时候快速想起API的用法,而不用每次都去Google. 这些都是使用价值,是你记录时投资时间换来的回报。记录信息的本质是提供使用价值,所以评价一个笔记系统也应该用”可使用性”为评价方式。有一种观点是:现在(几乎)所有的信息都能在因特网上搜索到,所以需要信息的时候都可以即刻去搜索。这种观点本身没有问题,但是以目前的搜索技术和因特网的归档程度而言,这种方式的时间开销更高:因为你要从海量信息中筛选、判断、提炼出你想要的那一部分信息。而笔记则是“一次记录,多次使用”,一劳永逸。并且有新的知识点时,可以修改、完善之前的知识(另一点电子笔记的优势就是修改起来十分简单)。我们记笔记,就是让笔记成为知识的载体,也是为了日后需要这个知识时提供便捷。

3. 文字的分类

理解如何记录信息的第一步,是意识到信息与信息之间是有很大差别的:一篇娱乐新闻的读后感和一篇期刊文章的评论,其承载的信息和用途是不同的。根据信息种类严肃程度的不同,我将这些信息进行以下分类:

Category 1. 生活类:备忘录、日记、随想等
Category 2. 严肃类:工作日志、读书笔记、文献记录等
Category 3. 专业类:博文、论文、出版物、专栏等

第一类我称之为生活类,这一类包括基本常识,以及个人主观的、随意的记录。比如我自己喜欢将每天代办的事情记录下来,或是写生活日记等。这一类信息的特点是:不需要对信息进行太大的加工,并且一般来说都是相对隐私的,不会公开给别人。所以在记录和措辞方式上也会比较随意,只要能满足自己的需要就可以。这里我推荐使用的软件是手机自带的备忘录,Evernote,和素签(iOS日记应用)。

第二类就相对严肃一点了,这一类承载的知识和信息相对专业,并且需要投入很多脑力活动。要求我们对原始信息进行抽取、提炼、总结等工作。如听了一场seminar报告,对作者的观点有疑虑或是认同,就写了一篇感想。这类信息的特点是,信息的专业度高,并且不同的人对于严肃的定义也不同(如物理专业的人会把物理分为第二类,却会把生物分类为第一类,因为生物方面的知识对其来说不属于专业知识)。此类信息需要日后自己或他人阅读,所以在结构和语言上更加严谨。对于这一类的信息,我使用一款名为Leanote的软件进行记录和管理 [1]。最早我使用的是Evernote,但是Evernote不支持Markdown、Code Highlight以及MathJax的功能,对于理工科专业的人很不方便。

第三类则是严谨程度要求最高,并且知识的系统化程度最高的。这类信息需要大量的时间收集、整理。因为知识的体系化程度高,一般需要有内在的层次和结构关系。Cat 3的知识有时会依赖Cat 2,或者说Cat 3是Cat 2的总结和升华。不同于一类和二类信息以自己使用为中心,三类信息的读者可以是除你之外的人,这就要求知识具有可传播性。也就是说除了你自己可以理解,别人也要能透过你的文字和其他信息来理解你所要表达的知识。这就要求你在表述“意思”之外,还要有好的文笔。对于论文或是书籍我使用的软件是LaTex,也是使用的比较广泛的写作环境。至于博文,我就相对比较随意了:有时候用MarkdownPad/Evernote写一个框架,有时候用Leanote写,有时候就干脆直接用Wordpress自带的编辑器了。

除了上述根据严肃程度的分类外,我还总结了另外一种分类方式,就是按照文字承载的功能划分信息:

A. 记录:没有知识加工的过程,作为辅助记忆的手段。
B. 整理:对原输入数据进行一次滤波,小幅度修改原文的内容和结构,删去不需要的信息等。
C. 归纳:通过脑力活动,对数据进行结构化的整理。
D. 发布:对归纳后的信息进行文学化、排版、公开/或非公开的发布过程。
E. 记忆、索引、查阅:对A、B、C、D过程中产出的文字进行使用。

其中A -> B -> C -> D是递进、逐层依赖关系,而D和E是最终目的。也就是说,我们在这个过程中所创造的知识最终都是期望被以后查阅、引用或者公之于众的。

4. 文字产生的工作流

在理解了以上的概念后,我们来看一下一个文字是如何从无到有的:


▲ Figure 1. 文字产生的work flow

简单说来,一个文字经历了资料采集、生产、输出的过程。采集的过程为之后的文字提供了引用依据、素材(包括图片素材,公式等)、话题(如”吴恩达将机器学习等同于电力”)或者原始资料本身提供了文字的主体(比如一般对于专业书籍的笔记,其内容来自于对原文的转义和理解)。采集的信息需要通过一定方式的整理,这里大部分人推荐的软件是Evernote [2][3]。Evernote支持文字、图片、附件,可以非常容易的copy-paste,有网页抓取,标签和搜索的功能,所以我认为极其适用于第一步的资料采集。

信息整理的过程就是将采集到的信息提炼,提取出自己期望或是对文字产出有用的信息。我提炼信息的过程有三种:

1、删,删掉无用部分;
2、加,将有用的信息单独整理出来;
3、概,使用自己的语言梳理、分段概括原文的内容。

信息整理的过程是将碎片化知识系统化,并且将别人的知识转化为自己知识的过程。有的人喜欢收集一堆信息,却没有自己的总结和整理。这样的信息与其放在电脑里浪费空间,不如删掉,需要的时候再去找就可以了。在互联网时代,知识的碎片化现象很严重,书本也不再成为最新知识的承载体 [5]。整理的过程就是让自己的碎片化知识逐步成型的过程。

有了一部分整理后的初始信息后,我们就进入了文字生产阶段。这个阶段可以十分简单,比如一句话概括看了的一部电影(豆瓣短评),一段话说明一篇论文使用的方法的优势、劣势、创新点;生产的过程也可以很复杂,比如一篇Survey需要几百篇的references作为输入。生产文字时需要迭代,需要收集新的资料。生产文字也需要大脑脑力投入。不同于资料整理的过程,这个过程需要揉入自己的想法、创新和思考。需要在理解原文字面上的意思之后,进行概括、引申、扩展、表示赞同或是否认。这个过程是整个系统中最核心的一部分,它决定了你手里的资料能否转化为自己的知识。

文字输出阶段就是将临时的文字存储/发布的过程。这个发布的对象可以是你自己、你的同事、你的group,或是你也不知道的概念化的一群人(概念化:不是具体的人,而是具有一定特征和标签的人(如我的博文的对象是电子爱好者、机器人研究者、嵌入式系统从业人员等)。文字输出是文字生产后的必然步骤,生产的文字总会以某种形式输出出来。比如一篇空间日记,写完之后就(公开或非公开)发布在了系统里。又比如一篇论文发布给了一个conference,一本书给出版社publish。即使是在电脑上用word写的文章,也存储在了电脑硬盘里。总之,输出的过程就是提供一个日后访问的接口,将临时的资料永久化保存的方法。对于现代人来说,网络式的输出是最优的发布渠道。无论是发布到自己的私有云笔记系统,或是公开到博客/专栏/公众号,都可以让自己/别人快速、随时的访问到这些信息。

最后,输出的文件可以选择进行归档。归档的过程是将输出的文件进行层次化管理、标签的过程。归档的本质就是将相同类型的文件统一整理,归档也是将个人知识体系化的过程。归档的方式很多,这里举我自己的几个例子:

  • 将新的博文添加到不同的分类目录下,并添加关键词标签;
  • 所有生活日记、工作日志按年份进行整理,并放到Evernote的单独子目录下;
  • 发现大量同质信息后,将其整理到一个单独的系统中(目前正在尝试将控制系统相关知识做成wiki [6])。

之所以要归档,是因为输出的文字有时只是零散的知识,需要有一个大的结构来让碎片化的信息行程体系结构。这个步骤有时候就是简单地建层次化的文件夹,有时候相对复杂,需要一个额外的新系统来管理,甚至是一个再创造的过程。举一个例子,你写了50篇文章C语言的文章后,发现这些知识可以按照章节或是深浅划分,甚至这些内容提供了一本新书的基础。归档的另一个作用是提供检索和使用时的方便。想想我的Evernote现在有100多篇note,从生活到科研的内容都有,如果不建立目录层次,需要的时候根本找不到想要找的内容。我自己是一个深度整理狂,喜欢事无巨细的整理。但我要说归档层次的建立是和note的数量相关的。如果你只有5篇文章,细分之后分了5个分类,每个分类下面一篇文章,那和不分类没有区别。而如果你简单分成 工作 和 生活,那你不仅区分开了两种完全不同的notes,而且也没有增加检索时的复杂度。但是如果你有30篇notes了,那你这时候要考虑再分出一些子分类,让每个分类下的内容尽量少一些。至于检索功能,很大程度依赖你所使用的软件。Evernote用户津津乐道的功能就是它的搜索所有笔记标题和内容的功能,以及可以给note打上不同标签。我觉得检索功能确实很方便,对于数据较多时也十分有效。想想有搜索引擎之前和之后的互联网就能理解数据可检索性的重要了。

5. 工具列表与比较

前几章我提到了很多我使用的工具,这里总结一下不同工具适用的使用环境和彼此的优劣:

工具
Good for
Bad for
Evernote
raw资料收集,工作日志,随时随地有可能需要查看的内容。
‘严肃的’文字生产,存在隐私和机密(日记等)。
Leanote
支持原生MD和LaTex,适合作为科研文字的生产平台。
可以快速发布blog和生成目录,适合知识的归档。
不适合:需要检索和长期存放的知识。
可靠性目前不如Evernote。
素签
对纯文字的记录,适合日记和文学创作(生活的、记录)。
LaTex
非常严肃的出版物(论文,pdf出版物)。
需要最终生成网页的知识(博文、wiki等),虽然有软件可以将LaTex转成网页,但是还需要将网页手工嵌入网站模板中。
共享程度不如博客,阅读群体数量少。
WordPress/Hexo/Jeklly
三个主流博客系统,适合知识的输出和共享。
尚未形成结构和体系的知识。
Mkdocs (Wiki)
体系、系统的知识整理。可以通过任意终端查阅、搜索。
尚未形成结构和体系的知识。
需要频繁添加、修改的知识。
OneNote
较为通用的知识,需要图片和文字共存,板式结构灵活。
缺乏对公式、程序代码的支持。
Tiddly Wiki
非线性、跳跃的知识管理和归档。
有明确层次和体系的知识

其中Evernote是目前最为广泛使用的云笔记软件。Leanote是我自己找到适合在科研工作中替代Evernote的云笔记软件。素签是我遍历了iOS可用的日记软件后选择用来进行日记、文学创作的手机app,功能单一但是有简洁的书写环境。LaTex不用多说了。三个主流博客软件,Wordpress功能强大,但是资源占用多。后两个可以在Github托管,可以使用Markdown生成静态网页。Mkdocs是我找到的一个维基系统,使用Markdown写作,可以支持MathJax公式和检索。OneNote有很多人推荐,我本人尝试使用过用来记生活常识、历史和地理,但是我用起来不是很习惯,所以没有坚持下去了。但是OneNote本身用来记录和整理通用知识的功能是非常强大的,读者可以自行研究、开发。TiddlyWiki是万维钢提到的他使用的知识系统 [4],我觉得很有意思就提一下,因为这个系统是”非线性”的维基,你可以按照任意顺序打开和排列维基条目。

6. 结论

写这篇文章花了我两天时间。我自己作为还在半路的实践者,觉得这其中还有很大可以开发和扩展的东西。很长一段时间我都在思考知识是什么,知识在我们大脑中的表现形式是什么,知识是如何产生和迭代的。这些思考最终产生了一个结论:就是知识有记忆时效性、层次性和关联性的,而我们的大脑所能记忆的细节又是那么有限。作为一个普通人而非记忆大师,我们最终在大脑中形成的知识实际上是一些概念和经验,而非具体的细节。我们所处的是一个信息爆炸而又缺乏秩序的时代,我们应该需要建立这样一种辅助的系统:通过整理资料、记录笔记、写博文、创建维基的方式,来强化自己对于知识的理解,扩充自己的知识库。这个过程耗时,甚至有时候我也不确定这样做是否就能加深对知识的理解。但是经过长时间的尝试之后,有一点我是可以肯定的,就是这种方式减少了我对同样信息的重复搜索,节约了时间也避免了分心。另一点我深刻理解的,就是你手上里的资料在你电脑里和在网络上没有区别,只有当你把它整理、转化为自己的理解时,它才成为你自己的知识。

参考资料

[1] Leanote官方主页,我认为是一款针对程序员和科研人员的笔记软件,https://leanote.com/

[2] Evernote官方主页,一款全能型云同步笔记软件,https://evernote.com/intl/zh-cn/

[3] 知乎 – 如何构建自己的笔记系统,https://www.zhihu.com/question/23427617

[4] 万维钢 – 《万万没想到》,Part Two – 笔记本就是力量

[5] 罗辑思维,第205期:这一代人的学习

[6] 我正在做的控制系统维基,http://wiki.xiaotiandai.com/control/

【C语言深入】陷阱:数组溢出导致内存被意外修改

C语言的指针在提供编程便利的同时,却带来了很多潜在的内存安全问题。见以下例子:

#include <stdio.h>

int main() {

	char string_buff[12];
	unsigned int i_not_zero = 0xFF;

	sprintf(string_buff, "Hello,world!");

	printf("i = %x\r\n", i_not_zero);

	return 0;
}

该程序(不正确)的输出为:

i = 0

上述代码使用sprintf修改了string_buff指针所指向的char型数组。但是由于在申明数组长度的时候,没有考虑到字符串结束符’\0’,所以实际写入时不慎篡改了下一位内存地址的内容(此例中为i_not_zero, 使用MinGW gcc)。在实际程序中,此类bug一般很难被发现,尤其是还有其他程序在正常修改该值时,一般先会去排查和该变量有关的程序。

这只是一个因为不慎所导致的内存溢出问题,而在一些极端的黑客代码中,经常会见到通过内存变量和函数的指针地址反向访问、修改堆栈,从而获得系统的权限。可见指针作为C语言的一个重要(但是晦涩的)组成部分,无形中降低了系统的可靠性和安全性,需要挑战programmer的debug能力。

你好,2017!

又是新的一年。岁月匆匆,不给人时间回头看看,就又让人上路了。

今年更新了10篇左右的博文:

  • 新增的”C语言深度”专题来源于我在嵌入式课程助教过程中发现的C语言的错误使用。我觉得C和C++是机器人领域最重要的两个语言,所以希望在这方面增加一些内容。
  • 另几篇博文是介绍新发布的树莓派3代的。今年除了树莓派3,还入手了若干树莓派Zero。因为Zero很难买到,所以一下屯了5、6个。这批Zero准备用在智能家庭的节点中,但是应用场景目前还不明确,所以没有给大家做专题介绍。

明年的工作计划:

  • 承诺的智能家庭系统还没有完全开发完成,全部完成后会找时间公布。目前已完成的部分:中心服务器的部署,NAS,多媒体中心,一个传感器节点部署好了(已经上报了半年的温湿度数据)。二氧化碳、PM2.5传感器和无线组网模块选购好了,但还没有时间调试。另一个困难是控制数据的下发和传感器数据、系统参数的展现,我想基于BS架构(Flask + socket.io / Node.js + Ajax)。我没有网页编程的基础,而且中间涉及Real-time和asynchronous的问题,所以还没有时间解决。
  • More topics on Robotics. 实验室还是以机器人为主题的,去年有点跑偏了(嵌入式系统),今年重新回归到主题上。重点我想要关注的内容点有:增强学习、概率决策、机器视觉、ROS、机器(深度)学习。
  • 博主现在长期在国外生活,关于将网站转换为全英文的想法已有很久。但是还是一直很挣扎,考虑到很多内容对国内的读者会有帮助,所以今年还是保留双语写作。

最后祝大家2017年工作、学习顺利!

【C语言深入】陷阱:数组指针作为函数参数返回

再来看一个指针问题,同样的来自一个本科生的代码。这段代码想要实现将一个全是小写字母的字符串转换成对应的大写字母字符串:

char *covert_to_upper_case(char *string) {
    char p[100];
    int i = 0;
    
    for(; i < strlen(string); i++ ) {
            p[i] = string[i] - ('a' - 'A'); 
    }
    p[i] = '\0';
    
    return p;
    
}

然而这段代码没有能实现期望的功能。原因如下:

  1. 主程序调用convert_to_upper_case()函数后,堆栈为p分配了内存空间;
  2. 函数体正确修改了p对应字符数组的内容,并将p的首地址作为指针返回;
  3. 函数返回后,所有临时变量从堆栈中弹出,包括p[100];
  4. 主程序得到返回的指针,对其进行解析。然而指针指向的字符数组此时已经从堆栈中弹出,解析后的数据无法被定义。

要想正确实现对应的功能,应该将目标指针作为额外参数传递给该函数,并由上层调用者提供内存空间的创建。当然也可以使用malloc()将内存分配在堆中,但是需要注意使用对应的free()释放空间,否则会有内存泄露的问题。

【C语言深入】指针的一个错误赋值

关于指针总是有说不完的故事。

最近给本科的学生带Embedded System课程设计,遇到了一个非常奇怪的bug。有一段代码需要实现I2C通信,核心代码已经由软件库提供了,学生只需要设置结构体后调用API即可。一个学生的代码是这样的:

struct I2C_CONFIG {
  // ...
  char *i2c_buff;
  int length;
  // ...
};

struct I2C_CONFIG cfg;
char *i2c_buff;

void I2C_init() 
{
  // ...
  cfg.buff = i2c_buff;
  cfg.length = sizeof(buff);
  // ...
}

void I2C_send(new_buff)
{
  // ...
  i2c_buff = new_buff;
  I2C_MasterTransferData(LPC_I2C1, cfg);
  // ...
}

初看一下没有什么问题:在I2C_init()函数中首先对结构体cfg进行初始化,而在I2C_send()函数中设置了需要发送的数据指针,之后使用I2C的API发送数据。

因为代码一直无法实现期望的功能,我又仔细看了一下其中的蹊跷。我注意到,这段代码中使用了一个中间变量:char *i2c_buff。在I2C_init()中虽然将cfg.buff指向了i2c_buff,但是因为cfg.buff本身也是指针变量,而非”指向指针的指针”,所以这里只实现了简单的按值传递,即将i2c_buff的值 (初始值为0) 赋给了cfg.buff。之后虽然在I2C_send()中修改了临时变量i2c_buff指向的位置,但却没有影响到cfg.buff中的内容,cfg.buff依然指向之前i2c_buff初始化时指向的内存地址,所以需要发送的缓冲指针new_buff其实并没有传递给之后的I2C_MasterTransferData()函数!为了解决这个问题,必须将更改后的i2c_buff的值再次赋给cfg.buff,即:

void I2C_send(new_buff)
{
  // ...
  i2c_buff = new_buff;
  cfg.buff = i2c_buff;
  I2C_MasterTransferData(LPC_I2C1, cfg);
  // ...
}

另外这段代码还有一个不容易注意的bug,就是在I2C_init()中使用了sizeof()来判断buffer的大小。因为sizeof()函数得到的只是数据类型的大小,所以对于指针char *i2c_buff来说,sizeof(i2c_buff) = 4,而不会返回buffer的实际大小。指针的大小并不等于指针指向缓冲的大小!

The Limitations of Classical PID Controller and Its Advanced Derivations

Since founded by N. Weiner in 1947, the control theory has been evolved for more than 60 years and is still full of challenges and opportunities. The most important principle of the control theory, in my opinion, is the feedback mechanism. Without feedback and closed-loop, almost no algorithm and control technique can be implied. The idea of feedback is that by comparing the reference input and the actual output, an error signal can be obtained and then can be used by the controller to trace and eliminate the difference between the input and the output. Apart from Watt’s steam engine, one could say that the first formally implication of (negative) feedback is the amplifier invented by H.S. Black. It is a genius idea when first came out in 1927 and was proved to be an extremely useful way to solve electronic and control problems. The idea of output feedback has also been extended to state feedback and error feedback to achieve state control and estimation in more advanced control techniques.

Classical control is the foundation of control theory and it is more concentrated on analysing the stability and performance of a controlled plant. However, only linear and SISO systems have been discussed in classical control theory. Although traditional control techniques such as PID controller are still widely used in industry, they cannot handle more complex engineering scenarios such as aerospace, chemistry and biology. Another problem of classical control is that all parameters are designed and tuned based on the current system model, in which case the system will be more vulnerable to further disturbance and parameters varying.

In order to solve these problems of classical PID controller which mentioned before, more advanced approaches have been derived nowadays. If using classical approach to control a MIMO system, one should divide the system into different modes and control each mode separately. However if the system inputs and outputs are coupled with each other, it cannot be decoupled and this method will not be practicable anymore. Here comes the state-space method, which solved the limitation of classical control by using state variables. The advantage of state-space is that it can be represented by matrices and such is very computer-friendly. State-space representation is actually defined in time domain instead of frequency domain and every state can have some extend of physical meaning which gives some clues about what is happening inside a controlled plant. One milestone which makes the state-space method more practicable is the invention of Kalman filter. Kalman filter uses a series of history measurements in the presence of noise to estimate the current state of the system. Kalman filter can work as a state estimator or simply a special filter which uses the physical system model to remove the process and the measurement noise.

Optimal control method such as MPC and LQR is another derivation of classical control. In most circumstances, there are more than one possible control inputs which can drive the system to work properly, but we need is to find the optimal one. Optimal control actually transforms the control problem into an optimal problem which tries to minimise an objective function to get the best outcome. Another advantage of optimal control is that it can take constraints into consideration. One defect of PID controller is that it cannot handle system constraints like actuator saturation or output limitation. In the optimal case control, design a controller with constraints could be feasible.

It is also known that no system is constant and some parameters are likely to vary with time or to the working condition. In classical control, the controller is designed just for the current system model and thus may loss performance or even be unstable due to the system change and uncertainties. In such aspect, adaptive control or robust control may be more applicable. Both adaptive control and robust control are designed to cope with uncertainties. The difference is that adaptive control identifies the system model and changes its parameters in real-time, but robust control fixed its parameters after deployed to the plant. For the truth that adaptive control has to calculate the system model every few periods, it needs much more computational time. What’s more, since the control parameters in the adaptive controller are changing every time, it may be difficult to prove its stability.  On the other hand, the gain of robust controller has already been designed before applied to the system, so it doesn’t need to do additional calculation during the operation. Since robust controller is globally optimised and especially designed to handle uncertainties, it may not have a performance as good as other controllers. But since the real control problems are always not ideal, it is meaningful to take uncertainties and disturbance into the system model.

Some more advanced control techniques such as neural network and expert control are being discussed today. In my opinion, these new approaches have the potential to be the next generation of control theory. With the developing of computer science, it is now possible to model extremely complex networks. This kind of controller can actually take all the possible system states and its corresponding solutions into a database and each time just search for the best solution according to the current system data.  New techniques such as machine learning can also be absorbed into the controller and make the controller more flexible which can handle different control problems using a same configuration.

However, no matter how powerful the control method is, there are rarely situations where we do not need to make trade-offs. As human-beings, we always need to make decisions and balance the income and the expense. Being too greedy is like giving an infinite gain to a helicopter, which may work at the beginning but will suddenly crash whenever there is any disturbance. So push yourself while keep in mind that you have limitation. Take it easy, be adaptive to the environment and always try to get the optimal solution of your life.

REFERENCES

[1] R.C. Dorf & R.H. Bishop, Modern Control Systems (Twelfth Edition), Pearson, USA.

[2] Wikipedia, Harold Stephen Black. Available at: http://en.wikipedia.org/wiki/Harold_Stephen_Black. Last accessed 26th Mar 2014

[3] E.F. Camacho and C. Bordons, Modern Predictive Control, Springer, London, 2003

【RPi树莓派使用指南】树莓派官方7寸屏入门指南

1. 引子

在树莓派官方触摸屏发布之前,市场上可用的屏幕有以下三种:

  • 直接和GPIO插口对接的屏幕,使用SPI与CPU进行通信。需要特殊的驱动程序将framebuffer的内容发送到LCD控制器上,一般带有触屏功能,大小以3.5寸为主流。受限于SPI通信速度,刷新速率不高;
  • 专用USB接口的屏幕,如RoboPeak Mini USB Display。这类屏幕通过USB连接,需要本地运行驱动程序;
  • 通用LCD屏幕,通过HDMI和树莓派连接。因其通用性不需要特殊的驱动程序,但是很多都不支持触屏功能,而且都需要额外的转接板,体积较大;

rpilcd-front-with-base
▲ 图.  树莓派官方7寸屏实拍

我自己的需求是将树莓派作为信息显示中心,在屏幕上显示我的HP服务器的运行信息,另外提供一些快捷的传感器监控和控制操作接口。最初一直在官方屏幕和HDMI屏幕之间犹豫,最后还是选择了官方触摸屏。归结起来主要有几个原因:

  • 官方屏的LCD模组最有保证,淘宝上的HDMI LCD一般成像质量不高;
  • 官方屏的触摸功能在所有方案中是支持的最好的,有十点电容触摸(目前Raspbian还只支持单点,以后会升级),且不需要额外驱动。而HDMI接口的LCD如果有触摸功能,都需要额外接一根USB用于提供触摸控制;
  • 官方触屏和树莓派3可以直接通过铜柱物理连接,无需额外的驱动电路板。连线也非常少,只需要一根DSI软排线和供电接口即可。

总体上来说,虽然官方屏的价格高了一些,但是却是所有方案中最可靠、简洁的,所以最后也没有多犹豫就从网上下单了。rpilcd-front-without-base
▲ 图. 树莓派官方LCD屏实拍 – 正面

 

2. 入手简评

这款屏幕官方公布的主要参数有:

  • 分辨率800 x 480像素,刷新率60fps;
  • 10点电容触摸,但是目前只支持单点触控;
  • 背后有可供背挂的安装定位孔;
  • 可视角度70度 ,可视区域大约为155 x 86mm;
  • 非方形像素 – 大约为0.19 x 0.175mm;
  • 电源功耗:455mA – 470mA之间,约为2.3W。

虽然说这款屏幕是相对来说比较好的选择,但是拿到手之后还是发现了很多存在的问题:

  • 首先是这款屏幕的分辨率只有800 x 480,是WVGA标准,很多应用软件的界面都无法完全显示,使用时要经常拖动窗口来显示需要的信息;同样的,如果是自己开发图形界面,能够显示内容的空间也会比较有限(尤其考虑到任务栏也占用了一部分空间);
  • 其次是这个屏幕的可视角不大。现在主流的LCD屏幕可视角都是120度以上了,而这款屏只有70度,工作时只要稍微变换一下角度屏幕的内容就看不清楚了。可视角的问题在仰视时还可以接受,俯视的时候就非常明显了;
  • 最后就是这个屏幕比较吃电,如果和树莓派供用电源,会影响到树莓派的供电。如果出现供电不足的情况,屏幕的右上角就会时不时出现一个方形的彩虹图标。

总体来说这款屏幕从设置到使用还是比较方便的,但是作为官方发布的屏幕,同时再考虑其600块钱的定价,整体性价比就显得不高了。

 

3. 使用方法

3.1 注意事项(使用前必读)

1) 这款屏幕主要支持的树莓派型号是Model A+, B+, Pi 2和Pi 3。 Model A和B虽然也可以使用,但是会牺牲I2C的功能(因为这两个型号只有一组I2C,而其他型号有两组,可用其中一组作触屏接口);

2) 至于操作系统,推荐使用最新版原生Raspbian OS。目前对NOOBS系统支持不好,所以还是推荐安装Raspbian;如果已经在使用旧版Raspbian,但是不想重新安装最新的系统,可以使用以下命令升级系统:

sudo apt-get update
sudo apt-get install --reinstall libraspberrypi0 libraspberrypi-{bin,dev,doc} raspberrypi-bootloader
sudo reboot

以下是官方公布的操作系统支持情况:
– Raspbian – Supported
– Ubuntu MATE – Supported
– RetroPie – Supported
– OpenElec – Supported
– OSMC – Supported
– Arch – Display works, Touch may be tricky: https://www.raspberrypi.org/forums/viewtopic.php?f=108&t=128452
– Kano OS – Not supported

3) 确保使用官方电源(或其他品牌电流供应能力 > 2A以上的电源),保证屏幕可以正常供电;

4) FPC连接头很脆,安装的时候需要小心一点。确保FPC连接头可靠链接,连接端口的卡口扣下。

 

3.2 安装方法

打开包装后,屏幕背面接口的情况如下图所示:

rpilcd-back-without-base00
▲ 图. 树莓派官方LCD屏背面接口

这里主要需要连接的就是LCD软排线和电源供电接口。LCD软排线连接的时候问题不大,主要就是注意接口触电的方向是否正确,不要装反了。至于电源接口,这款屏幕提供三种供电方式:

1) GPIO引脚供电(树莓派向LCD供电)

可以直接从树莓派跳VCC和GND线进行连接,然后给树莓派供电,但是这样就无法使用其他IO扩展板了(如SenseHat)。

rpilcd-back-without-base1
▲图. GPIO供电连接方法

2) USB串联供电(LCD向树莓派供电)

将LCD的Power Out端连接至树莓派,然后给LCD电源接口供电。经测试,这种方式供电经常会遇到之前说的供电不足的情况,屏幕右上角会出现彩虹图标。

rpilcd-back-without-base2
▲图. USB串联供电连接方法

3) 双USB同时供电

两个USB都单独供电,但是要保证两个USB可以同时上电,否则会出现开机没有初始化屏幕,而没有图形显示的问题。

电源和软排线连线完成后就可以固定树莓派了。树莓派的安装方法有两种:正装和反装。所谓正装,就是将树莓派以相同方向安装在LCD的铜柱固定柱上。而反装,则是将树莓派面朝LCD驱动板进行安装。反装可以节省空间,但是反装就无法再使用GPIO引脚了,所以一般情况下都是正装的。

官方屏不含支架,可以去网上购买专用的LCD支架,像我买的这款用起来就不错:

rpilcd-back-with-base
▲图. LCD亚克力支架

 

3.3 使用虚拟键盘

对于想使用触摸屏进行全部操作的朋友,可以安装一个虚拟键盘进行文字输入:

3.3.1 Florence

Suggested on the Pi forums by Hove is Florence: http://xmodulo.com/onscreen-virtual-keyboard-linux.html. Install with:

sudo apt-get install florence

 

3.3.2 Matchbox

Suggested by Alex ( the almighty @raspitv ), and scattered on various blogs, is Matchbox, which you can install like so:

sudo apt-get install matchbox-keyboard

And then find in Accessories > Keyboard.

 

4. FAQ问题解答

Q: 屏幕上下180度翻转

A: 打开SD卡中的/boot/config.txt文件,增加以下一行:

lcd_rotate=2

也可以直接使用以下命令:

echo "lcd_rotate=2" | sudo tee -a /boot/config.txt

 

Q: 程序控制背光

A: 打开背光:

echo 0 > /sys/class/backlight/rpi_backlight/bl_power

关闭背光:

echo 1 > /sys/class/backlight/rpi_backlight/bl_power

 

Q: 在Pi A, B上使用

A: 首先需要将树莓派的IIC线与LCD控制板的IIC总线手动连线在一起,包括SDA ( http://pinout.xyz/pinout/pin3_gpio2) 和 SCL (http://pinout.xyz/pinout/pin5_gpio3)。之后修改配置,在IIC总线上识别LCD:

ignore_lcd=0

注意: 其他IIC设备将无法使用。

 

Q: 屏幕左上角出现方形彩虹图标

A: 电源供电不足,请使用电流供应能力 > 2A的电源。

 

Q: 如何使用Kivy图形库

A: Kivy介绍:Kivy is a Python GUI development system for cross-platform applications. It is designed to work with touchscreen devices (phones and tablets), but also runs on the Raspberry Pi. To install Kivy onto your Pi follow the instructions at https://kivy.org/docs/installation/installation-rpi.html.

如果需要在树莓派上正常识别触摸屏输入,需要将触摸屏在Kivy中配置成为输入源。打开配置文件 ~/.kivy/config.ini ,在 [input] 一栏增加以下命令:

mouse = mouse
mtdev_%(name)s = probesysfs,provider=mtdev
hid_%(name)s = probesysfs,provider=hidinput

 

Reference

[1] Official 7” Raspberry Pi Touch Screen FAQ, PIMORONI, http://forums.pimoroni.com/t/official-7-raspberry-pi-touch-screen-faq/959

[2] Getting Started with the Pi 7″ Touchscreen LCD, PIMORONI, http://learn.pimoroni.com/tutorial/pi-lcd/getting-started-with-raspberry-pi-7-touchscreen-lcd

【C语言深入】C/C++变量命名规范

目前主流的C/C++命名风格有两种:一种是Windows风格的匈牙利命名法,主要是采用类型前缀 + 变量名首字母大写,另一个就是Unix/Linux命名习惯。我自己采用的是基于Unix的变种,融合了匈牙利命名法的一些优点,在这里分享给大家。

变量名的组成:(模块名) +  (作用域) + (类型前缀) + 变量名 + (变量名后缀),解释如下:

  • 变量名 以小写的英文字母构成,词与词之间用下划线连接,如key_value, data_src; 不可使用数字,不混用大小写;
  • 模块名 声明该变量属于的模块,防止模块与模块的命名冲突。如timer_prescalar_value, DMA_channel_name等;
  • 作用域前缀 (Scope Prefix) 标注变量的作用域,提高代码可读性:
    g_: 全局变量;
    n_: 局部变量;
    t_: 中间变量;
    s_: static静态变量;
  • 类型前缀 (Type Prefix) 指明变量的数据类型:
    ptr_: 指针变量,在程序中临时需要使用指针时,也常简写为p_,如*p_src;
    h_: 句柄,如h_file;
    n_: 整形,s_: 短整形,l_: 长整形, u_: 无符号整型,可增加数据位数,如u32;
    ch_: 字符型变量;
    f_: 浮点,d_: 双精度浮点;
    b_: boolean;
    by_: byte字节型(关注数据的位特性,需要位操作的情况下使用);
    reg_: 表示寄存器;
  • 后缀 (Suffix) 指明变量的性质:
    _src: 源,_dst: 目的;
    _str: 字符串;
    _t: 在声明数据类型时使用,表示为自定义的数据类型,如u32_t;
    _st: 表示为结构体;
    _buff: 数据缓冲, msg_buff;
    _arr, _a, _m: 数组或矩阵;

变量名的取名规则:

  • 循环控制变量 i, j, k, m, n,除循环控制外应避免使用这些变量名称;
  • 函数名 使用(模块名 + )动词 + 名词的形式,同样小写 + 下划线:sys_find_file(), IO_get_data(). 后者因为IO为专用名词故破例使用大写;
  • 类名或结构体名 使用首字母大写加下划线连接:如Mystring, Datetime_type;
  • 私有类成员 Private使用下划线_前缀,如_data_src_ptr, _init_module();
  • 宏定义或常量 使用全部大写:如MAX_NUMBER, LOOP_NUMBER;
  • 缩写 使用能广泛接受的缩写:如add, ans, avg, chk, cnt, col, ctrl, def, del, dst, disp, err, freq, idx, init, len, min, max, mid, msg, num, opt, pos, ptr, recv, res, ret, src, str, sub, num, ts (timestamp), val等。

本网站的所有实例代码和项目程序都将按此命名规范进行编写。