Author Archives: fangfang12138

奇怪的知识又增加了5——TCP

  1. 这篇 TCP 、UDP 绝了!
  2. 你还在为 TCP 重传、滑动窗口、流量控制、拥塞控制发愁吗?看完图解就不愁了…
  3. TIME_WAIT
     TIME_WAIT是主动关闭的一方产生的,一般是client。
     TCP连接中,主动关闭的一方在关闭后的一段时间内保持一个TIME_WAIT的状态。
     状态维持时间一般是2MSL(Maximum Segment Lifetime,最大分段寿命),作用有两点:
       (1)保证连接正常终止
       (2)保证网络中未接收的数据包正常过期

    摘抄1
    暂以 A、B 来代指 TCP 连接的两端,A 为主动关闭的一端。
    1、四次挥手中,A 发 FIN, B 响应 ACK,B 再发 FIN,A 响应 ACK 实现连接的关闭。而如果 A 响应的 ACK 包丢失,B 会以为 A 没有收到自己的关闭请求,然后会重试向 A 再发 FIN 包。
    如果没有 TIME_WAIT 状态,A 不再保存这个连接的信息,收到一个不存在的连接的包,A 会响应 RST 包,导致 B 端异常响应。
    此时, TIME_WAIT 是为了保证全双工的 TCP 连接正常终止。
    2、TCP 下的 IP 层协议是无法保证包传输的先后顺序的。如果双方挥手之后,一个网络四元组(src/dst ip/port)被回收,而此时网络中还有一个迟到的数据包没有被 B 接收,A 应用程序又立刻使用了同样的四元组再创建了一个新的连接后,这个迟到的数据包才到达 B,那么这个数据包就会让 B 以为是 A 刚发过来的。
    此时, TIME_WAIT 的存在是为了保证网络中迷失的数据包正常过期。
    摘抄2:时长的确定
    由原因来推实现,TIME_WAIT 状态的保持时长也就可以理解了。确定 TIME_WAIT 的时长主要考虑上文的第二种情况,保证关闭连接后这个连接在网络中的所有数据包都过期。
    说到过期时间,不得不提另一个概念: 最大分段寿命(MSL, Maximum Segment Lifetime),它表示一个 TCP 分段可以存在于互联网系统中的最大时间,由 TCP 的实现,超出这个寿命的分片都会被丢弃。
    TIME_WAIT 状态由主动关闭的 A 来保持,那么我们来考虑对于 A 来说,可能接到上一个连接的数据包的最大时长:A 刚发出的数据包,能保持 MSL 时长的寿命,它到了 B 端后,B 端由于关闭连接了,会响应 RST 包,这个 RST 包最长也会在 MSL 时长后到达 A,那么 A 端只要保持 TIME_WAIT 到达 2MS 就能保证网络中这个连接的包都会消失。
    MSL 的时长被 RFC 定义为 2分钟,但在不同的 unix 实现上,这个值不并确定,我们常用的 centOS 上,它被定义为 30s,我们可以通过 /proc/sys/net/ipv4/tcp_fin_timeout 这个文件查看和修改这个值。

  4. CLOSE_WAIT
     CLOSE_WAIT是被动关闭的一方产生的,一般是server。
     接收到关闭连接时,在发出ACK的同时,告知上层,并等待上层处理完后的通知,在此期间保持CLOSE_WAIT状态,在接收到上层通知后,发送FIN,并进入LAST_ACK状态。
     若大量出现CLOSE_WAIT,可能是程序响应慢、上层没有close连接等原因。

奇怪的知识又增加了3——git

Git简介

开源的分布式版本控制系统

# 安装
    sudo apt install git

常用的基本操作

# 下载远程库代码
    git clone <网址>              # 当http连不上时,把http改成git
    git clone -b <分支名> <网址>

# 切换分支与新建分支
    git branch -av                      # 查看所有分支
    # 选项
    # -a, --all             list both remote-tracking and local branches
    # -v, --verbose         show hash and subject, give twice for upstream branch

    git branch branchName               # 创建分支
    git checkout branchName             # 切换分支
    git checkout -b branchName          # 创建并切换到分支,等同于上面两条

