UWB中TOF测距法的公式推导

UWB中TOF测距法的公式推导

UWB常用测距方法有两种:飞行时间测距法(TOF)和到达时间差法(TDOA)。这里说一下TOF。

TOF

飞行时间法(Time of Flight,TOF)是一种双向测距技术,它通过测量UWB信号在基站与标签之间往返的飞行时间来计算距离。根据数学关系,一点到已知点的距离为常数,那么这点一定在以已知点为圆心,以该常数为半径的圆上。有两个已知点,就有两个交点。以三个已知点和距离作三个圆,他们交于同一个点,该点就是标签的位置。

TOF定位方式需要基站和标签往返通信,因此就造成了TOF功耗大大提高,续航时间相对较短.

TOF又分为两种:单边双向测距和双边双向测距。

单边双向测距

单边双向测距(Single-sided Two-way Ranging: SS-TWR)是对单个往返消息时间上的简单测量,设备A主动发送数据到设备B,设备B返回数据响应设备A。如下图所示:

单边双向测距的流程是这样的:设备A(Device A)主动发送(TX)数据,同时记录发送时间戳,设备B(Device B)接收到之后记录接收时间戳;延时 $T_{reply}$ 之后,设备B发送数据,同时记录发送时间戳,设备A接收数据,同时记录接收时间戳。

所以可以拿到两个时间差数据,设备A的时间差 $T_{round}$ 和设备B的时间差 $T_{reply}$ ,最终得到无线信号的飞行时间 $T_{prop}$ 如下:

$ T_{prop} = \frac{1}{2}(T_{round}-T_{reply}) $

两个差值时间都是基于本地的时钟计算得到的,本地时钟误差可以抵消,但是不同设备之间会存在微小的时钟偏移,假设设备A和B的时钟偏移分别为 $e_A$ 和 $e_B$ ,则飞行时间测量值为:

$ \hat{T}_{prop} = \frac{1}{2}[T_{round}(1+e_A)-T_{reply}(1+e_B)] $

于是测距误差如下:

$ Error = \hat{T}_{prop} - T_{prop} = \frac{1}{2}(T_{round}\cdot e_A-T_{reply}\cdot e_B) = \frac{1}{2}T_{reply}(e_A-e_B) + T_{prop}\cdot e_A $

因为 $ T_{reply} >> T_{prop} $ , 所以可以忽略后一项,得到

$ Error = \hat{T}_{prop} - T_{prop} \approx \frac{1}{2}T_{reply}(e_A-e_B) $

由此可以看出,随着 $T_{reply} $ 和时钟偏移的增加,会增加飞行时间的误差,从而使得测距不准确。因此单边双向测距(SS-TWR)并不常用,但对于特定的应用,如果对于精度要求不是很高,但是需要更短的测距时间可以采用。注意 $ T_{reply} $ 不仅仅是设备B接收到发送的时间,也包括装载数据和发送数据耗费的时间(UWB除了支持定位之外,也可以传输数据,标准可以装载128字节,扩展模式可以装载1024字节数据)。

双边双向测距

双边双向测距(Double-sided Two-way Ranging)是单边双向测距的一种扩展测距方法,记录了两个往返的时间戳,最后得到飞行时间,虽然增加了响应的时间,但会降低测距误差。

双边双向测距分为两次测距,设备A主动发起第一次测距消息,设备B响应,当设备A收到数据之后,再返回数据,最终可以得到如下四个时间差:$ T_{round1} $ 、$ T_{reply1} $ 、$ T_{round2} $ 、$ T_{reply2} $ ,如下图所示:

双边双向测距飞行时间计算方法:

由单边双向测距方法可得

$ T_{prop} = \frac{1}{2}(T_{round1}-T_{reply1}) $

$ T_{prop} = \frac{1}{2}(T_{round2}-T_{reply2}) $

所以

$ \begin{flalign}T_{round1} \times T_{round2} = (2T_{prop}+T_{reply1})(2T_{prop}+T_{reply2}) = 4T_{prop}^2+2T_{prop}(T_{reply1}+T_{reply2})+T_{reply1}T_{reply2} \end{flalign}$

