永恒的码流

万物皆流,无物常驻

0%

简介

删除集合中的元素,有两种删除的形式,一种是删除特定元素,一种是删除特定索引的元素。

删除的方式有:使用Java API (java 8)、从后往前的循环、使用迭代器、使用新的集合。

阅读全文 »

本主要总结个人作为App开发负责人时的一些经验,主要描述App的架构设计、开发管理以及相关事项等。

概述

我们接到需求后,准备开发一个新项目时,作为开发负责人该如何去做?主流程如下:

  1. 需求分析。确定业务事项、技术方案(难点、是否需要预研等)、需求拆分、开发估时等。
  2. 架构设计。根据技术方案,确定开发规范、设计App架构。
  3. 项目建立。建立服务端的版本仓库和CI/CD;搭建App架构;创建分支。
  4. 项目开发。团队各人根据分配的任务和分支,开发开发对应的功能点;分支合并需要进行服务端的自动测试和编译以及CodeReview。
  5. 测试调试。根据测试部分反馈的问题,解决BUG。
  6. 迭代维护。后期的维护和迭代开发。
  7. 团队管理。在以上的整个流程中都会涉及到管理,包括前期的需求拆分、任务分配,中期的项目进度管理和风险点管理,后期的联调,维护,以及期间与其他团队的交互等等,会涉及到一系列会议。
阅读全文 »

介绍实际车机开发中会用到的基础知识和概念,持续更新。

常见术语

简写 全称 描述
BAT Battery 蓄电池,BAT+一般表示电池有电状态
ACC Accessory 附件档,ACC-ON时可为部分车载附属设备供电,空调等除外。
IGN Ignition 点火档,IGN-ON时,可为所有设备供电,包括空调
IVI In-Vehicle Infotainment 车载娱乐信息系统,车机SoC是其中的核心部分
HVAC Heating, Ventilation and Air Conditioning 供热通风与空气调节,即空调
DVR Drive Video Recorder 行车记录仪
HUD Head-Up Display 抬头显示,在驾驶位正前方,一般与前车窗重合
M-CAN Multimedia CAN 多媒体CAN协议
V-CAN Vehicle CAN 车身CAN协议
RVC / RVM Rear View Camera / Monitor 后方车辆摄像头或监控,一般称为倒车影像,包括实时路况显示和雷达信息显示(距离)
AVM Around View Monitor 360度全景影像系统
阅读全文 »

本文档主要介绍利用某导航SDK开发AR导航的流程(获取并传递图片)和注意事项,基础导航、图像的检测、AR融合算法皆由SDK提供。

车载AR导航介绍

车载AR导航机制一般为:首先利用摄像头将前方道路的真实场景实时捕捉下来,再结合汽车当前定位及传感器(陀螺仪、惯导)数据、地图导航信息以及场景AI识别,进行融合计算,然后生成虚拟的导航指引模型,并叠加到真实道路上,从而创建出更贴近驾驶者真实视野的导航画面。

车载AR导航系统需要实现的能力:

  • 车道级导航能力。基础导航能力。
  • 图像获取、检测与识别能力。
  • AR融合算法能力。

前置条件

  • 基础导航。AR导航是基于基础导航的,基础导航的基本功能必须是完善的、没有问题的。
  • 基于DR的定位模式。DR定位模式可以提高AR导航的精度,使得AR引导提示得更加精准。
  • 摄像头。支持YUV格式帧流的摄像头(基本都支持),预览帧率必须不小于20fps(这个要根据具体SDK的AR服务要求来,我所使用的SDK中请求图片的帧率约为20fps)。
  • 传感器。三轴陀螺仪、三轴加速度,其丢帧率和稳定性要符合SDK提供方的要求。
  • 车身信息。车速信息、倒车信号、方向盘信息、车灯信息、雨刮器信息等。

其他具体要求看SDK提供方的文档。

阅读全文 »

之前做了基于某流行的导航SDK开发车机导航App的项目,现在做一个总结,仅仅总结与特定导航无关的基础知识和概念。

介绍

车载导航系统由硬件和软件组合而成,按功能可划分为由定位系统、系统软件、导航应用软件等。定位系统包括GNSS天线、各种传感器、滤波器、DR模块等;系统软件目前较流行的是Android系统;导航应用软件即常说的某某导航App,正式名称为导航电子地图,可通过触摸显示屏或者语音等进行交互操作, 实现实时定位、 目的地检索、路线规划、画面和语音引导等功能,帮助驾驶者准确、快捷地到达目的地。