# 添加新文件到工程
    git add <file1> <file2>             # 可添加一个或多个文件到

# 查看是否有未提交的修改(包括新建文件和修改文件)
    $ git status

# 查看具体的修改内容,删除的内容为红色减号,增加的内容为绿色加号   以行为单位,提示
    git diff            # 所有修改
    git diff <file1>

# 提交修改
    git commit <file1> <file2> -m "message"     # 提交一个或多个文件的修改  -m 注释提交的原因
    git commit -a -m "message"                  # 提交所有修改

# 查看提交日志
    git log 
    git log --pretty=oneline                    # 每条commit用一行显示 分布式控制,避免版本冲突,因此commitID不是1234
    git reflog                                  # 记录每一次的命令,可以查看干掉的日志,查看历史版本号/命令

# 回退
    # 方式一:reset 
        git reset --hard HEAD^                  # 回退:HEAD表示当前版本 一个^表示上一个,两个^表示上上个,HEAD~100表示上100个
        git reset --hard <刚干掉的版本号>          # 回退到指定版本号,比如错误回退后,可以根据版本号再还原回去
    # 方式二:revert
        git revert -n <版本号>

# 上传远程与更新本地
    git push [选项] origin <分支名>              # 将<分支名>推送到origin主机的对应分支
    # 选项
    # -f    强推
    # -u    如果当前分支与多个主机存在追踪关系,那么这个时候-u选项会指定一个默认主机,这样后面就可以不加任何参数使用git push

    # 拉取
    git fetch <远程主机名,如origin> <分支名>         # 拉取
    git pull <远程主机名,如origin> <分支名>      # 拉取并merge

# 合并分支
    git merge <要合并到本分支的分支名> 

# 恢复误删分支
    git branch <分支名> <hash_val>

创建与配置本地仓库

# 创建仓库目录
    $ mkdir repo_test1 && cd repo_test1

# 初始化
    $ git init 
        # 这时,会在该目录下创建一个 .git 目录用于跟踪管理版本库,并提示
        Initialized empty Git repository in [自己的目录]/git_test/repo_test1/.git/

# 配置仓库所有者信息
    # Omit --global to set the identity only in this repository.
    $ git config --global user.email "you@example.com"
    $ git config --global user.name "Your Name"

关联本地与远程仓库

先有远程仓库

  1. 建立远程仓库
  2. git clone 到本地仓库

先有本地仓库
( 待补充… )
关联本地仓库与远程仓库

关于回退

  1. 回退方式一:git reset,适用场景:最近几个错误版本全部舍弃
    在这里插入图片描述
  2. 回退方式二 :git revert,适用场景:回退中间某个版本,保留最近版本
    在这里插入图片描述
    !!! git reset –hard 撤销到某次提交,git revert 撤销某次提交,一般建议用revert
# 查看提交日志
    git log --pretty=oneline

# 回退方式一 git reset   
    $ git reset --hard <版本号>
# 推上去,使用reset时,如果用不加选项“-f”会报错,因为本地库HEAD指向的版本比远程库的要旧
    $ git push -f origin <分支名> 

# 回退方式二 git revert
    $ git revert -n <版本号>
# 推上去
    $ git push origin <分支名> 

# 更新本地
    $ git pull

修改历史commit

https://blog.csdn.net/lxf0613050210/article/details/52525083 每一个commit都要单独修改。迫不得已不要修改,需要强制commit

摘抄

master & origin/master

在 clone 完成之后,Git 会自动为你将此远程仓库命名为 origin(origin 只相当于一个别名,运行 git remote –v 或者查看 .git/config 可以看到 origin 的含义),并下载其中所有的数据,建立一个指向它的 master 分支的指针,我们用 (远程仓库名)/(分支名) 这样的形式表示远程分支,所以 origin/master 指向的是一个 remote branch(从那个 branch 我们 clone 数据到本地),但你无法在本地更改其数据。
同时,Git 会建立一个属于你自己的本地 master 分支,它指向的是你刚刚从 remote server 传到你本地的副本。随着你不断的改动文件,git add , git commit,master 的指向会自动移动,你也可以通过merge(fast forward)来移动 master 的指向。
————————————————
原文链接:https://blog.csdn.net/weixin_34075268/article/details/85869357