$ \begin{flalign} T_{round1} \times T_{round2} - T_{reply1}T_{reply2} = 4T_{prop}^2+2T_{prop}(T_{reply1}+T_{reply2}) \\ = T_{prop}(4T_{prop}+2T_{reply1}+2T_{reply2}) \\ = T_{prop}(T_{round1} + T_{round2} + T_{reply1} + T_{reply2}) \end{flalign}$

于是可以得到如下计算 $ T_{prop} $ 的公式:

$ T_{prop} = \frac{T_{round1} \times T_{round2} - T_{reply1} \times T_{reply2}}{T_{round1} + T_{round2} + T_{reply1} + T_{reply2}} $

以上测距的机制是非对称的测距方法,因为他们对于响应时间不要求是相同的。下面分析双边双向测距飞行时间的误差:

$ \begin{flalign} \hat{T}_{prop} = \frac{T_{round1}(1+e_A) \times T_{round2}(1+e_B) - T_{reply1}(1+e_B) \times T_{reply2}(1+e_A)}{T_{round1}(1+e_A) + T_{round2}(1+e_B) + T_{reply1}(1+e_B) + T_{reply2}(1+e_A)} \\ = \frac{(4T_{prop}^2+2T_{prop}(T_{reply1}+T_{reply2}))(1+e_A)(1+e_B)} {4T_{prop}+2(T_{reply1}+T_{reply2})+(2T_{prop}+T_{reply1}+T_{reply2})(e_A+e_B)}\\ =\frac{2(1+e_A)(1+e_B)}{(1+e_A)+(1+e_B)}T_{prop} \end{flalign}$

于是

$ T_{prop} = \frac{(1+e_A)+(1+e_B)}{2(1+e_A)(1+e_B)}\hat{T}_{prop} $

$ \begin{flalign} Error = \hat{T}_{prop} - T_{prop} = \left(1-\frac{(1+e_A)+(1+e_B)}{2(1+e_A)(1+e_B)}\right)\hat{T}_{prop} \\ = \frac{e_A+e_B+2e_A e_B}{2(1+e_A)(1+e_B)}\hat{T}_{prop} \end{flalign} $

因为 $ e_A <<1 $,$ e_B<<1$,略去高次项,可得

$ Error \approx \frac{e_A+e_B}{2}\hat{T}_{prop} $

由此可以看出,误差仅与钟漂和飞行时间有关。

假设一个使用场景:使用20ppm的晶体,UWB的工作距离范围为300m,则无线信号空中飞行时间大概为$1 \mu s$,误差为$ 20 \times 10^{-6} \times 1 \times 10^{-6} = 20 \times 10^{-12} = 20ps $时钟误差是在ps级别的,换算为距离之后仅为6mm。

注意:响应时间是不需要相等的,也就是 $ T_{reply1} $ 不一定要等于 $ T_{reply2} $ ,这样对于MCU系统的处理带来了很多便利。

若双边双向测距方法响应时间对称,也就是 $ T_{reply1} $ 和 $ T_{reply2} $ 相等,飞行时间计算方法如下:

$ T_{prop} = \frac{1}{4}(T_{round1}-T_{reply1}+T_{round2}-T_{reply2}) $

这种方法比较简单,只是需要一些时间戳做加减法,但其难点在于,怎么保证 $T_{reply1} $ 和 $ T_{reply2} $ 是相等的。

此种方法的误差分析如下:

$ \begin{flalign} \hat{T}_{prop} = \frac{1}{4}\left[T_{round1}(1+e_A)-T_{reply1}(1+e_B)+T_{round2}(1+e_B)-T_{reply2}(1+e_A)\right] \end{flalign}$

$ \begin{flalign} Error = \hat{T}_{prop} - T_{prop} = \frac{1}{4}\left[(T_{round1}-T_{reply2})e_A +(T_{round2}-T_{reply1})e_B\right] \\ =\frac{1}{4}\left[2(e_A+e_B)T_{prop} +(e_A-e_B)(T_{reply1}-T_{reply2})\right] \end{flalign} $