车载导航与手机导航的最大的优势是,一,提供更加精准的定位,尤其是在GPS信号不好的时候,对比更加明显;二,更大的显示屏、更好的散热、更好的稳定性等。

定位系统

在信号弱或者高速行驶时,仅依靠卫星定位是困难的,目前的定位不只是卫星定位,也会有硬件或软件的参与。硬件一般包括:GNSS信号接收机或称天线、DR微处理器(也可能由软件处理)、车速传感器、陀螺仪、加速度计等。 目前定位一般是通过包括GNSS信号在内的许多信号一起通过航位推算算法(DR)计算得到的,下图是DR模块位于硬件或软件系统中时的信号流程图,MMF表示地图匹配反馈信号。

auto-navi-dr

目前普通民用 gps 和 dr 组合定位设备 (gps 惯性设备 ) 已经可以达到 1 000 m 无 gps 信号的情况下的航向精度和 10 m 的距离精度。

车载天线接收GNSS信号的频率是有规定的,比如10Hz,频率太高对系统要求也会高,必定提高成本。10Hz的情况下,如果车速为36km/h(10m/s),间隔1m接收一次定位信号,似乎可以接收,如果车速为33m/s(120km/h),则间隔3.3m才接收一次定位,对于一些路口区域,很容易错过。

航位推算:这种根据已知信息(当前位置、速度、加速度、方向等)推算经过一段时间后的位置的方法称为DR,dead reckoning。

卫星定位系统:一般称为全球导航卫星系统( Global Navigation Satellite System,GNSS),GPS是美国的全球定位系统的简称,一般应该使用GNSS代表卫星定位或导航系统的总称。

陀螺仪:一般指三轴陀螺仪,是用于获取车辆三个轴向的角速度值,如果是单轴陀螺仪,则一般指Z轴,用来测量车辆的左右转向角度值。

阅读全文 »

数字图像表示

  • 二值图:二维矩阵,也叫黑白图,每个点的值用一个比特表示 0-1
  • 灰度图:二维矩阵,每个点的值用8比特表示 0-255
  • 彩色图:多重二位矩阵

cs-picture-structure

视频和图像格式基础

  • 封装格式:视频文件包括编码后的音频和编码后的视频,将这二者封装成一个文件,就涉及到封装格式
  • 视频编码:将视频源文件进行编码,主流编解码格式为H264
  • 帧格式:视频源文件包含一系列的帧文件,帧有帧的封装格式如YUV

YUV420-888

扩展:YUV_420_888 介绍

首先YUV是一种颜色空间,基于YUV的颜色编码是流媒体的常用编码方式。“Y”表示明亮度(Luminance、Luma),“U”和“V”则是色度、浓度(Chrominance、Chroma)。

YUV420是一类格式的集合,YUV420并不能完全确定颜色数据的存储顺序。举例来说,对于4x4的图片,在YUV420下,任何格式都有16个Y值,4个U值和4个V值,不同格式只是Y、U和V的排列顺序变化。

I420(YUV420Planar的一种)则为YYYYYYYYYYYYYYYYUUUUVVVV,NV21(YUV420SemiPlanar)则为YYYYYYYYYYYYYYYYUVUVUVUV。

在Camera2中,YUV_420_888通常为YUV420Planar排列(手机不同可能会存在差异,目前我用过的几个手机都是这个格式)。实际应用中,可以根据采集到的数据中的pixelStride值判断具体的格式,再去做数据转换,其中pixelStride代表行内颜色值间隔。

cs-yuv-structure

说明:

  • 图中PixelStride表示一个颜色点的字节长度
  • 图中LineStride也叫RowStride,表示一行字节长度
阅读全文 »

之前参与了Launcher项目,负责样式的自定义布局部分。这部分主要涉及到的技术点是自定义布局和动画,难度适中,但略繁杂,其中有几个技术相关的难点记录如下。

介绍

布局基于二维格子:将图标显示区划分为m x n个格子,每个图标占据一个或多个格子。自由布局样式里,格子划分得更小,每个图标的最小尺寸有规定,不能小于规定值。