pull & clone & fetch

从远程服务器克隆一个一模一样的版本库到本地,复制的是整个版本库,叫做clone.(clone是将一个库复制到你的本地,是一个本地从无到有的过程)
从远程服务器获取到一个branch分支的更新到本地,并更新本地库,叫做pull.(pull是指同步一个在你本地有版本的库内容更新的部分到你的本地库)
git pull相当于是从远程获取最新版本并merge(合并)到本地 git pull = git fetch + git merge,git fetch更安全一些
————————————————
版权声明:本文为CSDN博主「zhou_xiaomiao」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhou_xiaomiao/article/details/53185712

git fetch 将远程主机的最新内容拉到本地,用户在检查了以后决定是否合并到工作本机分支中。
git pull 将远程主机的最新内容拉下来后直接合并,即:git pull = git fetch + git merge,这样可能会产生冲突,需要手动解决。

参考

  1. https://blog.csdn.net/lvsehaiyang1993/article/details/80821976
  2. GitHub进行版本回退
  3. git checkout 命令详解
  4. Git恢复之前版本的两种方法reset、revert(图文详解)
  5. git clone、git pull和git fetch的用法及区别

奇怪的知识又增加了2——软件部署 & Docker

部署

  1. 什么是部署
    一般,软件从研发到用户使用包括开发和部署两个环节,其中,部署就是让写的程序或者开发的产品在某一个环境下跑起来。按环境的不同,可以分为远程部署和本地部署。
    解决问题:为什么在我的电脑上跑得起来,放另一台电脑就不行?

  2. 怎么去部署
    从源代码生成软件包 —–> 软件包放到某一环境 —–> 配置目标环境使得软件运行。
    (啊,乍一看,好像废话)

Docker:Build once,Run anywhere

  1. 简单/通俗:如何通俗解释Docker是什么? – 周宇刚的回答 – 知乎
  2. 生动/形象:如何通俗解释Docker是什么? – 小枣君的回答 – 知乎
  3. Docker包括镜像、容器和仓库三个重要的概念,以C++的角度,镜像可以看作类,容器是对象,仓库就是库。Docker是管理这些内容的工具。

    镜像(Image)
    Docker镜像可以看作是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。但没有动态的数据,是只读的。
    容器(Container)
    Docker容器与镜像几乎一样,但是可读写的。
    仓库(Repository)
    Docker Registry(仓库注册服务器)负责存储和分发镜像,每个Registry可以有多个Repository,每个Repository可以包含多个标签(Tag),每个标签对应着一个镜像。

  4. 虚拟机与Docker:Docker的容器技术更为轻量化。
虚拟机 Docker
隔离级别 操作系统级 进程级
启动时间 分钟级 秒级
镜像存储 GB~TB KB~MB
集群规模 上百 上万
  1. Docker的拉取、运行、退出

    sudo docker login xxxxx     # 登陆
    sudo docker pull yyyyy:tag  # 拉取
    sudo docker run -ti yyyyy   # 运行
            ## -t       tty
            ## -i       interactive
            ## --net        Connect a container to a network
            ## --ipc        IPC mode to use
            ## --device Add a host device to the container
            ## --privileged Give extended privileges to this container
            ## -v       Bind mount a volume 映射共享路径
    exit    # 退出
  2. 本地docker提交保存
    docker运行后的容器内,安装了一些包,在之后启动时,为避免重复安装的过程,可以保存提交容器是,生成一个新的镜像的标签,下次启动时,选择对应标签启动
    查看容器ID  docker ps
    查看仓库名   docker images
    提交镜像保存  docker commit 容器ID 仓库名:新标签

