Jetson Nano 的使用

工具准备和附件购买

  • Linux 系统的电脑(可以是虚拟机):根目录 10G 左右空间,用户目录 40G 左右的空间,会在上面安装 SDK Manager 来对 Jetson Nano 进行刷机和安装 SDK。
  • 屏幕:支持 HDMI 连接,屏幕使用 HDMI 接 HD IN 和 micro USB 小端接 CTOUCH 接口,Jetson Nano 分别接 HDMI 和 micro USB 大端接口。
  • 无线网卡:这里用无须使用驱动的 WIFI Adapter 代替,刷机成功后 micro USB 接入电脑也能连接网络,不过使用附件可以更快的使用网络。
  • 键盘和鼠标:由于 Jetson Nano 推动设备能力比较差,最好不要从 Jetson Nano 的接口使用太多电量。

建立好 VNC 远程连接后,屏幕和键盘鼠标可以不用了。

图1 Jetson 接线

安装系统

使用 NVIDIA SDK Manager 安装系统。安装系统之前需要将 FC REC 和 GND 相连,使 Jetson Nano 进入 REC 刷机模式,之后连接好屏幕、micro USB 到电脑,最后接电源。

SDK Manager 和 Jetson Nano 的连接依靠的是 micro USB 线,连接成功后图中的 Target Hardware 会显示连接成功。其他的按图中设置即可。准备无误后进入第二步。

图2 SDK Manager 刷机,其一

因为自带的空间很小,只有 14G,去掉系统文件只有 8G 了。这个容量刚好可以装得下 SDK,不过其他东西都装不下了,所以先不装 SDK。因此 Target Components 下只勾选 Jetson OS,取消 Jetson SDK Components。

图3 SDK Manager 刷机,其二

同意协议后,进入第三步,开始刷机。

运行过程中如果出现主机(Host)上的错误可以不用管,比如 Host Components 中的 CUDA、Computer Vision 等组件,这些东西安装出错完全是因为主机本身的问题,和 Jetson Nano 无关,所以不用理会。

下载完成后会开始安装镜像,有选择安装镜像方式的选项要填。在这里填入的账号密码就是安装完系统后的账号密码。默认使用自动安装(auto setup),这里就能看到 SDK Manager 为 Jetson Nano 分配的地址,如果无法进行自动安装,可以把安装方式改成使用手动安装(manually setup)。

由于我主机上安装错误,所以没能成功进入第四步,但这并无影响,因为已经把镜像写入 Jetson Nano 了。

刷机成功后 Jetson Nano 应该已经开机了,输入账号密码登陆后。接下来 micro USB 线不要拔,再为 Jetson Nano 连好键盘、鼠标,在 Jetson Nano 上工作。

使用 U 盘

以下工作需要在 Jetson Nano 上且在 root 权限下进行。

容量拓展

首先将 U 盘通过软件更改为 Linux 所使用的 ext4 文件系统。如果里面的空间没有完全分配,可以插在 Jetson Nano 上后通过 gparted 分区软件拓宽分区,之后也经常会使用 gparted 改变 U 盘的容量分配。

图4 gparted 界面

挂载 U 盘

使用 mount 命令将 U 盘挂载到系统中。

mount /dev/sda1 /mnt/s/
# U盘挂载到/mnt/s/目录

使用 U 盘空间,屏蔽原空间

因为原有的空间只剩 8G 了,所以将会使用 U 盘空间代替原有空间。

使用 rsync 命令将根目录下的文件同步到 U 盘中,需要排除 /proc/:/sys/:/dev/:/mnt/:/etc/ 这些目录。其中 /proc/:/sys/:/dev/ 是系统运行时自动生成的目录,记录了系统工作的动态情况,每次开机时都会重写,所以不需要同步;/mnt/ 是 U 盘的挂载位置,不能同步,否则会无限循环;/etc/ 经常记录一些开机服务,如果同步了可能会有一些麻烦,所以没有同步。视情况考虑,我又排除了一些比较小的目录。

使用 mount 转移目录风险比较小,因为只是屏蔽了原目录,并没有删除掉,方法是通过 mount --rbind 绑定 U 盘目录,屏蔽原目录。具体来说,运行命令 mount --rbind <source> <target> 之后,所有对 target 的访问实际上都是在访问 source。这种屏蔽方法对系统运行没有影响,且容易恢复,因为这完全是一种临时的绑定,重启后这种绑定会消失,不过后面会将这一绑定写入开机运行服务中。