因为 $ T_{reply1}-T_{reply2} >> T_{prop} $ ,可忽略$ T_{prop} $项,从而得到

$ Error \approx \frac{1}{4}(e_A-e_B)(T_{reply1}-T_{reply2})$

可见此误差与响应时间差成正比。

参考链接


使用SSH Key访问服务器

1. 配置

生成的 SSH Key 默认存储在 ~/.ssh 目录下,可以使用 ls ~/.ssh 查看之前是否已经生成过 SSH Key,如果提示 No such file or directory 可以使用如下指令用于生成 SSH Key:

ssh-keygen

接着会询问保存文件的位置以及是否要设定 passphrase,如果设定了 passsphrase 那么每次使用该 SSH Key 的时候都需要输入这个 passsphrase,可以根据自己对安全性的需求设定,空白表示不设定。

如果采用默认的设置,那么会在 ~/.ssh 路径下生成两个 Key,一个私钥 id_rsa ,另一个公钥 id_rsa.pub,私钥需要好好保管。

2. 使用 ssh-copy-id

如果操作系统中有 ssh-copy-id,那么可以直接使用以下命令设置:

ssh-copy-id -i ~/.ssh/id_rsa.pub username@server
  • username:连接服务器的用户名
  • server:服务器的域名或者 ip 地址
  • ~/.ssh/id_rsa.pub:默认的公钥地址,如果修改过 SSH Key 存储地址,请填写对应地址

3. 复制公钥到服务器的 authorized_keys 中

ssh username@server 'mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys' < ~/.ssh/id_rsa.pub

上述命令用于在远程服务器上创建 ~/.ssh 文件夹并用本机的公钥创建服务器上的 ~/.ssh/authorized_keys 文件,接着设置权限:

ssh username@server 'chmod 700 -R ~/.ssh && chmod 600 ~/.ssh/authorized_keys'

4. 验证

经过上述的配置之后,可以再次进行 SSH 连接验证配置是否生效:

ssh username@server

如果不需要输入密码就能够连接服务器,说明设置生效。

参考链接


使用 SSH Key 访问服务器

ubuntu 22.04 WARNING: [pool ***] server reached max_children, consider raising it

最近 WordPress 上传图片的时候,偶尔会发生失败,报错 503。以前比较少,最近越来越频繁,于是跟踪了一下服务器的日志,看到如下报错信息:

xxx.xxx.xxx.xxx - - [08/Aug/2023:14:50:36 +0800] "POST /wp-admin/async-upload.php HTTP/2.0" 503 417 "https://www.mobibrw.com/wp-admin/post.php?post=xxxx&action=edit&classic-editor" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"

查看 PHP 的日志,却找不到任何有用的日志,只看到如下警告:

起初是百思不得其解,按理说应该有相关报错才对。

后来仔细思考了一下,才想明白,原来是并发数超过最大限制了,默认的并发数量是 5,这个绝对是不够的,并发超过限制是没有任何报错信息的,日志只会给出一个警告。

详细的解决方案如下:

If you encounter such an error in the php logs, then it is time to modify max_children. The php logs are usually located in /var/log/,  and in my case they are in /var/log/php7.4-fpm.log

WARNING: [pool ********] server reached max_children setting (8), consider raising it

Search for the file where the max_children variable is located, in my case the file is www.conf and is located in the folder: /etc/php/7.4/fpm/pool.d/

Change the value pm.max_children = 8 to a larger one, 16 for example

$ sudo nano /etc/php/7.4/fpm/pool.d/www.conf

save the .conf file and restart the web server:

$ sudo systemctl restart apache2

or only the php-fpm service as follows:

$ sudo systemctl restart php7.4-fpm.service

Eventually you can check these logs, who knows, you may not even know you have such a problem!

** As I noticed, for HestiaCP (VestaCP) users max_children can be found in both the /etc/php/7.4/fpm/pool.d/www.conf file and the file /usr/local/hestia/php/etc/php-fpm.conf

