项目梳理

本文对个人从本科至今所做的项目进行一次系统的梳理总结

本科毕设 — 基于DBSCAN聚类的三维柱体的识别、提取与分割

论文:Bolt 3D Point Cloud Segmentation and Measurement Based on DBSCAN Clustering 链接

专利:基于DBSCAN聚类的点云图螺栓分割和高度测量方法 专利号:5221271

研究背景及意义:

高架高速铁路桥墩通常位于水域或山谷等各种环境中。根据设计图纸,需要在桥墩表面安装螺栓,紧固供电牵引电缆的支撑柱。铺设牵引电缆前应检查螺栓的数量、分布情况和高度,以确保安装实现与技术要求一致。通常,为了完成测量任务,工人需要通过搭设攀爬工具爬到高桥墩的顶部进行操作,并在操作后将其拆除。

目前的工作模式存在着三个严重的问题:

(1) 存在着严重的安全隐患。操作人员在铁路沿线桥墩上下攀爬的过程中,处在一个非常危险的工作环境中。

(2) 测量过程耗费了巨大的人力和时间成本,从而导致项目建设进度明显滞后、不可控。

(3) 测量结果的保存依赖于人工记录,不利于测量数据的管理和存储

因此亟需一种自动化的方式,使工作人员不再需要以攀登这种危险系数高的方式进行螺栓位置和高度等信息的测量,并且可以高效地存储数据,方便后续进行数据分析和统计。

技术要点

  1. 放弃深度学习的方法,原因:
    1. 单纯对二维RGB图像进行分割的分割精度不高,高度测量误差无法达到要求
    2. 对三维数据进行分割的时间复杂度很高,并且无法利用工程图纸的先验信息,精度也同样无法得到保证
  2. 无人机搭载深度相机获取三维点云数据图代替RGB图像,从而获得更精细的目标物体在三维空间中的形态。无人机可保证相机在高精度的水平状态下进行拍摄,因此不考虑螺栓安装平面的水平性,我们获取到的螺栓图像是与水平面完全垂直的
  3. 选取DBSCAN聚类算法进行数据处理(相较于其他聚类算法如K means,该算法不需要预先输入聚类的个数,而完全凭借设定的密度区间进行聚类。)
  4. 点云图中存在许多噪声:通过工程设计图纸的先验信息,可大致获得整个螺栓分布的范围。通过计算所有点到三维坐标原点的欧氏距离,消除了位置过远和过近的噪声点。
    1. 首先进行压缩聚类,将原始点云图的所有点按竖直方向压缩,然后通过DBSCAN找到密度最高的几个类,即可得到螺栓在水平方向的xy坐标,将除螺栓及其附近位置以外的所有点都舍弃。
    2. 将点云图复原,此时噪声已经消除了大部分,那么就该更精确地去获取螺栓所属的点了
    3. 将点云图沿水平方向进行切片,为了保证精度,可以选择2mm-5mm之间的切割间距,不能过小,因为会使点云密度过于稀疏。再对每一层的点云数据进行聚类,如果符合图纸的要求,即每个类之间的距离和角度是符合要求的,那么证明这个类就是属于螺栓的,这一层也是属于螺栓的。
    4. 把所有层处理完以后,我们拿出符合存在螺栓点云的层再进行组合,便可大致复原螺栓的所在
  5. 要处理的螺栓图像往往有多排,而前后排的螺栓由于距离深度相机的距离差异,导致点云密度不同。因此采用分割再组合的方式,分别对一张点云图前半部分和后半部分进行不同滤波强度的滤波处理然后再进行组合,从而平衡每个螺栓的点云密度
  6. 项目的最终目标使对螺栓高度进行精确的测量,并且要求高度测量误差在5mm之内(实际上就是要精确得到螺栓顶部坐标和底部和平台连接处的坐标相减)。为了提高测量精度,消除螺栓底部和平台连接处在点云图上模糊不清的影响。采用取螺栓底部向外的一圈圆环范围内的部分点,取其平均值即可较为精确地作为底部平台表面的y值大小。