图标大小可更改:不同尺寸的图标展示的信息不一样,最小的图标仅展示一个ICON,最大的图标可以展示应用丰富的信息以及操作。展示的信息是通过与应用提供的服务组件或广播来通信的。

手指长按触发拖动

下图边框表示一个ViewGroup,包含8个子View。我们希望长按某个子View,并拖动它到其他的位置,比如长按View-1后,显示能够拖动,然后拖动它到View-7的位置。

android-launcher-customized-1.drawio

Github上应该有相关框架,可以自己实现但不需要,因为Android提供了相关API,见官方文档。其原理是创建待拖动View的副本,并跟随手指触摸坐标更改副本View的位置。使用流程为在View的长按监听回调OnLongClickListener.onLongClick(View v)里调用开启拖动的方法View.startDragAndDrop(),然后在目标容器中注册拖动回调监听即可触发拖动事件的回调方法OnDragListener.onDragEvent(),之后根据坐标判断拖动结束的地方或View。

阅读全文 »

介绍

基本概念:所谓贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。

贪心算法没有固定的算法框架,算法设计的关键是贪心策略的选择。必须注意的是,贪心算法不是对所有问题都能得到整体最优解,选择的贪心策略必须具备无后效性,即某个状态以后的过程不会影响以前的状态,只与当前状态有关。

所以对所采用的贪心策略一定要仔细分析其是否满足无后效性。

贪心策略适用的前提是:局部最优策略能导致产生全局最优解。

实际上,贪心算法适用的情况很少。一般,对一个问题分析是否适用于贪心算法,可以先选择该问题下的几个实际数据进行分析,就可做出判断。

贪心算法的基本思路

  • 建立数学模型来描述问题。
  • 把求解的问题分成若干个子问题。
  • 对每一子问题求解,得到子问题的局部最优解。
  • 把子问题的解局部最优解合成原来解问题的一个解。

贪心算法的实现框架

1
2
3
4
5
// 从问题的某一初始解出发;
while (能朝给定总目标前进一步){
// 利用可行的决策,求出可行解的一个解元素;
}
// 由所有解元素组合成问题的一个可行解;
阅读全文 »

简介

双指针用法的一种,这个算法技巧的思路非常简单,就是维护一个窗口,不断滑动,然后更新答案。

代码结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
int left = 0, right = 0;

while (right < s.size()) {`
// 增大窗口
window.add(s[right]);
right++;

while (window needs shrink) {
// 缩小窗口
window.remove(s[left]);
left++;
}
}

示例

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public String minWindow(String s, String t) {
if (s == null || t == null || s.length() < t.length()) {
return "";
}

int left = 0, right = 0, minLeft = 0, minRight = Integer.MAX_VALUE;
// 目标范围
Map<Character, Integer> need = new HashMap<>();
for (int i = 0; i < t.length(); i ++) {
char c = t.charAt(i);
need.put(c, need.get(c) == null ? 1 : need.get(c) + 1);
}
// 滑动窗口
Map<Character, Integer> window = new HashMap<>();
// valid
int validCount = 0;

while (right < s.length()) {
// 扩大窗口
char c = s.charAt(right);
right ++;
if (need.containsKey(c)) {
window.put(c, window.get(c) == null ? 1 : window.get(c) + 1);
if (window.get(c).equals(need.get(c))) {
validCount ++;
}
}

// 缩小窗口
while (validCount == need.size()) {
// 更新长度
if (right - left < minRight - minLeft) {
minLeft = left;
minRight = right;
}
char cc = s.charAt(left);
left ++;
if (need.containsKey(cc)) {
if (Objects.equals(window.get(cc), need.get(cc))) {
validCount --;
}
window.put(cc, window.get(cc) - 1);
}
}
}

return minRight == Integer.MAX_VALUE ? "" : s.substring(minLeft, minRight);
}

简介

解决一个回溯问题,实际上就是一个决策树的遍历过程。你只需要思考 3 个问题:

  • 路径:也就是已经做出的选择。
  • 选择列表:也就是你当前可以做的选择。
  • 结束条件:也就是到达决策树底层,无法再做选择的条件。

时空复杂度:时间复杂度都不可能低于 O(N!),因为穷举整棵决策树是无法避免的。这也是回溯算法的一个特点,不像动态规划存在重叠子问题可以优化,回溯算法就是纯暴力穷举,复杂度一般都很高。

适用场景:一般用于求具体路径!

阅读全文 »