** Attention, you must also have enough memory for this, otherwise the results are not as expected!

Check the memory used by each php-fpm service-child with the following command:

$ ps -ef | grep '[f]'pm
root      618    1  0 Dec17 ?        00:00:27 php-fpm: master process (/usr/local/hestia/php/etc/php-fpm.conf)
root     1692    1  0 01:11 ?        00:00:00 php-fpm: master process (/etc/php/7.4/fpm/php-fpm.conf)
matrix    3539 1692 14 01:30 ?        00:00:03 php-fpm: pool mydomain314159265.com
matrix    3542 1692 10 01:30 ?        00:00:01 php-fpm: pool mydomain314159265.com
matrix    3547 1692  0 01:30 ?        00:00:00 php-fpm: pool mydomain314159265.com
matrix    3548 1692 15 01:30 ?        00:00:00 php-fpm: pool mydomain314159265.com

Then, to see the memory required for these processes, we need to add the rss parameter which is “Resident Set Size” or the physical memory. Note: Modify php-fpm7.4 to suit your version of PHP:

$ ps -C php-fpm7.4 -o rss=
81316
136872
138096
121728

These numbers are in Kbytes , so we calculate an average of the displayed values and multiply by max_children, and we find approximately the memory needed only for php, but don’t forget, the server also needs memory for Apache, Nginx, mysql … maybe Varnish, HAproxy, Redish, etc. .. so, check these values carefully!

参考链接


Solved! WARNING: [pool ***] server reached max_children, consider raising it

ubuntu 22.04 Warning: apt-key is deprecated

在尝试安装 nvidia-docker 的时候(ubuntu 22.04系统Docker和Nvidia-docker的安装、测试,及运行GUI应用),执行如下命令增加 nvidia 源:

$ distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
   && curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - \
   && curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list

系统发出如下警告:

Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8)).

经过分析,是脚本中的如下部分导致:

$ curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo tee /etc/apt/trusted.gpg.d/nvidia-docker.gpg 

解决方法为修改为使用如下脚本:

$ curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg 

$ sudo chmod 644 /etc/apt/trusted.gpg.d/nvidia-docker.gpg

完整修改后的脚本如下:

$ distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
   && curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
   && sudo chmod 644 /etc/apt/trusted.gpg.d/nvidia-docker.gpg \
   && curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list

参考链接


Warning: apt-key is deprecated (SOLVED)

ubuntu 22.04系统Docker和Nvidia-docker的安装、测试,及运行GUI应用

快速搭建所需开发环境

Docker文档:https://docs.docker.com/,Docker安装指南: Install Docker Engine on Ubuntu

Dokcer安装

Uninstall old versions
 
~$ sudo apt-get remove docker docker-engine docker.io containerd runc

~$ sudo apt-get install curl
 
Install using the repository

~$ curl https://get.docker.com | sh \
  && sudo systemctl --now enable docker
 
Verify that Docker Engine is installed correctly by running the hello-world image.
~$ sudo docker run hello-world

Docker测试

# 启动docker服务
$ sudo service docker start

# Docker: hello-world
$ sudo docker run hello-world

其他Docker命令:

Usage: service docker {start|stop|restart|status}

查看镜像
$ sudo docker images

查看容器
$ sudo docker container ls -a

Tips:Docker中一般Crtl+C退出,传送门:停止、删除所有的 docker 容器和镜像

Nvidia-docker安装

查看nvidia版本

$ nvidia-smi
Thu Nov 26 10:34:37 2020       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 450.80.02    Driver Version: 450.80.02    CUDA Version: 11.0     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  GeForce RTX 2060    Off  | 00000000:01:00.0  On |                  N/A |
|  0%   37C    P8     9W / 190W |    301MiB /  5931MiB |      2%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|    0   N/A  N/A       942      G   /usr/lib/xorg/Xorg                 35MiB |
|    0   N/A  N/A      2278      G   /usr/lib/xorg/Xorg                 96MiB |
|    0   N/A  N/A      2404      G   /usr/bin/gnome-shell              150MiB |
|    0   N/A  N/A      4051      G   /usr/lib/firefox/firefox            3MiB |
+-----------------------------------------------------------------------------+

