小孔成像相机模型原理以及标定实现
我们使用深度相机 D435i 为例,使用 ROS1+RealSense+Kalibr 获取相机内参和畸变参数,再介绍两种获取外参的方法。
小孔成像相机模型
首先要搞懂三个坐标系的关系:
像素坐标系:是以成像平面中心为原点的 二维坐标系,单位为像素,坐标记为 。
相机坐标系:是以小孔成像点作为原点的 三维坐标系,单位为米,坐标记为 。
世界坐标系:可以空间中任意一点为原点的 三位坐标系,单位米,坐标记为 。
注意: 就是深度相机中的深度图中的深度大小,单位米。
在像素坐标系与相机坐标系的变换关系为
其中 为内参矩阵( 分别为两个方向上的 ), 为畸变矫正前的归一化相机坐标,通过径向-切向畸变矫正变换 :
其中 ,然后可以得到矫正后的相机坐标系
其中 为投影变换(逆变换就是 ),相机坐标系与世界坐标系的变换关系为
其中 为旋转矩阵(正交阵且行列式为 ), 为平移向量。综上,设世界坐标为 ,像素坐标为 ( 维度用 填充),则整个投影转换过程如下
其中 为内参矩阵, 表示消除畸变变换, 表示相机本身产生基本变换, 表示投影变换, 为旋转矩阵, 为世界坐标系原点在相机坐标系中的坐标, 为深度相机计算出的深度值。左式是针孔相机拍照时的过程,而右式则是深度相机图像中点反向计算世界坐标时的过程(CV 中常用)。
基于 Docker 安装 Ros1, RealSense SDK, Kalibr
我们需要使用 Kalibr 工具箱获取相机的畸变矩阵,安装 Kalibr 方法如下,参考官方教程,两种方法我都进行了尝试,首先尝试的就是直接编译安装,再是Docker安装。
但是最后发现系统自带的 Python 版本为 3.10,然而编译时候必须要用到一个叫 Boost 的工具包必须要 Python 3.8 版本,所以需要将其降级,最终但安装 ROS1 又会自动安装新版本,因此没有解决办法(失败过程如下):
-
安装 ROS1:参考BiliBili - Ubuntu 22.04 apt 安装 ros1 ros Noetic Ninjemys
-
安装 Python 相关包:我用
mamba
(conda
也一样)创建一个新的python 3.8.*
的环境,使用pip
安装这些依赖包:pip install catkin-tools osrf-pycommon scipy matplotlib wxpython pyx
-
遇到报错
fatal error: libv4l2.h: No such file or directory
:sudo apt install libv4l-dev
fatal error: pyconfig.h: No such file or directory
,需要找到pyconfig.h
文件位置,使用find /usr/include -name pyconfig.h
产看返回结果,例如我返回的是/usr/include/python3.10/pyconfig.h
说明该文件在/usr/include/python3.10/
文件夹下,根据 overflow的解答 加入环境变量:export CPLUS_INCLUDE_PATH="$CPLUS_INCLUDE_PATH:/usr/include/python3.10/"
fatal error: boost/detail/endian.hpp: No such file or directory
:和上面的问题类似,只需要找到boost/detail/endian.hpp
所在的路径,例如我的就在/usr/include/
下,加入到 C++ 链接库中即可export CPLUS_INCLUDE_PATH="$CPLUS_INCLUDE_PATH:/usr/include/"
,注意:我的/detail
目录下没有endian.hpp
,所以我又做了个软链接映射过去:sudo ln -sf /usr/include/boost/endian.hpp /usr/include/boost/detail/endian.hpp
fatal error: stdlib.h: No such file or directory
:和上面类似,找到stdlib.h
,加入:CPLUS_INCLUDE_PATH="$CPLUS_INCLUDE_PATH:/usr/include/c++/11/"
- 仍然使用
boost
编译时始终爆出libboost_python310.so.1.74.0 (3.10, Boost_PYTHON_VERSION=3.8)
的错误,后来又尝试下载boost
手工编译python 3.8
的版本,但仍然报错,最后只好放弃。
Docker安装Kalibr(成功):注意 Docker 安装的仅仅是 Kalibr,还是需要利用宿主机上安装的 ROS1 对相机数据进行获取。
-
安装 ROS1:参考BiliBili - Ubuntu 22.04 apt 安装 ros1 ros Noetic Ninjemys
-
安装 Docker:参考腾讯云 - 最详细的ubuntu安装docker教程,一定要记得配置用户组,不然每次使用时候前面都加个
sudo
。(Docker 中常用命令可参考我的笔记 MyBlog - Docker 安装与常用命令) -
使用代理加速 Docker:由于2024年6月6日国内完全封杀 Docker 镜像源,因此我们只能通过代理方法连接 Docker Hub,参考 Myblog - Docker 代理加速(旧方法:更新 Docker 镜像站与 DNS 地址:参考CSDN - Docker拉取镜像过慢或卡死的坑!!!(亲测有效),源只需要一个即可,我用的是
https://ustc-edu-cn.mirror.aliyuncs.com
,DNS 一定要检查下是否被修改过。)
我将我自己基于 Ubuntu 20.04 配好的 Kalibr 也上传到了 Docker Hub wtyyy/kalibr2004,可以直接
pull
下来,从而直接使用步骤4中命令打开即可。
-
Docker-Kalibr 安装:参考官方教程 - Using the Docker Images,可视化CSDN - 如何可视化docker:以ROS环境为例(例如Gazebo,Rviz),
-
Docker 环境变量加载:
可以将这句话加到
~/.bashrc
中,注意我们修改了容器的内容后,需要对镜像使用docker commit kalibr kalibr:v1
更新,例如这样就是更新镜像名称从kalibr -> kalibr:v1
,下一次我们打开容器就从新的镜像kalibr:v1
中打开即可,删除多余镜像方法参考 如何优雅地删除Docker镜像和容器(超详细),相机样例标定文件 下载链接。 -
我们还需要安装 RealSense™ SDK 2.0,顺着官方教程 - Linux Distribution安装完成
librealsense2-dkms, librealsense2-utils
,再连接摄像头,运行命令realsense-viewer
看是否可以出现如下可视化界面,说明安装成功(记得把左边的摄像头功能开关打开):
内参标定(获取)
D435i 的内参矩阵 可以通过 RealSence SDK 直接获取(更加准确),但也可以通过下文中的畸变参数标定过程中获取。
在 ROS 中将摄像头加入话题(ROS 是一个控制系统,需要将每个设备手动打开,打开后成为一个 node,使用 rosnode list
查看,每个 node 还具有很多相关 topic,可以获取其参数信息,通过 rostopic list
查看全部 topic,使用 rostopic echo ${topic-name}
获取话题具体信息,而打开 D435 摄像头就需要使用 realsense 安装的相关依赖包), 内参获取方法如下:
相机分辨率需要先固定成和python获取到的一样,这里设置为 1280x720
,修改 rs_camera.launch
中:
执行 roslaunch realsense2_camera rs_camera.launch
将摄像头加入到 node,执行 rostopic list
查看(左图),并使用 rostopic echo /camera/color/camera_info
(右图):
内参矩阵为 K: [908.2644653320312, 0.0, 650.677978515625, 0.0, 907.4638671875, 370.1951904296875, 0.0, 0.0, 1.0]
对应 矩阵的展开,非零项分别为内参矩阵中的 (和上文ROS2获取中的内参矩阵不同是因为换了个相机)
畸变系数标定
参考 Kalibr 官方教程 和 CSDN - Intel Realsense D435i标定详细步骤
显示当前摄像头
首先要学会通过 rviz
显示当前摄像头,首先我们还是打开相机节点 roslaunch realsense2_camera rs_camera.launch
,打开一个新的终端(可以使用 tmux
或者用 vscode
打开 docker),执行 rviz
,会弹出可视化界面,如下操作从 Add -> By topic -> /camera/color/image_raw/image -> OK
中,即可加入当前相机图像(要从边上拖出来才能放大)。
获取标定板
标定首先需要先打印出来一个标定板,标定版配置可以参考官方的配置文件,我们在共享路径创建配置文件如下:
对应生成标记板 pdf
文件命令为
我们就能在本机的共享目录 $FOLDER
下看到标定i板 target.pdf
文件,从而进行打印。
录制ROS视频包
标定是离线进行的,所以我们需要先录制一个视频包下来,但是我们不用在相机原有的频率下标记,所以创建一个固定 10Hz 频率(随便给个低点的就行)的新节点 \color
:rosrun topic_tools throttle messages /camera/color/image_raw 10.0 /color
,然后我们再从上面的 rviz
中打开 /color
节点的图像,可以看到有明显掉帧,我们拿起标定板保持在摄像头中,开始视频录制 rosbag record -O /data/camd435i_10hz /color
,在录制过程中可以左右移动下标记板,然后 ctrl C
停止录制,文件保存在 /data/camd435i_10hz.bag
中。
內参矩阵及畸变系数计算
如果 kalibr_calibrate_cameras
无法执行,添加路径:export PATH=/catkin_ws/devel/lib/kalibr:$PATH
。
执行下述代码(可视化角点检测效果 Kalibr标记效果.webm)
等执行完成后,在 /data
文件夹(共享文件夹)下会生成三个文件 *.pdf, *.txt, *.yaml
消除畸变变换如下:
可以看出畸变系数为 [ 0.1136323 -0.24918569 -0.00006587 0.00135696]
,其中前两个为径向畸变 ,后两个为切向畸变 。对于内参矩阵,和 ROS+RealSense 直接读取出来的比较接近,但没有那个精确,我们还是使用直接读取出来的用于后续计算。
外参标定
使用内参和畸变矩阵可以得到从像素坐标系到相机坐标系的变换,再找到旋转+平移(仿射变换)矩阵就可以从相机坐标系到世界坐标系,我们关注 小孔成像相机模型 中的相机与世界坐标系 式,图像与世界坐标系 式:
两式分别对应计算 有两种方法:
-
通过 式直接计算:我们可以通过获取世界坐标系中的 三个点在图像中的位置(图像识别),通过内参矩阵 以及深度信息 得到对应的相机坐标 ,将三个点对分别带入 式中,从而计算出 中的 和 ,通过外积求得 。但是这种方法由于 的估计可能存在误差,不能保证 即 是旋转矩阵。(代码
get_extri.py
中的calPoseFrom3Points
) -
通过 式直接计算:另一种不依赖深度信息的方法是使用最小二乘法,通过
cv2
中求解solvePnP
的方法来获取(官方外参标定文档 Perspective-n-Point (PnP) pose computation),假设我们给出 个世界坐标系与相机坐标系的点对 ,通过求解最优化问题,得到 。
模拟小孔相机模型
即使没有摄像机,我们也可以通过 Python 中的 Numpy 和 cv2,将自己创建的三维空间中点通过小孔相机模型投影到二维平面上,并进行可视化,从而模拟成像过程,用上述两种方法反算出相机的外参,效果如下所示
定义空间物体
定义相机
外参标定
完整代码
demo3, demo4
的外参计算结果如下,看得出来通过相机坐标直接计算,在深度信息准确时,结果也是非常精准的
想要可视化文字建模,需要从 立体文字-Enjoying3D打印云平台 上下载空间建模文件 *.stl
,重命名为 test1.stl
放到同级目录下即可。
(可跳过)ROS2 + RealSense获取内参矩阵
[教程]安装realsense-ros,realsense-SDK 是用于相机可视化与获取相机内参,需要依赖 ROS 与相机进行通讯,我们根据上述教程进行安装:
- ROS2 安装:我们安装的是 ROS2 Humble(这个相比 ROS2 Iron 有更长的维护时间)
- 安装:直接进入官方的安装教程中,顺次执行下来即可,注意安装
sudo apt install ros-humble-desktop
时候可能会爆版本过高的问题,我们可以先安装sudo apt install aptitude
用于包版本降级,再使用sudo aptitude install ros-humble-desktop
进行安装(如此安装需要要对安装策略进行选择,当看到ros-humble-desktop
在uninstall
列表中时,按n
回车,让其继续给出新的安装策略,直到ros-humble-desktop
不再uninstall
中时,按y
回车) - 环境变量:安装完成
ros-humble-desktop
后还需要执行source /opt/ros/humble/setup.bash
对环境变量进行更新,由于我用的是zsh
,所以执行source /opt/ros/humble/setup.zsh
即可。如果不想每次都source
一次可以直接将source source /opt/ros/humble/setup.zsh
加入到~/.zshrc
中(如果是 bash 则加入到.bashrc
中) - 简单测试:在一个终端中打开一个小乌龟测试器
ros2 run turtlesim turtlesim_node
,再开一个终端打开ros2 run turtlesim turtle_teleop_key
控制器(节点),对里面按上下左右即可对小乌龟进行控制了!
- 安装:直接进入官方的安装教程中,顺次执行下来即可,注意安装
- 安装 RealSense™ SDK 2.0:安装教程,只需安装
librealsense2-dkms
和librealsense2-utils
即可,输入命令realsense-viewer
即可打开相机显示的可视化界面(连接相机即可看到画面,包含图像及深度图) - 向 ROS2 中安装 RealSense wrapper:我的是
humble
版本,所以直接安装sudo apt install ros-humble-realsense2-*
即可。
参考ubuntu20.08下获取realsense内参(使用ros功能包):我们需要用 type-c 3.0 (必须 3.0 哈)的 USB 线连接摄像头和电脑,运行命令 ros2 run realsense2_camera realsense2_camera_node
即可将当前相机加入 ROS 中的节点,然后通过 ros2 topic list
查看当前节点相关的话题,我们可以看到如下这些话题,再开个新的终端执行 ros2 topic echo /camera/color/camera_info
获取到当前相机相关参数,其中内参矩阵为 k
后面的 9 个参数,分别为 的内参矩阵横向展开的结果。
例如上图中,我的相机内参矩阵就是 k=[[616.3648681640625,0.0,316.91259765625],[0.0,616.5704345703125,243.251953125],[0.0,0.0,1.0]]
: