大学本科期间的一些成果

1. 可自主巡航、地图构建、路径搜索的智能车

简要概述

大三下学期,以助教员的身份,与一位队友,一位老师,一同负责开发一款多功能智能车,为崇新学堂搭建EECS课程的实验教学平台。课程引进于MIT,但相关设备均已不再生产,只有文档说明。我们需要根据文档自己搭建这款智能车,满足教学任务。

最终效果

88e1446bly1fucj2zxi6hj21ls17cqe2.jpg

  • 此为实物图,使用8个超声波模块进行距离的探测
  • 电路核心板在两层金属隔板之间
  • 智能车长 X 宽约为 30cm

88e1446bly1fucj3nwjpcj20fe09rq41.jpg

  • 左图为实验仿真环境(MIT提供)
  • 右图为实时构建地图的情况
    • 黑色方格:表示检测到有障碍物
    • 红色方格:虽然没有检测到障碍物,但是因为智能车自身体积原因,也不能占据的方格
    • 绿色方格:颜色的深浅表示外界有障碍物的概率值

在一个完全陌生的地图中,智能车可以自己搜索路径、构建地图、并能够抵达预设目标点。

技术细节

硬件

某宝购买的价格2500左右的智能车车模,包含硬件电路板,单片机使用STM32

  • 自己实现电机驱动、编码器驱动、超声波驱动等一系列底层硬件的驱动
  • 编写通信协议实现与上位机对接(使用MIT官方的一整套协议,包含协议头,协议尾,校验位等等)
  • 各种控制模块非常多,需要极大的编程能力与硬件设计能力

算法

  • 速度转换:智能车接收上位机传来的vel(直行速度)与rvel(旋转速度),需要转换为PID控制的参数(编码器单位时间的脉冲数)
    • 输入:vel;revl
    • 输出:左轮单位时间内目标编码数;右轮单位时间内目标编码数

88e1446bly1fv5u3w4s2xj20xg0izdlp.jpg

  • 坐标计算:二维坐标平面(x, y, theta)
    • 输入:单位时间内的左右轮编码器变化值
    • 输出:x, y, theta
    • 智能车上电初始位置为(0,0,0)
    • 经过若干次直行与旋转或者混合运动后,需要计算出某一时刻的坐标值
    • 使用微积分与三角函数相关知识

88e1446bly1fv5u3bzos4j20q3078js1.jpg

  • 构建地图

    • 地图分割:分割成很多网格(无向图)

    • 贝叶斯概率估计:每一个方格都看作为一个状态估计模型,每个时刻都会更新状态的概率(occupied, clear),一下均为针对于每一个方格

      • 初始状态估计 $P(S_{0})$,本例中

      • 观测模型 $P(O_{t}|S_{t})$ :当前状态下(occupied, clear),认为声呐观测到该方格为hit或free的可能性(后验概率,已知该方格处有障碍物occupied,实际本次观测中声呐对该方格检测为hit或free的可能性)

        1
        2
        3
        4
        5
        def oGivenS(s):
        if s == 'occupied':
        return dist.DDist({'hit': 0.8, 'free': 0.2})
        elif s == 'clear':
        return dist.DDist({'hit': 0.1, 'free': 0.9})
      • 转移模型 $P(S_{t+1}|S_{t}, I_{t})$ :当前状态与当前动作下,估计下一个状态(occupied, clear)的概率分布,此处用不到 input

        1
        2
        3
        4
        5
        6
        7
        8
        9
        def uGivenAS(a):
        def transGivenA(oldS):
        if a == None:
        if oldS == 'occupied':
        return dist.DDist({'occupied': 1.0, 'clear': 0})
        elif oldS == 'clear':
        return dist.DDist({'occupied': 0, 'clear': 1.0})

        return transGivenA
      • 每一个时刻 t,对于每一个方格,都更新 GridMap 中每一个方格的状态概率

    • 设置阈值:当前点是障碍物的可能性大于该阈值 $P(S_{t}=occupied) > 0.7$ ,则标记为障碍物。标记为障碍物的点,在路径搜索中忽略

    • 在 t 时刻

      • 更新本次所测直线上各方格的概率分布,即 GridMap 类中每一个方格的状态分布 $P(S_{t})$ ,并将$P(S_{t}=occupied) > 0.7$ 的所有方格标记为红色,将其余的按照概率值标记为绿色

      • 3 种情况下会重新规划路径

        1
        2
        3
        1. the current plan is C{None}
        2. the plan is invalid in the map (because it is blocked)
        3. the plan is empty and we are not at the goal (which implies that the last time we tried to plan, we failed).

        输出子目标点

      • 动态移动到子目标点

  • 路径搜索

    • 输入:初始点、目标点、地图

    • 启发值搜索 A*

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      def ucSearch(initialState, goalTest, actions, successor, heuristic):
      startNode = SearchNode(None, initialState, None, 0)
      if goalTest(initialState):
      return startNode.path()
      agenda = PQ()
      agenda.push(startNode, 0)
      expanded = { }
      while not agenda.isEmpty():
      n = agenda.pop() # 拿出当前cost最小的节点
      if not expanded.has_key(n.state): # 如果还没有遍历过,进行遍历
      expanded[n.state] = True # 标记为已经遍历过该节点
      if goalTest(n.state): # 如果是目标点,返回路径,实现剪枝操作
      return n.path()
      for a in actions: # 遍历n节点的所有相邻节点
      (newS, cost) = successor(n.state, a)
      if not expanded.has_key(newS): # 如果还没有被遍历过
      newN = SearchNode(a, newS, n, cost)
      agenda.push(newN, newN.cost + heuristic(newS)) # 将该节点添加进优先队列
      return None
      • 输入:状态;输出:对总cost的估计

      • 启发值:欧拉距离

        最好的启发值:当前节点到目标点的最短路径,这样的话,我们不需要多遍历任何一个节点。

        选择启发值:

        • 如果为 $h(n)$很大 ,即只计算任意顶点 n 到目标的评估函数,而不计算起点到顶点的距离,则算法转化为使用贪心策略的Best-First Search,速度最快,但可能得不出最优解;
        • 如果 $ h(n) $ 不高于顶点n到目标顶点的实际距离,则一定可以求出最优解,而且$h(n)$越小,需要计算的节点越多,算法效率越低,常见的评估函数有——欧几里得距离曼哈顿距离切比雪夫距离
        • 如果$h(n)=0$,即只需求出起点到任意顶点n的最短路径,而不计算任何评估函数,则转化为单源最短路径问题,即Dijkstra算法,此时需要计算最多的定点;
      • 整个搜索过程涉及多种基础算法与数据结构