奇怪的知识又增加了1——一些版本的问题

USB(通用串行总线)

USB 版本

Generation_Year Type_Speed Type_Speed Type_Speed Type_Speed
USB 3.2_2017 USB 3.2 Gen 1x1_5Gbps USB 3.2 Gen 1x2_10Gbps USB 3.2 Gen 2x1_10Gbps USB 3.2 Gen 2x2_20Gbps
USB 3.1_2008 USB 3.1 Gen 1_5Gbps USB 3.1 Gen 2_10Gbps
USB 3.0_2008 USB 3.0 _5Gbps
USB 2.0_2000 USB 2.0 _480 Mbps

USB 3.2

Type USB 3.2 Gen 1×1(USB 3.1 Gen 1) USB 3.2 Gen 1×2 USB 3.2 Gen 2×1(USB 3.1 Gen 2) USB 3.2 Gen 2×2
IO Device USB-A、 USB-C、microUSB USB-C USB-A、 USB-C、microUSB USB-C

参考
https://www.kingston.com/cn/usb-flash-drives/usb-30

Ubuntu版本代号

Ubuntu 版本 代号
Ubuntu 14.04 LTS Trusty Tahr
Ubuntu 16.04 LTS Xenial Xerus
Ubuntu 18.04 LTS Bionic Beaver
Ubuntu 20.04 LTS Focal Fossa

ubuntu16.04 + Intel realsense D435 + Qt

D435_SDK安装

版本支持

平台支持、设备支持、Firmware支持、语言支持等,参阅这里

下载、解压

下载并解压SDK,这里选择v2.39.0(原因参阅第一条链接)。

编译前的准备工作

1)查看linux内核,要求版本 >=4.4.0-50

$ uname -r

2)查看cmake版本,要求版本 >3.6

$ cmake --version

3)安装必要依赖

$ sudo apt-get install libusb-1.0-0-dev pkg-config libgtk-3-dev libssl-dev

编译、安装

$ sudo librealsense-2.39.0
$ mkdir build
$ cd build
$ cmake ..
$ make
$ sudo make install

安装后续工作

1)断开D435的连接,安装ideo4Linux视频内核驱动

$ sudo cp config/99-realsense-libusb.rules /etc/udev/rules.d/
$ sudo udevadm control --reload-rules && udevadm trigger

2)编译配置文件

$ librealsense-2.39.0/scripts/patch-realsense-ubuntu-lts.sh

3)接上D435,打开realsense-viewer,连接成功会有图像,没有图像检查USB是否为3.0等问题

$ realsense-viewer

D435_Qt

1、添加库,使用Qt添加安装的库_/usr/local/lib/librealsense2.so,使用默认头文件路径

2、使用D435,参考github提供的例程,以hello-realsense为例

1)包含头文件,注意是rs.hpp,不是rs.h,hpp文件带有函数实现

#include <librealsense2/rs.hpp>

2)添加个按钮

void MainWindow::on_pushButton_clicked()
{
    // Create a Pipeline - this serves as a top-level API for streaming and processing frames
    rs2::pipeline p;

    // Configure and start the pipeline
    p.start();

    int cnt=10;
    while (cnt>0)
    {
        // Block program until frames arrive
        rs2::frameset frames = p.wait_for_frames();

        // Try to get a frame of a depth image
        rs2::depth_frame depth = frames.get_depth_frame();

        // Get the depth frame's dimensions
        float width = depth.get_width();
        float height = depth.get_height();

        // Query the distance from the camera to the object in the center of the image
        float dist_to_center = depth.get_distance(width / 2, height / 2);

        // Print the distance
        qDebug()<<"The camera is facing an object "+QString::number(dist_to_center)+" meters away \r";

        cnt--;
    }
}

输出如下:

"The camera is facing an object 2.527 meters away \r"
"The camera is facing an object 2.527 meters away \r"
"The camera is facing an object 2.556 meters away \r"
"The camera is facing an object 2.546 meters away \r"
"The camera is facing an object 2.527 meters away \r"
"The camera is facing an object 2.556 meters away \r"
"The camera is facing an object 2.546 meters away \r"
"The camera is facing an object 2.556 meters away \r"
"The camera is facing an object 2.556 meters away \r"
"The camera is facing an object 2.585 meters away \r"

