Jack Huang's Blog


  • 首页

  • 标签

  • 归档

  • 搜索

计算机视觉之相机成像原理与坐标系转换

发表于 2018-11-19 | 更新于 2021-06-27

计算机视觉是一门研究用摄影机和计算机代替人眼对目标进行识别、跟踪和测量的学科。为了解该门学科,首先应掌握投影原理和世界坐标系、相机坐标系、图像坐标系、像素坐标系之间的转换关系。

三维投影

计算机3D图形学中,三维投影是将三维空间中的点映射到二维平面上的方法。常用三维投影有正交投影和透视投影。正交投影通常用于对现实物品的三维建模,而透视投影与人的视觉系统类似,常用于在二维平面呈现三维世界。

正交投影原理

正交投影是一系列用于显示三维物体的轮廓、细节或精确测量结果的变换方法。通常又称作截面图、鸟瞰图或立面图。

当视平面的法向(即摄像机的朝向)平行于笛卡尔坐标系三根坐标轴中的一根,数学变换定义如下: 若使用一个平行于y轴(侧视图)的正交投影将三维点 $a_{x}$, $a_{y}$,$a_{z}$投影到二维平面上得到二维点 $b_{x}$,$b_{y}$,可以使用如下公式

$$b_x=s_xa_x+c_x$$
$$b_y=s_za_z+c_z$$

其中向量s是一个任意的缩放因子,而c是一个任意的偏移量。这些常量可自由选择,通常用于将视口调整到一个合适的位置。该投影变换同样可以使用矩阵表示(为清晰起见引入临时向量d)

$$
\begin{bmatrix}
d_x \
d_y \
\end{bmatrix}
=
\begin{bmatrix}
1 & 0 & 0 \
0 & 0 & 1 \
\end{bmatrix}
\begin{bmatrix}
a_x \
a_y \
a_z \
\end{bmatrix}
$$
$$
\begin{bmatrix}
b_x\
b_y\
\end{bmatrix}
=
\begin{bmatrix}
s_x & 0 \
0 & s_z \
\end{bmatrix}
\begin{bmatrix}
d_x\
d_y\
\end{bmatrix}
+
\begin{bmatrix}
c_x\
c_z\
\end{bmatrix}
$$
虽然正交投影产生的图像在一定程度上反映了物体的三维特性,但此类投影图像和实际观测到的并不相同。特别是对于相同长度的平行线段,无论离虚拟观察者(摄像机)远近与否,它们都会在正交投影中显示为相同长度。这会导致较近的线段看起来被缩短了。

透视投影原理

透视投影是为了获得接近真实三维物体的视觉效果而在二维的纸或者画布平面上绘图或者渲染的一种方法,它也称为透视图。透视投影的绘制必须根据已有的几何规则进行。

常用的透视投影视椎体模型如图1所示。设视点E位于原点,视平面P垂直于Z轴,且四边分别平行于x轴和y轴,视椎体的近截面离视点的距离为n,远截面离视点的距离为f,且一般取近截面为视平面。

透视投影的标准视椎体模型

图1 透视投影的标准视椎体模型

坐标系之间的转换