关键技术

c++、python、stm32、贝叶斯概率估计、启发值搜索算法

2. 基于光流特征矢量模值和角度结合的微表情检测方法

简要概述

此研究为跟随导师进行的“微表情识别”方向的一部分内容。微表情识别主要分为两步:微表情检测与微表情识别。我主要进行的工作为前者,后续也参与了一点微表情识别的任务。

  • 工作流程

88e1446bly1fucj43orq6j20j007yaak.jpg

最终效果

  • 软件实现一

88e1446bly1fucj51hg2kj20hc09st9g.jpg

  • 软件实现二

88e1446bly1fuclblp8etj20po0m8ab4.jpg

技术细节

检测人脸68个关键点

使用python包dlib,内含已经训练好的人脸检测模型与人脸68个关键点模型。

  • 输入:图像
  • 输出:人脸的数目、人脸68个关键点的坐标

88e1446bly1fuclc1n2mmj20bc0b8784.jpg

光流追踪

使用python包opencv,使用金字塔Lucas-Kanade光流检测算法进行光流追踪。

  • 输入:基础帧人脸68个关键点坐标
  • 输出:每一帧人脸68个关键点的新坐标

模值与相角的计算

计算相邻两帧变化的模值与相角

88e1446bly1fuclcbjcshj206z06z74c.jpg

微表情检测

  • 将得到的每一帧的模值与相角$(A,\theta )$画在一个极坐标中,如下图:

88e1446bly1fuclclpxc5j20tm0l7dgx.jpg

  • 设定阈值:根据模值设定$ A_{i} = n\times A_{max} $,n为实验参数,可取0.6
    • 模值大于阈值:表现为在圆外,表示在高潮帧$A_{max}$附近
    • 相同光流角度:表现为散落在一条直线附近
  • 判断准则:满足以下条件可判定为微表情
    • 光流角度集中在一条线附近
    • 在圆外
    • 连续帧数大于最小帧长度帧,本实验为15帧

微表情初步识别

依据专业编码准则,根据AU运动单元的运动情况可进行判断

如:

88e1446bly1fuclctpqa6j208302jwef.jpg

迁移学习

宏表情域训练样本多,微表情训练样本少,利用在宏表情域中的训练模型和参数,在微表情域中稍加改动(仅训练最后两层或几层),以实现模型的迁移。

关键技术

python、opencv、光流追踪

3. 基于onenet的智能隔尿垫

简要概述

2018年度国家级创新立项项目。该设计是使用NodeMCU,雨滴传感器,蜂鸣器实现的物联网小型项目,可以实时上传婴儿或老人的小便状态,通知监护人及时更换尿垫。

最终效果

  • 扫描二维码可查看设备情况

88e1446bly1fucld1hfgrj20fe0k0mxv.jpg

88e1446bly1fucld9lufqj20e50ut42t.jpg

  • 微信提醒

88e1446bly1fucldhr59fj20as0j6ab7.jpg

技术细节

硬件

  • nodemcu:集成wifi模块的,比纯MCU性能更好,使用更方便的控制器。可使用多语言开发(c、python、lua等)
  • 雨滴传感器:用于检测尿液含量