参考

1)Ubuntu 16.04 安装RealSense D435教程
2)https://github.com/IntelRealSense/librealsense
3)https://www.intelrealsense.com/

CMake笔记

CMake语法

cmake_minimum_required( VERSION  3 )                    # cmake版本要求
project( project_name )                                 # 定义工程名

set( CMAKE_CXX_FLAGS “-std=c++11” )                     # 添加c++11标准
                                                        # set()用于设置变量

find_package( pkg_name VERSION REQUIED COMPONANT )      # 找到已安装的包:
                                                        #   程序会首先从/usr/local /usr中寻找pkg的cmake文件
                                                        # 对于已安装且只有头文件的库:
include_directories( “/usr/include/eigen3” )                    #   如Eigen,可以直接包含头文件路径即可
include_directories( ${ pkg_name_INCLUDE_DIRS } )       # 对于找到的库,可以通过这样的方式引用其头文件和库路径

add_library( lib_name lib.cpp )                         # 生成并添加静态库文件  .a
add_library( lib_shared_name SHARED lib_shared.cpp )    # 生成并添加共享库文件  .o

add_executable( pro_name exe.cpp )                      # 添加可执行文件
target_link_libraries( pro_name lib_name ${ pkg_name_LIBS } )   # 向可执行文件添加库链接:
                                                                #   如果库通过此cmakelists文件生成,则需要先add_library()
                                                                #   如果库已经编译安装,则需要先find_package()

CMake小知识

find_package()

find_package()
https://www.jianshu.com/p/39fc5e548310 深入理解find_package
https://blog.csdn.net/bytxl/article/details/50637277 find_package与CMake如何查找链接库详解
简单扯一下:find_package有两种模式(基本用法Module/完全用法Config)

0 默认是Module,当使用关键字CONFIG或NO_MODULE,亦或使用了非Module模式支持关键字,则转为Config。另外,按用户指定的配置却找不到包,就会自动进入Config模式,但使用MODULE关键字,则不转入Config
1 Module模式下,查找Find< PackageName >.cmake文件,先在CMAKE_MODULE_PATH变量对应的路径中查找。如果路径为空,或者路径中查找失败,则在cmake安装时的Modules目录,比如/usr/local/share/cmake/Modules查找
2 Config模式下,查找< PackageName >Config.cmake或< lower-case-package-name >-config.cmake。 < PackageName >_DIR为config模式特有,< PackageName >_DIR很傻,不会在子目录中寻找,有待验证(好像可以找?)。如果设置了< package >_DIR变量,但是它没有包含配置文件信息,那么CMake将会直接无视它,然后重新开始查找
3 module模式下,在find_package()前使用< PackageName >_DIR,并不能用来帮助find_package()找到包,并且在find_package()后,也并没有< PackageName >_DIR缓存变量自动存在。
4 配置文件*.cmake 记录包的各种文件路径信息,cmake文件的完整地址存储在cmake的变量< package >_CONFIG。可以通过list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules) 添加自定义模块路径
5 版本号问题

find_package关键字

REQUIRED 表示一定要找到包,找不到的话就立即停掉整个cmake
COMPONENTS 查找的包中必须要找到的组件,如果有任何一个找不到就立即停掉整个cmake
NAMES 其后的names参数会取代的角色,该命令会为每个在names中的name搜索名为Config.cmake或者<name全小写>-config.cmake的文件。

Linux笔记

0、Ubuntu系统安装

1、启动U盘格式化为FAT32

1、查看磁盘容量

$ df -h  //h : 将容量转换成常用单位

2、打开编辑Bash文件

$ gedit ~/.bashrc 

3、更新文件夹并搜索关键词文件/文件夹,如eigen3

$ sudo updatedb 
$ locate eigen3