计算机视觉通常涉及到四个坐标系:像素平面坐标系(u,v)、像平面坐标系(图像物理坐标第(x,y)、相机坐标系(Xc,Yc,Zc)和世界坐标系(Xw,Yw,Zw),如图2所示。

四个坐标系

图2 四个坐标系

1 : 世界坐标系:根据情况而定,可以表示任何物体。单位m。

2:相机坐标系:以摄像机光心为原点(在针孔模型中也就是针孔为光心),z轴与光轴重合也就是z轴指向相机的前方(也就是与成像平面垂直),x轴与y轴的正方向与物体坐标系平行,其中上图中的f为摄像机的焦距。单位m

3:图像物理坐标系(也叫平面坐标系):用物理单位表示像素的位置,坐标原点为摄像机光轴与图像物理坐标系的交点位置。坐标系为图上o-xy。单位是mm。单位毫米的原因是此时由于相机内部的CCD传感器是很小的,比如8mm x 6mm。但是最后图像照片是也像素为单位比如640x480.这就涉及到了图像物理坐标系与像素坐标系的变换了。下面的像素坐标系将会讲到。

4:像素坐标系:以像素为单位,坐标原点在左上角。这也是一些opencv,OpenGL等库的坐标原点选在左上角的原因。当然明显看出CCD传感器以mm单位到像素中间有转换的。举个例子,CCD传感上上面的8mm x 6mm,转换到像素大小是640x480. 假如dx表示像素坐标系中每个像素的物理大小就是1/80. 也就是说毫米与像素点的之间关系是piexl/mm.

世界坐标系到相机坐标系的转换

物体之间的坐标系变换都可以表示坐标系的旋转变换加上平移变换,则世界坐标系到相机坐标系的转换关系也是如此,他们之间的变换如图3所示。

世界坐标系到相机坐标系的转换

图3 世界坐标系到相机坐标系的转换

可以得到P点在相机坐标系下的坐标:
$$
\begin{bmatrix}
X_c\
Y_c\
Z_c\
\end{bmatrix}
=
R
\begin{bmatrix}
X_w\
Y_w\
Z_w\
\end{bmatrix}
+T
\Rightarrow
\begin{bmatrix}
X_c\
Y_c\
Z_c\
1\
\end{bmatrix}
=
\begin{bmatrix}
R & T\
\vec{0} & 1\
\end{bmatrix}
\begin{bmatrix}
X_w\
Y_w\
Z_w\
1\
\end{bmatrix}
,
R:33,T:31
$$

相机坐标系到图像物理坐标系的转换

从相机坐标系到图像坐标系,属于透视投影关系,从3D转换到2D。 也可以看成是针孔模型的变种。该转换满足三角形的相似定理,如图4所示。

相机坐标系到图像物理坐标系的转换

图4 相机坐标系到图像物理坐标系的转换

图像物理坐标系到像素坐标系的转换

图像物理坐标系到像素坐标系的转换不涉及旋转变换,但是坐标原点位置不一致,大小不一致,涉及伸缩变换及平移变换,如图5所示。

图像物理坐标系到像素坐标系的转换

图5 图像物理坐标系到像素坐标系的转换

小结

四个坐标系之间存在着下述关系 ( 矩阵依次左乘 ),如图6所示:

世界坐标系到像素坐标系的转换

图6 世界坐标系到像素坐标系的转换

其中相机的内参和外参可以通过张正友标定获取。通过最终的转换关系来看,一个三维中的坐标点,的确可以在图像中找到一个对应的像素点,但是反过来,通过图像中的一个点找到它在三维中对应的点就很成了一个问题,因为我们并不知道等式左边的Zc的值。

参考链接

  1. 三维投影,by wikipedia.
  2. 透视投影的原理和实现,by Goncely.
  3. 计算机视觉:相机成像原理:世界坐标系、相机坐标系、图像坐标系、像素坐标系之间的转换,by 生活没有if-else
  4. 【相机标定】四个坐标系之间的变换关系
  5. SLAM相机位姿估计(1),by Zhao xuhui.
  6. 单目SLAM理论基础,by Zhao xuhui.
  7. 透视投影详解,by zdd.
  8. 旋转变换(一)旋转矩阵,by csxiaoshui.
  9. 第十二课 透视投影,by 极客学院.
  10. 光栅化渲染-6-透视投影矩阵,by Aincrad.

生活中的统计学之购买车展黄牛票

发表于 2018-11-17 | 更新于 2019-06-23

前段时间去看车展,一出地铁口,就有黄牛兜售车展门票,只需30元一张,而从车展正规窗口购买需要50元一张,那么买黄牛票还是买正规车展门票呢?

黄牛票有可能是真的,这样我就只需30元就可以参观车展,也有可能是假的,这样我就得花80元才能参观车展。假设黄牛票为真的概率是p1,根据概率论的知识,我参观车展花费的期望是:

$$E = 30p_1+80(1-p_1)$$

在没有任何先验知识的前提下,假设黄牛票为真的概率是0.5,于是每次买黄牛票参观车展的花费期望是55,而每次买正规门票参观车展的花费期望是50,因此不建议买黄牛票,而应该去买正规车展门票。

参考链接

  1. Cmd Markdown 公式指导手册
  2. Markdown中写数学公式

中国电信光猫华为HG8245C开启IPV6的方法

发表于 2018-11-11 | 更新于 2019-04-04

据报道,目前中国电信已成功创建了IP骨干网全面支持IPv6,并且在4G网络开启了IPv6服务,在100多个城域网提供了IPv6服务[1]。那么如何使家里宽带用上IPv6服务呢?这个问题最关键是设置入户光猫使其支持IPv6。以如何光猫华为HG8245C为例,说明设置过程。

基础知识

IPv6简介

网际协议第6版(英文:Internet Protocol version 6,缩写:IPv6)是网际协议(IP)的最新版本,用作互联网的网上层协议,用它来取代IPv4主要是为了解决IPv4地址枯竭问题,不过它也在其他很多方面对IPv4有所改进。

IPv6格式

IPv6二进位制下为128位长度,以16位为一组,每组以冒号“:”隔开,可以分为8组,每组以4位十六进制方式表示。例如:2001:0db8:85a3:08d3:1319:8a2e:0370:7344 是一个合法的IPv6地址。

同时IPv6在某些条件下可以省略:

  1. 每项数字前导的0可以省略,省略后前导数字仍是0则继续,例如下组IPv6是等价的。
    1
    2
    3
    4
    5
    2001:0DB8:02de:0000:0000:0000:0000:0e13
    2001:DB8:2de:0000:0000:0000:0000:e13
    2001:DB8:2de:000:000:000:000:e13
    2001:DB8:2de:00:00:00:00:e13
    2001:DB8:2de:0:0:0:0:e13
  2. 可以用双冒号“::”表示一组0或多组连续的0,但只能出现一次。
    1
    2
    3
    4
    5
    6
    7
    * 2001:DB8:2de:0:0:0:0:e13
    2001:DB8:2de::e13
    * 2001:0DB8:0000:0000:0000:0000:1428:57ab
    2001:0DB8:0000:0000:0000::1428:57ab
    2001:0DB8:0:0:0:0:1428:57ab
    2001:0DB8:0::0:1428:57ab
    2001:0DB8::1428:57ab
  3. 如果这个地址实际上是IPv4的地址,后32位可以用10进制数表示;因此::ffff:192.168.89.9 相等于::ffff:c0a8:5909。

IPv6地址分类

常用地址

IPv6地址可分为三种:

  • 单播(unicast)地址:单播地址标示一个网上接口。协议会把送往地址的数据包送往给其接口。
  • 任播(anycast)地址: Anycast是IPv6特有的数据发送方式,它像是IPv4的Unicast(单点传播)与Broadcast(多点广播)的综合。
  • 多播(multicast)地址: 多播地址也称组播地址。多播地址也被指定到一群不同的接口,送到多播地址的数据包会被发送到所有的地址。

特殊地址

未指定地址

  • ::/128-所有比特皆为零的地址称作未指定地址。

链路本地地址

  • ::1/128-是一种单播绕回地址。如果一个应用程序将数据包送到此地址,IPv6堆栈会转送这些数据包绕回到同样的虚拟接口(相当于IPv4中的127.0.0.1/8)。
  • fe80::/10-这些链路本地地址指明,这些地址只在区域连线中是合法的,这有点类似于IPv4中的169.254.0.0/16。

唯一区域位域

  • fc00::/7-唯一区域地址(ULA,unique local address)只可在一群网站中绕送。

多播地址

  • ff00::/8-这个前置表明定义在”IP Version 6 Addressing Architecture”(RFC 4291)中的多播地址[

IPv4转译地址

  • ::ffff:x.x.x.x/96-用于IPv4映射地址。
  • 2001::/32-用于Teredo隧道。
  • 2002::/16-用于6to4。

IPv6优势

  • 巨大的地址空间
  • 新的协议头格式,加快路由速度
  • 有效地、分级的寻址和路由结构
  • 有状态和无状态的地址配置
  • 内置的安全性
  • 更好的支持Qos
  • 用新协议处理邻节点的交互
  • 可扩展性

设置华为光猫HG8245C开启IPv6步骤

在设置华为光猫HG8245C开启IPv6之前,一是要确保所在电信已支持IPv6服务。二是获取华为HG8245C隐藏管理员帐号telecomadmin的密码,通常为nE7jA%5m。

以隐藏管理员帐号telecomadmin登录华为HG8245C的Web管理控制台后,选择“网络->宽带设置”,选择连接”2_INTERNET_R_VID_”,设置协议类型为“IPv4/IPv6”,设置前缀获取方式为“DHCPv6-PD”,再点应用即可。

开启IPv6

在Debian Linux中打开终端,输入如下命令或者浏览网站http://test-ipv6.com/验证IPv6。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether e4:70:b8:30:f1:5b brd ff:ff:ff:ff:ff:ff
inet 192.168.1.100/24 brd 192.168.1.255 scope global dynamic noprefixroute wlan0
valid_lft 251566sec preferred_lft 251566sec
inet6 240e:bc:e60:3d00:ea5b:f704:6b65:fab1/64 scope global dynamic noprefixroute
valid_lft 258984sec preferred_lft 172584sec
inet6 fe80::4e2c:4397:f016:3eb4/64 scope link noprefixroute
valid_lft forever preferred_lft forever

参考链接

  1. 中国电信:IPv6在线用户已超千万 年底将完成端到端服务能力.2018-07-16
  2. 维基百科.IPv6
  3. 掌握IPv6网络协议的优势,2010-06-10

树莓派学习之SFTP管理文件

发表于 2018-11-09

当需要向树莓派发送文件时,可使用SFTP上传下载文件。下面介绍如何使用SFTP向树莓派发送下载文件。

基础知识

SFTP是Secure File Transfer Protocol的缩写,安全文件传送协议。可以为传输文件提供一种安全的网络的加密方法。sftp 与 ftp 有着几乎一样的语法和功能。

SFTP 为 SSH的其中一部分,是一种传输文件至服务器的安全方式。在SSH软件包中,已经包含了一个叫作SFTP(Secure File Transfer Protocol)的安全文件信息传输子系统,SFTP本身没有单独的守护进程,它必须使用sshd守护进程(端口号默认是22)来完成相应的连接和答复操作。

SFTP传输使用了加密/解密技术,所以传输效率比普通的FTP要低得多,如果您对网络安全性要求更高时,可以使用SFTP代替FTP。

SFTP客户端程序

在Windows平台,常用的SFTP客户端程序有:

  • FileZilla
  • WinSCP
  • Xftp
  • Core FTP

在Linux平台,可直接使用sftp命令进行连接服务器。

基本操作

下面介绍在Debian平台使用sftp命令连接树莓派,在此之前应配置树莓派开启ssh服务。

  1. 建立连接

使用如下命令连接树莓派:

1
sftp pi@192.168.0.103
  1. 查看帮助

通过help查看在sftp连接下能使用的命令。从帮助中可知,在命令前加前缀“l”或者“!”即可在本地操作系统shell执行命令。

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
sftp> help
Available commands:
bye Quit sftp
cd path Change remote directory to 'path'
chgrp grp path Change group of file 'path' to 'grp'
chmod mode path Change permissions of file 'path' to 'mode'
chown own path Change owner of file 'path' to 'own'
df [-hi] [path] Display statistics for current directory or
filesystem containing 'path'
exit Quit sftp
get [-afPpRr] remote [local] Download file
reget [-fPpRr] remote [local] Resume download file
reput [-fPpRr] [local] remote Resume upload file
help Display this help text
lcd path Change local directory to 'path'
lls [ls-options [path]] Display local directory listing
lmkdir path Create local directory
ln [-s] oldpath newpath Link remote file (-s for symlink)
lpwd Print local working directory
ls [-1afhlnrSt] [path] Display remote directory listing
lumask umask Set local umask to 'umask'
mkdir path Create remote directory
progress Toggle display of progress meter
put [-afPpRr] local [remote] Upload file
pwd Display remote working directory
quit Quit sftp
rename oldpath newpath Rename remote file
rm path Delete remote file
rmdir path Remove remote directory
symlink oldpath newpath Symlink remote file
version Show SFTP version
!command Execute 'command' in local shell
! Escape to local shell
? Synonym for help
  1. 下载远程文件到本地主机
    使用get命令下载远程文件到本地主机:

    1
    2
    3
    sftp> get README.TXT 
    Fetching /home/pi/wiringPi/README.TXT to README.TXT
    /home/pi/wiringPi/README.TXT 100% 606 39.1KB/s 00:00

    get命令还有一些有用参数,如递归选项“ -r ”来递归的复制一个文件夹里面的内容,“ -P ”或者“ -p ”参数来告诉 SFTP 保持文件的权限访问位的设置和访问时间。

  2. 上传本地文件到远程主机
    使用“ put ”命令将文件上传到远程主机:

    1
    2
    3
    sftp> put README.TXT 
    Uploading README.TXT to /home/pi/README.TXT
    README.TXT 100% 606 33.6KB/s 00:00

    ” put “具有类似“ get ”的参数。例如,递归选项“ -r ”可以上传整个文件夹。

参考文献

  1. 华华. 手把手教你使用 SFTP 安全地传输文件.2015-12-5.

Cesium摄像头跟踪飞机实体时晃动问题分析

发表于 2018-11-08 | 更新于 2020-01-13

在3D场景下使用Cesium跟踪飞机时会出现摄像头晃动问题,导致地图背景不断晃动,影响观看。下面以最新的Cesium1.51源码为例,解析Cesium 渲染过程原理,分析跟踪实体时摄像头晃动的原因,找出可能的解决方法。

Cesium渲染过程分析

使用Cesium最简单示例代码如下:

1
var viewer = new Cesium.Viewer('cesiumContainer');

Viewer是Cesium构建应用的最基础的组件。它又是其他组件的容器,包括:

  • animation:控制时间前进、倒退、暂停以及前进和倒退速度的组件
  • baseLayerPicker:图层选择组件
  • fullscreenButton:控制是否全屏的组件
  • vrButton:控制是否VR显示的组件
  • geocoder:地理位置搜索组件
  • homeButton:返回摄像头默认位置按钮组建
  • infoBox:信息框组件
  • sceneModePicker:场景模式选择组件
  • selectionIndicator:选择指示组件
  • timeline:时间线组件
  • navigationHelpButton:导航帮助按钮,告诉使用者如何使用鼠标和触摸屏操纵虚拟地球
  • CesiumWidget:虚拟地球组件

其中,虚拟地球组件CesiumWidget是Viewer包含核心组件,在Viewer中创建CesiumWidget对象时,将设置其useDefaultRenderLoop属性。设置该属性将启动渲染函数startRenderLoop。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//from Source/Widgets/CesiumWidget/CesiumWidget.js
useDefaultRenderLoop : {
get : function() {
return this._useDefaultRenderLoop;
},
set : function(value) {
if (this._useDefaultRenderLoop !== value) {
this._useDefaultRenderLoop = value;
if (value && !this._renderLoopRunning) {
startRenderLoop(this);
}
}
}
},

函数startRenderLoop是Cesium渲染的开始,其代码如下:

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
function startRenderLoop(widget) {
widget._renderLoopRunning = true;

var lastFrameTime = 0;
function render(frameTime) {
if (widget.isDestroyed()) {
return;
}

if (widget._useDefaultRenderLoop) {
try {
var targetFrameRate = widget._targetFrameRate;
if (!defined(targetFrameRate)) {
widget.resize();
widget.render();
requestAnimationFrame(render);
} else {
var interval = 1000.0 / targetFrameRate;
var delta = frameTime - lastFrameTime;

if (delta > interval) {
widget.resize();
widget.render();
lastFrameTime = frameTime - (delta % interval);
}
requestAnimationFrame(render);
}
} catch (error) {
...
}
} else {
widget._renderLoopRunning = false;
}
}

requestAnimationFrame(render);
}

CesiumWidget组件的render方法随后调用Scene的render方法。

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
Scene.prototype.render = function(time) {
if (!defined(time)) {
time = JulianDate.now();
}

var frameState = this._frameState;
this._jobScheduler.resetBudgets();

var cameraChanged = this._view.checkForCameraUpdates(this);
var shouldRender = !this.requestRenderMode || this._renderRequested || cameraChanged || this._logDepthBufferDirty || (this.mode === SceneMode.MORPHING);
if (!shouldRender && defined(this.maximumRenderTimeChange) && defined(this._lastRenderTime)) {
var difference = Math.abs(JulianDate.secondsDifference(this._lastRenderTime, time));
shouldRender = shouldRender || difference > this.maximumRenderTimeChange;
}

if (shouldRender) {
this._lastRenderTime = JulianDate.clone(time, this._lastRenderTime);
this._renderRequested = false;
this._logDepthBufferDirty = false;
var frameNumber = CesiumMath.incrementWrap(frameState.frameNumber, 15000000.0, 1.0);
updateFrameNumber(this, frameNumber, time);
}

// Update
this._preUpdate.raiseEvent(this, time);
tryAndCatchError(this, update);
this._postUpdate.raiseEvent(this, time);

if (shouldRender) {
// Render
this._preRender.raiseEvent(this, time);
tryAndCatchError(this, render);

RequestScheduler.update();
}

updateDebugShowFramesPerSecond(this, shouldRender);
callAfterRenderFunctions(this);

if (shouldRender) {
this._postRender.raiseEvent(this, time);
}
};

Scene的render方法中tryAndCatchError函数将调用render函数。在该render函数中,地球的主要要素(地形&影像)的渲染,将在Globe的beginFrame和endFrame之间完成的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function render(scene) {
...

if (defined(scene.globe)) {
scene.globe.beginFrame(frameState);
}

updateEnvironment(scene);
updateAndExecuteCommands(scene, passState, backgroundColor);
resolveFramebuffers(scene, passState);

passState.framebuffer = undefined;
executeOverlayCommands(scene, passState);

if (defined(scene.globe)) {
scene.globe.endFrame(frameState);

if (!scene.globe.tilesLoaded) {
scene._renderRequested = true;
}
}

...
}

其中updateAndExecuteCommands负责数据的调度,比如哪些Tile需要创建,这些Tile相关的地形数据,以及涉及到的影像数据之间的调度,都是在该函数中维护。而scene.globe.endFrame中,会对该帧所涉及的GlobeTile的下载,解析等进行处理。

Cesium跟踪实体

在Viewer组件构造函数内,Viewer订阅了场景组件Scene的渲染后事件postRender,以执行Viewer自己的_postRender函数。

1
eventHelper.add(scene.postRender, Viewer.prototype._postRender, this);

Viewer的_postRender函数代码如下,其中updateTrackedEntity函数将更新被跟踪实体的摄像头位置:

1
2
3
4
Viewer.prototype._postRender = function() {
updateZoomTarget(this);
updateTrackedEntity(this);
};

updateTrackedEntity函数代码如下:

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
function updateTrackedEntity(viewer) {
if (!viewer._needTrackedEntityUpdate) {
return;
}

var trackedEntity = viewer._trackedEntity;
var currentTime = viewer.clock.currentTime;

//Verify we have a current position at this time. This is only triggered if a position
//has become undefined after trackedEntity is set but before the boundingSphere has been
//computed. In this case, we will track the entity once it comes back into existence.
var currentPosition = Property.getValueOrUndefined(trackedEntity.position, currentTime);

if (!defined(currentPosition)) {
return;
}

var scene = viewer.scene;

var state = viewer._dataSourceDisplay.getBoundingSphere(trackedEntity, false, boundingSphereScratch);
if (state === BoundingSphereState.PENDING) {
return;
}

var sceneMode = scene.mode;
if (sceneMode === SceneMode.COLUMBUS_VIEW || sceneMode === SceneMode.SCENE2D) {
scene.screenSpaceCameraController.enableTranslate = false;
}

if (sceneMode === SceneMode.COLUMBUS_VIEW || sceneMode === SceneMode.SCENE3D) {
scene.screenSpaceCameraController.enableTilt = false;
}

var bs = state !== BoundingSphereState.FAILED ? boundingSphereScratch : undefined;
viewer._entityView = new EntityView(trackedEntity, scene, scene.mapProjection.ellipsoid);
viewer._entityView.update(currentTime, bs);
viewer._needTrackedEntityUpdate = false;
}

除此之外,Viewer组件订阅了Clock组建的onTick事件,以执行其自身的_onTick事件处理函数:

1
eventHelper.add(clock.onTick, Viewer.prototype._onTick, this);

在Viewer组件的_onTick事件处理函数中,同样会更新被跟踪实体的摄像头位置。而Cesium摄像头跟踪飞机实体时产生晃动的根源即在此处。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Viewer.prototype._onTick = function(clock) {
var time = clock.currentTime;

var isUpdated = this._dataSourceDisplay.update(time);
if (this._allowDataSourcesToSuspendAnimation) {
this._clockViewModel.canAnimate = isUpdated;
}

var entityView = this._entityView;
if (defined(entityView)) {
var trackedEntity = this._trackedEntity;
var trackedState = this._dataSourceDisplay.getBoundingSphere(trackedEntity, false, boundingSphereScratch);
if (trackedState === BoundingSphereState.DONE) {
entityView.update(time, boundingSphereScratch);
}
}

...
};

可行的解决方案

在Viewer组件的_onTick函数做如下修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Viewer.prototype._onTick = function(clock) {
var time = clock.currentTime;

var isUpdated = this._dataSourceDisplay.update(time);
if (this._allowDataSourcesToSuspendAnimation) {
this._clockViewModel.canAnimate = isUpdated;
}

var entityView = this._entityView;
if (defined(entityView)) {
var trackedEntity = this._trackedEntity;
var trackedState = this._dataSourceDisplay.getBoundingSphere(trackedEntity, false, boundingSphereScratch);
if (trackedState === BoundingSphereState.DONE) {
//entityView.update(time, boundingSphereScratch);
var range=this.camera.distanceToBoundingSphere(boundingSphereScratch);
var targetRange=range>boundingSphereScratch.radius*10?range:boundingSphereScratch.radius*10;
var offset=new HeadingPitchRange(0.0,-Math.toRadians(45.0),targetRange);
this.camera.viewBoundingSphere(boundingSphereScratch,offset)
}
}

...
};

参考文献

  1. Cesium原理篇:1最长的一帧之渲染调度, by 法克鸡丝

树莓派学习之远程登录

发表于 2018-11-07 | 更新于 2023-02-06

每次通过USB转串口登录树莓派比较麻烦,可以设置树莓派开启ssh和vnc服务,以便通过ssh或vnc远程登录树莓派。下面介绍在Debian中通过ssh或vnc远程登录树莓派的过程。

基础知识

SSH

Secure Shell(安全外壳协议,简称SSH)是一种加密的网络传输协议,可在不安全的网络中为网络服务提供安全的传输环境。SSH通过在网络中创建安全隧道来实现SSH客户端与服务器之间的连接。SSH最常见的用途是远程登录系统,人们通常利用SSH来传输命令行界面和远程执行命令。

SSH基本用法

1
2
3
4
# 以用户名user,登录远程主机host
$ ssh user@host
# SSH的默认端口是22。使用p参数,可以修改这个端口。
$ ssh -p 2222 user@host

SSH通信过程及风险

SSH采用了公钥加密保证安全。

整个过程是这样的:(1)远程主机收到用户的登录请求,把自己的公钥发给用户。(2)用户使用这个公钥,将登录密码加密后,发送回来。(3)远程主机用自己的私钥,解密登录密码,如果密码正确,就同意用户登录。

这个过程本身是安全的,但是实施的时候存在”中间人攻击”风险:如果有人截获了登录请求,然后冒充远程主机,将伪造的公钥发给用户,那么用户很难辨别真伪。因为不像https协议,SSH协议的公钥是没有证书中心(CA)公证的,也就是说,都是自己签发的。

VNC

VNC(Virtual Network Computing),为一种使用RFB协议的显示屏画面分享及远程操作软件。此软件借由网上,可发送键盘与鼠标的动作及即时的显示屏画面。

VNC与操作系统无关,因此可跨平台使用,例如可用Windows连线到某Linux的计算机,反之亦同。甚至在没有安装客户端程序的计算机中,只要有支持JAVA的浏览器,也可使用。

原理

VNC系统由客户端,服务端和一个协议组成

VNC的服务端目的是分享其所运行机器的屏幕,服务端被动的允许客户端控制它。VNC客户端(或Viewer)观察控制服务端,与服务端交互。VNC协议Protocol(RFB)是一个简单的协议,传送服务端的原始图像到客户端(一个X,Y位置上的正方形的点阵数据),客户端传送事件消息到服务端。

服务器发送小方块的帧缓存给客户端,在最简单的情况,VNC协议使用大量的带宽,因此各种各样的方法被发明出来减少通讯的开支,举例来说,有各种各样的编码方法来决定最有效率的方法来传送这些点阵方块。

VNC默认使用TCP端口5900至5906,而JAVA的VNC客户端使用5800至5806。一个服务端可以在5900端口用“监听模式”连接一个客户端,使用监听模式的一个好处是服务端不需要设置防火墙。

安全性

VNC并非是安全的协议,虽然VNC伺服程序需设置密码才可接受外来连线,且VNC客户端与VNC伺服程序之间的密码传输经过加密,但仍可被轻易的拦截到并使用暴力破解法破解。不过VNC可设计以SSH或VPN传输,以增加安全性。

VNC软件

由于VNC以GPL授权,派生出了几个VNC软件:

  • RealVNC:由VNC团队部分成员开发,分为全功能商业版及免费版。
  • TightVNC:强调节省带宽使用。
  • UltraVNC:加入了TightVNC的部分程序及加强性能的图型映射驱动程序,并结合Active Directory及NTLM的账号密码认证,但仅有Windows版本。
  • Vine Viewer:MacOSX的VNC客户端。

配置树莓派支持SSH和VNC

通过USB转串口登录树莓派后,按如下步骤开启SSH和VNC:

  1. 运行命令raspi-config
    1
    sudo raspi-config
  2. 选择第五项Interfacing Options
  3. 选择enable SSH
  4. 启用SSH,重复步骤一和二,再选择enable VNC。

通过SSH或VNC连接树莓派

debian主机与树莓派在同一个局域网内,则按如下步骤通过SSH或VNC连接树莓派。

通过nmap命令获取树莓派ip地址

1
2
3
4
5
6
7
8
9
10
11
$ nmap 192.168.0.1/24
Starting Nmap 7.70 ( https://nmap.org ) at 2018-11-08 13:08 CST

Nmap scan report for 192.168.0.103
Host is up (0.041s latency).
Not shown: 998 closed ports
PORT STATE SERVICE
22/tcp open ssh
5900/tcp open vnc

Nmap done: 256 IP addresses (6 hosts up) scanned in 35.77 seconds

开启22和5900端口的主机即是树莓派。

通过ssh远程登录树莓派

1
2
# 首次登录会给出主机认证不能建立的提示,输入yes可继续,再输入用户密码即可远程登录树莓派
$ ssh pi@192.168.0.103

通过vnc远程登录树莓派

  1. 通过ssh登录树莓派后查看其使用的vnc程序

    1
    2
    3
    $ apt list --installed | grep vnc
    realvnc-vnc-server/now 6.3.1.36657 armhf [已安装,可升级至:6.3.2.39069]
    # 可知,树莓派上使用realvnc程序
  2. 在Debian上安装realvnc客户端程序realvnc-vnc-viewer

  3. 在终端输入vncviewer,输入树莓派ip、用户名、密码即可登录树莓派

参考文献

  1. SSH原理与运用(一):远程登录,by 阮一峰.
  2. Secure Shell,by wikipedia.
  3. VNC,by wikipedia.
  4. VPN原理与简单应用,by 新盟-胡巴.

树莓派学习之USB转串口

发表于 2018-11-05 | 更新于 2022-05-11

最近研究树莓派,需要通过usb转串口去连接树莓派,然后设置其wifi连接。于是将用到计算机硬件知识整理一下,并记录通过usb转串口设置树莓派wifi连接的过程。

硬件知识

计算机硬件常用接口有并口和串口,对应串行通信和并行通信。串行通信(英语:Serial communication)是指在计算机总线或其他数据信道上,每次传输一个比特数据,并连续进行以上单次过程的通信方式。与之对应的是并行通信,它在串行端口上通过一次同时传输若干比特数据的方式进行通信。

串行通信被用于长距离通信以及大多数计算机网络,在这些应用场合里,电缆和同步化使并行通信实际应用面临困难。凭借着其改善的信号完整性和传播速度,串行通信总线正在变得越来越普遍,甚至在短程距离的应用中,其优越性已经开始超越并行总线不需要串行化组件(serializer),并解决了诸如时钟偏移(Clock skew)、互联密度(interconnect density)等缺点。PCI到PCI Express的升级就一个例子。

并口

并行接口,简称并口。并口采用的是25针D形接头。所谓“并行”,是指8位数据同时通过并行线进行传送,这样数据传送速度大大提高,但并行传送的线路长度受到限制,因为长度增加,干扰就会增加,数据也就容易出错,目前,并行接口主要作为打印机端口等。

串口

串口叫做串行接口,也称串行通信接口,即COM口。按电气标准及协议来分包括RS-232-C、RS-422、RS485、USB等。 RS-232-C、RS-422与RS-485标准只对接口的电气特性做出规定,不涉及接插件、电缆或协议。

串行端口可以用于连接外置调制解调器、绘图仪或串行打印机。它也可以控制台连接的方式连接网络设备,例如路由器和交换机,主要用来配置它们。

  • RS-232-C

也称标准接口,是目前最常用的一种串行通讯接口。它是在1970年由美国电子工业协会(EIA)联合贝尔系统、 调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。

传统的RS-232-C接口标准有22根线,采用标准25芯D型插头座。自IBM PC/AT开始使用简化了的9芯D型插座。计算机一般有两个串行口:COM1和COM2,9针D形接口通常在计算机后面能看到。现在有很多手机数据线或者物流接收器都采用COM口与计算机相连。

  • RS-422

为改进RS-232通信距离短、速率低的缺点,RS-422定义了一种平衡通信接口,将传输速率提高到10Mb/s,传输距离延长到4000英尺(速率低于100kb/s时),并允许在一条平衡总线上连接最多10个接收器。

  • RS-485

为扩展应用范围,EIA又于1983年在RS-422基础上制定了RS-485 标准,增加了多点、双向通信能力,即允许多个发送器连接到同一条总线上,同时增加了发送器的驱动能力和冲突保护特性,扩展了总线共模范围,后命名为 TIA/EIA-485-A标准。

  • Universal Serial Bus(通用串行总线)

简称USB,是目前计算机上应用较广泛的接口规范,由Intel、Microsoft、Compaq、IBM、NEC、Northern Telcom等几家大厂商发起的新型外设接口标准。USB接口是计算机主板上的一种四针接口,其中中间两个针传输数据,两边两个针给外设供电。USB接口速度快、连接简单、不需要外接电源,传输速度12Mbps,新的USB 2.0可达480Mbps;电缆最大长度5米,USB电缆有4条线:2条信号线,2条电源线,可提供5伏特电源,USB电缆还分屏蔽和非屏蔽两种,屏蔽电缆传输速度可达12Mbps,价格较贵,非屏蔽电缆速度为1.5Mbps,但价格便宜;USB通过串联方式最多可串接127个设备;支持热插拔。最新的规格是USB 3.1。

  • RJ-45接口

是以太网最为常用的接口,RJ45是一个常用名称,指的是由IEC(60)603-7标准化,使用由国际性的接插件标准定义的8个位置(8针)的模块化插孔或者插头。

USB转串口原理

硬件接口电气特性

  • TTL电平:一般用作数字芯片的电平,例如芯片的供电电压是5V,那么高电平就是5V,低电平就是0V,这里所说的电平,就是TTL电平。

  • 232电平:232电平特制电脑串口的电平,-12V左右为正电平,+12V左右为低电平。我们刚才所见到的“USB转串口线”和电脑原生的串口,就是232电平。

USB转串口方法

PC的串口电气特性是232电平,单片机的串口电气特性是TTL电平,这两个就不一样,肯定需要某个芯片或者电路来进行转换匹配才可以通信。这个时候我们就需要TTL转232芯片了,常见的是MAX232,MAX3232等。连接方式如下:

232转TTL

但是随着USB接口的普及,当前计算机已经取消了串口。为实现232到TTL的转换,又需要USB转232。连接方式如下:

USB转232

为简化,可将USB转232和232转TTL集成到一个芯片上。这样的芯片常见的有CH340、PL2303。连接方式如下:

USB转TTL

常见的CH340芯片如下图所示:

USB转TTL

通过USB转串口连接树莓派

下面介绍在Debian Linux主机通过USB转串口连接树莓派的过程。

配置树莓派开启串口通信

将树莓派操作系统镜像烧录到SD卡后,打开boot分区,编辑其config.txt,在其末尾添加如下代码,以开启串口通信权限。

1
enable_uart=1

通过USB转串口物理连接树莓派

通过将USB转串口将Debian主机和树莓派物理连接好之后,给树莓派加电启动。

查看Debian系统能否识别ch340芯片

在官方Linux内核版本中自Kernel2.6以后就默认包含了对CH340/CH341芯片的驱动支持。在系统的默认驱动目录/lib/modules/$(uname -r)/kernel/drivers内可找到ch340芯片的驱动文件ch341.ko。

使用命令lsusb或dmesg查看linux系统是否识别USB转串口硬件。

1
2
3
4
5
6
7
8
~$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 005: ID 0bda:0129 Realtek Semiconductor Corp. RTS5129 Card Reader Controller
Bus 001 Device 004: ID 8087:0a2a Intel Corp.
Bus 001 Device 003: ID 1bcf:2b8a Sunplus Innovation Technology Inc.
Bus 001 Device 006: ID 1a86:7523 QinHeng Electronics HL-340 USB-Serial adapter
Bus 001 Device 002: ID 046d:c062 Logitech, Inc. M-UAS144 [LS1 Laser Mouse]
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Bus 001 Device 006: ID 1a86:7523 QinHeng Electronics HL-340 USB-Serial adapter表明Linux系统识别了usb转串口线缆,芯片类型为HL-340。

‘dmesg’命令显示linux内核的环形缓冲区信息,我们可以从中获得诸如系统架构、cpu、挂载的硬件,RAM等多个运行级别的大量的系统信息。当计算机启动时,系统内核(操作系统的核心部分)将会被加载到内存中。在加载的过程中会显示很多的信息,在这些信息中我们可以看到内核检测硬件设备。运行dmesg,输出如下:

1
2
3
4
5
6
7
8
9
10
11
~$ sudo dmesg | tail
[ 4248.441104] usbcore: registered new interface driver usbserial_generic
[ 4248.441112] usbserial: USB Serial support registered for generic
[ 4248.456079] usbcore: registered new interface driver ch341
[ 4248.456088] usbserial: USB Serial support registered for ch341-uart
[ 4248.456097] ch341 1-3:1.0: ch341-uart converter detected
[ 4248.456464] usb 1-3: ch341-uart converter now attached to ttyUSB0
[ 4284.405593] i2c_hid i2c-ELAN1010:00: i2c_hid_get_input: incomplete report (14/65535)
[ 4284.407844] i2c_hid i2c-ELAN1010:00: i2c_hid_get_input: incomplete report (14/65535)
[ 4657.772761] i2c_hid i2c-ELAN1010:00: i2c_hid_get_input: incomplete report (14/65535)
[ 4657.774969] i2c_hid i2c-ELAN1010:00: i2c_hid_get_input: incomplete report (14/65535)

[ 4248.456097] ch341 1-3:1.0: ch341-uart converter detected;
[ 4248.456464] usb 1-3: ch341-uart converter now attached to ttyUSB0说明linux系统识别了usb转串口适配器,并附加到ttyUSB0文件上。

使用minicom连接树莓派

minicom是linux平台的串行通信程序,类似于windows的超级终端程序。

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
$ usermod -a -G dialout $USER
#首次运行minicom使用-s选项,用于设置串行通信参数
$ minicom -s
+-----[configuration]------+
| Filenames and paths |
| File transfer protocols |
| Serial port setup |
| Modem and dialing |
| Screen and keyboard |
| Save setup as dfl |
| Save setup as.. |
| Exit |
| Exit from Minicom |
+--------------------------+
#通过上下键选择Serial port setup
+----------------------------------- +
| A - Serial Device : /dev/ttyUSB0 |
| B - Lockfile Location : /var/lock |
| C - Callin Program : |
| D - Callout Program : |
| E - Bps/Par/Bits : 115200 8N1 |
| F - Hardware Flow Control : Yes |
| G - Software Flow Control : No |
| |
| Change which setting? |
+----------------------------------+
# 设置Serial Device为/dev/ttyUSB0,然后推出到上一界面,使用enter键Save setup as dfl,保存配置。下次即可直接运行minicom使用之前保存的配置进行串口通信
# 这时候要选择单独Exit(退出),不要选择Exit from Minicom(退出Minicom),不然你就把minicom关了。
# 这时即可连接树莓派。

连接上树莓派后,输入用户名pi和密码raspberry,即可进入系统。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Raspbian GNU/Linux 9 raspberrypi ttyS0                                       
raspberrypi login: pi
Password:
Last login: Tue Oct 9 13:12:40 UTC 2018 on tty1
Linux raspberrypi 4.14.71-v7+ #1145 SMP Fri Sep 21 15:38:35 BST 2018 armv7l

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

Wi-Fi is disabled because the country is not set.
Use raspi-config to set the country before use.

pi@raspberrypi:~$

退出minicom,按Ctrl+A,再按下X键,会提示你是否退出,yes就可以了。

配置树莓派wifi连接

查看周围wifi热点信息

1
pi@raspberrypi:~$ iwlist scan

配置连接到wifi热点

1
2
3
4
5
6
7
8
9
10
11
12
# 编辑wifi文件
pi@raspberrypi:~$ sudo vi /etc/wpa_supplicant/wpa_supplicant.conf
# 在该文件最后添加下面的话
network={
  ssid="WIFINAME"
  psk="password"
}
# 引号部分分别为wifi的名字和密码
# 重启系统
pi@raspberrypi:~$ sudo init 6
# 登录后查看是否连接成功
pi@raspberrypi:~$ ip addr

参考文献

  1. https://www.cnblogs.com/zcshan/archive/2010/12/03/com.html ,by 水寒
  2. https://zh.wikipedia.org/wiki串行端口 , by wikipedia
  3. https://blog.csdn.net/he_wen_jie/article/details/50983076. by hwj666
  4. https://linux.cn/article-3587-1.html , by linux中国
  5. https://blog.csdn.net/JAZZSOLDIER/article/details/70170466 , by SoldierJazz2018
  6. http://blog.51cto.com/irinilu/289622, by feng9422
  7. Pipci. Linux 串口终端调试工具minicom[EB/OL].https://blog.csdn.net/Pipcie/article/details/79379451, 2018-02-26.
  8. https://blog.csdn.net/huayucong/article/details/51376279, by huayucong
  9. USB转串口CH340接线方法,2015-11-03.

树莓派操作系统镜像烧录方法指南

发表于 2018-11-03 | 更新于 2018-11-04

树莓派是一个微型计算机硬件平台,为使其正常工作还需要安装操作系统。通常将树莓派操作系统烧录到SD卡上,再将SD卡插到树莓派上。加电启动后,树莓派就会从SD卡引导启动操作系统,开始工作。这里SD卡就相当于普通计算机的硬盘。下面记录在Debian Linux下烧录树莓派操作系统到SD卡的方法。

镜像烧录工具

Etcher

Etcher是树莓派官方推荐的镜像烧录工具。它采用图形界面,支持Windows、Linux、Mac,使用简单方便,推荐普通用户使用。

dd

Linux的dd命令用于复制文件并对原文件的内容进行转换和格式化处理。dd命令功能很强大的,对于一些比较底层的问题,使用dd命令往往可以得到出人意料的效果。用的比较多的还是用dd来备份裸设备。同样也可用dd命令烧录系统镜像。

使用dd命令必须非常小心,如果输出指定了错误分区,将摧毁该分区的所有数据。

Linux下镜像烧录过程

使用Etcher烧录镜像比较简单,重点介绍使用dd命令烧录镜像的方法。

查找sd卡设备

可使用lsblk或者fdisk命令查找sd卡存储设备。需要注意的是:

  • 块设备以/dev/sdX命名,其中X是小写字母,例如/dev/sda。
  • 测试时发现当计算机只有usb3.0接口时,将不识别usb2.0的sd读卡器。

烧录镜像到sd卡

将镜像烧录到sd卡

使用如下dd命令将树莓派镜像少量到sd卡:

1
dd bs=4M if=2018-10-09-raspbian-stretch.img of=/dev/sdX conv=fsync

其中:

  • if=文件名:输入文件名,缺省为标准输入。即指定源文件。
  • of=文件名:输出文件名,缺省为标准输出。即指定目的文件。
  • bs=bytes:同时设置读入/输出的块大小为bytes个字节。
  • conv=conversion:用指定的参数转换文件。

将压缩镜像烧录到sd卡

当文件系统不支持大于4GB的文件时,可使用利用管道技术烧录镜像,命令如下:

1
unzip -p 2018-10-09-raspbian-stretch.zip | sudo dd of=/dev/sdX bs=4M conv=fsync

检查烧录进度

默认情况下dd命令不给出烧录进度信息,最新的dd提供status=progress选项给出进度信息,命令如下:

1
dd bs=4M if=2018-10-09-raspbian-stretch.img of=/dev/sdX status=progress conv=fsync

或者可使用dcfldd命令替换dd进行烧录操作。

检查镜像是否正确的烧录到sd卡

先使用dd命令将sd卡中内容复制到硬盘,在使用diff或者md5sum检查镜像文件与sd卡内容镜像之间的一致性。

之前dd命令烧录镜像到sd卡完成后会在shell中输出如下结果:

1
2
3
xxx+0 records in
yyy+0 records out
yyyyyyyyyy bytes (yyy kB, yyy KiB) copied, 0.00144744 s, 283 MB/s

xxx是我们需要的,表示读取xxx块记录烧录到sd卡,xxx乘以bs=4M应该等于原始镜像的大小。

使用如下命令复制sd卡内容到硬盘上:

1
2
# 经测试count命令没有其作用,该命令将整个sd内容复制到了硬盘,但我们期望只复制之前烧录到sd卡内容
dd bs=4M if=/dev/sdX of=from-sd-card.img count=xxx

如果from-sd-card.img文件大于原始镜像文件,那么先使用truncate命令将from-sd-card.img缩小到原始镜像文件的大小,命令如下:

1
truncate --reference 2018-10-09-raspbian-stretch.img from-sd-card.img

使用diff命令比较两者直接的一致性,如果不一致,可能烧录过程中有错误。

1
diff -s from-sd-card.img 2018-10-09-raspbian-stretch.img

使用sync命令强制将缓存写入硬盘,然后umount sd卡文件系统,最后移除sd卡。

参考文献

  1. https://www.raspberrypi.org/documentation/installation/installing-images/linux.md, by raspberrypi.
  2. https://blog.csdn.net/liumang_D/article/details/3899462, by liumang_d.
  3. https://linux.cn/article-8024-1.html, by linux中国
  4. https://www.jianshu.com/p/ff09ceffa816, by 二石兄.

树莓派学习笔记

发表于 2018-11-03 | 更新于 2018-12-20

WiringPi简介

WiringPi是应用于树莓派平台的GPIO控制库函数,WiringPi遵守GUN Lv3。wiringPi使用C或者C++开发并且可以被其他语言包转,例如python、ruby或者PHP等。WiringPi中的函数类似于Arduino的wiring系统,这使得熟悉arduino的用户使用wringPi更为方便。

树莓派具有26个普通输入和输出引脚。在这26个引脚中具有8个普通输入和输出管脚,这8个引脚既可以作为输入管脚也可以作为输出管脚。除此之外,树莓派还有一个2线形式的I2C、一个4线形式的SPI和一个UART接口。树莓派上的I2C和SPI接口也可以作为普通端口使用。如果串口控制台被关闭便可以使用树莓派上的UART功能。如果不使用I2C,SPI和UART等复用接口,那么树莓派总共具有8+2+5+2 =17个普通IO。

wiringPi包括一套gpio控制命令,使用gpio命令可以控制树莓派GPIO管脚。用户可以利用gpio命令通过shell脚本控制或查询GPIO管脚。wiringPi是可以扩展的,可以利用wiringPi的内部模块扩展模拟量输入芯片,可以使用MCP23x17/MCP23x08(I2C 或者SPI)扩展GPIO接口。另外可通过树莓派上的串口和Atmega(例如arduino等)扩展更多的GPIO功能。另外,用户可以自己编写扩展模块并把自定义的扩展模块集成到wiringPi中。WiringPi支持模拟量的读取和设置功能,不过在树莓派上并没有模拟量设备。但是使用WiringPi中的软件模块却可以轻松地应用AD或DA芯片。

I2C

I²C(Inter-Integrated Circuit)字面上的意思是集成电路之间,它其实是I²C Bus简称,所以中文应该叫集成电路总线,它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展。I²C的正确读法为“I平方C”(”I-squared-C”),而“I二C”(”I-two-C”)则是另一种错误但被广泛使用的读法。自2006年10月1日起,使用I²C协议已经不需要支付专利费,但制造商仍然需要付费以获取I²C从属设备地址。

设计说明

I²C只使用两条双向漏极开路(Open Drain)(串行数据(SDA)及串行时钟频率(SCL))并利用电阻将电位上拉。I²C允许相当大的工作电压范围,但典型的电压准位为+3.3V或+5v。

I²C的参考设计使用一个7比特长度的地址空间但保留了16个地址,所以在一组总线最多可和112个节点通信[a]。常见的I²C总线依传输速率的不同而有不同的模式:标准模式(100 Kbit/s)、低速模式(10 Kbit/s),但时钟频率可被允许下降至零,这代表可以暂停通信。而新一代的I²C总线可以和更多的节点(支持10比特长度的地址空间)以更快的速率通信:快速模式(400 Kbit/s)、高速模式(3.4 Mbit/s)。

虽然最大的节点数目是被地址空间所限制住,但实际上也会被总线上的总电容所限制住,一般而言为400 pF。

I²C通信

如上所述,参考设计为使用串行数据线(SDA)和串行时钟线(SCL)、拥有7bit寻址空间的总线。 总线上有两种类型角色的节点:

  • 主节点 - 产生时钟并发起与从节点的通信
  • 从节点 - 接收时钟并响应主节点的寻址

该总线是一种多主控总线,即可以在总线上放置任意多主节点。此外,在停止位(STOP)发出后,一个主节点也可以成为从节点,反之亦然。

总线上有四种不同的操作模式,虽然大部分设备只作为一种角色和使用其中两种操作模式:

  • 主节点发送 - 主节点发送数据给从节点
  • 主节点接收 - 主节点接收从节点数据
  • 从节点发送 - 从节点发送数据给主节点
  • 从节点接收 - 从节点接收主节点数据

一开始,主节点处于主节点发送模式,发送起始位(START),跟着发送希望与之通信的从节点的7bit位地址,最后再发送一个bit读写位,该数据位表示主节点想要与从节点进行读(1)还是写(0)操作。

如果从节点在总线上,它将以ACK字符比特位应答(低有效)该地址。主节点收到应答后,根据它发送的读写位,处于发送模式或者接收模式,从节点则处于对应的相反模式(接收或发送)。

地址和数据首先发送最高有效位。 起始位在SCL位高时,由SDA上电平从高变低表示;停止位在SCL为高时,由SDA上电平从低变高表示。其他SDA上的电平变化在SCL为低时发生。

如果主节点想要向从节点写数据,它将发送一个字节,然后从节点以ACK位应答,如此重复。此时,主节点处于主节点发送模式,从节点处于从节点接收模式。

如果主节点想要读取从节点数据,它将不断接收从节点发送的一个个字节,在收到每个字节后发送ACK进行应答,除了接收到的最后一个字节。此时,主节点处于主节点接收模式,从节点处于从节点发送模式。

此后,主节点要么发送停止位终止传输,要么发送另一个START比特以发起另一次传输(即“组合消息”)。

应用

I²C被应用在简单且其制造成本较传输速度更为重要的外设上。一些常见的应用如下:

  • 为了保存用户的设置而访问NVRAM芯片。
  • 访问低速的数字模拟转换器(DAC)。
  • 访问低速的模拟数字转换器(ADC)。
  • 改变监视器的对比度、色调及色彩平衡设置(视频数据通道)。
  • 改变音量大小。
  • 获取硬件监视及诊断数据,例如中央处理器的温度及风扇转速。
  • 读取实时时钟(Real-time clock)。
  • 在系统设备中用来打开或关闭电源供应。

I²C的另一个强大用途在于微控制器的应用,利用两根通用的输入输出接脚及软件的规划,可以让微控制器控制一个小型网络。

外设可以在系统仍然在运作的同时加入或移出总线,这代表对于有热插拔需求的设备而言是个理想的总线。

像I²C这样的总线之所以流行起来,是因为计算机工程师发现到对于集成电路设计而言,许多的制造成本源自于封装尺寸及接脚数量。更小的包装通常能够减少重量及电源的消耗,这对于移动电话及手持式计算机而言格外重要。

UART

在通信和计算机科学中,Serial communication是一个通用概念,泛指所有的串行的通信协议,如RS232、USB、I2C、SPI、1-Wire、Ethernet等。这里的串行(serial),是相对并行通信(parallel communication)来说的,如下图:

串行与并行通信

理解串行通信的概念之后,大家可能会有疑问:接收方接收到一长串的、表示0/1电平跳变的信号之后,怎么还原出有效的信息呢?有两种方法:

  • 发送端在发送串行数据的同时,提供一个时钟信号,并按照一定的约定(例如在时钟信号的上升沿的时候,将数据发送出去)发送数据,接收端根据发送端提供的时钟信号,以及大家的约定,接收数据。这就是常说的同步串行通信(Synchronous serial communication),I2C、SPI等有时钟信号的协议,都属于这种通信方式。本文不再详述。

  • 发送端在数据发送之前和之后,通过特定形式的信号(例如START信号和STOP信号),告诉接收端,可以开始(或者停止)接收数据了。与此同时,收发两方会约定一个数据发送的速度(就是大名鼎鼎的波特率),发送端在发送START信号之后,就按照固定的节奏发送串行数据,与此同时,接收端在收到START信号之后,也按照固定的节奏接收串行数据。这就是常说的异步串行通信(Asynchronous serial communication),我们本节的主角—-串口通信,就是这种通信方式。

UART(Universal Asynchronous Receiver/Transmitter) 即是规定编码格式、bit rate,产生通信所需的bit流的标准。

SPI

串行外设接口(Serial Peripheral Interface Bus,SPI),是一种用于短程通信的同步串行通信接口规范,主要应用于单片机系统中。类似I²C。 这种接口首先被Motorola(摩托罗拉)公司开发,然后发展成了一种行业规范。典型应用包含SD卡和液晶显示器。 SPI设备之间使用全双工模式通信,包含一个主机和一个或多个从机。主机产生待读或待写的帧数据,多个从机通过一个片选线路 决定哪个来响应主机的请求。 有时SPI接口被称作四线程接口,SPI准确来讲称为同步串行接口,但是与同步串行接口协议(SSI)不同,SSI是一个四线程 同步通信协议,但是使用差分信号输入同时仅提供一个单工通信信道。

接口

SPI总线规定了4个保留逻辑信号接口:

  • SCLK(Serial Clock):串列时脉,由主机发出
  • MOSI(Master Output,Slave Input):主机输出从机输入信号,由主机发出
  • MISO(Master Input,Slave Output):主机输入从机输出信号,由从机发出
  • SS(Slave Selected):选择信号,由主机发出,一般是低电位有效

尽管上面的引脚名称是最常用的,但在过去,有时会使用其他引脚命名约定,因此旧IC产品的SPI端口引脚名称可能有所不同。

1-Wire

1-Wire是Maxim子公司达拉斯半导体的专利技术,仅用单一信号线就可像I²C、SPI一样,传输时钟(clock)又传输数据(data),并且数据传输是双向的。1-Wire使用较低的数据传输速率,通常是用来沟通小型设备,如数字温度计。1-Wire有两种速率:标准模式16kbps,驱动模式142kbps。

单总线只有一根数据线。设备主机或从机通过一个漏极开路或三态端口连接至该数据线,这样允许设备在不发送数据时释放数据总线,以便总线被其它设备所使用。单总线端口为漏极开路其内部等效电路如下图所示。

单总线硬件接口示意图

参考文献

  1. 树莓派学习笔记——wiringPi简介、安装和管脚说明 , by xukai871105.
  2. I²C, by wikipedia.
  3. UART、RS232、TTL关系浅析, by 老狼.
  4. 串行外设接口,by wikipedia.
  5. 1-Wire,by wikipedia.
  6. 1-Wire单总线的基本原理,by ce123.
  7. 解析单总线协议(1-wire),by zhengqijun_.

Debian下编译QGroundControl源码

发表于 2018-11-01 | 更新于 2019-05-13

MAVLink(Micro Air Vehicle Link,微型空中飞行器链路通讯协议)是无人飞行器与地面站之间通讯,以及无人飞行器之间通讯最常用的协议。它已经在PX4、APM、PIXHAWK和Parrot AR.Drone飞控平台上进行了大量测试。

QGroundControl则是一种操纵基于MAVLink通信协议的无人机的跨平台地面站开源软件。下面记录在Debian Linux下从源码编译QGroundControl的过程。

下载源码

1
2
3
4
# 下载QGroundControl源码
git clone --recursive https://github.com/mavlink/qgroundcontrol.git
# 更新子模块
git submodule update

安装编译环境

  • 下载Qt社区版在线安装器,安装5.11.0版本的Qt,安装路径可在/opt下。
1
2
3
4
# 注意要选择安装5.9+以上版本的Qt,因为构建QGC时需要
# 运行qtcreater
$ cd /opt/Qt/Tools/QtCreator/bin/
$ ./qtcreator
  • 安装必要的包
1
sudo apt-get install speech-dispatcher libudev-dev libsdl2-dev
  • 解决编译时libQt5PositioningQuick.so.5不存在的bug
1
2
3
sudo apt-get install libqt5positioningquick5
sudo find /usr/ -name libQt5PositioningQuick.so.5
cp /usr/lib/x86_64-linux-gnu/libQt5PositioningQuick.so.5 /opt/Qt/5.10.0/gcc_64/lib/libQt5PositioningQuick.so.5
  • 将当前用户添加到dialout组,获取串口访问权限
1
2
# 运行QGroundControl需要当前用户拥有串口访问权限
sudo usermod -a -G dialout $USER

编译运行

打开Qt Creater,打开qgroundcontrol.pro工程,构建后运行,即可打开QGroundControl地面站软件。

参考链接

  1. https://blog.csdn.net/hebbely/article/details/79022799, by hebbely
  2. https://dev.qgroundcontrol.com/en/getting_started/, by Dronecode
上一页1…474849…53下一页

Jack Huang

521 日志
67 标签
© 2025 Jack Huang
由 Hexo 强力驱动
|
主题 — NexT.Muse