遇到的问题

  1. 最大的问题就是室内与室外环境的不同。由于一开始是在室内对螺栓模型进行拍摄的,噪声点很少,干扰物体也比较简单。当算法设计完成实际进行使用时,发现深度相机在室外得到的图像质量很低,由于室外光的缘故,存在大量不规则且密集的噪声点。这也催生了我设计的相对复杂并且过滤多次的噪声过滤算法。另外很重要的一点是深度相机需要调整滤波强度,滤波强度越高,噪声点就越少,但是同时螺栓的所属的点被过滤的可能性就越大,点云密度也越低,不利于算法的实现。因此需要找到一个最合适的滤波强度。

    img
  2. 实际搭载上无人机去现场拍摄后还发现由于场地和无人机的螺旋桨比较大的情况,很难与螺栓处于同一个平面进行拍摄,因此需要旋转摄像头的角度,

简述

  • 为中国铁建公司设计了一种可以从无人机拍摄得到的三维点云图中提取柱体螺栓的算法。应用了分层聚类的思想,采取了 DBSCAN 的聚类方式,并能够很好地结合设计图纸,无人机拍摄角度等先验信息提高分割精度,最终可控制螺栓高度测量误差在 5mm 之内。

  • 研究成果获得了一项发明专利,最终论文已被 IEEE xplore 数据库收录,获得了校优秀毕业设计

硕士毕设 — 探索强化学习在金融投资问题中的应用

研究背景及意义

金融市场中没有模型能够做到准确的预测,深度学习往往也不能实际指导买卖的操作。而强化学习可以利用action空间自主地进行金融市场中的资产配置,也就是自主买卖所持有的的股票。在大量的数据和合适的模型构建以及算法选择之下,是可以做到在高频交易的场景下获取超额收益的。

由于强化学习的技术相对比较新,又由于这个应用场景比较特殊,很少有效果优秀的模型作为论文发表出来(更多的可能是一些量化投资公司内部进行开发和使用)。因此对这种新技术的实际应用能力还是需要进行进一步的研究。

技术点:

  • 框架选择:基于一个开源的强化学习框架FinRL进行开发和设计

  • 训练数据集:

    • 近20年的北美道琼斯三十的股票数据(时间跨度长,频率低),参照FinRL框架所对应的论文里的实现方式,发现效果差,复现程度低
    • 中国期货市场的基差数据(频率高,15秒间隔的市场数据),数据量比第一个数据集大20倍
  • 选择特定的适合高频交易的alpha因子作为增加状态空间状态的因子,提高模型的操作准确性

  • 数据的预处理:标准化

  • 奖励函数的重新设计:由原本的当前收益再加上一项回撤率的权重,可以发现明显提高了模型的稳定性和提高了最终收益并降低了波动率(也就是降低了风险)

  • 收敛性的验证:在增加环境复杂度之后,必须同时增加训练时间,否则会使模型无法收敛,这是通过tensor board查看value_loss的变化曲线得到的

  • 模型结构:主要用的是PPO算法,模型由两个网络组成:一个价值网络(输出当前策略的预估价值)和一个策略网络(决定action,在该实验中就是买卖的股票数量),每个网络由两层全连接层组成,

  • 不同复杂度环境下的算法效果验证,与传统的布林带方法进行对比,可以获得明显更优的收益

用到的库

  • pandas,numpy用于处理数据

    • read_csv():读取csv文件数据
    • to_csv():处理完数据后再以csv文件形式输出
  • matplotlib:

    • 画图:主要是总回报的走势图
  • gym库:提供训练环境框架:

    • init函数,设置环境中需要的一些参数以及一些默认值

    • step函数,最关键的函数,设置在一个action下agent要如何行动,返回下一个环境状态,和当前的奖励状态

    • reset函数,设置初始化环境并重置相应的参数

  • stable_baseline3库:强化学习算法库

    • PPO算法:使用torch库来建立神经网络,一个策略网络,一个价值网络(用于预测每个策略的价值)
    • A2C算法
  • finrl框架:提供训练函数(可以输入模型训练的算法,训练数据数据集,和RL环境),测试函数(输入训练好的模型,测试数据,RL环境)

