当前位置: 首页 > news >正文

自学SLAM(8)《第四讲:相机模型与非线性优化》作业

前言

在这里插入图片描述

小编研究生的研究方向是视觉SLAM,目前在自学,本篇文章为初学高翔老师课的第四次作业。

文章目录

  • 前言
  • 1.图像去畸变
  • 2.双目视差的使用
  • 3.矩阵微分
  • 4.高斯牛顿法的曲线拟合实验


1.图像去畸变

现实⽣活中的图像总存在畸变。原则上来说,针孔透视相机应该将三维世界中的直线投影成直线,但是当我们使⽤⼴⾓和鱼眼镜头时,由于畸变的原因,直线在图像⾥看起来是扭曲的。本次作业,你将尝试如何对⼀张图像去畸变,得到畸变前的图像。
在这里插入图片描述
在这里插入图片描述

对于畸变,用两张鲜明的照片来展示:
在这里插入图片描述
在这里插入图片描述
undistort_image.cpp:

//
// Created by ljh on 2023/11/5.
//#include <opencv2/opencv.hpp>
#include <string>using namespace std;string image_file = "/home/lih/video4_homework/homework1/test.png"; // 请确保路径正确int main(int argc, char **argv) {// 本程序需要你自己实现去畸变部分的代码。尽管我们可以调用OpenCV的去畸变,但自己实现一遍有助于理解。// 畸变参数double k1 = -0.28340811, k2 = 0.07395907, p1 = 0.00019359, p2 = 1.76187114e-05;// 内参double fx = 458.654, fy = 457.296, cx = 367.215, cy = 248.375;cv::Mat image = cv::imread(image_file,0);   // 图像是灰度图,CV_8UC1int rows = image.rows, cols = image.cols;cv::Mat image_undistort = cv::Mat(rows, cols, CV_8UC1);   // 去畸变以后的图// 计算去畸变后图像的内容for (int v = 0; v < rows; v++)for (int u = 0; u < cols; u++) {double u_distorted = 0, v_distorted = 0;// TODO 按照公式,计算点(u,v)对应到畸变图像中的坐标(u_distorted, v_distorted) (~6 lines)// start your code here// 按照公式,计算点(u,v)对应到畸变图像中的坐标(u_distorted, v_distorted)
double x = (u-cx)/fx, y = (v-cy)/fy; // 计算图像点坐标到光心的距离;
double r = sqrt(x*x+y*y);// 计算投影点畸变后的点
double x_distorted = x*(1+k1*r+k2*r*r)+2*p1*x*y+p2*(r+2*x*x); 
double y_distorted = y*(1+k1*r+k2*r*r)+2*p2*x*y+p1*(r+2*y*y); // 把畸变后的点投影回去
u_distorted = x_distorted*fx+cx;
v_distorted = y_distorted*fy+cy;// end your code here// 赋值 (最近邻插值)if (u_distorted >= 0 && v_distorted >= 0 && u_distorted < cols && v_distorted < rows) {image_undistort.at<uchar>(v, u) = image.at<uchar>((int) v_distorted, (int) u_distorted);} else {image_undistort.at<uchar>(v, u) = 0;}}// 画图去畸变后图像cv::imshow("image undistorted", image_undistort);cv::waitKey();return 0;
}

string image_file = “/home/lih/video4_homework/homework1/test.png”; // 填写你自己的图片路径

CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)PROJECT(undistort_image)IF(NOT CMAKE_BUILD_TYPE) #(可选)如果没有指定cmake编译模式,就选择Relealse模式,必须写成三行SET(CMAKE_BUILD_TYPE Release)
ENDIF()MESSAGE("Build type: " ${CMAKE_BUILD_TYPE}) #终端打印cmake编译模式的信息set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}  -Wall  -O3 -march=native ") #添加c标准支持库
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall   -O3 -march=native") #添加c++标准支持库# Check C++11 or C++0x support #检查c++11或c++0x标准支持库
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")add_definitions(-DCOMPILEDWITHC11)message(STATUS "Using flag -std=c++11.")
elseif(COMPILER_SUPPORTS_CXX0X)set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")add_definitions(-DCOMPILEDWITHC0X)message(STATUS "Using flag -std=c++0x.")
else()message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()find_package(OpenCV 3.0 QUIET) #find_package(<Name>)命令首先会在模块路径中寻找 Find<name>.cmake
if(NOT OpenCV_FOUND)find_package(OpenCV 2.4.3 QUIET)if(NOT OpenCV_FOUND)message(FATAL_ERROR "OpenCV > 2.4.3 not found.")endif()
endif()include_directories(${OpenCV_INCLUDE_DIRS})add_executable(image undistort_image.cpp)#链接OpenCV库
target_link_libraries(image ${OpenCV_LIBS})

然后
mkdir build
cd build
cmake …
make
./image
在这里插入图片描述

2.双目视差的使用

双⽬相机的⼀⼤好处是可以通过左右⽬的视差来恢复深度。课程中我们介绍了由视差计算深度的过程。本题,你需要根据视差计算深度,进⽽⽣成点云数据。本题的数据来⾃Kitti 数据集 [2]。 Kitti中的相机部分使⽤了⼀个双⽬模型。双⽬采集到左图和右图,然后我们可以通过左右视图恢复出深度。经典双⽬恢复深度的算法有 BM(Block Matching), SGBM(Semi-Global Matching)[3, 4]等,但本题不探讨⽴体视觉内容(那是⼀个⼤问题)。我们假设双⽬计算的视差已经给定,请你根据双⽬模型,画出图像对应的点云,并显⽰到 Pangolin 中。题给定的左右图见 code/left.png 和 code/right.png,视差图亦给定,见code/right.png。双⽬的参数如下:
fx= 718.856; fy = 718.856; cx =607.1928; cy = 185.2157
且双⽬左右间距(即基线)为:
d = 0.573 m
请根据以上参数,计算相机数据对应的点云,并显⽰到 Pangolin 中。程序请code/disparity.cpp ⽂件。
在这里插入图片描述

disparity.cpp:

// start your code here// 根据双目模型计算 point 的位置double x = (u - cx) / fx;double y = (v - cy) / fy;double depth = fx * b / (disparity.at<float>(v, u));point[0] = x * depth;point[1] = y * depth;point[2] = depth;// end your code here

double x和double y的计算方式和上一题一样,depth就算如下:
在这里插入图片描述
计算出depth后,那么point模仿课上五对图片那个实践仿写即可。只不过实践中的d(视差)没有给出,而此题中视差d已给,所以公式写出来略有不同。
CMakeLists.txt:

cmake_minimum_required( VERSION 2.8 )
project(stereoVision)
set( CMAKE_CXX_FLAGS "-std=c++11 -O3")include_directories("/usr/include/eigen3")
find_package(Pangolin REQUIRED)
include_directories( ${Pangolin_INCLUDE_DIRS} )find_package(OpenCV 3.0 QUIET) #find_package(<Name>)命令首先会在模块路径中寻找 Find<name>.cmake
if(NOT OpenCV_FOUND)find_package(OpenCV 2.4.3 QUIET)if(NOT OpenCV_FOUND)message(FATAL_ERROR "OpenCV > 2.4.3 not found.")endif()
endif()include_directories(${OpenCV_INCLUDE_DIRS})add_executable(disparity disparity.cpp)
target_link_libraries(disparity ${OpenCV_LIBRARIES})
target_link_libraries(disparity ${Pangolin_LIBRARIES})

然后就是编译五部曲:
mkdir build
cd build
cmake …
make
./disparity
在这里插入图片描述
如果你出现了这张图片,那就是你的disparity.cpp中的图片位置没有写对,找不到图片所导致的!!

运行成功如下:
在这里插入图片描述

3.矩阵微分

在这里插入图片描述