参考链接:官网 installation guide
Github:NVIDIA/nvidia-docker

#
$ distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
      && curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
      && curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
            sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
            sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
 
$ sudo apt-get update

$ sudo apt-get install -y nvidia-container-toolkit

$ sudo nvidia-ctk runtime configure --runtime=docker
 
$ sudo systemctl restart docker

$ docker pull nvidia/cudagl:11.0-base
 
# 测试
$ docker run --rm --gpus all nvidia/cudagl:11.0-base nvidia-smi
 
Thu Nov 26 02:30:34 2020       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 450.80.02    Driver Version: 450.80.02    CUDA Version: 11.0     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  GeForce RTX 2060    Off  | 00000000:01:00.0  On |                  N/A |
|  0%   37C    P8    10W / 190W |    307MiB /  5931MiB |     13%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+

测试

$ sudo docker run --runtime=nvidia --rm nvidia/cudagl:11.0-base nvidia-smi

Docker 容器 GUI

$ sudo apt-get install x11-xserver-utils

# 关闭权限控制,允许其他X客户端绘制
$ xhost +
access control disabled, clients can connect from any host

$ docker run -e DISPLAY=$DISPLAY -e GDK_SCALE -e GDK_DPI_SCAL -v /tmp/.X11-unix:/tmp/.X11-unix --rm -it container-name-or-id

若遇到X Error时,添加参数:--ipc=host 或 --env="QT_X11_NO_MITSHM=1",参考链接:
How to fix X Error: BadAccess, BadDrawable, BadShmSeg while running graphical application using Docker?
Docker: gazebo: cannot connect to X server
若遇到 libGL error: No matching fbConfigs or visuals found libGL error... ,参考链接:
使用docker时出现libGL error: No matching fbConfigs or visuals found libGL error: failed to load driver...
已成功测试上述链接中的 pull image 方式
使用nvidia-smi查看nvidia driver和cuda版本,根据 nvidia/cudagl ,选择合适的TAG

$ sudo apt-get install x11-xserver-utils

$ nvidia-smi

$ docker pull nvidia/cudagl:11.0-base

# 关闭权限控制,允许其他X客户端绘制
$ xhost +
access control disabled, clients can connect from any host

$ sudo docker run --rm --runtime=nvidia -it -e DISPLAY=$DISPLAY -e GDK_SCALE -e GDK_DPI_SCAL -v /tmp/.X11-unix:/tmp/.X11-unix nvidia/cudagl:11.0-base

$ apt-get update

$ apt-get install mesa-utils

$ glxgears

创建新的长期镜像:

$ sudo apt-get install x11-xserver-utils

$ nvidia-smi

$ docker pull nvidia/cudagl:10.2-base

# 关闭权限控制,允许其他X客户端绘制
$ xhost +
access control disabled, clients can connect from any host

$ sudo docker run -it --name isaac --runtime=nvidia -it -e DISPLAY=$DISPLAY -e GDK_SCALE -e GDK_DPI_SCAL -v /tmp/.X11-unix:/tmp/.X11-unix -v /data/isaac:/data/isaac nvidia/cudagl:10.2-base

$ apt-get update

$ apt-get install mesa-utils

$ glxgears

$ exit

$ sudo docker ps -a

$ sudo docker start isaac

$ sudo docker attach isaac

如果遇到如下报错:

Nvidia-container-cli: initialization error: load library failed: libnvidia-ml.so.1: cannot open shared object file: no such file or directory

上述报错目前没有找到很好的解决方法,应该是某个安装包意外修改了系统配置,导致出现问题,重装系统可以顺利解决此问题。

参考链接


使用 dart extension

如何使用

dart extension 的使用场景是无法修改原类的时候,通过扩展的方式来增加原类的方法,也可以增加 getter,setters,and operators。