遇到的问题

  • 主要问题是受到模型训练速度的影响,无法验证
  • 另外奖励函数中回撤率的权重对实验影响是比较大的,如果没有一个合适的权重,可能导致模型无法收敛

补充:强化学习和传统机器学习的区别

强化学习和深度学习的主要区别在于:

  1. 相比深度学习,强化学习的训练不需要标签,它通过环境给出的奖惩来学习。
  2. 深度学习的学习过程是静态的,强化学习则是动态的,动态体现在是否会与环境进行交互。也就是说,深度学习是给什么样本就学什么,而强化学习是要和环境进行交互,再通过环境给出的奖惩来学习。
  3. 深度学习解决的更多是感知问题,强化学习解决的主要是决策问题。因此有监督学习更像是五官,而强化学习更像大脑的说法。但是深度学习和强化学习之间并不是泾渭分明,它们之间可以相互组合,比如在强化学习系统中使用深度学习(深度强化学习)等等。

简述:

  • 探索了强化学习在资产配置领域的应用

  • 使用 FinRL 库在北美市场的道琼斯 30 数据集和中国市场的期货基差数据集上构建环境并验证了强化学习算法(A2C, PPO, DDPG 等)的有效性

  • 创新点:在奖励函数中新增了一定权重的回撤率因子,可以获得在更严格环境下的高收益低风险回报

  • 贡献:验证了强化学习在金融领域的能力并且提出了几点为了达到更好的训练效果所需的处理步骤

实现C++标准库STL

项目初衷

  1. 算法题中用到很多STL标准库中的容器和算法,一直都知道他们是非常高效的,对象的耦合性低、复用性高,但是却不知道是怎么样实现了高效。因此很想了解他们的底层实现。也一直知道内存管理是C++的精髓,但是只从语法上学习并没有很直接的感受,因此希望通过阅读STL源码中对内存的管理方法,对智能指针的实现来对内存管理有更好的认识。
  2. 实现一份类似STL的标准库也能学习到很多巧妙的设计方法,也是我从单单从C++primer上学习语法和做算法题到真正实现设计和开发一些东西的跨越。
  3. SGI版本中源码中未实现的:move相关的操作,另外智能指针实现的只有auto_ptr。
  4. 我们版本的TinySTL:灵活使用了auto关键字,因为很多很长也很复杂的类型名。另外static_cast的使用也很多,增加了move的相关操作。

设计经验

  1. 声明与定义分离,类中尽量只放声明。尽可能多用const和引用。
  2. 类型偏特化的使用:不同类型的数据使用相同的算法也有可能具有不一样的效率。对于某些只有一个字节大小的类型(如char、const unsigned char(原生指针))我们会设计针对特定类型的特化版本的函数来增进效率(通过使用C标准库函数如memmove、memcmp)。
  3. 模板出错比较难检查,因为必须运行时才能发现错误,编译阶段无法发现。一般模板中既包含声明又包含定义
  4. 做一个类似这样的库的项目最先要实现的是底层的内存分配,保证不发生内存泄露。然后要设计一系列基础的算法例如copy、fill等,并且尽可能做到最高效(通过尽可能地调用C库中的函数来写)。因为我们需要设计一系列容器,而对于容器进行整体的拷贝和赋值等操作需要大量应用copy等算法,因此需要事先设计好并且保证高效。同时由于容器的操作涉及到一系列对象的拷贝、移动等,因此要保证异常的安全,需要更多的思考。

美团后端开发实习

所属的小组是美团优选的营销开发组,接触到的任务大多都是直接面向实际需求的。

最主要的挑战还是从零开始接触Java到能实际上手做项目解决问题,也算是接触了互联网,或者计算机行业从业者的每日工作和一个项目的解决过程吧。

读代码和分析代码的能力获得了很大的提升。

广告浏览任务的详细描述

需求

在优选的游戏中心增加一个广告浏览的任务,即用户在点击相应的UI入口之后跳转到广告页面,停留足够的时间之后(比如设定为10秒)再返回,会提示领取奖励。可以设置每日的领取上限,并且显示当前的领取进度。