①第一问:如果大家有不理解的地方可以看看这个印度三哥的视屏,我认为讲的还是非常清晰的,至少我搜了很多国内的都没有这个讲得好,虽然语言不通,但是一点都不影响学习。高博的清华PPT还是不适合我这种人看。
链接:
矩阵求导讲解
在这里插入图片描述

②第二问:如果大家有不理解的地方可以看看这个印度三哥的视屏,我认为讲的还是非常清晰的,至少我搜了很多国内的都没有这个讲得好,虽然语言不通,但是一点都不影响学习。高博的清华PPT还是不适合我这种人看。
链接:
矩阵求导讲解
在这里插入图片描述

③第三问:
在这里插入图片描述

4.高斯牛顿法的曲线拟合实验

在这里插入图片描述

当然我觉得大家很有必要了解一下这一块的由来,我的上一篇博客讲述了海斯矩阵,凸函数等基本概念,大家看这个之前我认为很必要学习一下: 链接:
SLAM第四讲实践中的最优化知识

在做这道题之前我们非常有必要了解一下什么是牛顿法法,因为高斯牛顿法是牛顿法的改进,我以一道最优化的简单立体让你明白什么是牛顿法:
在这里插入图片描述
在这里插入图片描述
下来我们再看高斯牛顿法,我搜查了很多资料,很难找到一道高斯牛顿法的数学题来让大家理解,所以我只能找到一个更为详细点的高斯牛顿法的计算步骤让大家理解:
在这里插入图片描述

到这里,我们开始做题:
gaussnewton.cpp:

#include <iostream>
#include <chrono>
#include <opencv2/opencv.hpp>
#include <Eigen/Core>
#include <Eigen/Dense>using namespace std;
using namespace Eigen;int main(int argc, char **argv) {double ar = 1.0, br = 2.0, cr = 1.0;         // 真实参数值double ae = 2.0, be = -1.0, ce = 5.0;        // 估计参数值int N = 100;                                 // 数据点double w_sigma = 1.0;                        // 噪声Sigma值double inv_sigma = 1.0 / w_sigma;cv::RNG rng;                                 // OpenCV随机数产生器vector<double> x_data, y_data;      // 数据for (int i = 0; i < N; i++) {double x = i / 100.0;x_data.push_back(x);y_data.push_back(exp(ar * x * x + br * x + cr) + rng.gaussian(w_sigma * w_sigma));}// 开始Gauss-Newton迭代int iterations = 100;    // 迭代次数double cost = 0, lastCost = 0;  // 本次迭代的cost和上一次迭代的costchrono::steady_clock::time_point t1 = chrono::steady_clock::now();for (int iter = 0; iter < iterations; iter++) {Matrix3d H = Matrix3d::Zero();             // Hessian = J^T W^{-1} J in Gauss-NewtonVector3d b = Vector3d::Zero();             // biascost = 0;for (int i = 0; i < N; i++) {double xi = x_data[i], yi = y_data[i];  // 第i个数据点double error = yi - exp(ae * xi * xi + be * xi + ce);//计算雅可比矩阵J(Xk)和误差f(Xk)Vector3d J; // 雅可比矩阵J[0] = -xi * xi * exp(ae * xi * xi + be * xi + ce);  // de/daJ[1] = -xi * exp(ae * xi * xi + be * xi + ce);  // de/dbJ[2] = -exp(ae * xi * xi + be * xi + ce);  // de/dcH += inv_sigma * inv_sigma * J * J.transpose();b += -inv_sigma * inv_sigma * error * J;cost += error * error;}// 求解线性方程 Hx=bVector3d dx = H.ldlt().solve(b);if (isnan(dx[0])) {cout << "result is nan!" << endl;break;}if (iter > 0 && cost >= lastCost) {cout << "cost: " << cost << ">= last cost: " << lastCost << ", break." << endl;break;}ae += dx[0];be += dx[1];ce += dx[2];lastCost = cost;cout << "total cost: " << cost << ", \t\tupdate: " << dx.transpose() <<"\t\testimated params: " << ae << "," << be << "," << ce << endl;}chrono::steady_clock::time_point t2 = chrono::steady_clock::now();chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);cout << "solve time cost = " << time_used.count() << " seconds. " << endl;cout << "estimated abc = " << ae << ", " << be << ", " << ce << endl;return 0;
}

CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)
project(homework4)
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_CXX_FLAGS "-std=c++14 -O3")
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
# OpenCV
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
# Eigen
include_directories("/usr/include/eigen3")add_executable(homework4 gaussnewton.cpp)
target_link_libraries(homework4 ${OpenCV_LIBS})

然后老五套
mkdir build
cd build
cmake …
make
./homework4
在这里插入图片描述

在这里插入图片描述

相关文章:

自学SLAM(8)《第四讲:相机模型与非线性优化》作业

前言 小编研究生的研究方向是视觉SLAM&#xff0c;目前在自学&#xff0c;本篇文章为初学高翔老师课的第四次作业。 文章目录 前言1.图像去畸变2.双目视差的使用3.矩阵微分4.高斯牛顿法的曲线拟合实验 1.图像去畸变 现实⽣活中的图像总存在畸变。原则上来说&#xff0c;针孔透…...

STL—next_permutation函数

目录 1.next_permutation函数的定义 2.简单使用 2.1普通数组全排列 2.2结构体全排列 2.3string 3.补充 1.next_permutation函数的定义 next_permutation函数会按照字母表顺序生成给定序列的下一个较大的排列&#xff0c;直到整个序列为降序为止。与其相对的还有一个函数—…...

Mysql 三种不使用索引的情况

目录 1. 查询语句中使用LIKE关键字 例 1 2. 查询语句中使用多列索引 例 2 3. 查询语句中使用OR关键字 例 3 总结 索引可以提高查询的速度&#xff0c;但并不是使用带有索引的字段查询时&#xff0c;索引都会起作用。使用索引有几种特殊情况&#xff0c;在这些情况下&…...

Ladybug 全景相机, 360°球形成像,带来全方位的视觉体验

360无死角全景照片总能给人带来强烈的视觉震撼&#xff0c;有着大片的既视感。那怎么才能拍出360球形照片呢&#xff1f;它的拍摄原理是通过图片某个点位为中心将图片其他部位螺旋式、旋转式处理&#xff0c;从而达到沉浸式体验的效果。俗话说“工欲善其事&#xff0c;必先利其…...

centos 6.10 安装swig 4.0.2

下载地址 解压文件。 执行下面命令 cd swig-4.0.2 ./configure --prefix/usr/local/swig-4.0.2 make && make install...

mask: rle, polygon

RLE 编码 RLE&#xff08;Run-Length Encoding&#xff09;是一种简单而有效的无损数据压缩和编码方法。它的基本思想是将连续相同的数据值序列用一个值和其连续出现的次数来表示&#xff0c;从而减少数据的存储或传输量。 在图像分割领域&#xff08;如 COCO 数据集中&#…...

【JMeter】JMeter压测过程中遇到Non HTTP response code错误解决方案

压测过程中并发逐步加大后遇到60%的错误率&#xff0c;查看错误是JMeter网页版聚合报告中显示 Non HTTP response code: java.net.NoRouteToHostException/Non HTTP response message: Cannot assign requested address (Address not available) 这是第二次遇到&#xff0c;故…...

【Kingbase FlySync】评估工具安装及使用

【Kingbase FlySync】评估工具使用 概述准备环境目标资源1.测试虚拟机下载地址包含node1,node22.评估工具下载地址3.exam.sql下载地址 评估工具安装1.上传并解压评估工具安装包2.安装数据库驱动包3.设置环境变量4.node1载入样例信息 收集并阅读node1信息1.收集报告2.阅读报告 收…...

pandas教程:Data Aggregation 数据聚合