比如

int.parse('42')

如果 String 有 pareseInt 方法,我们可以这样写

'42'.parseInt()

为了达到这个目标,需要写一个 Extension

extension NumberParsing on String {
  int parseInt() {
    return int.parse(this);
  }
  // ···
}

然后就可以使用了。

// Import a library that contains an extension on String.
import 'string_apis.dart';
// ···
print('42'.padLeft(5)); // Use a String method.
print('42'.parseInt()); // Use an extension method.

处理冲突

注意到前面加的 NumberParsing 了吗?这个是为 extension 起的名字。起名字的作用是有冲突的时候可以方便的控制显隐。比如 NumberParsing2 也定义了 parseInt 方法与 NumberParsing 的 parseInt 冲突,如果只想要 NumberParsing 的 parseInt,只需要把 NumberParsing2 隐藏就好了。

// Defines the String extension method parseInt().
import 'string_apis.dart';

// Also defines parseInt(), but hiding NumberParsing2
// hides that extension method.
import 'string_apis_2.dart' hide NumberParsing2;

// ···
// Uses the parseInt() defined in 'string_apis.dart'.
print('42'.parseInt());

或者可以 给 NumberParsing2 经起个别名

import 'string_apis.dart';
import 'string_apis_2.dart' as rad;

print(NumberParsing('42').parseInt());

// Use the ParseNumbers extension from string_apis_3.dart.
print(rad.NumberParsing('42').parseInt());

// Only string_apis_3.dart has parseNum().
print('42'.parseNum());

泛型 extension

可以只给 int 类型的 list 加 sum 方法,其它类型的 list 不能使用 sum。

// Import a library that contains an extension on String.
import 'string_apis.dart';
// ···
print('42'.padLeft(5)); // Use a String method.
print('42'.parseInt()); // Use an extension method.
["1", "2", "3"].sum();  //Error: The method 'sum' isn't defined for the class 'List<String>'

[1,2,3].sum(); // ok, output 6

要注意的问题

dart extension 不可以用于 dynamic 类型

dynamic d = '2';
print(d.parseInt()); // Runtime exception: NoSuchMethodError

dart extension 权限很大,这也意味着,很可能被过度使用。所以在使用的时候一定要再三权衡,一定要可读性,可维护性优先。

上面的代码是比较简单的,但是如果类型比较复杂,不明确指定类型,类似如下的形式,也是可能会报错的:

class Provider {

 late final d;

 Provider() {
    this.d = '';
    this.d.parseInt(); // Runtime exception: NoSuchMethodError
  }

}

参考链接


智能硬件Nvidia Jetson Nano B01

产品参数

GPU 128-core Maxwell
CPU Quad-core ARM A57 @ 1.43 GHz
内存 4 GB 64-bit LPDDR4 25.6 GB/s
存储 micro SD 卡 
视频编码 4K @ 30   |   4x 1080p @ 30   |   9x 720p @ 30
(H.264/H.265)
视频解码 4K @ 60   |   2x 4K @ 30   |   8x 1080p @ 30   |   18x 720p @ 30
(H.264/H.265)
摄像头 2x MIPI CSI-2 D-PHY lanes
联网 千兆以太网,M.2 Key E 接口外扩 (可外接: AC8265 双模网卡 )
显示 HDMI 和 DP 显示接口
USB 4x USB 3.0,USB 2.0 Micro-B
扩展接口 GPIO,I2C,I2S,SPI,UART
其他 260-pin 连接器

Jetson Nano系统安装

1、JetPack介绍

JetPack SDK包括最新的Linux驱动程序包(L4T),带有Linux操作系统和CUDA-X加速库,以及用于深度学习、计算机视觉、加速计算和多媒体的API。 它还包括用于主机和开发人员套件的示例、文档和开发人员工具,并支持更高级别的SDK,例如用于流式视频分析的DeepStream和用于机器人的Isaac。

2、JetPack 4.4

JetPack 目前最新版本是4.4,支持Vulkan 1.2、TensorRT 7.1.3 、cuDNN 8.0、CUDA 10.2 等。