4、修改文件权限,如配置文件 interfaces

$ sudo chmod 777 interfaces

说明:
权限分为三类,分别是文件所有者、群组用户、其他用户,对每一类都有读/写/执行三种权限。
权限由三位二进制表示,二进制置1表示允许,置0表示禁止,从高位到低位依次表示读/写/执行,如 111 表示可读/可写/可执行,101表示可读/不可写/可执行,二进制转换成16进制则分别是7和5。
**注意:如果在挂载的U盘里修改权限,是可能无法修改的。

5、软件安装

通过终端安装的库:
其头文件一般在 /user/include 库文件一般在 /user/lib
通过源码安装的库:
其头文件一般在 /user/local/include 库文件一般在 /user/local/lib

6、linux office

liberoffice单元格换行 Ctrl+enter

7、关机与重启

$ poweroff          //正常关机
$ reboot            //正常重启
$ shutdown -h now   //立即关机
$ shutdown -r now   //立即重启
$ shutdown -r +3        //三分钟后重启
$ shutdown -h +3 “The system will shutdown after 3minites” //三分钟后关机 提示用户
$ shutdown -r 20:23     //在20:23重启

8、网络

$ ifconfig                      //查看网络信息
$ ip addr 
$ sudo iw dev                   //查看无线设备
$ sudo iw dev wlan0 link        //检查对于无线设备(这里是wlan0)情况
$ sudo ip link set wlan0 up         //开启无线设备电源 
$ sudo iw wlan0 connect [essid] //连接WiFi,名为essid
$ sudo dhclient wlan0           //通过DHCP获取IP地址

9、vim使用

$ vim [file name]

按“i”键,进入编辑模式;编辑结束后,按“esc”键退出编辑模式并开启命令模式,之后输入“:wq”,保存并关闭

:wq //保存并挂关闭
:w      //保存
:q      //退出
:q! //强制退出不保存
Ctrl + s    // vim停止向终端输出
Ctrl + q    // 即可恢复向终端输出

10、已安装软件

python3 --version   //版本查看
dpkg -l | grep xxx  //已安装软件查看

11、已安装库

objdump -x xxx.so | grep NEEDED //库依赖查看

12、查看进程并杀死

ps -A | grep apt //查看apt相关进程
kill -9 进程ID号

遇到问题:dpkg: error: dpkg frontend is locked by another process

sudo apt-get install -f

然后,杀死对应进程

13、进入/退出root模式

$ sudo su
$ exit

14、linux rar

http://linux.51yip.com/search/rar

sudo apt install rar unrar

//解压
unrar e xxx.rar //所有文件存在一个文件夹
unrar x xxx.rar //按目录解压

15、各种截屏方式

PrtSc                   获取整个屏幕的截图并保存到 Pictures 目录。
Shift + PrtSc           获取屏幕的某个区域截图并保存到 Pictures 目录。
Alt + PrtSc             获取当前窗口的截图并保存到 Pictures 目录。
Ctrl + PrtSc            获取整个屏幕的截图并存放到剪贴板。
Shift + Ctrl + PrtSc    获取屏幕的某个区域截图并存放到剪贴板。
Ctrl + Alt + PrtSc      获取当前窗口的 截图并存放到剪贴板。

16、gdb使用

// 安装
sudo apt-get update
sudo apt-get install gdb

// 基本使用
gdb --help  // 查看使用帮助
gdb [可执行程序名] [core文件名]
where   // 查看具体原因

thread apply all bt
看到的lock_wait就是被死锁的线程

// 常见错误
// (待补充)

signal SIGSEGV, Segmentation fault
signal SIGABRT, Aborted

17、文件操作

pwd # 显示当前目录
scp # secure copy, linux 系统下基于 ssh 登陆进行安全的远程文件拷贝命令
    # scp 是加密的,rcp 是不加密的,scp 是 rcp 的加强版
ls -ah  # 查看隐藏目录

18、环境变量