文章目录 10.2 Data Aggregation&#xff08;数据聚合&#xff09;1 Column-Wise and Multiple Function Application&#xff08;列对列和多函数应用&#xff09;2 Returning Aggregated Data Without Row Indexes&#xff08;不使用行索引返回聚合数据&#xff09; 10.2 Data…...

开启创造力之门:掌握Vue中Slot插槽的使用技巧与灵感

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 ⭐ 专栏简介 &#x1f4d8; 文章引言 一、s…...

【算法练习Day48】回文子串最长回文子序列

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 回文子串最长回文子序列总结…...

ubuntu下C++调用matplotlibcpp进行画图(超详细)

目录 一、换源 二、安装必要的软件 三、下载matplotlibcpp 四、下载anaconda 1.anaconda下载 2.使用anaconda配置环境 五、下载CLion 1.下载解压CLion 2.替换jbr文件夹 3.安装CLion 4.激活CLion 5.CLion汉化 6.Clion配置 六、使用CLion运行 七、总结 我的环…...

芯科科技推出新的8位MCU系列产品,扩展其强大的MCU平台

新的BB5系列为简单应用提供更多开发选择 中国&#xff0c;北京 - 2023年11月14日 – 致力于以安全、智能无线连接技术&#xff0c;建立更互联世界的全球领导厂商Silicon Labs&#xff08;亦称“芯科科技”&#xff0c;NASDAQ&#xff1a;SLAB&#xff09;&#xff0c;今日宣布…...

Flink CDC

1、Flink CDC的介绍&#xff1a; 是一种技术&#xff0c;可以帮助我们实时的捕获数据库中数据的变化&#xff0c;并将这些变化的数据以流的形式传输到其他的系统中进行处理和存储。 2、Flink CDC的搭建&#xff1a; 1、开启mysql的binlog功能&#xff1a; # 1、修改mysql配置…...

数据结构-链表的简单操作代码实现3-LinkedList【Java版】

写在前: 本篇博客主要介绍关于双向链表的一些简答操作实现&#xff0c;其中有有部分代码的实现和前两篇博客中的单向链表是相类似的。例如&#xff1a;查找链表中是否包含关键字key、求链表的长度等。 其余的涉及到prev指向的需要特别注意&#xff0c;区分和单向链表之间的差异…...

JTS: 24 MinimumDiameter 最小矩形

文章目录 版本代码 版本 org.locationtech.jts:jts-core:1.19.0 链接: github 代码 package pers.stu.algorithm;import org.locationtech.jts.algorithm.MinimumDiameter; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import…...

MacOS Ventura 13 优化配置(ARM架构新手向导)

一、系统配置 1、About My MacBook Pro 2、在当前标签打开新窗口 桌面上创建目录的文件夹&#xff0c;每次新打开一个目录&#xff0c;就会创建一个窗口&#xff0c;这就造成窗口太多&#xff0c;不太好查看和管理&#xff0c;我们可以改成在新标签处打开新目录。需要在&…...

多区域OSPF配置

配置命令步骤&#xff1a; 1.使用router ospf 进程ID编号 启用OSPF路由 2.使用network 直连网络地址 反掩码 area 区域号 将其归于对应区域 注意&#xff1a; 1.进程ID编号可任意&#xff08;1-65535&#xff09; 2.反掩码用4个255相减得到 3.area 0 为主干区域 4.连接不…...

【强化学习】day1 强化学习基础、马尔可夫决策过程、表格型方法

写在最前&#xff1a;参加DataWhale十一月组队学习记录 【教程地址】 https://github.com/datawhalechina/joyrl-book https://datawhalechina.github.io/easy-rl/ https://linklearner.com/learn/detail/91 强化学习 强化学习是一种重要的机器学习方法&#xff0c;它使得智能…...

openwrt Docker不能联网

文章参考:docker上网(docker安装openwrt无法上网) - 老白网络 外网不能访问内网是应为防火墙。内网访问外网如下: 清理容器垃圾 docker volume prune -f 创建一个网络 docker network create --subnet172.18.0.0/16 mynet 通过该网络创建gerrit docker run -tid --name ge…...