3、下载和安装

  • 下载 Jetson Nano镜像,镜像中包含提供引导加载程序、Ubuntu18.04、必要的固件、NVIDIA驱动程序、示例文件系统等。
  • 使用 Etcher 或者 Raspberry Pi Imager 将镜像烧录到SD卡(建议至少32G)中。

设置VNC服务

1.执行更新

$ sudo apt-get update

2.安装vino服务端

这个vino服务端我使用的镜像文件是安装好了的,但是古早版的镜像文件可能没有,所以可以执行下代码看看是否有安装。

$ sudo apt-get install vino

3.开启VNC 服务

$ sudo ln -s ../vino-server.service /usr/lib/systemd/user/graphical-session.target.wants

4.配置VNC服务

$ gsettings set org.gnome.Vino prompt-enabled false

$ gsettings set org.gnome.Vino require-encryption false

设置开机自启动

1.创建VNC自动启动文件

创建文件夹,然后创建一个自动启动文件

$ mkdir -p ~/.config/autostart

$ sudo gedit ~/.config/autostart/vino-server.desktop

2.添加以下内容到vino-server.desktop文件中

[Desktop Entry]
 
Type=Application
 
Name=Vino VNC server
 
Exec=/usr/lib/vino/vino-server
 
NoDisplay=true

这个时候,虽说是自动启动了,但是只有进入桌面后才自动启动服务,所以需要取消登录密码,启动就进入桌面。

参考链接


解决 Only fullscreen opaque activities can request orientation

这段时间把App的targetSDKVersion升级到了27,昨晚上线之后今早看到后台一堆崩溃,全是 Android 8.0 的设备,因为手头设备有限,测试的时候只测了Android 8.1的设备,没想到还有一个这个坑埋在这里,记录一下处理办法。

问题分析

targetSDKVersion为26或者27时,在 Android 8.0 的设备上,一些设置了windowIsTranslucent标志,将背景设为透明,同事将屏幕方向锁定的Activity,会崩溃并抛出这个异常:

Caused by: java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation
       at android.app.Activity.onCreate(Activity.java:1007)
       at android.support.v4.app.SupportActivity.onCreate(SupportActivity.java:66)
       at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:321)
       at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:84)
       at ...

这个问题网上有很多讨论以及解决方法,问题的原因出自这里

if (ActivityInfo.isFixedOrientation(requestedOrientation) && !fullscreen 
	&& appInfo.targetSdkVersion >= O) {
	throw new IllegalStateException("Only fullscreen activities can request orientation");
}

这里做了当屏幕方向锁定了并且不为全屏并且 App 的targetSdkVersion 大于 Android O的话,就会抛出这个异常。

是否为全屏的判定如下:

public static boolean isTranslucentOrFloating(TypedArray attributes) {
	final boolean isTranslucent = attributes.getBoolean(
		com.android.internal.R.styleable.Window_windowIsTranslucent, false);
	final boolean isSwipeToDismiss = 
        !attributes.hasValue(com.android.internal.R.styleable.Window_windowIsTranslucent) 
		&& attributes.getBoolean(com.android.internal.R.styleable.Window_windowSwipeToDismiss, false);
	final boolean isFloating = 
        attributes.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false);
	return isFloating || isTranslucent || isSwipeToDismiss;
}

手头的 Android 8.1 的机器并没有触发这个问题,是因为这个问题在 8.1 里已经修复了。

解决方案

解决方法有如下几种:

  1. 降级targetSDKVersion到26以下(废话!!)

  2. 移除mainfest文件里的screenOrientation属性

  3. 取消Activity主题里的windowIsTranslucent属性或者windowSwipeToDismiss属性或者windowIsFloating属性(根据你设置了什么属性来具体分析)

  4. (推荐)移除manifest文件里的screenOrientation属性,并在ActivityonCreate方法里设置屏幕方向

if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O) {
	setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}

参考链接


解决 Only fullscreen opaque activities can request orientation