服务器端

接入中移物联网开放平台onenet,其上提供了一整套完整的物联网应用开发服务

通信协议

MQTT

  • 是一种基于发布/订阅publish/subscribe)模式的“轻量级”通讯协议
  • MQTT最大优点在于,低开销、低带宽占用
  • 在物联网、小型设备、移动应用等方面有较广泛的应用。

客户端

微信公众号

因资金问题及服务号的注册规范问题,目前仍使用第三方微信公众号Sever酱 提供的服务。

但此项目申请为2018年度国家级创新立项项目,目前正在重新完善开发中,正在将公众号后端迁移到自己的云服务器上

关键技术

python、nodemcu、微信公众号、onenet、mqtt

4. 基于django搭建的工作数据分析网站

简要概述

开发背景

当代大学生,对自己专业将来能做什么一无所知,多数同学考试考什么、考研考什么、保研需要什么,我就学习什么。这已经违背了学习的初衷。因此,我想设计一个网站,用于给同学们提供各类工作的详细介绍与统计,并给予推荐相关学习课程,希望同学们在大学本科阶段的学习不仅限于书本

主要功能

  • 检索:支持关键词搜索功能,得不同岗位的具体要求
  • 数据可视化:统计分析,并进行数据可视化
  • 学习课程:每种工作的相关技术链接、学习教程
  • 登录系统:注册、登录、修改密码功能

最终效果

  • 总体概览

88e1446bly1fucldqp90hj21h80podhw.jpg

  • 总体分析

88e1446bly1fucldx4klhj212r0nggmt.jpg

  • 工作详情界面

88e1446bly1fucle57m9dj21h70ppae5.jpg

  • 词云分析

88e1446bly1fuclebwre9j21gf0lbdj6.jpg

  • 工作经验与薪金的关系

88e1446bly1fuclej9pnbj214g0j8wfj.jpg

  • 学习课程

88e1446bly1fucleqyt6kj21540mudiv.jpg

技术细节

数据获取

  • 使用python包Requests + BeautifulSoup4

  • 使用爬虫爬取拉勾网上万条工作招聘数据,存入MySQL数据库

  • 爬取IT程序员教程慕课网菜鸟教程等,存入MySQL数据库

统计词频

  • 使用python的包 wordcloud+jieba 进行词频统计

数据可视化

  • 使用 ECharts 进行数据可视化

网站实现

  • 使用 python3.6 + Django 1.9.1 实现web框架
  • 使用 bootstrap 作为前端UI框架

关键技术

python、django、echarts、bootstrap、词频统计、爬虫

5. 基于深度学习的面部表情识别

88e1446bly1fv5uh5ff88j20mb0htqeq.jpg

数据集

kaagle fer2013

size: 48*48

categrories: 0=Angry, 1=Disgust, 2=Fear, 3=Happy, 4=Sad, 5=Surprise, 6=Neutral

3万条数据

本文的模型成功率可达 65%,56 只队伍中可排名第 7

模型

88e1446bly1fv5u4yyjtmj20m30cy3zf.jpg

1
2
3
4
5
max_train_step = 30000 # 最大训练步数

next_batch = 50 # 每一步训练的数据量

# 在本地GPU GTX-950上训练需要半小时左右

流程

读取摄像头

1
2
video_captor = cv2.VideoCapture(0)
ret, frame = video_captor.read() # 得到图片frame

检测人脸

opencv人脸分类器使用.xml描述人脸特征,具体特征检测方法为Haar

1
2
3
4
5
6
7
8
9
10
CASC_PATH = './data/haarcascade_files/haarcascade_frontalface_default.xml'
cascade_classifier = cv2.CascadeClassifier(CASC_PATH)
faces = cascade_classifier.detectMultiScale(
image,
scaleFactor=1.3, # 每次图片缩放的比例
minNeighbors=5 # 每个人脸至少检测多少次才算是人脸
)
# ...
# 得到faces中最大的人脸
image = cv2.resize(image, (48, 48), interpolation=cv2.INTER_CUBIC) # 转化坐标

得到人脸坐标

1
detected_face, face_coor = format_image(frame) # 检测人脸, 得到人脸坐标

face_coor 形式为 [x, y, w, h]

训练

将 detected_face 归一化处理

1
2
tensor = np.asarray(image).reshape(-1, 2304) * 1 / 255.0
result = sess.run(probs, feed_dict={face_x: tensor})

按下空格开始转换

其他项目

血压脉搏测试仪

88e1446bly1fucley7hwvj20uc0kfwsr.jpg

微信“跳一跳”物理助手

  • 没找到图片-^-

智能推荐购物APP

88e1446bly1fuclf7a4d2j20fq0ryh5f.jpg

双足机器人

88e1446bly1fuclfgwporj20lc0stqn9.jpg