EtherCAT从站EEPROM组成信息详解(2):字8-15产品标识区

0 工具准备 1.EtherCAT从站EEPROM数据&#xff08;本文使用DE3E-556步进电机驱动器&#xff09;1 字8-字15产品标识区 1.1 产品标识区组成规范 对于不同厂家和型号的从站&#xff0c;主站是如何区分它们的呢&#xff1f;这就要提起SII的字8-字15区域存储的产品标识&#xff…...

SpringBoot--中间件技术-4:整合Shiro,Shiro基于会话SessionManager实现分布式认证,附案例含源代码!

SpringBoot整合安全中间件Shiro 技术栈&#xff1a;SpringBootShiro 代码实现 pom文件加坐标 Springboot版本选择2.7.14 &#xff1b;java版本1.8 &#xff1b; shiro做了版本锁定 1.3.2 <properties><java.version>1.8</java.version><!--shiro版本锁定…...

【QT基础入门】QT中的容器类

QT中有多种容器类,它们可以用来存储和操作不同类型的数据。根据容器的特性和用途,可以分为以下几类: 序列容器 这些容器按照一定的顺序存储数据,可以通过下标或迭代器访问。QT中的序列容器有: QList: 这是最通用的序列容器,它在内部实现为一个数组列表,可以快速地在头…...

IDEA没有Add Framework Support解决办法

点击File—>Settings 点击第一个设置快捷键 点击apply和ok即可 我们要点击一下项目&#xff0c;再按快捷键ctrlk 即可...

《009.SpringBoot之汽车租赁系统》

《009.SpringBoot之汽车租赁系统》 项目简介 [1]本系统涉及到的技术主要如下&#xff1a; 推荐环境配置&#xff1a;DEA jdk1.8 Maven MySQL 前后端分离; 后台&#xff1a;SpringBootMybatisPlus; 前台&#xff1a;Layuivue; [2]功能模块展示&#xff1a; 前端门户 1.登录&a…...

第四代智能井盖传感器,万宾科技助力城市安全

在迈向更为智能化、相互联系更为紧密的城市发展过程中&#xff0c;智能创新产品无疑扮演了一种重要的角色。智能井盖传感器作为新型科学技术产物&#xff0c;不仅解决传统井盖管理难的问题&#xff0c;也让城市变得更加安全美好&#xff0c;是城市生命线的一层重要保障。这些平…...

ClickHouse 面试题

文章目录 什么是 ClickHouse&#xff1f;ClickHouse 有哪些应用场景&#xff1f;ClickHouse 列式存储的优点有哪些&#xff1f;ClickHouse 的缺点是是什么&#xff1f;ClickHouse 的架构是怎样的&#xff1f;ClickHouse 的逻辑数据模型&#xff1f;ClickHouse 的核心特性&#…...

Python代码运行速度提升技巧!Python远比你想象中的快~

文章目录 前言一、使用内置函数二、字符串连接 VS join()三、创建列表和字典的方式四、使用 f-Strings五、使用Comprehensions六、附录- Python中的内置函数总结关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项…...

P6入门:项目初始化11-项目详情之计算Calculations

前言 使用项目详细信息查看和编辑有关所选项目的详细信息&#xff0c;在项目创建完成后&#xff0c;初始化项目是一项非常重要的工作&#xff0c;涉及需要设置的内容包括项目名&#xff0c;ID,责任人&#xff0c;日历&#xff0c;预算&#xff0c;资金&#xff0c;分类码等等&…...

<MySQL> 查询数据进阶操作 -- 联合查询

目录 一、什么是笛卡尔积&#xff1f; 二、什么是联合查询&#xff1f; 三、内连接 3.1 简介 3.2 语法 3.3 更多的表 3.4 操作演示 四、外连接 4.1 简介 4.2 语法 4.3 操作演示 五、自连接 5.1 简介 5.2 自连接非必要不使用 六、子查询(嵌套查询) 6.1 简介 6.…...