export PATH=$PATH:自己的路径
# 不加 $PATH: 意味着将之前的路径全部删除,替换成上面这个路径
# 这将导致很多linux命令不能用,因为很多命令的环境变量一般是/usr/bin和/usr/sbin

19、terminator

Ctrl+Shift+E 垂直分割窗口
Ctrl+Shift+O 水平分割窗口
F11 全屏
Ctrl+Shift+C 复制
Ctrl+Shift+V 粘贴
Ctrl+Shift+N 或者 Ctrl+Tab 在分割的各窗口之间切换
Ctrl+Shift+X 将分割的某一个窗口放大至全屏使用
Ctrl+Shift+Z 从放大至全屏的某一窗口回到多窗格界面
Ctrl+Shift+W 关闭当前终端
Ctrl+Shift+Q 退出Terminator
Ctrl+Shift+up left right down 扩展当前窗口

20、查找字符串

  1. 查找文件夹内文件中的字符串
grep -r -e string directory
  1. 查找文件内的字符串
grep 'word' filename

树莓派4B——Ubuntu20.04 mate

安装操作系统

1、从树莓派官网下载镜像烧写工具,点这里,如图选择对应系统地烧写工具。
在这里插入图片描述
2、下载好工具后,将SD卡插入读卡器,进行烧录。首先选择下载好的Ubuntu20.04 mate(32位,后面解释),然后,烧写工具会读出读卡器所在盘符,需要确认一下,之后开始WRITE。
在这里插入图片描述
(如果是Ubuntu mate 版本,请忽略3567步,因为mate版本自带桌面,涉及到密码等内容的设置会有图形界面引导)
3、由于之后需要连接网络安装一些库或者软件,因此在开机前需要确认联网的形式。如果通过网线连接,可以忽略该条剩下内容,如果通过WiFi,请参考这篇博客的第4条——如何设置开机后自动连接指定WiFi
4、烧写结束后,SD卡插回树莓派,连接网线和显示器,开机。
5、用户名和密码:第一次开机需要重新设置用户名和密码,默认用户名和密码都是ubuntu,修改的密码要求至少8位。
6、ROOT密码:用户名密码设置后要设置root的密码

$ sudo passwd

7、Ubuntu20.04桌面安装:
首先更改软件源,操作如下。
找到 sources.list:

$ cd /etc/apt/sources.list

先备份,备份的后缀是.list.bak,在还原的时候删掉.bak即可。

$ sudo cp source.list source.list.bak  //备份

下面换源,点这里,将这些源,写入source.list,注意xenial要替换成focal

$ sudo chmod 777 sources.list   //更改读写属性
$ vim sources.list              //编辑

编辑、保存和退出vim参考这里
之后,进行桌面安装。

$ sudo apt-get update                   //更新软件源
$ sudo apt-get install ubuntu-desktop   //安装桌面

最后,重启,使之生效。
8、Ubuntu20.04 mate 开机引导
(具体的设置情况请忽略,这里只是记录自己的设置,怕忘了…)设置:语言English,时区ShangHai,用户名raspi,密码(四个空格),勾选自动登录

安装必要的库

1、安装树莓派GPIO库

$ sudo apt-get install python3-rpi.gpio 

2、安装wiringPi 参考这里
problem:Waiting for cache lock: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 27622 (unattended-upgr)
solved: sudo kill -9 [processID(27622 just for here)]

//安装默认版本
$ sudo apt install wiringpi 
//安装更新版本                
$ cd /tmp
$ wget https://project-downloads.drogon.net/wiringpi-latest.deb//更新版本
$ sudo dpkg -i wiringpi-latest.deb          
$ gpio -v                                   //检查安装情况
$ gpio readall                              //获取wirngPi与gpio的映射图

安装软件

1,安装输入法

$ sudo apt-get install ibus
$ sudo apt-get install ibus-pinyin
$ sudo ibus-setup//打开的窗口,选择“input method”->“add”,添加Chinese-pinyin

在系统设置->语言支持那里,更新输入法框架为ibus,然后重启。

2,Qt5.12
以及相关的一些库,如串口通信等。