for d in bin home lib opt sbin srv usr var; do
    rsync -av /$d /mnt/s/
    # 同步,实际上是一种更高级的复制操作,它会对比源目录和目标目录的文件再决定是否复制
    mount --rbind /mnt/s/$d /$d
    # 绑定和屏蔽
done

自动化

将 U 盘的加载和绑定自动化。

首先是进行各种启动时的加载信息的设置,分别设置自动挂载和自动绑定和屏蔽。

  • 存放文件系统的静态信息的文件 /etc/fstab 添加一行

    /dev/sda1 /mnt/s/ ext4 defaults 0 2
    

    将 U 盘在开机时就挂载好,以上设置说明将 /dev/sda1 开机时挂载到 /mnt/s/,其文件系统为 ext4,参数默认,不需要 dump 备份,需要 fsck 检查但优先级较低。

  • 修改 /etc/rcX.d/

    先确定应该在那个目录下添加文件

    runlevel
    # 输出N5,说明在等级5启动,这很自然,因为等级5代表图形化界面启动
    vim /etc/init.d/auto-mount
    # 输入启动脚本内容
    update-rc.d auto-mount start 99 5
    # 加入启动项,等级5最低优先级启动
    

    自动绑定和屏蔽的脚本内容如下

    for d in bin home lib opt sbin srv usr var; do
        mount --rbind /mnt/s/$d /$d
    done
    

安装工具

使用 U 盘扩容后,空间足够安装 SDK 了。

安装 Jetson SDK

想要不使用 SDK Manager 安装 Jetson SDK 是不可能的,所以还是需要用到 SDK Manager。第一步和“安装系统”一样,只不过第二步安装选项改成 SDK。

同样,主机(Host)上的错误可以不用管。在安装 SDK 的过程中,可以在 Jetson Nano 上使用 df -h -text4 | grep sda 观察到 U 盘容量逐渐减少。

图5 SDK Manager 安装 SDK

安装 PyTorch

使用 nvcc 查看 CUDA 版本,nvcc -v

按照官网指导安装,选择对应的系统、包管理工具、语言和 CUDA 版本,运行给出的命令即可。

图6 安装 PyTorch 选项

远程连接

其实在安装系统和 Jetson SDK 的时候就可以看到,SDK Manager 自动为 Jetson Nano 分配了一个 IP 地址 192.168.55.1,系统(如果选择了自动安装的话)和 SDK 的安装也是通过这个地址。实际上,通过这个地址就可以与 Jetson Nano 建立网络连接和远程通信了。

使用 SSH 建立远程命令行连接

直接使用最简单方便的 SSH 建立连接。因为 SSH 连接非常基础,所以没什么要讲的。不过由于我们很多工作还是需要运行在图形界面的,所以只靠 SSH 连接是不够的。

图7 命令行 ssh

因为我比较习惯通过 vscode 进行 SSH 连接,所以直接配置好 .ssh 后让 vscode 读取即可。

图8 .ssh 配置

图9 vscode ssh 连接

使用 VNC 建立远程桌面连接

首先配置服务端,也就是 Jetson Nano。主要来说,是要运行 Vino Server 服务。

在 Jetson Nano 上运行命令,同样需要 root 权限。

apt update
apt-get install vino
# 安装服务程序
ln -s ../vino-server.service /usr/lib/systemd/user/graphical-session.target.wants
# 允许VNC服务
gsettings set org.gnome.Vino prompt-enabled false
gsettings set org.gnome.Vino require-encryption false
# 配置VNC Server

/usr/share/glib-2.0/schemas/org.gnome.Vino.gschema.xml 后面加入下列 key 字段

<key name='enabled' type='b'>
  <summary>Enable remote access to the desktop</summary>
  <description>
    If true, allows remote access to the desktop via the RFB
    protocol. Users on remote machines may then connect to the
    desktop using a VNC viewer.
  </description>
  <default>false</default>
</key>

之后继续运行 Shell 命令,

glib-compile-schemas /usr/share/glib-2.0/schemas
# 设置为Gnome编译模式
/usr/lib/vino/vino-server
# 启动VNC服务

注意需要在图形化界面启动 VNC 服务,也就是说不能在 SSH 界面使用 /usr/lib/vino/vino-server 这一条命令,会失败。

设置 VNC 账号密码

gsettings set org.gnome.Vino authentication-methods "['vnc']"
gsettings set org.gnome.Vino vnc-password $(echo -n '<password>' | base64)

设置登录启动 VNC 服务,新建 ~/.config/autostart/vino-server.desktop 后加入 ini 格式的内容。

gsettings set org.gnome.Vino enabled true
mkdir -p ~/.config/autostart
vi ~/.config/autostart/vino-server.desktop
[Desktop Entry]
Type=Application
Name=Vino VNC server
Exec=/usr/lib/vino/vino-server
NoDisplay=true

设置完后还是需要登陆后才能开启服务,所以再加上自动登录,通过修改 GNOME 的显示配置文件可以更改 GNOME 图形化界面的行为,可以通过这一方式实现自动登录。

配置文件为 /etc/gdm/custom.conf,在其中加入以下语句即可实现自动登录账号 jetson。

[daemon]
AutomaticLogin=jetson
AutomaticLoginEnable=True

客户端使用 VNC Viewer 远程连接,Linux、Windows、MacOs 都有这个软件。

重启后,等待一会即可使用 VNC Viewer 登录。

自此,基本上不再需要外接屏幕、键盘和鼠标了,只需要连接 micro USB 和电源即可。

备份系统

使用 Linux 系统备份,把 U 盘插好。

为了方便,采用了比较简单的备份方法,以下说明了可选的三种方法:

  • gparted 改变容量的方法;
  • gzip, bzip2 压缩方法;
  • dd conv=sparse 跳过空白区的方法;

gparted

图10 gparted 缩小容量前

图11 gparted 缩小容量后

使用 gparted 将 U 盘容量减小到尽可能小。顺序和前面的“容量拓展”反着来就行了,尽量取消白色部分,只保留黄色部分。与扩容相比,减小容量的过程会长一点,因为需要重新排列数据后才能删除多出的空闲空间。

之后再用其余的两种方法把 U 盘复制到本地,不过从 U 盘中读取数据时需要通过 bs,count 参数设定空间大小。比如上面将 /dev/sda1 缩小到了 15.29G,那么需要在使用 dd 命令时加入 bs=1M count=16200,其中 $$\text{bs}\times\text{count}$$ 需要大过 /dev/sda 的大小,但不能超过太多,这样才能只读取一部分数据,否则和会将过多未分配的空间也读取进来。

恢复的时候也需要用 gparted 将容量扩充。

使用了 gparted 的另一个好处在于,因为在压缩之前缩小了磁盘体积,所以可以转移到容量更小的 U 盘上;而坏处则是多了一些操作,也需要计算 bs、count,如果计算错误,会导致内容无法完全转移,导致数据丢失。

压缩方法

gzip, bzip2 或者其他压缩软件中选择一个。

ls /dev/sda*
# 查看设备
mkdir build_image && cd build_image
mkdir src && mount /dev/sda1 src && cd src
# 准备工作目录,挂载
dd if=/dev/zero of=tempzero
rm tempzero
# 在空闲内存中填充零,方便后续压缩
dd if=/dev/sda | gzip --best > jetson.img.gz
dd if=/dev/sda | bzip2 --best > jetson.img.bz2
# 因为linux上一切皆文件,所以读取压缩即可

比较关键的方法是通过填零来压缩空闲空间,这一部分需要等待到无法写零为止(如果 U 盘的使用率比较低的话,过程会比较漫长,建议使用 gparted),如果使用了 gparted 可以跳过填零这一步。然后使用 dd | xzip > .img 将 U 盘文件直接传输给压缩软件。

恢复时使用命令

gzip -dc jetson.img.gz | dd of=/de/etc/gdm/custom.confv/sda
bzip -dc jetson.img.bz2 | dd of=/dev/sda

由于直接将压缩结果输出到目标 U 盘中,不像在 Windows 系统下可能需要解压后再用专门的软件写入 U 盘,所以能节省空间。

conv=sparse

压缩过程会很久,如果不想压缩的话,也最好使用 conv=sparse 来跳过空白区域;如果要压缩,那么就不需要这个参数了,因为全零数据会被高度压缩。

前面的过程和压缩方法一样,也要填零之后再使用,或者使用 gparted 的就不用填零了。

dd conv=sparse bs=512 if=/dev/sda of=jetson.img.raw

恢复时使用命令

dd if=jetson.img.raw of=/dev/sda

备份结果

图12 压缩结果

如果使用了 gparted,那么压缩后原来 14G 的大小变为了 7.0G;
如果只使用了填 0,那么压缩后原来 14G 的大小变为了 7.2G;
如果只是压缩而没有填零或者使用 gparted 的话,原来 14G 的大小变成大约 8.4 G。

以上结果都是使用 bzip2 --best 的情况下得出的。

压缩总结

从上面可以看出,gparted+bzip2 压缩率大约 50.0%,zerofill+bzip2 压缩率大约为 51.4%,如果单纯使用 bzip2 ,相差不大。

去雾实验

图13 含雾图像形成过程

含雾图像的模型:\(\pmb I(\pmb x)\) 含雾图像,\(\pmb J(\pmb x)\) 原始图像,\(\pmb A\) 雾(大气颗粒、大气光照、atmospheric light),\(t(\pmb x)\) 雾的比例(通透度、transmission),\(d(\pmb x)\) 该像素点的深度(离前景的距离),\(\beta\) 系数。

\[\begin{align*} &\begin{cases} \pmb I(\pmb x)&=\pmb J(\pmb x)t(\pmb x)+\pmb A(1-t(\pmb x))\tag{1}\\ t&=e^{-\beta d(\pmb x)} \end{cases}\\ \implies&t={\Vert\pmb A-\pmb J(\pmb x)\Vert\over\Vert\pmb A-\pmb J(\pmb x)\Vert}={A^c-I^c(\pmb x)\over A^c-J^c(\pmb x)},c\in\lbrace r,g,b\rbrace \end{align*} \]

因为像素的每个通道都使公式成立,故 \(c\) 可以取任意通道。

暗通道

图14 暗通道实机演示,只是简单演示,存在拍摄失真
暗通道模型

大多数地面景观的像素区块至少有一个通道的部分像素的亮度接近 0,称为暗通道。用公式说明,即

\[\begin{align*} J^{dark}(\pmb x)&=\min_{\pmb y\in\Omega(\pmb x)}\left(\min_{c\in\lbrace r,g,b\rbrace}J^c(\pmb y)\right)\tag{2}\\ J^{dark}(\pmb x)&\to0 \end{align*} \]

其中,\(\Omega(\pmb x)\) 代表一个以 \(\pmb x\) 为中心的区块,在一个区块中的像素可能代表它们是同一个物体上的像素。

  • 阴影
  • 色彩单一物体
  • 暗色物体
计算通透度 \(t\)

重写模型公式,假定一个区块中的通透度是相同的\(\forall\pmb y\in\Omega(\pmb x)[t(\pmb y)\equiv\rm constant]\)

\[\begin{align*} &&{I^c(\pmb x)\over A^c}&={J^c(\pmb x)\over A^c}t(\pmb x)+1-t(\pmb x)\\ &&\min_{\pmb y\in\Omega(\pmb x)}\left(\min_{c\in\lbrace r,g,b\rbrace}{I^c(\pmb x)\over A^c}\right)&=\min_{\pmb y\in\Omega(\pmb x)}\left[\min_{c\in\lbrace r,g,b\rbrace}\left({J^c(\pmb x)\over A^c}t(\pmb x)+1-t(\pmb x)\right)\right]\\ &&\min_{\pmb y\in\Omega(\pmb x)}\left(\min_{c\in\lbrace r,g,b\rbrace}{I^c(\pmb x)\over A^c}\right)&=\tilde t(\pmb x)\min_{\pmb y\in\Omega(\pmb x)}\left(\min_{c\in\lbrace r,g,b\rbrace}{J^c(\pmb x)\over A^c}\right)+1-\tilde t(\pmb x)\\ \text{because}&&J^{dark}(\pmb x)&=\min_{\pmb y\in\Omega(\pmb x)}\left(\min_{c\in\lbrace r,g,b\rbrace}J^c(\pmb y)\right)\to0\\ \text{therefore}&&\min_{\pmb y\in\Omega(\pmb x)}\left(\min_{c\in\lbrace r,g,b\rbrace}{J^c(\pmb x)\over A^c}\right)&\to0\\ \text{then}&&\min_{\pmb y\in\Omega(\pmb x)}\left(\min_{c\in\lbrace r,g,b\rbrace}{I^c(\pmb x)\over A^c}\right)&=1-\tilde t(\pmb x)\\ &&\tilde t(\pmb x)&=1-\min_{\pmb y\in\Omega(\pmb x)}\left(\min_{c\in\lbrace r,g,b\rbrace}{I^c(\pmb x)\over A^c}\right)\tag{3} \end{align*} \]

其中 \(\min_{\pmb y\in\Omega(\pmb x)}\left(\min_{c\in\lbrace r,g,b\rbrace}{I^c(\pmb x)\over A^c}\right)\) 正是归一化后的含雾图像的暗通道。

保留少许雾气的修正公式加入了参数 \(\omega<1\)

\[\begin{align*} &&\tilde t(\pmb x)&=1-\omega\min_{\pmb y\in\Omega(\pmb x)}\left(\min_{c\in\lbrace r,g,b\rbrace}{I^c(\pmb x)\over A^c}\right)\tag{4} \end{align*} \]

计算大气光源 \(\pmb A\)

我们还需要求出 \(A\),可以用天空区域的暗通道像素来确定,因为天空区域几乎全由大气光组成,所以有如下公式。

\[\begin{align*} &&\forall\Omega(\pmb x)\in\text{SkyRegion}&\left[\min_{\pmb y\in\Omega(\pmb x)}\left(\min_{c\in\lbrace r,g,b\rbrace}{I^c(\pmb x)\over A^c}\right)\to1\right]\\ \text{such that}&&\tilde t(\pmb x)&\to 0 \end{align*} \]

这与深度和通透度的公式是相符的,天空区域的深度应该为无穷,这在暗通道中表现为亮度比较高的像素区域。

因此也可以写为,即 \(\pmb A\) 的计算公式,选择暗通道中最亮的点即可

\[\begin{align*} &d(\pmb x)\to\infty,t(\pmb x)\to0,\pmb A(\pmb x)=\pmb I(\pmb x)\\ \implies&\pmb A=\max_{\pmb y\in\lbrace \pmb x\mid t(\pmb x)\le t_0\rbrace}\pmb I(\pmb y)\tag{5} \end{align*} \]

去雾

修改含雾图像的模型公式 \((1)\),再将 \(t(\pmb x)\) 改为 \(\max\lbrace t(\pmb x),t_0\rbrace\) 可以保留天空区域的部分雾气,否则还原的天空区域会是接近黑色的一片噪声。相当于在天空区域提高了参数 \(\omega\)

\[\begin{align*} \pmb J(\pmb x)&={\pmb I(\pmb x)-\pmb A\over\max\lbrace t(\pmb x),t_0\rbrace}+\pmb A\tag{6} \end{align*} \]

总结

计算暗通道 —> 求 \(\pmb A\) —> 用 \(\pmb A\) 对含雾图像归一化 —> 求归一化后的暗通道 —> 计算 \(t(\pmb x)\) —> 计算 \(\pmb J(\pmb x)\)

神经网络

Dehaze-Net 方法

Dehaze-Net 方法分开计算 \(t(\pmb x)\)\(\pmb A\):先用神经网络计算 \(t(\pmb x)\),然后选择(暗通道中)亮度最高的区域作为 \(\pmb A\)。最后用模型公式还原图像。

AOD-Net 采用的方法

Dehaze-Net 方法分开计算 \(t(\pmb x)\)\(\pmb A\),存在部分失真。AOD-Net 将 \(t(\pmb x)\)\(\pmb A\) 合成为一个量 \(K(\pmb x)\),由神经网络来学习 \(K(\pmb x)\) 后直接进行去雾。

\[\begin{align*} \pmb J(\pmb x)&=\pmb K(\pmb x)\pmb I(\pmb x)-\pmb K(\pmb x)+\pmb b,\text{ where}\\ \pmb K(\pmb x)&={\frac1{t(\pmb x)}\left[\pmb I(\pmb x)-\pmb A\right]+(\pmb A-\pmb b)\over\pmb I(\pmb x)-\pmb 1} \end{align*}\tag{7} \]

设计网络进行监督学习以获得 \(\pmb K\)

采用不同大小的卷积核(multi-scale mapping)来区分不同大小的特征,连接不同大小的卷积核的结果形成一个新的多通道图像。并行的、不同大小的卷积核可以使神经网络对物体的尺度和位置的变化更不敏感。

连接不同卷积核的输出可以互相修正结果。比如,卷积核比较大的输出的结果就具有更多整体特征、更少细节特征,而较小的卷积核可以发现更多细节特征。结合两者的结果,是将大卷积核的结果中的整体特征再用小卷积核处理,可以获得更好的结果。

在 Dehaze-Net 中还使用了边缘检测,用来识别一个整体,其目的是为了减少同一个物体上的纹理变化导致的检测到的深度变化。如果没有考虑到纹理导致的 $$t(\pmb x)$$ 的变化,那么输出的图像中的相应纹理会变得模糊。

图15 Dehaze-Net 网络模型,绿色部分使用大卷积核,红色部分使用小卷积核,紫色部分加入了边缘检测

图16 AOD-Net 网络模型,采用了不同大小的卷积核的并行和连接

图17 训练集的生成过程

可以使用人工合成的含雾图像做训练集,来训练神经网络,过程如图所示。

合成图像需要原图像和相应的深度数据,然后选择合适的 \(\pmb A\)\(\beta\),将 \(\pmb A\) 按深度与原始图像混合即可。

剩下的训练部分与通常的监督学习相同,不再赘述。

posted @ 2022-10-27 15:55  Violeshnv  阅读(448)  评论(0编辑  收藏  举报