B端的任务配置设置

  • 提供修改这个任务的生效时间、奖励额度、每日上限、面向人群、面向地点等等的配置
  • 同时要设置每一个字段的校验逻辑,即不能在未配置这些字段的情况下使任务生效

涉及到的C端接口

  • 主界面展示 loadMainView
  • 任务展示 queryTask
  • 领取任务 takeTask
  • 领取奖励 takeReward
  • 批量领取奖励 batchTakeReward

由于广告页面的跳转(拿到的url)是由广告平台那里提供的接口通过输入一些额外的参数拿到的(mtAppVersion,clientType,dpCityId),因此要在这些接口上增加这些参数

C端的业务逻辑

  • 任务中心领奖后根据taskStatus表示的任务状态(即是否还有剩余浏览次数)执行不同的性功能:
    • 已做完,不调用广告接口,无法继续观看
    • 没做完,调用广告接口:
      • 如果能获得广告,则返回广告的相关信息,允许观看
      • 无法通过接口获得广告,保持原有的信息不变,无法继续观看
  • 中间可能会出现传过来的任务进度与数据库中的任务进度不相等的情况(可能因为在上报的过程中出现网络问题中断了),要以数据库中的记录为准。

技术点

  • spring boot框架的使用()

  • rpc框架thrift的使用(注解的方式)

    • 由于我们不同的模块(服务接口模块、任务中心模块、奖励中心模块)是分布在不同的机器上的,因此我们要使用rpc远程调用框架进行模块之间的串联。
    • thrift框架是什么,Thrift是一套包含序列化功能和支持服务通信的RPC(远程服务调用)框架,也是一种微服务框架。其主要特点是可以跨语言使用
    • 框架为c/s模式,我们在服务器端实现代码,提供接口;客户端可以直接调用接口。
  • 不同模块之间的交互

  • 一条完整的http请求如何从输入到返回

  • 上线的基本步骤与注意点

  • 远程调试与错误的排查(日志中心的使用)

涉及到的几个包

mapi包(芥末端/B端接口)—— 用于创建任务配置,提供给运营,用于设置玩法任务,其中包括玩法的生效时间,奖励,城市,poi,与玩法相关的一些配置(如浏览任务的浏览时长)等等

capi(服务接口层)——比较薄的一层controller接口,调用后端的服务

game-center包——核心的包,

task-center-view——任务中心,调用这个模块的接口获取任务实例和任务配置

设计模式

  • 门面模式:用来封装系统的底层实现,隐藏系统的复杂性,提供一组更加简单易用、更高层的接口(mapi、capi)。(另外Linux系统调用函数就可以看作一种“门面”。它是Linux操作系统暴露给开发者的一组“特殊”的编程接口,它封装了底层更基础的Linux内核调用)
  • 建造者模式(builder),各种配置Config基本都是通过builder而不是new构造的(lombok注解在java进行编译时进行代码的构建)。它提供在设计数据实体时,对外保持private setter,而对属性的赋值采用Builder的方式,这种方式最优雅,也更符合封装的原则,不对外公开属性的写操作
  • 简单工厂模式 SampleDataConstructorFactory,把所有第一次遇到的活动类型或者任务类型存到对象内的HashMap中,之后要调用的时候直接通过
  • 代理模式(AOP、RPC框架)

遇到的异常:

调试的时候遇到最多的还是NPE null pointer exception,大多数情况还是代码:

  • 没有考虑到比较极端的一些用例
  • 级联调用没有校验判空(fooService.getFunc1().getFunc2().getFunc3())
  • 函数中校验参数不够完善导致的(入参没有校验为空直接使用)
  • 方法或者远程服务返回的List不是空而是null
  • 字符串比较出现NPE

简述

  • 从零开始学习 Java 相关的内容,基于 Spring Boot 框架和美团自研的 RPC 框架(基于 Thrift)和消息队列(基于 Kafka)进行一系列需求(挽留弹窗,交叉引流任务,配置卡控…)的开发

  • 对实际应用中的开发流程有了一定的了解,从技术方案的形成,到前后端任务的分配,需求的开发与自测,联调与上线的流程都进行了熟悉和实际的操作

  • 调试定位问题解决问题的能力有所提升

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022 ZHU
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信