$ sudo apt-get install qt5-default qtcreator //这里安装的是qt5.12
$ sudo apt-get install libqt5serialport5-dev 

3,安装网络相关工具

$ sudo apt-get install net-tools

4,安装串口工具cutecom

$ sudo apt-get install cutecom

VNC和SSH

点这里

开机启动

点这里

问题

1、软件安装过程中,遇到:

The following packages have unmet dependencies:
qtbase5-dev : Depends: libegl-dev but it is not going to be installed
               Depends: libgles-dev but it is not going to be installed
E: Unmet dependencies. Try 'apt --fix-broken install' with no packages (or specify a solution).

输入:

$ sudo apt --fix-broken install

然后出现:

Errors were encountered while processing:
 /var/cache/apt/archives/libegl-dev_1.3.1-1ubuntu0.20.04.1_armhf.deb
 /var/cache/apt/archives/libgles-dev_1.3.1-1ubuntu0.20.04.1_armhf.deb
E: Sub-process /usr/bin/dpkg returned an error code (1)

然后,force:

$ sudo dpkg -i --force-overwrite /var/cache/apt/archives/libegl-dev_1.3.1-1ubuntu0.20.04.1_armhf.deb
$ sudo dpkg -i --force-overwrite /var/cache/apt/archives/libgles-dev_1.3.1-1ubuntu0.20.04.1_armhf.deb

重新安装出错的软件。

《视觉SLAM十四讲 从理论到实践 第2版》——三维刚体运动

在第三讲(三维空间刚体运动)这部分的3.1.2节,提到“关于平移t12,它实际对应的是坐标系1原点指向坐标系2原点的向量,在坐标系1下取的坐标,记作“从1到2的向量”,但反过来的t21,即从2指向1的向量在坐标系2下的坐标,却不等于-t12,而是和旋转还有关系”。

这里记录一下一个理解:

从相机的角度来说明可能更好理解一些。

1 相机的运动可以描述为 相对世界坐标的 旋转和平移运动的组合
2 物体在相机坐标系下和世界坐标系下的坐标值一般是不同的,假设物体在世界坐标系下的坐标和相机坐标系下的坐标如下
在这里插入图片描述
3 两个坐标的关系通常描述为
在这里插入图片描述
其中,R表示相机的旋转,t表示相机的平移

4 问题1:世界坐标系的原点 在相机坐标系下的表示,是t吗?显然是的。
世界坐标系原点在世界坐标系下的坐标就是一个零向量,把Pworld写作零向量代入3式即得证。

5 问题2:相机坐标系的原点 在世界坐标系下的表示,是-t吗?一般不是。
我们从空间的角度去想象,好像世界原点和相机原点,这两个点构成一个向量,“你望向我”跟“我望向你”似乎就是一个相反的关系,实际上,这种相反的关系只在某些特殊的情况下才会出现,因为我们往往不太容易去想象一个经过旋转的坐标系如何如何。
那先不想象,先从原理公式出发,看一看哪里出了问题。
我们说物体在相机和世界这两个坐标系下有不同的表示,实际上相机本身也是一个物体,相机坐标系的原点实际上就是相机本身,也就是说,Pcamera取零向量就表示了相机本身在相机坐标系的坐标(原点)。这时候,把Pcamera取零代入3中的式子,我们发现
在这里插入图片描述
相机的原点 在世界坐标系中的坐标并不是-t,而是附加了一个旋转,只有当旋转矩阵R为单位矩阵的时候,其坐标值才是-t。而旋转矩阵为单位矩阵,意味着,相机相对世界坐标系没有进行旋转运动。恩,没有了旋转似乎就跟我们想象的结果有些符合了(想象这东西只可意会不可言传)。

当然了,3中的式子是先旋转再平移(一般是这样),如果改成先平移再旋转,也是一样的,一般也不是相反关系。
在这里插入图片描述

6 其他
比如,在OpenCV中solvePnP等函数计算出来的结果是按照3中的公式,即先旋转再平移,具体计算相机位置还需要一些附加的计算和转换。