├── .gitignore ├── Connected_Components ├── .ipynb_checkpoints │ ├── 如何理解二值图连通域快速标记算法快速连通物体检测Fast-Connected-Component教程附带python代码实现-checkpoint.ipynb │ └── 如何理解快速连通物体检测Fast-Connected-Component教程附带python代码实现-checkpoint.ipynb ├── fast_label_algorithm_example.gif ├── real_scene_practice │ ├── .ipynb_checkpoints │ │ ├── 图像处理实践_find_the_white_circle_找图中所有白色的环-checkpoint.ipynb │ │ └── 带标记的目标实时跟踪实践python-checkpoint.ipynb │ ├── circles.jpg │ ├── oneCCC.wmv │ ├── robot.jpg │ ├── 图像处理实践_find_the_white_circle_找图中所有白色的环.ipynb │ └── 带标记的目标实时跟踪实践python.ipynb └── 如何理解二值图连通域快速标记算法快速连通物体检测Fast-Connected-Component教程附带python代码实现.ipynb ├── LICENSE ├── Otsu's_Method_algorithm ├── eight.png ├── eight_二值化效果.png └── 如何理解图像处理中的Otsu's 二值化算法(大津算法)Python编程实践.md ├── RANSAC ├── .ipynb_checkpoints │ └── RANSAC-checkpoint.ipynb └── RANSAC.ipynb ├── README.md ├── Robot Mapping WS SLAM └── ppt │ ├── homework1_octave_tutorial_framework.tar.gz │ ├── homework1_octave_tutorial_framework │ └── octave_tutorial_framework │ │ ├── data │ │ ├── sensor_data.dat │ │ └── world.dat │ │ └── octave │ │ ├── main.m │ │ ├── motion_command.m │ │ ├── octavehelp.m │ │ └── tools │ │ ├── chi2invtable.m │ │ ├── drawellipse.m │ │ ├── drawprobellipse.m │ │ ├── drawrobot.m │ │ ├── normalize_all_bearings.m │ │ ├── normalize_angle.m │ │ ├── plot_state.m │ │ ├── read_data.m │ │ └── read_world.m │ └── slam01-intro.pdf ├── bayes_filter └── 易懂的贝叶斯滤波理解与推导教程bayes_filter.md ├── feature_extract ├── Bresenham布雷森汉姆算法画圆教程.md ├── FAST_feature_extraction.py ├── ORB_feature_extract.py ├── bresenham_circle.py ├── feature.png ├── right.png └── 从零开始实现FAST特征点提取算法教程.md ├── finding_an_image_transform ├── .ipynb_checkpoints │ └── 如何找到两张图片中的旋转_从视频中还原相机发生的旋转-checkpoint.ipynb ├── book_A.jpg ├── book_B.jpg └── 如何找到两张图片中的旋转_从视频中还原相机发生的旋转.ipynb ├── image_smooth_blur ├── .ipynb_checkpoints │ └── 如何理解高斯模糊原理与具体Python编程实现-checkpoint.ipynb ├── lenna.jpg ├── 图像光滑化处理与高斯模糊毛玻璃效果.md ├── 如何理解高斯模糊原理与具体Python编程实现.ipynb └── 高斯模糊效果.png ├── img └── orb_效果图.png ├── introduction to robotics ├── homework1 │ ├── laserscan.dat │ └── sensing_solution.py ├── homework2 │ └── sheet03.pdf ├── homework3 │ └── sheet04.pdf └── 教科书_textbook │ └── 贝叶斯推理好书非常多的例子bayesian reasoning and maching learning.pdf ├── joint_robot_simulation ├── two_joint_arm_robot.py ├── two_link_arm_robot_result.gif └── 两连杆关节机械臂机器人给定位置求解各关节转动角度教程模拟Python实现.md ├── slam_book_list └── 前50页介绍李群李代数An elementary introduction to groups and representations.pdf ├── solve_least_square ├── .ipynb_checkpoints │ └── 最小二乘法有什么用_如何用最小二乘法求解方程-checkpoint.ipynb └── 最小二乘法有什么用_如何用最小二乘法求解方程.ipynb └── 前50页介绍李群李代数An elementary introduction to groups and representations.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | .spyproject -------------------------------------------------------------------------------- /Connected_Components/.ipynb_checkpoints/如何理解快速连通物体检测Fast-Connected-Component教程附带python代码实现-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": { 5 | "image.png": { 6 | "image/png": "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" 7 | } 8 | }, 9 | "cell_type": "markdown", 10 | "metadata": {}, 11 | "source": [ 12 | "# 如何快速在一张二值化了的图片检测那些像素是属于一个物体块?\n", 13 | "注意快速连通物体检测算法只能识别二值化了的图片中哪些像素是相互连接的一个整体。换句话说就是这个算法可以检测到哪些像素是相互连通的。它其实就是可以看作是计算机算法中的连通子图问题。\n", 14 | "连通物体检测算法有哪些用?1. OCR文字识别中可以用连通物体检测算法识别哪些像素是属于一个汉字。然后对这个汉字识别 2. 物体检测。\n", 15 | "\n", 16 | "![image.png](attachment:image.png)" 17 | ] 18 | } 19 | ], 20 | "metadata": { 21 | "kernelspec": { 22 | "display_name": "Python 3", 23 | "language": "python", 24 | "name": "python3" 25 | }, 26 | "language_info": { 27 | "codemirror_mode": { 28 | "name": "ipython", 29 | "version": 3 30 | }, 31 | "file_extension": ".py", 32 | "mimetype": "text/x-python", 33 | "name": "python", 34 | "nbconvert_exporter": "python", 35 | "pygments_lexer": "ipython3", 36 | "version": "3.7.0" 37 | } 38 | }, 39 | "nbformat": 4, 40 | "nbformat_minor": 2 41 | } 42 | -------------------------------------------------------------------------------- /Connected_Components/fast_label_algorithm_example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/Connected_Components/fast_label_algorithm_example.gif -------------------------------------------------------------------------------- /Connected_Components/real_scene_practice/.ipynb_checkpoints/图像处理实践_find_the_white_circle_找图中所有白色的环-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 二值图与连通域检测综合实践:找到图中所有白色的环\n", 8 | "假设现在我们获得了下面这张图,如何检测到下图中所有白色的环?这个在实际场景中应用非常广泛。比如拍电影的时候可以用白色的环标记贴在目标物体上来追踪目标物体的轨迹。本章节的理论知识有:{[快速标记连通域检测](../)}。略微涉及了{[图像二值化](../../Otsu's_Method_algorithm)}。如果你想从原理上了解如何检测二值图中的连通物体推荐看这篇教程{[快速标记连通域检测](../%E5%A6%82%E4%BD%95%E7%90%86%E8%A7%A3%E4%BA%8C%E5%80%BC%E5%9B%BE%E8%BF%9E%E9%80%9A%E5%9F%9F%E5%BF%AB%E9%80%9F%E6%A0%87%E8%AE%B0%E7%AE%97%E6%B3%95%E5%BF%AB%E9%80%9F%E8%BF%9E%E9%80%9A%E7%89%A9%E4%BD%93%E6%A3%80%E6%B5%8BFast-Connected-Component%E6%95%99%E7%A8%8B%E9%99%84%E5%B8%A6python%E4%BB%A3%E7%A0%81%E5%AE%9E%E7%8E%B0.ipynb)}\n", 9 | "![](./circles.jpg)" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 53, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "data": { 19 | "image/png": "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\n", 20 | "text/plain": [ 21 | "
" 22 | ] 23 | }, 24 | "metadata": { 25 | "needs_background": "light" 26 | }, 27 | "output_type": "display_data" 28 | } 29 | ], 30 | "source": [ 31 | "# 热身训练:调用opencv中的库函数识别连通域\n", 32 | "import cv2\n", 33 | "circles_img_mat = cv2.imread('circles.jpg',cv2.IMREAD_GRAYSCALE)\n", 34 | "\n", 35 | "circles_img_mat = cv2.threshold(circles_img_mat,250,255,cv2.THRESH_BINARY)[1]# 因为连通域算法只适用于二值化了图片所以要进行二值化操作\n", 36 | "ret, labels = cv2.connectedComponents(circles_img_mat)\n", 37 | "# 用矩形框标记所识别的连通域\n", 38 | "for i in range(1,ret):\n", 39 | " y,x = np.where(labels==i)\n", 40 | " labeled_img_mat = cv2.rectangle(circles_img_mat,(x.min(),y.min()),(x.max(),y.max()),65,3)\n", 41 | "\n", 42 | "import matplotlib.pyplot as plt\n", 43 | "plt.imshow(labeled_img_mat)\n", 44 | "plt.show()" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": null, 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [] 53 | } 54 | ], 55 | "metadata": { 56 | "kernelspec": { 57 | "display_name": "Python 3", 58 | "language": "python", 59 | "name": "python3" 60 | }, 61 | "language_info": { 62 | "codemirror_mode": { 63 | "name": "ipython", 64 | "version": 3 65 | }, 66 | "file_extension": ".py", 67 | "mimetype": "text/x-python", 68 | "name": "python", 69 | "nbconvert_exporter": "python", 70 | "pygments_lexer": "ipython3", 71 | "version": "3.7.0" 72 | } 73 | }, 74 | "nbformat": 4, 75 | "nbformat_minor": 2 76 | } 77 | -------------------------------------------------------------------------------- /Connected_Components/real_scene_practice/circles.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/Connected_Components/real_scene_practice/circles.jpg -------------------------------------------------------------------------------- /Connected_Components/real_scene_practice/oneCCC.wmv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/Connected_Components/real_scene_practice/oneCCC.wmv -------------------------------------------------------------------------------- /Connected_Components/real_scene_practice/robot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/Connected_Components/real_scene_practice/robot.jpg -------------------------------------------------------------------------------- /Connected_Components/real_scene_practice/图像处理实践_find_the_white_circle_找图中所有白色的环.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 二值图与连通域检测综合实践:找到图中所有白色的环\n", 8 | "假设现在我们获得了下面这张图,如何检测到下图中所有白色的环?这个在实际场景中应用非常广泛。比如拍电影的时候可以用白色的环标记贴在目标物体上来追踪目标物体的轨迹。本章节的理论知识有:{[快速标记连通域检测](../)}。略微涉及了{[图像二值化](../../Otsu's_Method_algorithm)}。如果你想从原理上了解如何检测二值图中的连通物体推荐看这篇教程{[快速标记连通域检测](../%E5%A6%82%E4%BD%95%E7%90%86%E8%A7%A3%E4%BA%8C%E5%80%BC%E5%9B%BE%E8%BF%9E%E9%80%9A%E5%9F%9F%E5%BF%AB%E9%80%9F%E6%A0%87%E8%AE%B0%E7%AE%97%E6%B3%95%E5%BF%AB%E9%80%9F%E8%BF%9E%E9%80%9A%E7%89%A9%E4%BD%93%E6%A3%80%E6%B5%8BFast-Connected-Component%E6%95%99%E7%A8%8B%E9%99%84%E5%B8%A6python%E4%BB%A3%E7%A0%81%E5%AE%9E%E7%8E%B0.ipynb)}\n", 9 | "![](./circles.jpg)" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 53, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "data": { 19 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQYAAAD8CAYAAACVSwr3AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvIxREBQAAGKdJREFUeJztnXvsHUd1xz8Hx4+8yA+HhPrVOiluBapak1pxUBqUxqQOboRRFCAUgakiOWpiCZSq4KhSVaQiQf/gUVEFjILqVEBCk6BYEchN81CLCgYnOJCQhjhR2vxqKxZNHB4WkJDTP+5c+/p3X3t3d3bPzJ6PdHV35+7dPXt25rtnZmdmRVVxHMcZ5FVtG+A4jj1cGBzHGcKFwXGcIVwYHMcZwoXBcZwhXBgcxxkiijCIyBUi8oSIHBSRnTGO4ThOPKTufgwisgj4EXA5MA98F3iPqv6w1gM5jhONGBHDhcBBVX1aVX8F3AZsjXAcx3EicUqEfa4Cnh1Ynwc2TvrDElmqS+bOjWBK87zq6M/bNiFbXpk7vW0TTiK1a/1TXvixqp5TZNsYwiAj0obqKyKyHdgOsIzT+INNHwTgtLv2jd3x3kMH6rGwJJtXrh/727Greto3yX6nGsc2jfdxjLwx7nqneq3/Te/476LbxqhKzANrBtZXA4cWbqSqu1R1g6puWMzSqTttWxSs2OCczN5DB6Jdly5f7xjC8F1gnYicJyJLgGuAPVV2aOkCWbLFiU9Xr3ftVQlVfVlEdgB7gUXAF1X1sbL7s3hh9h46MLFaUYZ+eBqL1MLeIjSVN2Jcb+tE6cegql9X1d9R1d9W1Y+V3Y9FUYhBbFFo6hhN0nTe6Epe7BOj8bETxLiL1NGoNsqm3ETBiY/ZLtFdU+hRlPFBF/zW1jl2wbd9TApDKhcgpp1V9p2K/xy7mBSGlIhRCOvYp4tDHLriV3PC0BXHj6PO8++6L53ymBOGFKmrAFqNPnJg88r1xz/OdFwYnOxZKAYuENNxYXCyxgWgHC4MTrZMEwUXjfG4MDiOM0Qnej4uvDN4g5zjTCZrYRgXKvbTUxSIUeeU4nk4tsm2KlGk/uh1zLyZJpguqOPJVhiK4uLgOMNkWZXwwu706UcFg3nCI4XpZCkMObNwuLdn8mLU5afNK9fDVbXsyjQuDAniYuDEpvNtDJaIUQXKsVrV1jnl6MtxuDAYo87M16WM7NRLlsKQeqjtBXo6Tfuoa9ckS2GA4uJgVUSqZMSujB5s6hy74MuFZCsMkH4HlzIZsouZOCZd9Wf2TyVGzeZctyDU9Qhr1GzOl+yYcYbnDjxKGyRm9/auigIYFIbNK9fXfpGtRwan3bXPp3ivSJ35psuC0MecMKRGXZkoxzdFNU0d0YOLQg+TwhAjaoiBZ6J2mBZdzVz9GqRjVbFxmG189ELnWCb3CM9kxJACLlzNk3thtITZiAHsFj6rdjlOXZgWBrBXCK3Z4zgxMC8M4IXRcZommTaGtp9U1ClO3mdhNrxtoXmSiBj65DDc1kVhdtxnzZOUMICPqnOcJphalRCRLwJXAkdU9fdC2nLgdmAt8AzwLlV9QUQE+AywBTgGfEBVH67b6Camf48tCNbDYytjD1KMFqzZXCavFYkY/gm4YkHaTuA+VV0H3BfWAd4GrAuf7cDNM1s0AzHeYNyVIcuTiCW4KfRmzZEyQjU1YlDVfxeRtQuStwKXhuXdwIPAR0L6raqqwLdFZE5EVqjq4aIGlVXbSt1gB+lwl9gmCu6oWZtzZdqduqq/i/iwbHkq+1Tidf3CrqqHReTckL4KeHZgu/mQNiQMIrKdXlTBMk4raYYtrFcPJtH03bxLArGQunwd04d1P66UEWk6akNV3QXsAni1LNeUC1XqtBnij5ovI2di+DqGD8s+lXhORFYAhO8jIX0eWDOw3WrgUHnznNhYqPdbsKEJYp5n3fsuKwx7gG1heRtw90D6+6XHRcCLs7QvOM1iqUBasiUGTbbf1MFUYRCRrwDfAn5XROZF5Frg48DlIvIkcHlYB/g68DRwEPgCcH1tljq1YrEgWrSpDpo8r7qOVeSpxHvG/LRpxLYK3FDVKCcuuRZApz6S6/no5E1uotXG+dRxTBeGjpFbwbNM2097quDC0CFSEYVU7MwZFwbHJC4O1aniQxeGjuAFzZmFZCZqqYsmRr55L8562HvoQH1jYJyZ6JwwNMGxqza6ODiliP06xaKYEIZX5k7n2KZyd4ayBXDU/2a9CKP6p1sbix8LKxk4F8aNdWhrSkNvY6CXqcs4P5XCUKed4+ar8HksyjPNb2341UTEAPAfn/388eWY48wXUrXQdGl0YJHzbHvSXqceTEYMZe/gZY5jaT+50BWhzBmTwtAnpWGquYvDpMJ+7KqNxz9FtnfsY1oYIP8C5zgWMS8MkI44pGJn3fij2WpYzDdmGh+n0aVGvhRxcajGuPkbO92PoSguDu0xq+8t3gVTwIrfkhKGprGi3o5Ths0r15d+HUISbQyDNFU4x3Xi6TJFfe8C2qPN/FL12MkJg9Mu0wq9i0IeJFmV6Nqou6KFran+9qPaG1wQRtNGT9A6IpUkhaEJ2s78VcduxA5jXQjyxqsSExjM/NZFIcY+nHposq2hrmOZEQarDXtNjduIcawmbY+B1TxRhibOpc5jmBEGSD8jVLE/9riQ1H2bA7GGpsfYrylhgPTFoQwpjSRtipzzQZ3nFstP5oQB0gu7quwvtQLbBDmLQp86zjGmnzr9VKLv2KqFswsZ2ZmdaZMJVX7kXrJXYxFMRgwpYbVdIVVcZOunzAC3TkcMfcpEDlUzsIvCMLmIQg4jTTsrDKPCvJlCu4hhXBNYm5uxTVGwOLN32+LiVYkItH1Ri+CPMHtYFAVo3y6TEUPMO1kKhbZJ2o4crIhTkXzRVCN126IABiMGS+FtV2jrnRBWRKEIXeumPjViEJE1wK3AbwCvALtU9TMishy4HVgLPAO8S1VfEBEBPgNsAY4BH1DVh4sYk5Lj2iCnEY1dE4WF+7J+/kWqEi8Df6mqD4vImcBDInIv8AHgPlX9uIjsBHYCHwHeBqwLn43AzeF7Iiln8thMGk4N5X23sJ2hrn4dk7BeIAZp4vUFVv0xVRhU9TBwOCz/VEQeB1YBW4FLw2a7gQfpCcNW4FZVVeDbIjInIivCfpwEGMysdRUOqwXAGc1MbQwishZ4E7APeF2/sIfvc8Nmq4BnB/42H9JqwzPZycT0R9X2hxTfadn0iFqLFH4qISJnAHcCH1LVn/SaEkZvOiJNR+xvO7AdYMmpc8CyoqY4LZBa4S5LGwXV4qPjQhGDiCymJwpfUtW7QvJzIrIi/L4COBLS54E1A39fDRxauE9V3aWqG1R1w+KlZxQ22JoDHacOrEUOU4UhPGW4BXhcVT858NMeYFtY3gbcPZD+fulxEfCity/EpWymcpF1xlEkYrgYeB9wmYgcCJ8twMeBy0XkSeDysA7wdeBp4CDwBeD6uoztaka2djfJEQuzXbV9/EGKPJX4JqPbDQA2jdhegRsq2jVEV0WhT9uT0zrdwmSX6IV0XRT61CkE7lObWHk1grku0QvxDOw4zWNaGHIWhZRfX+bkj8mqhGdcx2kXM8JwyY7rTqwkPglKUVJ9fZnTY5wvc2gUNl2V6AIpvqXImezLHPxsImJ41dGfd3oClSYihxwyqxW64EuPGIwQeyCU48yCC4MhYr2+zHFmxURVok4szJdXhHF2bl7Z+/aX4DhtkpUwpCIKk+ifg+W3FHUdi8Ok68arEo5TMzk8rswqYuhT5glHjIs5612lHy10+QlNKoyaszEHQeiTpTDMSqwL2oWQs+vUnXc2r1xvohrYaWFoQuGtzwbsOKPoZBtDG5Ny5BRm5oiFSWvbPv4gnROGNguoi4OTCp0SBgsF04INjj0sRQvQIWGwVCAt2eKcjL/Ds0cnhMFiQbRok9OjyfYGi6IAHREGq7g4OFbJ/nFl1wqftW7hKXfWivmSX6uRQh+PGFqmzkxnTRRyoc5CbOGxaBGyjhhSiRbq7iFZ5C5dxTdFbM1NpOqYTCcFQeiTrTCkIgpNU4dfcuzNWUTIujTi1asSRogtZDF6e1p4rVtVUm4DiUmWEUPqmdVpFheHYbIUBucEPlDMKYNXJTLGB4o5ZfGIYUb8jdNOF/CIoSDjnj9bfS7dlmC5UOaBC0MBLBZ8x4mJC0NNWBKPtu/abR/fqY4LwxRmKfCWxMFxqjBVGERkmYh8R0QeEZHHROSjIf08EdknIk+KyO0isiSkLw3rB8Pva+OeguM4dVPkqcQvgctU9Wcishj4poh8A7gR+JSq3iYinwOuBW4O3y+o6utF5BrgE8C7I9nv1MioiMerBd1kasSgPX4WVheHjwKXAXeE9N3AO8Ly1rBO+H2TiEhtFjtRGFcN8upRNynUxiAii0TkAHAEuBd4Cjiqqi+HTeaBVWF5FfAsQPj9ReDsEfvcLiL7RWT/S/yy2lk4jlMrhYRBVX+tquuB1cCFwBtGbRa+R0UHOpSguktVN6jqhsUsLWpv48wSSqcadk+LCjxq6B4zPZVQ1aPAg8BFwJyI9NsoVgOHwvI8sAYg/H4W8HwdxjqO0wxTGx9F5BzgJVU9KiKnAm+l16D4AHA1cBuwDbg7/GVPWP9W+P1+VR2KGFKiyEQqqUYLjn1qm/TmzjumbxMo8lRiBbBbRBbRizC+qqr3iMgPgdtE5O+A7wG3hO1vAf5ZRA7SixSumcV2q4wbQeiC4OTIVGFQ1e8DbxqR/jS99oaF6b8A3lmLdQbJUQimRUQ5nnOKFJ03YtT1umTHdTMdK8uej95YNjvjCr+LQjrUOaOWD7s2goXXn7sIpEmM65ZlxAAeNTjdIJaYZysMkI44pGKn0x2yFoYUiCEKbQtN28fvArFn6M5eGDyTOrnRRFtQ9sJgmZii1ZYguhDnQSeEweK8jE3Y0/Q5W/NxjjT15KgTwmCNJgtQU8dyUciLTgmDhcihjePHPmbbPu0KTfYz6ZQw9GmrcLZZgGId20UhTzrb87GO15qnRt3nnJIo1DZCcQI5vQOzs8IAJzJ2LIGwWHAGbSpz3hbPyQrHrtqYjTh0Whj61C0QbReeonfHS3aUuIu2PJ6jKgsLbl3i2ERE0iRZCkPZi1SqoIyipcJz2l37ssugMSl7IygycU/qZCUMXjDyqufGpGp0OG7iHouUGbmblTCAFwxnOl1rdC5DJx9XOt2lblHIVWRcGBynIjmKgwuD42ROGeFyYXAcZ4jsGh8dpyz+aoATeMTgOGNI4VFkLFwYHMcZwoXBcZwhXBgcJ7CwTcHbGBzHOQmLotBkm4cLg+MMEHta9qo0JQ4uDI7jDOHC4DgVyXE2bhcGp1PUXahyfX+HC4PTOXLpuBTzPFwYnE5StVC1Pev3oB0xKDxWQkQWAfuB/1XVK0XkPOA2YDnwMPA+Vf2ViCwFbgX+EPg/4N2q+sykfb8ydzrHNqU785JPDpMmZWfNtiAIg8SY8XyWiOGDwOMD658APqWq64AXgGtD+rXAC6r6euBTYbus6fp0cikz653fmij0qduuQhGDiKwG/hT4GHCjiAhwGfBnYZPdwN8CNwNbwzLAHcBnRURUVScdo8pdt6paVnGqi0JajLtehScCNjxLdp2znRetSnwa+DBwZlg/Gziqqi+H9XlgVVheBTwLoKovi8iLYfsfD+5QRLYD2wGWnDpXyvi6wqeUJvZ0ytHERMGxq5RRXwuwgKnCICJXAkdU9SERubSfPGJTLfDbiQTVXcAugDNes0b5RSF7gXjdVV0g8sbbgopTJGK4GHi7iGwBlgGvphdBzInIKSFqWA0cCtvPA2uAeRE5BTgLeL4OY5vqqmpZIFKvunjhnJ02fDa18VFVb1LV1aq6FrgGuF9V3ws8AFwdNtsG3B2W94R1wu/3T2tfKEIb/det9ZlPXRQgj3PoAlX6MXyEXkPkQXptCLeE9FuAs0P6jcDOaia2W0CtiYPjNMFMcz6q6oPAg2H5aeDCEdv8AnhnDbYBNgqmtVeSlQkt6/ZjGX94tJAOpieDtSAKfayJQ1G62FCbugBZaIcx2yXakij0sWjTJJqw15pPUhcFsHEOJiMGa5ltkBQih6b9ZzF6KHvXrcN3OXSYMxsxOOXwhtpy1Dlzk/VZoIpgThhScKhVGy3YZcGGWYnZDpOiP8CgMKSCtQtuyR5LtkyiqYKbokCYEobUnGcFi36zaNMg3mFuMqaEwXGawNthpmNGGFJx2CAWbLZgwzgs2mbBJgs2TMOMMDizk0IGs2Sj21Ick/0Y6mDSs2TrF6UIOZxDk1j0l+U+MVlGDNOcbfVi5ErbhbLt40/Cqm3ZCUPRQu/i4DjjyU4YukCsu0x/YtQYotnWndHqHXkQizZmJQyzZmiPGnqMEgMr702ogsUClwpZCYMzO94eYwNrIubC0GFybo+xVtBSw4XBcYxgScyyEoZZHWvpQjSNt8fMRsyGWYtk18GpaKeRLouCU4xx+Wgwva18VGpClzvvKLxpVhGD4zj1kF3EAJOnGvNIwSnCLA2zbeapUVPYjbLnkh3XzbTfLIWhj4vAeGbtp98lX5Zpf7Hgnzpt8KpEhymakSxkemcydV8jFwZnIi4K9olxjcxUJayEY7OweeV6uKptK6ox6HMLre3ObMS6TmaEwWkfF4Me3v5irCrRlc4jjmMdU8KQEi5iedP1hllzwpBCgUvBRqc60wp9rqIABoUBbBc8C7ZZsGFWUrQZRr8sJpUXyFQZ32G28THFpxROvnQtL5qMGKyS6l2vbdxv6WFaGKwMc7VixyDW7HGqY+maFhIGEXlGRH4gIgdEZH9IWy4i94rIk+H7NSFdROQfROSgiHxfRC6oaqQlh1kiBb+0ZWMKvolNlerPLBHDH6vqelXdENZ3Avep6jrgvrAO8DZgXfhsB24ubd0AbVxoi5HCQizbZ9m2rlC2obRKVWIrsDss7wbeMZB+q/b4NjAnIisqHOc4TWY0z9Tpk9I1tGZrUWFQ4F9F5CER2R7SXqeqhwHC97khfRXw7MB/50PaSYjIdhHZLyL7X/rlzwobHPsunkKUsBCL9lq0ySoWfVVUGC5W1QvoVRNuEJG3TNhWRqTpUILqLlXdoKobFi89o6AZJ6i7AKcoCINYst1tSZ9C/RhU9VD4PiIiXwMuBJ4TkRWqejhUFY6EzeeBNQN/Xw0cmnaMUnPYAZfsKPe/IRIfJQk2+n5YLIgW/DKOqv7q/7/x+RhE5HQRObO/DPwJ8CiwB9gWNtsG3B2W9wDvD08nLgJe7Fc5nPi0FfmkHnG1gWV/iepQlH/yBiLnA18Lq6cAX1bVj4nI2cBXgd8E/gd4p6o+LyICfBa4AjgG/Lmq7p90jFfLct0om6qdSQcoG1VZY9Q8hXXRFR+VOc//vPOvHhp4qjiRqcLQBCLyU+CJtu0oyGuBH7dtRAFSsRPSsTUVO2G0rb+lqucU+bOVsRJPFFWythGR/SnYmoqdkI6tqdgJ1W013SXacZx2cGFwHGcIK8Kwq20DZiAVW1OxE9KxNRU7oaKtJhofHcexhZWIwXEcQ7QuDCJyhYg8EYZp75z+j6i2fFFEjojIowNpjQ0vn9HWNSLygIg8LiKPicgHLdorIstE5Dsi8kiw86Mh/TwR2RfsvF1EloT0pWH9YPh9bRN2Dti7SES+JyL3GLcz7lQIqtraB1gEPAWcDywBHgHe2KI9bwEuAB4dSPt7YGdY3gl8IixvAb5Bb2zIRcC+hm1dAVwQls8EfgS80Zq94XhnhOXFwL5w/K8C14T0zwF/EZavBz4Xlq8Bbm/YrzcCXwbuCetW7XwGeO2CtNqufWMnMubk3gzsHVi/CbipZZvWLhCGJ4AVYXkFvT4XAJ8H3jNqu5bsvhu43LK9wGnAw8BGep1vTlmYD4C9wJvD8ilhO2nIvtX05ha5DLgnFCRzdoZjjhKG2q5921WJQkO0W6bS8PImCGHsm+jdjc3ZG8LzA/QG2t1LL0o8qqovj7DluJ3h9xeBs5uwE/g08GHglbB+tlE7IcJUCIO03fOx0BBto5iwXUTOAO4EPqSqP+kNVRm96Yi0RuxV1V8D60Vkjt64mzdMsKUVO0XkSuCIqj4kIpcWsKXt63+xqh4SkXOBe0XkvyZsO7OtbUcMpYZoN8xz/Rmo6hheXicispieKHxJVe8KyWbtVdWjwIP06rlzItK/MQ3actzO8PtZwPMNmHcx8HYReQa4jV514tMG7QROngqBntgenwoh2FTp2rctDN8F1oWW3yX0GnH2tGzTQkwOLw+jWG8BHlfVT1q1V0TOCZECInIq8FbgceAB4Ooxdvbtvxq4X0PFOCaqepOqrlbVtfTy4f2q+l5rdkJDUyE02fg0phFlC70W9aeAv27Zlq8Ah4GX6KnstfTqjfcBT4bv5WFbAf4x2P0DYEPDtv4RvXDw+8CB8NlizV7g94HvBTsfBf4mpJ8PfAc4CPwLsDSkLwvrB8Pv57eQDy7lxFMJc3YGmx4Jn8f65abOa+89Hx3HGaLtqoTjOAZxYXAcZwgXBsdxhnBhcBxnCBcGx3GGcGFwHGcIFwbHcYZwYXAcZ4j/B1Hmpr37JqA3AAAAAElFTkSuQmCC\n", 20 | "text/plain": [ 21 | "
" 22 | ] 23 | }, 24 | "metadata": { 25 | "needs_background": "light" 26 | }, 27 | "output_type": "display_data" 28 | } 29 | ], 30 | "source": [ 31 | "# 热身训练:调用opencv中的库函数识别连通域\n", 32 | "import cv2\n", 33 | "circles_img_mat = cv2.imread('circles.jpg',cv2.IMREAD_GRAYSCALE)\n", 34 | "\n", 35 | "circles_img_mat = cv2.threshold(circles_img_mat,250,255,cv2.THRESH_BINARY)[1]# 因为连通域算法只适用于二值化了图片所以要进行二值化操作\n", 36 | "ret, labels = cv2.connectedComponents(circles_img_mat)\n", 37 | "# 用矩形框标记所识别的连通域\n", 38 | "for i in range(1,ret):\n", 39 | " y,x = np.where(labels==i)\n", 40 | " labeled_img_mat = cv2.rectangle(circles_img_mat,(x.min(),y.min()),(x.max(),y.max()),65,3)\n", 41 | "\n", 42 | "import matplotlib.pyplot as plt\n", 43 | "plt.imshow(labeled_img_mat)\n", 44 | "plt.show()" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": null, 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [] 53 | } 54 | ], 55 | "metadata": { 56 | "kernelspec": { 57 | "display_name": "Python 3", 58 | "language": "python", 59 | "name": "python3" 60 | }, 61 | "language_info": { 62 | "codemirror_mode": { 63 | "name": "ipython", 64 | "version": 3 65 | }, 66 | "file_extension": ".py", 67 | "mimetype": "text/x-python", 68 | "name": "python", 69 | "nbconvert_exporter": "python", 70 | "pygments_lexer": "ipython3", 71 | "version": "3.7.0" 72 | } 73 | }, 74 | "nbformat": 4, 75 | "nbformat_minor": 2 76 | } 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 varyshare 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Otsu's_Method_algorithm/eight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/Otsu's_Method_algorithm/eight.png -------------------------------------------------------------------------------- /Otsu's_Method_algorithm/eight_二值化效果.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/Otsu's_Method_algorithm/eight_二值化效果.png -------------------------------------------------------------------------------- /Otsu's_Method_algorithm/如何理解图像处理中的Otsu's 二值化算法(大津算法)Python编程实践.md: -------------------------------------------------------------------------------- 1 | # Otsu's 二值化(大津算法) 2 | ## 二值化是什么?有什么用? 3 | PDF扫描成电子版,文字识别,车牌识别等等图像处理场合均需要使用“二值化”操作。我们知道图像是一个矩阵组成,矩阵的元素是一个数字,这个数字是当前像素点对应的颜色(即像素值)。而图片的二值化操作就是将所有像素值变成要么是0要么是1.一般二值化怎么做的呢?答:“设置一个数字d,只要像素值大于这个阈值d那就设置为1,小于这个阈值d那就设置为0。当然也可以大于这个阈值设置为0,小于设置为1”。**但是这个阈值怎么找到的呢?计算出一个合适的阈值出来这就是 Otsu's 二值化(大津算法)要做的事情**。 4 | 下面是一幅图片对应的像素值矩阵(图片就是矩阵): 5 | $\begin{bmatrix} 6 | 200,30,40\\ 7 | 13, 40,45 8 | \end{bmatrix}$ 9 | 假设现在我通过Otsu's 二值化(大津算法)计算出上面那个图片二值化的最优阈值是39. 10 | 那么上面那个图片就会被二值化为: 11 | $\begin{bmatrix} 12 | 1,0,1\\ 13 | 0, 1,1 14 | \end{bmatrix}$ 15 | 下面我们实验下。 16 | 17 | ## 实验1. 造一个数据 18 | 做图像处理必备技能就是人工制造一个纯净的图片检验算法正确性 19 | ```python 20 | import numpy as np 21 | import cv2 22 | import matplotlib.pyplot as plt 23 | 24 | ######我们先制造一个200x200的图片用于二值化实验####### 25 | def get_test_img(): 26 | img_mat = np.zeros((200,200),dtype=np.uint8)# 记得设置成整数,不然opencv会将大于1的浮点数全显示成白色 27 | for row in range(200): 28 | for col in range(200): 29 | img_mat[row][col] = col 30 | return img_mat 31 | img_mat = get_test_img() 32 | plt.imshow(img_mat,cmap='gray')# 显示图片 33 | plt.xlabel("raw img") 34 | ``` 35 | 如下所示: 36 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20191012211830811.png) 37 | ## 2. 手工设置阈值进行二值化实验 38 | ```python 39 | ##########我们设置二值化的阈值为100,将像素值小于100设置为0 (黑色)大于100设置为1 (白色)####### 40 | img_mat = get_test_img() # 注意这是实验1中那个函数 41 | img_mat[img_mat<=100]=0 42 | img_mat[img_mat>100]=1 43 | plt.imshow(img_mat,cmap='gray')# 显示图片 44 | plt.xlabel("binary img") 45 | ``` 46 | 我们将实验1中的图片二值化为下面这张图。 47 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/2019101221200258.png) 48 | # Otsu's 二值化(大津算法)是怎么根据一张图片计算出它二值化的最优阈值的? 49 | 它就是统计各个像素值的出现的次数,然后遍历各种阈值(总共有256种可能的选择),然后让被划分的那两类的像素值的方差的加权和最小。加权和的权重就是对应类中像素值之和。这个方差就用统计学中的方差计算公式即可。 50 | 我总结下Otsu伪代码: 51 | ``` 52 | 统计各个像素值的出现次数 53 | 54 | while(遍历各种阈值的取值(0到255种可能)) 55 | { 56 | 1. 根据当前阈值对图像所有出现的像素值进行分类。 57 | 大于阈值的像素值分为一个类A,小于阈值则分为另外一个类B。(到时候你可以让A类中所有像素值为1,B类所有像素值为0。也可以让类A所有像素值为0.这都是可以你自己定,所以我就用A,B代替了。) 58 | 2. 计算类A的所有像素值的方差SA,计算类B中所有像素值的方差SB 59 | 3. 计算类A中所有像素值之和IA,计算类B中所有像素点的像素值的像素值之和IB 60 | 4. 计算当前阈值划分下两个类的像素值方差的加权和S=IA*SA+SB*IB 61 | 5. 像素值方差的加权和S是否是目前遍历到的阈值划分下最小的那一个值?如果是那就保存当前这种取值 62 | } 63 | 64 | 通过上面操作最终得到最优的阈值d。 65 | 66 | while(遍历所有像素点) 67 | { 68 | 像素值大于阈值d赋值为1, 69 | 像素值小于阈值d赋值为0 70 | } 71 | ``` 72 | ## otsu二值化实验 73 | 先用cv2中的otsu库函数看看效果 74 | ```python 75 | # 调用cv2中的otsu库 76 | img_mat = get_test_img() # 这是实验1中的那个函数 77 | img_mat = img_mat.astype(np.uint8) 78 | threshold,img_mat = cv2.threshold(img_mat,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) 79 | print(threshold) 80 | plt.imshow(img_mat,cmap='gray') 81 | ``` 82 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20191012212222452.png) 83 | 再我们自己造一个轮子Python代码从零实现otsu二值化算法 84 | ```python 85 | 86 | import numpy as np 87 | # 自己造轮子写otsu二值化算法 88 | img_mat = get_test_img() 89 | img_mat = img_mat.astype(np.uint8) 90 | ##############统计各个像素值的出现次数############## 91 | img_mat_vector = img_mat.flatten() 92 | pixel_counter = np.zeros(256) 93 | for pixel_value in img_mat_vector: 94 | pixel_counter[pixel_value] += 1 95 | 96 | ############遍历阈值的各种可能的取值############ 97 | min_variance = np.inf 98 | best_threshold = 0 99 | pixel_value = np.arange(256) 100 | for threshold in range(256): 101 | # 1. 根据阈值对各个像素值进行划分 102 | pixel_value_A = pixel_value[0:threshold] 103 | pixel_value_B = pixel_value[threshold:] 104 | # 2. 计算类A的所有像素值的方差SA,计算类B中所有像素值的方差SB 105 | totalPixelNum_A = np.sum(pixel_counter[pixel_value_A]) 106 | totalPixelNum_B = np.sum(pixel_counter[pixel_value_B]) 107 | 108 | Probability_pixelvalue_A = pixel_counter[pixel_value_A]/totalPixelNum_A 109 | Probability_pixelvalue_B = pixel_counter[pixel_value_B]/totalPixelNum_B 110 | 111 | meanPixelValue_A = np.sum(pixel_value_A * Probability_pixelvalue_A) 112 | meanPixelValue_B = np.sum(pixel_value_B * Probability_pixelvalue_B) 113 | 114 | varianceA = np.sum(Probability_pixelvalue_A * (pixel_value_A-meanPixelValue_A)**2) 115 | varianceB = np.sum(Probability_pixelvalue_B * (pixel_value_B-meanPixelValue_B)**2) 116 | 117 | current_total_variance = totalPixelNum_A*varianceA + totalPixelNum_B*varianceB 118 | if current_total_variance=best_threshold] = 1 126 | plt.imshow(img_mat,cmap='gray') 127 | ``` 128 | 最优像素值的阈值为 100 129 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20191012212345739.png) 130 | # 用实际数据实践 131 | ```python 132 | # 手写一个otsu二值化 133 | img = cv2.imread('./eight.png',cv2.IMREAD_GRAYSCALE) 134 | # 图片数据我已放到github了 135 | ##############统计各个像素值的出现次数############## 136 | img_vector = img.flatten() 137 | pixel_counter = np.zeros(256) 138 | for pixel_value in img_vector: 139 | pixel_counter[pixel_value] += 1 140 | 141 | ############遍历阈值的各种可能的取值############ 142 | min_variance = np.inf 143 | best_threshold = 0 144 | pixel_value = np.arange(256) 145 | for threshold in range(256): 146 | # 1. 根据阈值对各个像素值进行划分 147 | pixel_value_A = pixel_value[0:threshold] 148 | pixel_value_B = pixel_value[threshold:] 149 | # 2. 计算类A的所有像素值的方差SA,计算类B中所有像素值的方差SB 150 | totalPixelNum_A = np.sum(pixel_counter[pixel_value_A]) 151 | totalPixelNum_B = np.sum(pixel_counter[pixel_value_B]) 152 | 153 | Probability_pixelvalue_A = pixel_counter[pixel_value_A]/totalPixelNum_A 154 | Probability_pixelvalue_B = pixel_counter[pixel_value_B]/totalPixelNum_B 155 | 156 | meanPixelValue_A = np.sum(pixel_value_A * Probability_pixelvalue_A) 157 | meanPixelValue_B = np.sum(pixel_value_B * Probability_pixelvalue_B) 158 | 159 | varianceA = np.sum(Probability_pixelvalue_A * (pixel_value_A-meanPixelValue_A)**2) 160 | varianceB = np.sum(Probability_pixelvalue_B * (pixel_value_B-meanPixelValue_B)**2) 161 | 162 | current_total_variance = totalPixelNum_A*varianceA + totalPixelNum_B*varianceB 163 | if current_total_variance=best_threshold] = 1 171 | plt.imshow(img,cmap='gray') 172 | ``` 173 | 实验结果如下所示 174 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20191012212420495.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 175 | 176 | 它的原始图片为: 177 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20191012212521922.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_26,color_FFFFFF,t_70) 178 | -------------------------------------------------------------------------------- /RANSAC/.ipynb_checkpoints/RANSAC-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# RANSAC算法的核心思想\n", 8 | "它的核心思想是不断随机采样少量点去拟合一个模型。然后用其他数据点判断这个模型是否足够好。判断这个模型是否足够好的标准就是有多少个点满足这个模型。如果这个模型足够好的话那么用满足这个模型的那些点去重新拟合一个新的模型。(不满足条件的那些点就扔了。这个和最小二乘法不同,最小二乘法是把所有点都放一起取平均)\n", 9 | "\n", 10 | "基本概念:每次最少采样点数m(一条直线最少需要m=2个点),判断其他数据点是否满足模型时候用的最大容忍误差e(当代入到m个点所拟合的那个模型中发现误差大于e就认为是不合格的点outliers),足够好的模型的最少合格点数n(如果满足这个模型的合格点大于等于n就认为这个模型足够好了终止迭代),迭代次数K(如果一直找不到足够好的模型则不断迭代采样直到迭代次数截止为止)。\n", 11 | "\n", 12 | "一般:$n-m>5,K=2p^{(-m)}$,其中p是指随机的一个数据点落在拟合模型范围内的概率(这个概率是由最大容忍误差决定的),当然K你随便取也可以。\n", 13 | "下面是Python代码实践用RANSAC去除离群点然后拟合一条曲线:" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 13, 19 | "metadata": {}, 20 | "outputs": [ 21 | { 22 | "data": { 23 | "image/png": "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\n", 24 | "text/plain": [ 25 | "
" 26 | ] 27 | }, 28 | "metadata": { 29 | "needs_background": "light" 30 | }, 31 | "output_type": "display_data" 32 | } 33 | ], 34 | "source": [ 35 | "import numpy as np\n", 36 | "import matplotlib.pyplot as plt\n", 37 | "\n", 38 | "def generate_data():\n", 39 | " x = np.linspace(0,5,100)\n", 40 | " y = 2*x+np.random.rand(x.shape[0])\n", 41 | " # 制造离群点\n", 42 | " outlier_num = 20\n", 43 | " for i in range(outlier_num):\n", 44 | " y[np.random.randint(100)] += 10*(np.random.rand()-0.5)\n", 45 | " return x,y\n", 46 | "x,y = generate_data()\n", 47 | "plt.scatter(x,y)\n", 48 | "plt.show()" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": 15, 54 | "metadata": {}, 55 | "outputs": [ 56 | { 57 | "data": { 58 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAD8CAYAAABn919SAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvIxREBQAAFehJREFUeJzt3W+MXOV1x/Hf8TKpByhsKtwUBlxTKTJtcIvDKqJdNQpLWtOAiOWoJZESpVUkv0lbIC3R8or0ReWVqBL6qpKVpE1FBE6BuhFIcaKYKsIqJGvslIChisKfsNB6o3iTEK9gMacvdsYeX987c+f+//P9SJbt8fx5RgnHx+c5z3nM3QUAqL8NZS8AAJANAjoANAQBHQAagoAOAA1BQAeAhiCgA0BDENABoCEI6ADQEAR0AGiI84r8sEsuucS3bNlS5EcCQO0dPnz4J+6+adzzCg3oW7Zs0eLiYpEfCQC1Z2YvxXkeJRcAaAgCOgA0BAEdABqCgA4ADUFAB4CGIKADQEMU2rYIAG2y/8iS7jnwvF5dWdVl013duWOrdm7v5fZ5BHQAyMH+I0u66+Gntbp2SpK0tLKqux5+WpJyC+qUXAAgB/cceP50MB9YXTulew48n9tnEtABIAevrqxO9HgWKLkAQIQ0NfDLprtaCgnel013s17maWToABBiUANfWlmV60wNfP+RpVivv3PHVnU7U2c91u1M6c4dW3NY7ToCOgCESFsD37m9pz27tqk33ZVJ6k13tWfXNrpcAKAogzJLWLlEmqwGvnN7L9cAHkRAB4C+YKthmDxr4GkR0AGgL6zMMmxQAy/6wFBcYwO6mX1Z0s2Sjrv71f3Hfk3SPklbJL0o6c/c/UR+ywSA/I0qp0x3OzKTbt93VCbJ+48XcWAorjibov8i6cbAY/OSvu3u75b07f7vAaDWosop092O3njrbZ04uSbpTDAfyPvAUFxjA7q7f0fSTwMPf1jSV/q//oqknRmvCwAKF9VqaKaRpRgp3wNDcSVtW3yXu78mSf2ffz3qiWa228wWzWxxeXk54ccBQP6iWg1X+pn5KFXYLM19U9Td90raK0kzMzPBf6kAQOnGbXKOamOUzj4wVOaGadIM/f/M7FJJ6v98PLslAUBx4pwIDSvFWP/n4QNDaU+XppU0oH9d0if7v/6kpP/IZjkAUKw4J0LDSjFfuPUavbhwkw7Nz0mSZhcO6vZ9RwufsDgsTtvi/ZI+IOkSM3tF0t2SFiR9zcw+JellSX+a5yIBIC9xpyJGnfqMcxipqA3TsQHd3T8W8Uc3ZLwWAChc2qmI4w4jTfJeaTGcC0CrpZ2KOC77znvC4jCO/gNotUEZJeu559J6rb3ILhcCOoDWSzMV8c4dW8+poXc7U7mPyg1DQAeAFNJm+FkioANASkXPPY/CpigANAQZOoDaq+p88qIR0AHUWvBgz6j55E0P/JRcANRa3Mucy56zUgQCOoBai3t0P27grzMCOoBaizpWH3w8buCvMwI6gFqLe3Q/buCvMwI6gFqLumUouNmZdmZLHdDlAqD2Rh3sGe5subjb0cbOBq2cXGtklwsBHUBjBVsaV1bX1O1M6Qu3XtOoQD5AyQVAY7Whs2UYGTqASktzGKgNnS3DyNABVFbaw0Bt6GwZRkAHkKv9R5Y0u3BQV84/qtmFgxOdzExbMmlDZ8swSi4AcjPJnJUwo0omcUoxVZpVXgRz98I+bGZmxhcXFwv7PADlml04GHo9W2+6q0Pzc4lfP93t6I233q7ELUFFMLPD7j4z7nmUXADkJu2mZFTJxEyt6l6Ji4AOIDdpNyWjToGunFwLfX5Tu1fiooYOIDdRFyiP25QcVx+/58DzoaWYpnavxEWGDiA3ceesDIvTqti27pW4yNABZCIqq570AuVRrYqD92lb90pcBHQAqaVtTxwWdyN10r8o2oCSC4DUspyZ0rbTnVkiQweQWtysOliWuf6qTXrsueWzyiZJN1JBhg4ghcGx/qjjicNZddhm531PvHzO5qekiTdSsY4MHUAiwbp5UDCrDivLBA3KNIfm5wjgCaTK0M3sDjN7xsx+YGb3m9nGrBYGoNpGBeiwrDruoZ+2Hw5KI3FAN7OepL+WNOPuV0uakvTRrBYGoNqiAq9JoRl23E1NNj+TS1tyOU9S18zWJJ0v6dX0SwJQBeNOa1423Y08rRn22rDNziA2P9NJnKG7+5Kkf5D0sqTXJP3M3b+Z1cIAlCfNac3rr9oU+lrp3M3Oj1+3mc3PDCUen2tm75T0kKRbJa1I+jdJD7r7fYHn7Za0W5I2b9587UsvvZRqwQDyF3fsbVgmHjVnJe7IXJwr7vjcNCWXD0p6wd2X+x/4sKQ/kHRWQHf3vZL2Suvz0FN8HoCCRNXHl1ZWNbtwcOSx/jv2HZ3oPZGdNF0uL0u6zszONzOTdIOkY9ksC0CZRm1MjrvXk5Oe5UlTQ39S0oOSnpL0dP+99ma0LgAlCquPD1tdO6Xb9x0NvSOUSYjlSdXl4u53S7o7o7UAqIjhaYZh9fCBsCFcTEIsD3eKAg0V5xLlOKI2SIex4Zkv7hQFWixO22Fc48ovEhueVUFABxooy3G2w7cORWHDsxoI6EADxR1nG9fO7T0dmp/Tvbdew4ZnhTFtEWigUcfyhw3X2S/udmQmrZxci6y5s+FZbWyKAg0UNtrWJLnWNzAHGfW48bccxa+GIk6KAqiYYMa9sbNBJ06unQ7m0pkN0o2dDSMHZQUvZkb1EdCBhghm5Sura+p2pvTO8zs6cXLtrOeurp0ae9mERPdK3RDQgYaI6myJE7ij0L1SL3S5AA0xaTY93e2M7C+ne6V+yNCBGgo7BRrV2TLd7eiNt94+K1Pvdqb0uVveI0kTdbmg2uhyASoq6uh+WAdLtzOlj1zb00OHl855fM+ubZJoNawzulyAGhoE8aWV1dDOFCm6Vv7Yc8vas2tbZOAmgDcfAR2oiGDmHfy386CNcNTlE/cceJ7su8XYFAUqIizzDhpk3lHSDOFC/RHQgYqI06UyKKOMu3wiyRAu1B8lF6AEk3SpDAzaCONcPsGBoHYiQwcKFjWr/PqrNp2TeVv/595096y5KoPph1EjbTkQ1E5k6EDBknaphLlzx9bQFkYOBLUTAR3IUVhpZdSs8p3bexN1qDDOFsMI6EBOgm2Ig9LKdMiwLCl5mWTSvwTQXAR0ICdRpZVfOW+Dup2picokWV34jGZjUxTISVRp5Wera6fv6DSdu+EZlOWFz2g2MnQgJ6OugZukTDLqwmeydAwjQwdyEnYAKEkHStYXPqO5COhATnZu701UWokStVlKrzmCKLkAORourQw2Nu/Yd3SijU16zREXAR0oQFQLozR+rC295oiLgA4UIO3GJr3miIOADsSUphd81MYmPebICgEdiCFNyUSKbmG8uNtJ9b7AsFRdLmY2bWYPmtlzZnbMzH4/q4UBVTKqZLL/yJJmFw7qyvlHNbtwMPTAT1QLo5ki3xeYVNq2xX+U9A13v0rS70k6ln5JQPWMuvYtzinOqBbGlZCZLqM+DxglccnFzC6S9H5Jfy5J7v6mpDezWRZQnkkun5gyi73ZGbaxGXVJBT3mSCJNhv5bkpYl/bOZHTGzL5rZBRmtCyjFpJdPnPLgVc7rllZWR5ZgBrI6TQpI6QL6eZLeK+mf3H27pF9Kmg8+ycx2m9mimS0uLy+n+Dggf+MunxjcEGSSwkP5GXEGaWV1mhSQJPOIDGPsC81+Q9IT7r6l//s/lDTv7jdFvWZmZsYXFxcTfR6QhXEtglfOPxoaqE3SCwvr/9eeXTg48u7PML3prg7Nz6VYOdrMzA67+8y45yXO0N39fyX92MwG/za8QdKzSd8PyFucMbRx5qYk2bBkkxNFSNuH/leSvmpm75D0I0l/kX5JQHaGM/INZufUvIMbmHHmpkRtkA7KMWxyoiyp2hbd/ai7z7j777r7Tnc/kdXCgLSCGXnUBuZw9hynpj1qI5NNTpSJk6JorLANzjDB7Hnc3JQ4w7I4yo8yENDRWHHq1kmz51FBn0FaKAsXXKCxourWU2a0CKKRyNDRWFEbnARxNBUBHY2VxcUQjLZFnRDQ0Whp6tlpR+YCRaOGDkQYNTIXqCICOhBh1C1DQBUR0IEIccYAAFVCQAcicOoTdcOmKGqlyK6TLLpkgCIlHp+bBONzkUaw60Q6M5e8R0siGizu+FwydNRGWNfJIB2ZtKWQlkQ0ETV01Ma47pJJWgppSUQTEdBRG3G6S+K2FNKSiCYioKM0+48saXbhYKzLlKXwrpOguC2FtCSiiQjoKEWc6+CChi+fkNY3RIdN0lJISyKaiE1RlGJUDXvc5RKDP0/TpUJLIpqIgI5SZFHDTnuRBBdRoGkouaAU1LCB7JGhI3dhpZGoyyeoYQPJkaEjV1Gbn5JOb3ByHRyQDTJ05GKQlS+F1MQHm5+H5ucI4ECGCOjIXNjMlSAO8ADZI6Ajc2EtiUHjNj8ZnAVMjoCOzI3LvsdtfjI4C0iGTVFkblT2HWfzk8FZQDJk6JjYuHJIVEti3C4WBmcByRDQMZE45ZC0x+ovm+6Gdsdw6AgYjYCOicSdwZLkWP1wq+PgJqIBDh0B4xHQMZG8yiHBzN+V7no5oI0I6AgVVSfPqxwSdb1cb7qrQ/Nzqd4baIvUXS5mNmVmR8zskSwWhPKNmlWe1xxxNkKB9LLI0G+TdEzSRRm8FypgVJ18kC2HZe9pDgOxEQqklyqgm9nlkm6S9PeSPpPJilC6cdly2IZn2sNATF8E0ktbcrlX0mclvR31BDPbbWaLZra4vLyc8uNQhCSzytMeBhq+Xo7pi0AyiTN0M7tZ0nF3P2xmH4h6nrvvlbRXkmZmZjzqeaiOJNlyFW4gAtouTYY+K+kWM3tR0gOS5szsvkxWhVIlyZa5gQgon7mnT5r7GfrfuvvNo543MzPji4uLqT8P1RM2MneS4/4AopnZYXefGfc8+tCRibTH/QGkl0mGHhcZOgBMLm6GzvhcAGgISi44jVuCgHojoEMStwQBTUDJBZK4JQhoAgI6JDEcC2gCSi4tN6ibR/U6cTAIqA8CeouFHQYaxnAsoF4I6C0WVjcf4JYgoH4I6C00fHdnGJO4JQioIQJ6y4wrs0jUzYG6osulZUaVWSTq5kCdkaG3zKg2ROrmQL0R0Fsm6u7O3nSXujlQc5RcWubOHVvV7Uyd9RhlFqAZyNBbhrnlQHMR0FuIuzuBZiKgVxjjbAFMghuLSjIuWIf1i5skF90oQNtwp2iFxZk9HtYvPvirl1nlAMLQ5VKCOLPHx42tZVY5gCACegnizB6Pc/yeWeUAhhHQSxAVrIcfD+sXj/s+ANqJgF6COId7dm7vac+uber1g7YF3oPDQACC6HIpyXCXy8XdjsyklZNrke2JtDAC7RW3y4WAnrFJA29Ye2K3M6U9u7YRsAFIih/QKblkaBCcl1ZW5TrTXrj/yFLka+J0vABAHPShJxSWiY8KzlHZdpyOFwCIg4CeQNTBoKiLI0YF56hxtnSwAJgUJZcEojLxKQv2oqwbFZwZZwsgKwT0BKIy7lPu5wRn03oGP7twMLSWPtyeaFqf08KGKIAk6HJJYHbhYOStP4Na+tLK6ulhWgOdDaYLN543sj0RAIJy73IxsyvM7DEzO2Zmz5jZbUnfq25GlUl2bu/p0PycetNdBf+qXHvbdeLkWuwOGACYRJqSy1uS/sbdf1vSdZI+bWa/k82yqi1OmSROlwrtiQCylLjLxd1fk/Ra/9e/MLNjknqSns1obZU2fOvPoIXxjn1HT5dSorpXgmhPBJCVTNoWzWyLpO2Snszi/aoqrPdcUmgL40eu7emhw0uRrYwDtCcCyErqgG5mF0p6SNLt7v7zkD/fLWm3JG3evDntx5Umqvd8Y2dDaAvjY88ta8+ubWfNa/nlm29p7dSZyjrtiQCylKrLxcw6kh6RdMDdPz/u+XXuconqbIlikl5YuOmsxxiwBSCJ3K+gMzOT9CVJx+IE87oaBOFJgrkUXkoZrrsDQNbSdLnMSvqEpDkzO9r/8aGM1lUJw8O2okx3O5z0BFAJabpcHte59y5U2qQlj7Aj/sO6nSl97pb3nH4upRQAZWrNcK6oTU1JE09ClM6cCh28lgAOoGytmeWSZO54VEthb7qrQ/NzBHEAldL4DH3cpmZYFj78muA8FurjAKqq0QE97Hq3oGAWHnyNS6eDerDMAgBV0uiAHmdTM5hth71mEMwPzc/lsUwAyESjA/okm5rjXsPMFQBV1+iAHjUga1S2zZVwAOqq0V0uSa5340o4AHXV6Ax9UE6Z5NBPktcAQBVwBR0AVFzuV9ABAKql0SWXURhlC6BpWhnQk8x1AYCqa2XJJclcFwCoulYGdA4PAWii2pVcsqh9c3gIQBPVKkMfvkHIdab2vf/I0kTvw+EhAE1Uiwx91AjcQe17kiydw0MAmqjyAT3OCNwktW8ubAbQNJUvuYwbgStR+wYAqQYBfVz2Te0bANZVPqCPyr57013t2bWN0gkAqAYBPaoj5d5br+GiZgAYUvlNUTpSACCeygd0iY4UAIij8iUXAEA8BHQAaAgCOgA0BAEdABqCgA4ADVHoJdFmtizppQlecomkn+S0nCrje7cL37tdknzv33T3TeOeVGhAn5SZLca56bpp+N7twvdulzy/NyUXAGgIAjoANETVA/reshdQEr53u/C92yW3713pGjoAIL6qZ+gAgJgqG9DN7EYze97Mfmhm82Wvpwhm9mUzO25mPyh7LUUysyvM7DEzO2Zmz5jZbWWvqQhmttHMvmtm3+9/778re01FMbMpMztiZo+UvZYimdmLZva0mR01s8XM37+KJRczm5L0P5L+SNIrkr4n6WPu/mypC8uZmb1f0uuS/tXdry57PUUxs0slXeruT5nZr0o6LGlnC/73NkkXuPvrZtaR9Lik29z9iZKXljsz+4ykGUkXufvNZa+nKGb2oqQZd8+l/76qGfr7JP3Q3X/k7m9KekDSh0teU+7c/TuSflr2Oorm7q+5+1P9X/9C0jFJjZ+X7Ote7/+20/9RvQwrY2Z2uaSbJH2x7LU0TVUDek/Sj4d+/4pa8B84JDPbImm7pCfLXUkx+qWHo5KOS/qWu7fhe98r6bOS3i57ISVwSd80s8NmtjvrN69qQLeQxxqfubSdmV0o6SFJt7v7z8teTxHc/ZS7XyPpcknvM7NGl9rM7GZJx939cNlrKcmsu79X0p9I+nS/zJqZqgb0VyRdMfT7yyW9WtJaUIB+DfkhSV9194fLXk/R3H1F0n9KurHkpeRtVtIt/VryA5LmzOy+cpdUHHd/tf/zcUn/rvXycmaqGtC/J+ndZnalmb1D0kclfb3kNSEn/c3BL0k65u6fL3s9RTGzTWY23f91V9IHJT1X7qry5e53ufvl7r5F6/9dH3T3j5e8rEKY2QX9TX+Z2QWS/lhSph1tlQzo7v6WpL+UdEDrG2Rfc/dnyl1V/szsfkn/JWmrmb1iZp8qe00FmZX0Ca1na0f7Pz5U9qIKcKmkx8zsv7WexHzL3VvVxtcy75L0uJl9X9J3JT3q7t/I8gMq2bYIAJhcJTN0AMDkCOgA0BAEdABoCAI6ADQEAR0AGoKADgANQUAHgIYgoANAQ/w/7h47kyaTEyQAAAAASUVORK5CYII=\n", 59 | "text/plain": [ 60 | "
" 61 | ] 62 | }, 63 | "metadata": { 64 | "needs_background": "light" 65 | }, 66 | "output_type": "display_data" 67 | } 68 | ], 69 | "source": [ 70 | "def f_two_point_line(x1,y1,x2,y2):\n", 71 | " '''\n", 72 | " 两点法确立一个直线,返回所拟合的直线斜率和截距\n", 73 | " '''\n", 74 | " k = (y2-y1)/(x2-x1)\n", 75 | " b = y1 - k*x1\n", 76 | " return k,b\n", 77 | " \n", 78 | "\n", 79 | "def RANSAC_filte_outlier(x,y):\n", 80 | " '''\n", 81 | " 删除离群点\n", 82 | " '''\n", 83 | " m = 2 #确定一条直线最少每次需要取2个点\n", 84 | " e = 0.5 # 只要某个点离所拟合的直线距离小于0.5那么认为不是离群点\n", 85 | " n = 20 # 只要某个拟合的直线能够让20个点足够接近它那么认为当前直线是比较理想的直线\n", 86 | " K = int(2 * (1/5)**(-m)) # 最大迭代次数,这个可以随便设置的\n", 87 | " for _ in range(K):\n", 88 | " # 1. 随机选m个点来拟合一条直线这里是两个点所以用两点法确定一条直线\n", 89 | " p1_index = np.random.randint(x.shape[0])\n", 90 | " p2_index = np.random.randint(x.shape[0])\n", 91 | " \n", 92 | " # 两点法确定一条直线\n", 93 | " p1_x = x[p1_index]\n", 94 | " p1_y = y[p1_index]\n", 95 | " p2_x = x[p2_index]\n", 96 | " p2_y = y[p2_index]\n", 97 | " \n", 98 | " k,b = f_two_point_line(p1_x,p1_y,p2_x,p2_y)\n", 99 | " y_predict = k*x + b\n", 100 | " inliner_bool = (np.abs(y_predict-y)<0.5)\n", 101 | " if np.sum(inliner_bool)>=20:\n", 102 | " return x[inliner_bool],y[inliner_bool]\n", 103 | " \n", 104 | " return NULL\n", 105 | "x_filted,y_filted = RANSAC_filte_outlier(x,y)\n", 106 | "plt.scatter(x_filted,y_filted)\n", 107 | "plt.show()" 108 | ] 109 | }, 110 | { 111 | "cell_type": "markdown", 112 | "metadata": {}, 113 | "source": [ 114 | "可以看到上图许多噪声点已经被去除。拟合这条曲线可以用最小二乘法求解。" 115 | ] 116 | } 117 | ], 118 | "metadata": { 119 | "kernelspec": { 120 | "display_name": "Python 3", 121 | "language": "python", 122 | "name": "python3" 123 | }, 124 | "language_info": { 125 | "codemirror_mode": { 126 | "name": "ipython", 127 | "version": 3 128 | }, 129 | "file_extension": ".py", 130 | "mimetype": "text/x-python", 131 | "name": "python", 132 | "nbconvert_exporter": "python", 133 | "pygments_lexer": "ipython3", 134 | "version": "3.7.0" 135 | } 136 | }, 137 | "nbformat": 4, 138 | "nbformat_minor": 2 139 | } 140 | -------------------------------------------------------------------------------- /RANSAC/RANSAC.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# RANSAC算法的核心思想\n", 8 | "它的核心思想是不断随机采样少量点去拟合一个模型。然后用其他数据点判断这个模型是否足够好。判断这个模型是否足够好的标准就是有多少个点满足这个模型。如果这个模型足够好的话那么用满足这个模型的那些点去重新拟合一个新的模型。(不满足条件的那些点就扔了。这个和最小二乘法不同,最小二乘法是把所有点都放一起取平均)\n", 9 | "\n", 10 | "基本概念:每次最少采样点数m(一条直线最少需要m=2个点),判断其他数据点是否满足模型时候用的最大容忍误差e(当代入到m个点所拟合的那个模型中发现误差大于e就认为是不合格的点outliers),足够好的模型的最少合格点数n(如果满足这个模型的合格点大于等于n就认为这个模型足够好了终止迭代),迭代次数K(如果一直找不到足够好的模型则不断迭代采样直到迭代次数截止为止)。\n", 11 | "\n", 12 | "一般:$n-m>5,K=2p^{(-m)}$,其中p是指随机的一个数据点落在拟合模型范围内的概率(这个概率是由最大容忍误差决定的),当然K你随便取也可以。\n", 13 | "下面是Python代码实践用RANSAC去除离群点然后拟合一条曲线:" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 13, 19 | "metadata": {}, 20 | "outputs": [ 21 | { 22 | "data": { 23 | "image/png": "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\n", 24 | "text/plain": [ 25 | "
" 26 | ] 27 | }, 28 | "metadata": { 29 | "needs_background": "light" 30 | }, 31 | "output_type": "display_data" 32 | } 33 | ], 34 | "source": [ 35 | "import numpy as np\n", 36 | "import matplotlib.pyplot as plt\n", 37 | "\n", 38 | "def generate_data():\n", 39 | " x = np.linspace(0,5,100)\n", 40 | " y = 2*x+np.random.rand(x.shape[0])\n", 41 | " # 制造离群点\n", 42 | " outlier_num = 20\n", 43 | " for i in range(outlier_num):\n", 44 | " y[np.random.randint(100)] += 10*(np.random.rand()-0.5)\n", 45 | " return x,y\n", 46 | "x,y = generate_data()\n", 47 | "plt.scatter(x,y)\n", 48 | "plt.show()" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": 15, 54 | "metadata": {}, 55 | "outputs": [ 56 | { 57 | "data": { 58 | "image/png": "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\n", 59 | "text/plain": [ 60 | "
" 61 | ] 62 | }, 63 | "metadata": { 64 | "needs_background": "light" 65 | }, 66 | "output_type": "display_data" 67 | } 68 | ], 69 | "source": [ 70 | "def f_two_point_line(x1,y1,x2,y2):\n", 71 | " '''\n", 72 | " 两点法确立一个直线,返回所拟合的直线斜率和截距\n", 73 | " '''\n", 74 | " k = (y2-y1)/(x2-x1)\n", 75 | " b = y1 - k*x1\n", 76 | " return k,b\n", 77 | " \n", 78 | "\n", 79 | "def RANSAC_filte_outlier(x,y):\n", 80 | " '''\n", 81 | " 删除离群点\n", 82 | " '''\n", 83 | " m = 2 #确定一条直线最少每次需要取2个点\n", 84 | " e = 0.5 # 只要某个点离所拟合的直线距离小于0.5那么认为不是离群点\n", 85 | " n = 20 # 只要某个拟合的直线能够让20个点足够接近它那么认为当前直线是比较理想的直线\n", 86 | " K = int(2 * (1/5)**(-m)) # 最大迭代次数,这个可以随便设置的\n", 87 | " for _ in range(K):\n", 88 | " # 1. 随机选m个点来拟合一条直线这里是两个点所以用两点法确定一条直线\n", 89 | " p1_index = np.random.randint(x.shape[0])\n", 90 | " p2_index = np.random.randint(x.shape[0])\n", 91 | " \n", 92 | " # 两点法确定一条直线\n", 93 | " p1_x = x[p1_index]\n", 94 | " p1_y = y[p1_index]\n", 95 | " p2_x = x[p2_index]\n", 96 | " p2_y = y[p2_index]\n", 97 | " \n", 98 | " k,b = f_two_point_line(p1_x,p1_y,p2_x,p2_y)\n", 99 | " y_predict = k*x + b\n", 100 | " inliner_bool = (np.abs(y_predict-y)<0.5)\n", 101 | " if np.sum(inliner_bool)>=20:\n", 102 | " return x[inliner_bool],y[inliner_bool]\n", 103 | " \n", 104 | " return NULL\n", 105 | "x_filted,y_filted = RANSAC_filte_outlier(x,y)\n", 106 | "plt.scatter(x_filted,y_filted)\n", 107 | "plt.show()" 108 | ] 109 | }, 110 | { 111 | "cell_type": "markdown", 112 | "metadata": {}, 113 | "source": [ 114 | "可以看到上图许多噪声点已经被去除。拟合这条曲线可以用最小二乘法求解。" 115 | ] 116 | } 117 | ], 118 | "metadata": { 119 | "kernelspec": { 120 | "display_name": "Python 3", 121 | "language": "python", 122 | "name": "python3" 123 | }, 124 | "language_info": { 125 | "codemirror_mode": { 126 | "name": "ipython", 127 | "version": 3 128 | }, 129 | "file_extension": ".py", 130 | "mimetype": "text/x-python", 131 | "name": "python", 132 | "nbconvert_exporter": "python", 133 | "pygments_lexer": "ipython3", 134 | "version": "3.7.0" 135 | } 136 | }, 137 | "nbformat": 4, 138 | "nbformat_minor": 2 139 | } 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Easy SLAM and Robotic Tutorial Python Implement 2 | 3 | # 简单的SLAM与机器人教程Python代码实现 4 | **欢迎点小星星收藏本github项目**,如果你想增加内容欢迎fork,然后再在自己项目下修改,然后提交合并请求。或者发私信给[知乎@司南牧](https://www.zhihu.com/people/yuanmuou/activities),please feel free。 5 | 项目作者:[知乎@司南牧](https://www.zhihu.com/people/yuanmuou/activities) 6 | # How to learn SLAM and Robotic 7 | 8 | [![Build Status](https://travis-ci.org/AtsushiSakai/PythonRobotics.svg?branch=master)](https://travis-ci.org/varyshare/easy_slam_tutorial) 9 | [![Documentation Status](https://readthedocs.org/projects/pythonrobotics/badge/?version=latest)](https://github.com/varyshare/easy_slam_tutorial/blob/master/README.md) 10 | [![Build status](https://ci.appveyor.com/api/projects/status/sb279kxuv1be391g?svg=true)](https://ci.appveyor.com/project/varyshare/easy_slam_tutorial) 11 | [![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/AtsushiSakai/PythonRobotics.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/AtsushiSakai/PythonRobotics/context:python) 12 | [![CodeFactor](https://www.codefactor.io/repository/github/atsushisakai/pythonrobotics/badge/master)](https://www.codefactor.io/repository/github/varyshare/easy_slam_tutorial/overview/master) 13 | [![tokei](https://tokei.rs/b1/github/varyshare/easy_slam_tutorial/)](https://github.com/varyshare/easy_slam_tutorial) 14 | [![Say Thanks!](https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg)](https://www.zhihu.com/people/yuanmuou/activities) 15 | 16 | 简单的从零开始实现视觉SLAM理论与实践教程,使用Python实现。包括:ORB特征点提取,对极几何,视觉里程计后端优化,实时三维重建地图。Otsu二值化、贝叶斯滤波、快速连通域标记算法,带标记的目标跟踪实践 17 | # A easy SLAM practical tutorial (Python). 18 | 19 | ## 目录 20 | 21 | ## [特征提取](./feature_extract/) 22 | ### [从零开始实现FAST特征点提取算法教程](./feature_extract/从零开始实现FAST特征点提取算法教程.md) 23 | [FAST教程](./feature_extract/从零开始实现FAST特征点提取算法教程.md) [代码](./feature_extract/FAST_feature_extraction.py) 24 | 25 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190722103253875.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 26 | 27 | ### 计算机图形学Bresenham画圆法Python实现 28 | [教程](./feature_extract/Bresenham布雷森汉姆算法画圆教程.md) [代码](./feature_extract/bresenham_circle.py) 29 | 30 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190721174903599.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 31 | 32 | ### ORB特征提取Python调用OpenCV2实现 33 | ORB特征提取主要是[FAST提取特征点](./feature_extract/从零开始实现FAST特征点提取算法教程.md)+[BRIEF算法](https://blog.csdn.net/varyshare/article/details/96568030)提取周围信息 34 | [代码](./feature_extract/ORB_feature_extract.py) 35 | 36 | ![1563794343832](./img/orb_效果图.png) 37 | 38 | ## Otsu二值化算法/大津算法(Otsu's Method Algorithm ) 39 | [教程+Python源代码](./Otsu's_Method_algorithm/如何理解图像处理中的Otsu's 二值化算法(大津算法)Python编程实践.md) 40 | ![在这里插入图片描述](./Otsu's_Method_algorithm/eight_二值化效果.png) 41 | # 高斯模糊代码+教程 42 | [教程+Python源代码](./image_smooth_blur/如何理解高斯模糊原理与具体Python编程实现.ipynb) 43 | ![高斯模糊效果](./image_smooth_blur/高斯模糊效果.png) 44 | 45 | ### Fast Labeling快速标记连通物体检测与标记目标追踪 46 | [连通组件检测与标记目标追踪代码与教程](./Connected_Components) 47 | [连通组件检测与标记目标追踪真实场景实践](./Connected_Components/real_scene_practice) 48 | 49 | # 十分钟如何理解RANSAC算法Python实践 50 | 51 | [RANSAC教程与代码](RANSAC) 52 | 53 | 54 | # 机器人模拟 55 | 56 | ## [两连杆机械臂机器人(2DOF)模拟](./joint_robot_simulation/) 57 | 58 | 鼠标选定屏幕上一点,然后求逆解进行运动Python实现代码 59 | 60 | 代码地址(同一个文件夹):[two_joint_arm_robot.py](./joint_robot_simulation/two_joint_arm_robot.py) 61 | 62 | 下面是效果图,**打开你的编辑器跟着我写的代码实践吧,你的star收藏和关注是我持续分享的动力** 。 63 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190724160425592.gif) 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /Robot Mapping WS SLAM/ppt/homework1_octave_tutorial_framework.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/Robot Mapping WS SLAM/ppt/homework1_octave_tutorial_framework.tar.gz -------------------------------------------------------------------------------- /Robot Mapping WS SLAM/ppt/homework1_octave_tutorial_framework/octave_tutorial_framework/data/world.dat: -------------------------------------------------------------------------------- 1 | 1 2 1 2 | 2 0 4 3 | 3 2 7 4 | 4 9 2 5 | 5 10 5 6 | 6 9 8 7 | 7 5 5 8 | 8 5 3 9 | 9 5 9 10 | -------------------------------------------------------------------------------- /Robot Mapping WS SLAM/ppt/homework1_octave_tutorial_framework/octave_tutorial_framework/octave/main.m: -------------------------------------------------------------------------------- 1 | % This script runs the main loop and calls all the required 2 | % functions in the correct order. 3 | % 4 | % You can disable the plotting or change the number of steps the filter 5 | % runs for to ease the debugging. You should however not change the order 6 | % or calls of any of the other lines, as it might break the framework. 7 | % 8 | % If you are unsure about the input and return values of functions you 9 | % should read their documentation which tells you the expected dimensions. 10 | 11 | % Make tools available 12 | addpath('tools'); 13 | 14 | % Read world data, i.e. landmarks. 15 | landmarks = read_world('../data/world.dat'); 16 | % Read sensor readings, i.e. odometry and range-bearing sensor 17 | data = read_data('../data/sensor_data.dat'); 18 | 19 | % Initialize belief 20 | % x: 3x1 vector representing the robot pose [x; y; theta] 21 | x = zeros(3, 1); 22 | 23 | % Iterate over odometry commands and update the robot pose 24 | % according to the motion model 25 | for t = 1:size(data.timestep, 2) 26 | 27 | % Update the pose of the robot based on the motion model 28 | x = motion_command(x, data.timestep(t).odometry); 29 | 30 | %Generate visualization plots of the current state 31 | plot_state(x, landmarks, t, data.timestep(t).sensor); 32 | 33 | disp("Current robot pose:") 34 | disp("x = "), disp(x) 35 | endfor 36 | 37 | % Display the final state estimate 38 | disp("Final robot pose:") 39 | disp("x = "), disp(x) 40 | -------------------------------------------------------------------------------- /Robot Mapping WS SLAM/ppt/homework1_octave_tutorial_framework/octave_tutorial_framework/octave/motion_command.m: -------------------------------------------------------------------------------- 1 | function [x] = motion_command(x, u) 2 | % Updates the robot pose according to the motion model 3 | % x: 3x1 vector representing the robot pose [x; y; theta] 4 | % u: struct containing odometry reading (r1, t, r2). 5 | % Use u.r1, u.t, and u.r2 to access the rotation and translation values 6 | 7 | %TODO: update x according to the motion represented by u 8 | 9 | %TODO: remember to normalize theta by calling normalize_angle for x(3) 10 | 11 | end 12 | -------------------------------------------------------------------------------- /Robot Mapping WS SLAM/ppt/homework1_octave_tutorial_framework/octave_tutorial_framework/octave/octavehelp.m: -------------------------------------------------------------------------------- 1 | % GNU Octave is a (programmable) calculator and is very good at performing 2 | % matrix operations. The basic syntax is the same as MATLAB's. At Octave's 3 | % command prompt, a command can be entered. If you end a line with a semicolon, 4 | % the output is suppressed. If the output is longer than one screen, you might 5 | % have to press 'q' to get back to the prompt. Everything you enter at the 6 | % prompt can as well be written into a script file with extension .m (like this 7 | % one). Scripts can be executed by calling its name. Comments are done with the 8 | % '%' sign. 9 | 10 | %%%%%% GETTING HELP 11 | % The command 'help ' displays the help text for the desired 12 | % command. 13 | help rand 14 | 15 | % Search for the given string in the help text of all functions. 16 | lookfor eigenvalues 17 | 18 | % List all currently defined variables 19 | who 20 | 21 | % Delete all variables defined until now 22 | clear 23 | 24 | % Clear screen 25 | clc 26 | 27 | %Turn off output pagination 28 | more off 29 | 30 | %%%%%% DATA ENTRY 31 | 32 | % Vectors and matrices are entered using square brackets [ ]. 33 | % Elements are seperated by a space or a comma, a new row is 34 | % started with a semicolon: 35 | 36 | % A 1x4 row vector 37 | a = [ 1, 2, 3, 4 ] 38 | a2 = [ 1 2 3 4 ] 39 | 40 | % A 2x2 matrix 41 | A = [ 1, 2; 3, 4 ] 42 | A2 = [ 1 2; 3 4 ] 43 | % Get the size of a matrix 44 | size(A) 45 | size(A,1) 46 | size(A,2) 47 | 48 | %%%%%% DATA GENERATION 49 | 50 | % Generate a row vector with elements 1, ..., 10 51 | b = [1:10] 52 | 53 | % Generate a row vector with elements 1, 1.1, 1.2, ..., 10 54 | c = [1:0.1:10] 55 | % Get the length of a vector 56 | length(c) 57 | 58 | 59 | % Create a 2x3 matrix filled with zeros or ones respectively 60 | C = zeros(2,3) 61 | D = ones(2,3) 62 | 63 | % Create a 2x2 identity matrix 64 | E = eye(2) 65 | 66 | %Create matrix from other matrices/vectors (dimensions must agree) 67 | X = [c;c] 68 | Y = [A2 A2] 69 | help repmat 70 | Z = repmat(A,2,3) 71 | 72 | % Create a column vector of 10 uniformly distributed random numbers 73 | % between 5 and 15. 74 | u = unifrnd(5, 15, 10, 1) 75 | % Create a 5x5 matrix with normally distributed random variables with a 76 | % mean of 2.5 and a sigma of 5.0. 77 | N = normrnd(2.5, 5.0, 5, 5) 78 | 79 | %%%%%% DATA ACCESS 80 | 81 | % All indices in Octave start with 1, as opposed to 0 as usual in other 82 | % programming languages. 83 | 84 | % Retrieve the element in row 1 and column 2 85 | A(1,2) 86 | 87 | % Retrieve all elements of row 1 in the matrix 88 | A(1,:) 89 | 90 | % Retrieve all elements of column 2 in the matrix 91 | A(:,2) 92 | 93 | % Retrieve a submatrix 94 | Z2 = Z(1:2,3:6) 95 | 96 | % Retrieve every third element of a vector 97 | x = [1:20] 98 | x2 = x(1:3:length(x)) 99 | 100 | % Saving and loading data 101 | save A 102 | clear A 103 | load A 104 | 105 | %%%%%% MATRIX OPERATIONS 106 | 107 | % Transpose 108 | A' 109 | 110 | % Matrix addition, subtraction, multiplication and inversion 111 | F = A + E + C * D' 112 | G = F * inv(F) 113 | 114 | % Element-wise operations 115 | H = A * 2 + A .* E + A .^ 2 116 | 117 | % Matrix-scalar addition/multiplication 118 | threes = 3 + zeros(3) 119 | tens = 10*ones(3) 120 | 121 | %%%%%% OTHER FUNCTIONS 122 | % Can be used on scalars as well as matrices. When applied to matrices the 123 | % operations are performed elementwise. 124 | a = 2 125 | b = 3 126 | v = [2 4 6] 127 | w = [3 5 7] 128 | sin(a) 129 | sin(v) 130 | cos(a) 131 | cos(v) 132 | atan2(a, b) 133 | atan2(v, w) 134 | sqrt(a) 135 | sqrt(v) 136 | 137 | %%%%%% PROGRAMMING CONSTRUCTS 138 | 139 | % Functions 140 | % Functions have the following layout: 141 | % function [retval1, retval2, ...] (arg1, arg2, ...) 142 | % 143 | % end 144 | % Returning values is performed by assigning values to the return values 145 | % defined in the header of the function. 146 | function y = add_two_numbers(a, b) 147 | y = a + b; 148 | end 149 | 150 | % For loops 151 | for i=[1:10] 152 | if mod(i,2) == 0 153 | disp(['even: ', num2str(i)]) 154 | else 155 | disp(['odd: ', num2str(i)]) 156 | endif 157 | endfor 158 | 159 | % Always try to vectorize operations when possible! 160 | v1 = [1:10] 161 | v2 = [3:12] 162 | dotProduct = 0 163 | 164 | for i=1:length(v1) 165 | dotProduct = dotProduct + v1(i)*v2(i) 166 | endfor 167 | % Better: 168 | dotProduct = sum(v1.*v2) 169 | 170 | 171 | %%%%%% BASIC PLOTTING 172 | 173 | % Create a vector of values in the range [1, 10] with an increment of 0.1 174 | % and suppress the output (semicolon at the end). 175 | x = -2*pi:0.1:2*pi; 176 | % Compute sin() for all elements of the vector 177 | y = sin(x); 178 | 179 | % Close all existing plot windows 180 | close all 181 | % Plot the the values of x against those in y 182 | plot(x, y) 183 | % Draw following plots into the same figure. If this is not set subsequent 184 | % plots erase the previously generated plots. 185 | hold on 186 | % Plot the cosine of the data points in green (g) with markers (+) 187 | % instead of lines. 188 | plot(x, cos(x), '+g'); 189 | % Plot a blue point 190 | plot(2, 0.5, 'ob'); 191 | 192 | title("sine and cosine") 193 | xlabel('x (rad)') 194 | ylabel('y = f(x)') 195 | 196 | % Add a grid 197 | grid on 198 | %Useful options: 'markersize', 'linewidth' 199 | %See also the commands: xlabel, ylabel, title 200 | 201 | % Save the complete plot to a file. 202 | print('/tmp/plot.png', '-dpng') 203 | 204 | -------------------------------------------------------------------------------- /Robot Mapping WS SLAM/ppt/homework1_octave_tutorial_framework/octave_tutorial_framework/octave/tools/drawellipse.m: -------------------------------------------------------------------------------- 1 | %DRAWELLIPSE Draw ellipse. 2 | % DRAWELLIPSE(X,A,B,COLOR) draws an ellipse at X = [x y theta] 3 | % with half axes A and B. Theta is the inclination angle of A, 4 | % regardless if A is smaller or greater than B. COLOR is a 5 | % [r g b]-vector or a color string such as 'r' or 'g'. 6 | % 7 | % H = DRAWELLIPSE(...) returns the graphic handle H. 8 | % 9 | % See also DRAWPROBELLIPSE. 10 | 11 | % v.1.0-v.1.1, Aug.97-Jan.03, Kai Arras, ASL-EPFL 12 | % v.1.2, 03.12.03, Kai Arras, CAS-KTH: (x,a,b) interface 13 | 14 | 15 | function h = drawellipse(x,a,b,color); 16 | 17 | % Constants 18 | NPOINTS = 100; % point density or resolution 19 | 20 | % Compose point vector 21 | ivec = 0:2*pi/NPOINTS:2*pi; % index vector 22 | p(1,:) = a*cos(ivec); % 2 x n matrix which 23 | p(2,:) = b*sin(ivec); % hold ellipse points 24 | 25 | % Translate and rotate 26 | xo = x(1); yo = x(2); angle = x(3); 27 | R = [cos(angle) -sin(angle); sin(angle) cos(angle)]; 28 | T = [xo; yo]*ones(1,length(ivec)); 29 | p = R*p + T; 30 | 31 | % Plot 32 | h = plot(p(1,:),p(2,:),'Color',color, 'linewidth', 2); 33 | -------------------------------------------------------------------------------- /Robot Mapping WS SLAM/ppt/homework1_octave_tutorial_framework/octave_tutorial_framework/octave/tools/drawprobellipse.m: -------------------------------------------------------------------------------- 1 | %DRAWPROBELLIPSE Draw elliptic probability region of a Gaussian in 2D. 2 | % DRAWPROBELLIPSE(X,C,ALPHA,COLOR) draws the elliptic iso-probabi- 3 | % lity contour of a Gaussian distributed bivariate random vector X 4 | % at the significance level ALPHA. The ellipse is centered at X = 5 | % [x; y] where C is the associated 2x2 covariance matrix. COLOR is 6 | % a [r g b]-vector or a color string such as 'r' or 'g'. 7 | % 8 | % X and C can also be of size 3x1 and 3x3 respectively. 9 | % 10 | % For proper scaling, the function CHI2INVTABLE is employed to 11 | % avoid the use of CHI2INV from the Matlab statistics toolbox. 12 | % 13 | % In case of a negative definite matrix C, the ellipse collapses 14 | % to a line which is drawn instead. 15 | % 16 | % H = DRAWPROBELLIPSE(...) returns the graphic handle H. 17 | % 18 | % See also DRAWELLIPSE, CHI2INVTABLE, CHI2INV. 19 | 20 | % v.1.0-v.1.3, 97-Jan.03, Kai Arras, ASL-EPFL 21 | % v.1.4, 03.12.03, Kai Arras, CAS-KTH: toolbox version 22 | 23 | 24 | function h = drawprobellipse(x,C,alpha,color); 25 | 26 | % Calculate unscaled half axes 27 | sxx = C(1,1); syy = C(2,2); sxy = C(1,2); 28 | a = sqrt(0.5*(sxx+syy+sqrt((sxx-syy)^2+4*sxy^2))); % always greater 29 | b = sqrt(0.5*(sxx+syy-sqrt((sxx-syy)^2+4*sxy^2))); % always smaller 30 | 31 | % Remove imaginary parts in case of neg. definite C 32 | if ~isreal(a), a = real(a); end; 33 | if ~isreal(b), b = real(b); end; 34 | 35 | % Scaling in order to reflect specified probability 36 | a = a*sqrt(chi2invtable(alpha,2)); 37 | b = b*sqrt(chi2invtable(alpha,2)); 38 | 39 | % Look where the greater half axis belongs to 40 | if sxx < syy, swap = a; a = b; b = swap; end; 41 | 42 | % Calculate inclination (numerically stable) 43 | if sxx ~= syy, 44 | angle = 0.5*atan(2*sxy/(sxx-syy)); 45 | elseif sxy == 0, 46 | angle = 0; % angle doesn't matter 47 | elseif sxy > 0, 48 | angle = pi/4; 49 | elseif sxy < 0, 50 | angle = -pi/4; 51 | end; 52 | x(3) = angle; 53 | 54 | % Draw ellipse 55 | h = drawellipse(x,a,b,color); -------------------------------------------------------------------------------- /Robot Mapping WS SLAM/ppt/homework1_octave_tutorial_framework/octave_tutorial_framework/octave/tools/drawrobot.m: -------------------------------------------------------------------------------- 1 | %DRAWROBOT Draw robot. 2 | % DRAWROBOT(X,COLOR) draws a robot at pose X = [x y theta] such 3 | % that the robot reference frame is attached to the center of 4 | % the wheelbase with the x-axis looking forward. COLOR is a 5 | % [r g b]-vector or a color string such as 'r' or 'g'. 6 | % 7 | % DRAWROBOT(X,COLOR,TYPE) draws a robot of type TYPE. Five 8 | % different models are implemented: 9 | % TYPE = 0 draws only a cross with orientation theta 10 | % TYPE = 1 is a differential drive robot without contour 11 | % TYPE = 2 is a differential drive robot with round shape 12 | % TYPE = 3 is a round shaped robot with a line at theta 13 | % TYPE = 4 is a differential drive robot with rectangular shape 14 | % TYPE = 5 is a rectangular shaped robot with a line at theta 15 | % 16 | % DRAWROBOT(X,COLOR,TYPE,W,L) draws a robot of type TYPE with 17 | % width W and length L in [m]. 18 | % 19 | % H = DRAWROBOT(...) returns a column vector of handles to all 20 | % graphic objects of the robot drawing. Remember that not all 21 | % graphic properties apply to all types of graphic objects. Use 22 | % FINDOBJ to find and access the individual objects. 23 | % 24 | % See also DRAWRECT, DRAWARROW, FINDOBJ, PLOT. 25 | 26 | % v.1.0, 16.06.03, Kai Arras, ASL-EPFL 27 | % v.1.1, 12.10.03, Kai Arras, ASL-EPFL: uses drawrect 28 | % v.1.2, 03.12.03, Kai Arras, CAS-KTH : types implemented 29 | 30 | 31 | function h = drawrobot(varargin); 32 | 33 | % Constants 34 | DEFT = 2; % default robot type 35 | DEFB = 0.4; % default robot width in [m], defines y-dir. of {R} 36 | WT = 0.03; % wheel thickness in [m] 37 | DEFL = DEFB+0.2; % default robot length in [m] 38 | WD = 0.2; % wheel diameter in [m] 39 | RR = WT/2; % wheel roundness radius in [m] 40 | RRR = 0.04; % roundness radius for rectangular robots in [m] 41 | HL = 0.09; % arrow head length in [m] 42 | CS = 0.1; % cross size in [m], showing the {R} origin 43 | 44 | % Input argument check 45 | inputerr = 0; 46 | switch nargin, 47 | case 2, 48 | xvec = varargin{1}; 49 | color = varargin{2}; 50 | type = DEFT; 51 | B = DEFB; 52 | L = DEFL; 53 | case 3; 54 | xvec = varargin{1}; 55 | color = varargin{2}; 56 | type = varargin{3}; 57 | B = DEFB; 58 | L = DEFL; 59 | case 5; 60 | xvec = varargin{1}; 61 | color = varargin{2}; 62 | type = varargin{3}; 63 | B = varargin{4}; 64 | L = varargin{5}; 65 | otherwise 66 | inputerr = 1; 67 | end; 68 | 69 | % Main switch statement 70 | if ~inputerr, 71 | x = xvec(1); y = xvec(2); theta = xvec(3); 72 | T = [x; y]; 73 | R = [cos(theta), -sin(theta); sin(theta), cos(theta)]; 74 | 75 | switch type 76 | case 0, 77 | % Draw origin cross 78 | p = R*[CS, -CS, 0, 0; 0, 0, -CS, CS] + T*ones(1,4); % horiz. line 79 | h = plot(p(1,1:2),p(2,1:2),'Color',color,p(1,3:4),p(2,3:4),'Color',color); 80 | 81 | case 1, 82 | % Draw wheel pair with axis and arrow 83 | xlw = [x+B/2*cos(theta+pi/2); y+B/2*sin(theta+pi/2); theta]; 84 | h1 = drawrect(xlw,WD,WT,RR,1,color); % left wheel 85 | xlw = [x-B/2*cos(theta+pi/2); y-B/2*sin(theta+pi/2); theta]; 86 | h2 = drawrect(xlw,WD,WT,RR,1,color); % right wheel 87 | % Draw axis cross with arrow 88 | p = R*[0, 0; -B/2+WT/2, B/2-WT/2] + T*ones(1,2); 89 | h3 = plot(p(1,:),p(2,:),'Color',color); 90 | p = R*[L/2; 0] + T; 91 | h4 = drawarrow(T,p,1,HL,color); 92 | h = cat(1,h1,h2,h3,h4); 93 | 94 | case 2, 95 | % Draw wheel pair with axis and arrow 96 | xlw = [x+B/2*cos(theta+pi/2); y+B/2*sin(theta+pi/2); theta]; 97 | h1 = drawrect(xlw,WD,WT,RR,1,color); % left wheel 98 | xlw = [x-B/2*cos(theta+pi/2); y-B/2*sin(theta+pi/2); theta]; 99 | h2 = drawrect(xlw,WD,WT,RR,1,color); % right wheel 100 | % Draw axis cross with arrow 101 | p = R*[0, 0; -B/2+WT/2, B/2-WT/2] + T*ones(1,2); 102 | h3 = plot(p(1,:),p(2,:),'Color',color); 103 | p = R*[(B+WT)/2; 0] + T; 104 | h4 = drawarrow(T,p,1,HL,color); 105 | % Draw circular contour 106 | radius = (B+WT)/2; 107 | h5 = drawellipse(xvec,radius,radius,color); 108 | h = cat(1,h1,h2,h3,h4,h5); 109 | 110 | case 3, 111 | % Draw circular contour 112 | radius = (B+WT)/2; 113 | h1 = drawellipse(xvec,radius,radius,color); 114 | % Draw line with orientation theta with length radius 115 | p = R*[(B+WT)/2;0] + T; 116 | h2 = plot([T(1) p(1)],[T(2) p(2)],'Color',color,'linewidth',2); 117 | h = cat(1,h1,h2); 118 | 119 | case 4, 120 | % Draw wheel pair with axis and arrow 121 | xlw = [x+B/2*cos(theta+pi/2); y+B/2*sin(theta+pi/2); theta]; 122 | h1 = drawrect(xlw,WD,WT,RR,1,color); % left wheel 123 | xlw = [x-B/2*cos(theta+pi/2); y-B/2*sin(theta+pi/2); theta]; 124 | h2 = drawrect(xlw,WD,WT,RR,1,color); % right wheel 125 | % Draw axis cross with arrow 126 | p = R*[0, 0; -B/2+WT/2, B/2-WT/2] + T*ones(1,2); 127 | h3 = plot(p(1,:),p(2,:),'Color',color); 128 | p = R*[L/2; 0] + T; 129 | h4 = drawarrow(T,p,1,HL,color); 130 | % Draw rectangular contour 131 | h5 = drawrect(xvec,L,B,RRR,0,color); 132 | h = cat(1,h1,h2,h3,h4,h5); 133 | 134 | case 5, 135 | % Draw rectangular contour 136 | h1 = drawrect(xvec,L,B,RRR,0,color); 137 | % Draw line with orientation theta with length L 138 | p = R*[L/2; 0] + T; 139 | h2 = plot([T(1) p(1)],[T(2) p(2)],'Color',color,'linewidth',2); 140 | h = cat(1,h1,h2); 141 | 142 | otherwise 143 | disp('drawrobot: Unsupported robot type'); h = []; 144 | end; 145 | else 146 | disp('drawrobot: Wrong number of input arguments'); h = []; 147 | end; 148 | -------------------------------------------------------------------------------- /Robot Mapping WS SLAM/ppt/homework1_octave_tutorial_framework/octave_tutorial_framework/octave/tools/normalize_all_bearings.m: -------------------------------------------------------------------------------- 1 | function [zNorm] = normalize_all_bearings(z) 2 | %Go over the observations vector and normalize the bearings 3 | 4 | for(i=2:3:length(z)) 5 | z(i) = normalize_angle(z(i)); 6 | endfor 7 | zNorm = z; 8 | -------------------------------------------------------------------------------- /Robot Mapping WS SLAM/ppt/homework1_octave_tutorial_framework/octave_tutorial_framework/octave/tools/normalize_angle.m: -------------------------------------------------------------------------------- 1 | function [phiNorm] = normalize_angle(phi) 2 | %Normalize phi to be between -pi and pi 3 | 4 | while(phi>pi) 5 | phi = phi - 2*pi; 6 | endwhile 7 | 8 | while(phi<-pi) 9 | phi = phi + 2*pi; 10 | endwhile 11 | phiNorm = phi; 12 | 13 | end 14 | -------------------------------------------------------------------------------- /Robot Mapping WS SLAM/ppt/homework1_octave_tutorial_framework/octave_tutorial_framework/octave/tools/plot_state.m: -------------------------------------------------------------------------------- 1 | function plot_state(mu, landmarks, timestep, z) 2 | % Visualizes the robot in the map. 3 | % 4 | % The resulting plot displays the following information: 5 | % - the landmarks in the map (black +'s) 6 | % - current robot pose (red) 7 | % - observations made at this time step (line between robot and landmark) 8 | 9 | clf 10 | hold on 11 | grid("on") 12 | L = struct2cell(landmarks); 13 | figure(1, "visible", "on"); 14 | plot(cell2mat(L(2,:)), cell2mat(L(3,:)), 'k+', 'markersize', 10, 'linewidth', 5); 15 | 16 | for(i=1:size(z,2)) 17 | id = z(i).id; 18 | mX = landmarks(id).x; 19 | mY = landmarks(id).y; 20 | line([mu(1), mX],[mu(2), mY], 'color', 'b', 'linewidth', 1); 21 | endfor 22 | 23 | drawrobot(mu(1:3), 'r', 3, 0.3, 0.3); 24 | xlim([-2, 12]) 25 | ylim([-2, 12]) 26 | filename = sprintf('../plots/odom_%03d.png', timestep); 27 | print(filename, '-dpng'); 28 | hold off 29 | end 30 | -------------------------------------------------------------------------------- /Robot Mapping WS SLAM/ppt/homework1_octave_tutorial_framework/octave_tutorial_framework/octave/tools/read_data.m: -------------------------------------------------------------------------------- 1 | function data = read_data(filename) 2 | % Reads the odometry and sensor readings from a file. 3 | % 4 | % filename: path to the file to parse 5 | % data: structure containing the parsed information 6 | % 7 | % The data is returned in a structure where the u_t and z_t are stored 8 | % within a single entry. A z_t can contain observations of multiple 9 | % landmarks. 10 | % 11 | % Usage: 12 | % - access the readings for timestep i: 13 | % data.timestep(i) 14 | % this returns a structure containing the odometry reading and all 15 | % landmark obsevations, which can be accessed as follows 16 | % - odometry reading at timestep i: 17 | % data.timestep(i).odometry 18 | % - senor reading at timestep i: 19 | % data.timestep(i).sensor 20 | % 21 | % Odometry readings have the following fields: 22 | % - r1 : rotation 1 23 | % - t : translation 24 | % - r2 : rotation 2 25 | % which correspond to the identically labeled variables in the motion 26 | % mode. 27 | % 28 | % Sensor readings can again be indexed and each of the entris has the 29 | % following fields: 30 | % - id : id of the observed landmark 31 | % - range : measured range to the landmark 32 | % - bearing : measured angle to the landmark (you can ignore this) 33 | % 34 | % Examples: 35 | % - Translational component of the odometry reading at timestep 10 36 | % data.timestep(10).odometry.t 37 | % - Measured range to the second landmark observed at timestep 4 38 | % data.timestep(4).sensor(2).range 39 | input = fopen(filename); 40 | 41 | data = struct; 42 | data.timestep.sensor = struct; 43 | first = 1; 44 | 45 | odom = struct; 46 | sensor = struct; 47 | 48 | while(!feof(input)) 49 | line = fgetl(input); 50 | arr = strsplit(line, ' '); 51 | type = deblank(arr{1}); 52 | 53 | if(strcmp(type, 'ODOMETRY') == 1) 54 | if(first == 0) 55 | data.timestep(end+1).odometry = odom; 56 | data.timestep(end).sensor = sensor(2:end); 57 | odom = struct; 58 | sensor = struct; 59 | endif 60 | first = 0; 61 | odom.r1 = str2double(arr{2}); 62 | odom.t = str2double(arr{3}); 63 | odom.r2 = str2double(arr{4}); 64 | elseif(strcmp(type, 'SENSOR') == 1) 65 | reading = struct; 66 | reading.id = str2double(arr{2}); 67 | reading.range = str2double(arr{3}); 68 | reading.bearing = str2double(arr{4}); 69 | sensor(end+1) = reading; 70 | endif 71 | endwhile 72 | 73 | data.timestep = data.timestep(2:end); 74 | 75 | fclose(input); 76 | end 77 | -------------------------------------------------------------------------------- /Robot Mapping WS SLAM/ppt/homework1_octave_tutorial_framework/octave_tutorial_framework/octave/tools/read_world.m: -------------------------------------------------------------------------------- 1 | function landmarks = read_world(filename) 2 | % Reads the world definition and returns a structure of landmarks. 3 | % 4 | % filename: path of the file to load 5 | % landmarks: structure containing the parsed information 6 | % 7 | % Each landmark contains the following information: 8 | % - id : id of the landmark 9 | % - x : x-coordinate 10 | % - y : y-coordinate 11 | % 12 | % Examples: 13 | % - Obtain x-coordinate of the 5-th landmark 14 | % landmarks(5).x 15 | input = fopen(filename); 16 | 17 | landmarks = struct; 18 | 19 | while(!feof(input)) 20 | line = fgetl(input); 21 | data = strsplit(line, ' '); 22 | 23 | landmark = struct( 24 | "id", str2double(data{1}), 25 | "x" , str2double(data{2}), 26 | "y" , str2double(data{3}) 27 | ); 28 | landmarks(end+1) = landmark; 29 | endwhile 30 | 31 | landmarks = landmarks(2:end); 32 | 33 | fclose(input); 34 | end 35 | -------------------------------------------------------------------------------- /Robot Mapping WS SLAM/ppt/slam01-intro.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/Robot Mapping WS SLAM/ppt/slam01-intro.pdf -------------------------------------------------------------------------------- /bayes_filter/易懂的贝叶斯滤波理解与推导教程bayes_filter.md: -------------------------------------------------------------------------------- 1 | 像卡尔曼滤波(Kalman filters)、粒子滤波(Particle filters),隐含马尔科夫模型(Hidden Markov models),动态贝叶斯网络(Dynamic Bayesian networks)等等算法。这些算法都和贝叶斯滤波算法非常相似.因此学好了贝叶斯滤波再学其他算法那就容易很多了。 2 | # 破解概念上的束缚之贝叶斯滤波算法到底有什么用? 3 | **学东西最烦的是看了半个月的算法细节,结果发现自己还是不知道这算法有什么用,怎么实现**。今天博主就开篇破解贝叶斯中的概念上的障碍。让大家快速学习贝叶斯滤波算法,并且能够用到自己的项目中。贝叶斯滤波算法**它做的工作就是根据已有的信息来计算概率。比如我想根据一些信息识别某个人性别是不是女,那贝叶斯滤波算法要做的是就是计算这个人是女的概率P(女)。来了新的信息就计算新的概率。**。如果你不理解这句话看接下来这个例子你就懂了。**假如现在贝叶斯滤波算法是要根据你已输入的信息分析某个人是男是女。贝叶斯滤波算法最终的输出是计算出根据现有信息判断这个人是男性的概率是多少,女性的概率是多少。然后你的算法就选择概率更大的那种情况输出结果。如果有新的信息进来(比如“那个人有长头发”),贝叶斯算法要重新计算这个人是男性的概率是多少,女性的概率是多少。**。 4 | 5 | 那它具体怎么做的呢? 6 | **当你什么信息都不提供给贝叶斯滤波算法的时候**,然后你问贝叶斯算法那个人的性别是女的概率是多少。贝叶斯滤波算法会告诉你P(女)=0.5。注意:P(女)=0.5这个表示的含义是“根据已有信息,那个人是女性概率是0.5”. 现在P(女)=P(男)=0.5我们是没法判断性别。这个数值“0.5”是我们设置的一个初始值,一般是需要根据你的统计经验设置的(如果你是想用贝叶斯滤波算法进行垃圾邮件识别,那么它是垃圾邮件这个概率肯定不会有0.5这么大。毕竟垃圾邮件还是少数。你需要统计收到的邮件中垃圾邮件的占比,然后作为算法的初始值。好让算法在没有任何提示信息的情况下能输出当前邮件是垃圾邮件的概率)。 7 | 然后,回到性别识别那个问题。 8 | 9 | **当你告诉算法这个人有长头发,你再问算法这个人现在性别是女的概率是多少**。贝叶斯滤波算法会根据概率论中的条件概率知识算出“在知道某个人有长头发的条件下,这个人性别是女的概率P(女)=0.87”。至于它到底怎么算的文章后面会讲。现在你只需要对整个算法有个直观理解即可。为什么它能知道根据“有长头发”这个信息来更新概率呢?**这是因为算法需要我们告诉它“长头发中女性占比是多少(这个叫做先验知识)”,P(女)=0.87这个也是需要我们经过统计才能得到的数据**。 10 | 11 | **当你再告诉算法这个人使用口红,然后你再问算法这个人现在性别是女的概率是多少**?贝叶斯滤波算法会根据概率论中条件概率知识,以及需要你告诉它“使用口红的人中女性占比是多少”,来更新当前这个人是女性的概率P(女)=0.91。 12 | 13 | 以上就是贝叶斯算法做的工作。现在我想聪明的你已经对贝叶斯滤波算法已经有了一个直观的理解。**我总结一下贝叶斯滤波算法做的工作:它做的工作就是根据不断接收到的新信息和我提供的一些已经知道的统计值,来不断更新概率。更新概率值的方法是根据概率论中的条件概率计算公式来更新的。贝叶斯滤波算法计算的结果是一个概率值有什么用呢**? 14 | 比如我要它根据我提供的一些信息识别某个人是不是女,那它就得计算这个人是女的概率P(女).如果我想让它根据雷达测距判断机器人离障碍物距离,那它就得计算出机器人离障碍物各种距离取值的概率(为何要计算概率?因为雷达测量会存在误差),比如计算机器人离障碍物10cm远的概率是0.87,离障碍物11cm远的概率是0.21,离障碍物9cm远的概率是0.01。那么我就认为机器人离障碍物距离是10cm。 15 | # 贝叶斯滤波算法怎么随着收到的新信息来更新概率的? 16 | 还是以前面的根据已有信息识别性别例子举例。我们要想识别一个人是否是女性,现在要贝叶斯滤波算法做的就是需要它计算出根据当前信息判断当前这个人是女的概率P(女)。 17 | 我们**已经知道的先验知识是(这些数据是我们统计到的,你项目中的这些先验知识有些是需要统计得到,有些是需要建模比如假设它是正态分布等等)**: 18 | **在没有任何信息的情况下初始值:P(女)=0.5 19 | 留有长头发的人中女性占比:0.8。 20 | 使用口红的人中女性占比:0.9** 21 | 22 | 那么**在没有任何额外信息的情况下,贝叶斯滤波认为当前这个人是女性的概率P(女)=0.5** 23 | 当新知道当前这个人留有长头发这个信息时,怎么更新概率值呢? 24 | 此时当前这个人是女性的概率可以这么表示P(女|长头发), 这个表达式的意思是在知道现在这个人的长头发的条件下女性的概率。 25 | 事实上**这个值就是我们在先验知识中已知的“留有长头发的人中女性占比”,即0.8**。你会说万一我不知道留有长头发中的人之中女性占比呢?这意味着我们需要用概率论中的条件[概率知识](https://blog.csdn.net/varyshare/article/details/97614323)求解P(女|长头发)。(如果你对条件概率怎么求还不清楚可以参考这篇文章[十分钟复习概率论知识](https://blog.csdn.net/varyshare/article/details/97614323))。根据概率论知识我们知道$P(女|长头发)=\frac {P(女 并且有 长头发)}{P(长头发)}=\frac {P(长头发|女)P(女)}{P(长头发)}$。所以我们有两种方式可以求P(女|长头发)。 26 | 27 | 1. 如果我们知道长头发的人占总人口比例即P(长头发),并且还知道有长头发的女性占总人口的比例即P(女 并且有 长头发),那么就用$P(女|长头发)=\frac {P(女 并且有 长头发)}{P(长头发)}$这种方式求留有长头发的人中女性占比P(女|长头发)。 28 | 29 | 2. 如果我们知道长头发的人占总人口比例即P(长头发),而且还知道女性中留有长头发的人占比P(长头发|女),并且还知道女性占总人口比例P(女)。那么就用$P(女|长头发)=\frac {P(长头发|女)P(女)}{P(长头发)}$这个方式求解。 30 | 31 | 你知道什么先验知识那就用什么方式求解,贝叶斯滤波通常是用$P(女|长头发)=\frac {P(长头发|女)P(女)}{P(长头发)}$这种方式求解。这是因为这种方式在在实际应用场景中这个公式中需要的先验知识更容易获得。**你在实际实现贝叶斯滤波算法时候只需要考虑你能够知道公式中的哪些先验知识概率值,然后再确定用哪种方式求$P(女|长头发)$**。 32 | 33 | **现在你已经学会了贝叶斯滤波算法**。**你一定会问那为何很多书上感觉贝叶斯滤波算法比这个复杂得多啊**。这是实际应用中往往是会同时接收到多个信息。即P(女|长头发, 有口红)这种形式,这个表示在知道一个人长头发,并且有口红的条件下它是女性的概率是多少。然后**书上或者博客省略了很多化简步骤直接告诉你**$P(女|长头发, 有口红)=\frac{P(长头发|女,有口红)P(女|有口红)}{P(长头发|有口红)}$。**省略了这么多当然这个你自然看不懂**,而且事实上有时候这种求P(女|长头发, 有口红)的方式里面的先验知识你压根就不知道,比如P(长头发|有口红)这个先验知识值你不知道怎么可能求出这个公式$P(女|长头发, 有口红)=\frac{P(长头发|女,有口红)P(女|有口红)}{P(长头发|有口红)}$。 34 | 35 | > “很多时候一些算法看起来极其复杂,极其难懂。不是因为我们愚不可及。而是因为我们看到的已经是别人省略过很多步骤和省略很多脑海中思考过的思路最后呈现出的公式和文字。而好的博客就是应该尽量还原这些思考过程以及省略掉的步骤。”——[@Ai酱](https://www.zhihu.com/people/yuanmuou) 36 | 37 | 我将还原这个公式的推导过程$P(女|长头发, 有口红)=\frac{P(长头发|女,有口红)P(女|有口红)}{P(长头发|有口红)}$。相信你看完下面这个推导会有所收获。 38 | 以下: 39 | 根据定义我们可以知道P(女|长头发, 有口红)就是同时具备“长头发, 有口红”这个特征的人中,具备“女,长头发, 有口红”这三个标签的人所占的比例。所以得到: 40 | $P(女|长头发, 有口红)=\frac{P(女,长头发, 有口红)}{P(长头发,有口红)}$ 41 | 然后分子P(女,长头发, 有口红)这个联合概率可以变成一个条件概率公式。分母P(长头发,有口红)这也是联合概率 42 | $P(女|长头发, 有口红)=\frac{P(女,长头发, 有口红)}{P(长头发,有口红)}=\frac{P(长头发|女, 有口红)P(女, 有口红)}{P(长头发|有口红)P(有口红)}=\frac{P(长头发|女, 有口红)P(女| 有口红)P(有口红)}{P(长头发|有口红)P(有口红)}=\frac{P(长头发|女, 有口红)P(女| 有口红)}{P(长头发|有口红)}$ 43 | 以上。 44 | 45 | 只要我们知道P(长头发|女, 有口红)、P(女| 有口红)、P(长头发|有口红)这三个值,那我们就可以求得P(女|长头发, 有口红),即可以算出在知道一个人有长头发、有口红的情况下这个人是女性的概率。如果这个概率值大于0.5那就可以认为根据已有的知识我们可以认为现在这个人是女性,如果小于0.5那就认为现在这个人是男性。如果不知道P(长头发|女, 有口红)、P(女| 有口红)、P(长头发|有口红)这三个值怎么求呢? 46 | 我们用定义也是可以求P(女|长头发, 有口红)的,比如下面这个公式: 47 | $P(女|长头发, 有口红)=\frac{P(女,长头发, 有口红)}{P(长头发,有口红)}$ 48 | 49 | # 机器人状态估计中的贝叶斯滤波 50 | 如果你是为了机器人状态估计而学习贝叶斯滤波的那欢迎继续看下去。如果你只是想了解贝叶斯滤波怎么做的那你已经学会了,你可以完成点赞这个仪式后离开当前页面。**为什么我还继续讲讲机器人状态估计下的贝叶斯滤波?因为机器人状态估计中的贝叶斯滤波里面省略了非常多的假设和细节。我想尝试着将这些细节和省略掉的假设还原**。 51 | # 机器人状态估计到底是什么? 52 | 一句话说清楚机器人状态估计就是:根据机器人之前的状态,传感器观测数据和控制器控制命令来估计当前机器人的状态。那么什么是机器人状态?机器人离障碍物距离,机器人的速度,机器人的姿态,机器人在世界坐标系下的地址这些都是机器人的状态。我就以机器人离某个障碍物的距离作为机器人状态来举例,假设机器人在时刻t,它观测到自己离障碍物距离是$z_t$,它控制自己移动的命令是让它移动$u_t$这么远的距离,算法根据传感器数据和控制器数据估计出离某个障碍物的距离是$x_t$,算法上个时刻估算出的距离是$x_{t-1}$。 53 | 54 | 由于传感器,控制器,上个状态这些数据总会有误差。我怎么知道当前估计出机器人离障碍物距离$x_t$是不是正确的呢?答:计算概率$P(x_t|z_t,u_t,x_{t-1})$。只要这个概率有80%那不就是大概率是对的。$P(x_t|z_t,u_t,x_{t-1})$这个概率公式指的是在知道传感器数据是$z_t$,控制器数据是$u_t$,上个状态是$x_{t-1}$的情况下当前状态是$x_t$的概率。 55 | 56 | 现在我们已知: 57 | - $P(z_t|x_t)$,这个表示的含义是:当机器人处于$x_t$这个状态时,它观测值是$z_t$的概率。这个是观测模型,需要我们自己建模,不同的传感器肯定是不一样的,比如我们设计一种函数$f(x_t,z_t)$它的值就是$P(z_t|x_t)$。这个就是卡尔曼滤波要做的事,卡尔曼滤波设计的函数就是认为这个概率函数是一个正态分布(高斯分布)。 58 | - $P(x_t|x_{t-1},u_t)$,这个表示的含义是:当机器人上个状态是$x_{t-1}$,然后它采取的动作(控制命令)是$u_t$时,机器人的状态是$x_t$的概率。这个是控制模型,也是需要我们自己建模不同的电机模型不一样。 59 | - $P(u_t|x_{t-1})$,这个表示的含义是:当机器人处于$x_{t-1}$这个状态时做出$u_t$的动作概率。 60 | 机器人状态估计中的假设: 61 | - 假设当前观测值$z_t$只与当前状态$x_t$有关,跟机器人的控制命令$u_t$和上个状态$x_{t-1}$无关。 62 | - 假设当前状态$x_t$只与上个时刻状态$x_{t-1}$以及与控制命令$u_t$有关。 63 | 64 | 好推理开始: 65 | 1. 根据定义有: 66 | $P(x_t|z_t,u_t,x_{t-1})=\frac {P(x_t,z_t,u_t,x_{t-1})}{P(z_t,u_t,x_{t-1})}$ 67 | 2. 由于含有$z_t$的只有一个已知,并且是一个条件概率$z_t$还在竖线的坐边,因此我们把所有的含有$z_t$的联合概率用$z_t$都在坐边的那种形式的条件概率表示. 68 | $P(x_t|z_t,u_t,x_{t-1})=\frac {P(z_t|x_t,u_t,x_{t-1})P(x_t,u_t,x_{t-1})}{P(z_t|u_t,x_{t-1})P(u_t,x_{t-1})}$ 69 | 3. 由于我们已知$P(u_t|x_{t-1})$,因此我们把上式的分母$P(u_t,x_{t-1})$也变成条件概率形式。 70 | $P(x_t|z_t,u_t,x_{t-1})=\frac {P(z_t|x_t,u_t,x_{t-1})P(x_t,u_t,x_{t-1})}{P(z_t|u_t,x_{t-1})P(u_t|x_{t-1})P(x_{t-1}))}$ 71 | 4. 由于我们还已知$P(x_t|x_{t-1},u_t)$,所以我们把上式分子中的$P(x_t,u_t,x_{t-1})$变成一个条件概率。 72 | $P(x_t|z_t,u_t,x_{t-1})=\frac {P(z_t|x_t,u_t,x_{t-1})P(x_t|u_t,x_{t-1})P(u_t,x_{t-1})}{P(z_t|u_t,x_{t-1})P(u_t|x_{t-1})P(x_{t-1}))}$ 73 | 5. 现在分子出现了和第3步一样的$P(u_t,x_{t-1})$。我们一样把它变成这种条件概率$P(u_t|x_{t-1})$形式。 74 | $P(x_t|z_t,u_t,x_{t-1})=\frac {P(z_t|x_t,u_t,x_{t-1})P(x_t|u_t,x_{t-1})P(u_t|x_{t-1})P(x_{t-1})}{P(z_t|u_t,x_{t-1})P(u_t|x_{t-1})P(x_{t-1}))}$ 75 | 6. 分子分母同时消去$P(x_{t-1}),P(u_t|x_{t-1})$, 76 | $P(x_t|z_t,u_t,x_{t-1})=\frac {P(z_t|x_t,u_t,x_{t-1})P(x_t|u_t,x_{t-1})}{P(z_t|u_t,x_{t-1})}$ 77 | 在前面中我们知道机器人状态估计中有几个假设: 78 | - 首先是假设当前观测值$z_t$只与当前状态$x_t$有关,跟机器人的控制命令$u_t$和上个状态$x_{t-1}$无关。这意味着分子中的$P(z_t|x_t,u_t,x_{t-1})=P(z_t|x_t)$,并且分母中的$P(z_t|u_t,x_{t-1})=P(z_t)$ 79 | 所以: 80 | $P(x_t|z_t,u_t,x_{t-1})=\frac {P(z_t|x_t)P(x_t|u_t,x_{t-1})}{P(z_t)}$ 81 | 现在分子已经都是已知量,分母那个$\frac{1}{P(z_t)}$是一个与$x_t$无关的常量,一般用$\eta$表示。 82 | $P(x_t|z_t,u_t,x_{t-1})=\eta P(z_t|x_t)P(x_t|u_t,x_{t-1})$ 83 | 这就是我们在机器人状态估计中最常见的公式,无论是贝叶斯估计还是卡尔曼滤波都是这个公式。现在我已经将它推导的细节进行了还原。 84 | 85 | 以上。 -------------------------------------------------------------------------------- /feature_extract/Bresenham布雷森汉姆算法画圆教程.md: -------------------------------------------------------------------------------- 1 | ## Bresenham 布雷森汉姆算法画圆的原理与Python编程实现教程 2 | 注意:Bresenham的圆算法只是中点画圆算法的优化版本。区别在于Bresenham的算法只使用整数算术,而中点画圆法仍需要浮点数。注意:不要因为我提到了中点画圆法你就去先看完[计算机图形学中点画圆法教程](https://blog.csdn.net/varyshare/article/details/96839691)再看Bresenham算法,这样是浪费时间。**中点画圆法和Bresenham画圆法只是思想一样,但是思路并没有很大关联。所以直接看Bresenham算法就可以**。 3 | 4 | 看下面这个图,这就是一个像素一个像素的画出来的。我们平常的圆也是一个像素一个像素的画出来的,你可以试试在“画图”这个软件里面画一个圆然后放大很多倍,你会发现就是一些像素堆积起来的。 5 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190718192037816.png) 6 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/2019071819203165.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 7 | 我们看出来圆它是一个上下左右都对称,而且也是中心对称的。所以我们只用画好八分之一圆弧就可以,其他地方通过对称复制过去就好。 8 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190718192849587.png) 9 | 看下面这幅图,绿线夹住的那部分就是八分之一圆弧。**注意我们是逆时针画圆的(即从水平那个地方即(r,0)开始画因为一开始我们只知道水平位置的像素点该放哪其他地方我们都不知道)**。Bresenham 算法画完一个点(x,y)后`注意x,y都是整数。他们代表的是x,y方向上的第几个像素。`,它下一步有两个选择(x,y+1),(x-1,y+1)。也就是说y一定增加,但是x要么保持不变要么减一(你也可以让x一定增加y要么不变要么加一,其实差不多的)。当程序画到粉红色那个像素点的时候,程序选择下一步要绘制的点为(x-1,y+1)。当程序画到黄色的那个像素点时候,程序选择下一步要绘制的点为(x,y+1)。 10 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190718193719108.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_7F1FFF,t_70) 11 | 我们看看粉色的那个点的下一步是如何抉择的。 12 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190718201205329.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 13 | 14 | 15 | Bresenham是根据待选的两个点哪个离圆弧近就下一步选哪个。那它是怎么判断的呢?这两个点一定有一个在圆弧内一个在圆弧外。到底选哪个?Bresenham的方法就是直接计算两个点离圆弧之间的距离,然后判断哪个更近就选哪个。如下图所示: 16 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190719115849411.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 17 | 那么怎么用数学量化他们离圆弧的距离呢? 18 | 答:前面我们提到了,当前粉红色这个点坐标为$(x_k,y_k)$,下一步它有两种可能的走法绿色$(x_k-1,y_k+1)$,紫色坐标为$(x_k,y_k+1)$ 19 | $d1 = (x_k-1)^2+(y_k+1)^2-r^2$ 20 | $d2 = (x_k)^2+(y_k+1)^2-r^2$ 21 | 注意:$d1 = (x_k-1)^2+(y_k+1)^2-r^2$小于0的,因为绿色那个点一定在圆内侧。$d2 = (x_k)^2+(y_k+1)^2-r^2$一定是大于等于0的,因为紫色那个点一定在圆外侧。 22 | 23 | **所以我们只用比较$P_k = d1+d2$到底是大于0还是小于0就能确定选哪个点了。大于0选绿色$(x_k-1,y_k+1)$那个点(因为紫色那个点偏离圆弧程度更大)。小于0则选紫色$(x_k,y_k+1)$那个点**。 24 | 25 | **好了Bresenham画圆法我讲完了**。 26 | 27 | 你或许会问,不对啊。我在网上看到的关于Bresenham画圆法的博客还有其他公式。确实我还有一个小细节没讲。**你用上面的方法是已经可以画圆了,剩下的就是一些提高计算效率的小细节**。 28 | 29 | $P_k = d1+d2= (x_k-1)^2+(y_k+1)^2-r^2+(x_k)^2+(y_k+1)^2-r^2$这个公式走到下一步时候$P_{k+1} = d1+d2$又要重新计算。为了提高效率。人们就想能不能通过递推的方式来算$P_{k+1}$,即能不能找一个这样的公式$P_{k+1}=P_k+Z$提高计算效率。 30 | 31 | 这个也很简单,这个递推公式关键在于求Z。而我们变换下公式$P_{k+1}=P_k+Z$得到$Z=P_{k+1}-P_k$。 32 | 注意:$P_k= d1+d2= (x_k-1)^2+(y_k+1)^2-r^2+(x_k)^2+(y_k+1)^2-r^2$我们已知的,而$P_{k+1}$这个根据$P_k$大于0还是小于0也可以算出来。 33 | 1. 当$P_k>=0$则证明靠近外侧的那个待选点$(x_k,y_k+1)$离圆弧更远,所以我们下一步选的点是另外一个靠近内侧圆弧的那个点$(x_k-1,y_k+1)$。也就是说第k+1步那个点$(x_{k+1},y_{k+1})=(x_k-1,y_k+1)$。 34 | $Z=P_{k+1}-P_k= (x_{k+1}-1)^2+(y_{k+1}+1)^2-r^2+(x_{k+1})^2+(y_{k+1}+1)^2-r^2 35 | -[ (x_k-1)^2+(y_k+1)^2-r^2+(x_k)^2+(y_k+1)^2-r^2] \\ 36 | = (x_k-1-1)^2+(y_k+1+1)^2-r^2+(x_k-1)^2+(y_k+1+1)^2-r^2 37 | -[ (x_k-1)^2+(y_k+1)^2-r^2+(x_k)^2+(y_k+1)^2-r^2]\\ 38 | =-4x_k+4y_k+10$。所以$P_{k+1}=P_k-4x_k+4y_k+10$ 39 | 2. 当$P_k<0$时,们下一步选的点是另外一个靠近内侧圆弧的那个点是$(x_k,y_k+1)$。也就是说第k+1步那个点$(x_{k+1},y_{k+1})=(x_k,y_k+1)$。我们看看现在的Z是多少。 40 | $Z=P_{k+1}-P_k= (x_{k+1}-1)^2+(y_{k+1}+1)^2-r^2+(x_{k+1})^2+(y_{k+1}+1)^2-r^2 41 | -[ (x_k-1)^2+(y_k+1)^2-r^2+(x_k)^2+(y_k+1)^2-r^2] \\ 42 | = (x_k-1)^2+(y_k+1+1)^2-r^2+(x_k)^2+(y_k+1+1)^2-r^2 43 | -[ (x_k-1)^2+(y_k+1)^2-r^2+(x_k)^2+(y_k+1)^2-r^2]\\ 44 | =4y_k+6$。所以$P_{k+1}=P_k+4y_k+6$ 45 | 46 | 注意:根据初始点为(r,0)来计算$P_k$的初始值=$-2r+3$。 47 | ```python 48 | # -*- coding: utf-8 -*- 49 | """ 50 | Created on Sun Jul 21 15:02:36 2019 51 | Bresenham画圆法实现 52 | 博客教程地址: 53 | https://blog.csdn.net/varyshare/article/details/96724103 54 | @author: 知乎@Ai酱 55 | """ 56 | import numpy as np 57 | import matplotlib.pyplot as plt 58 | 59 | img = np.zeros((105,105)) # 创建一个105x105的画布 60 | 61 | def draw(x,y): 62 | """ 63 | 绘制点(x,y) 64 | 注意:需要把(x,y)变换到数组坐标系(图形学坐标系) 65 | 因为数组(0,0)是左上,而原先坐标系(0,0)是中心点 66 | 而且数组行数向下是增加的。 67 | """ 68 | # 平移原点 69 | x += int(img.shape[0]/2) 70 | y += int(img.shape[1]/2) 71 | # 72 | img[-y,x] = 1 73 | pass 74 | 75 | r_pixel = 50 # 圆的半径,单位:像素 76 | # 初始化,画第一个点,从水平最右边那个点开始画 77 | (x,y) = (r_pixel,0) 78 | 79 | """ 80 | 从定义来讲就是 81 | P_k=d1+d2 82 | d1 = 第1个下一步待选点离圆弧的距离(负数) 83 | d2 = 第2个下一步待选点离圆弧的距离(正数) 84 | 但是为了提高效率通常使用递推来求P_{k+1}=P_k + 一个数 85 | """ 86 | P_k = -2*r_pixel + 3 87 | 88 | # 迭代的求完1/8圆弧 89 | while x>=y: 90 | # 下一步有两个待选点,具体选哪个要看P_k>0 或 <0 91 | if P_k>=0:# 外侧候选点偏离圆弧更远 92 | P_k_next = P_k - 4*x + 4*y + 10 93 | (x_next,y_next) = (x-1, y+1) 94 | else:# 内侧候选点偏离圆弧更远 95 | P_k_next = P_k + 4*y + 6 96 | (x_next,y_next) = (x, y+1) 97 | # 对称法画其他地方 98 | draw(x,y) 99 | draw(-x,y) 100 | draw(x,-y) 101 | draw(-x,-y) 102 | 103 | draw(y,x) 104 | draw(y,-x) 105 | draw(-y,x) 106 | draw(-y,-x) 107 | # 更新坐标和P_k 108 | (x,y) = (int(x_next),int(y_next)) 109 | P_k = P_k_next 110 | pass 111 | # 绘制图片 112 | plt.imshow(img) 113 | ``` 114 | 115 | 程序运行的结果为: 116 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190721174903599.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 117 | 118 | github代码下载地址:[计算机图形学Bresenham画圆法Python代码](./bresenham_circle.py) 119 | 120 | 121 | 122 | # 如何使用这个项目的教程和代码? 123 | 124 | 1. Clone 下载这个项目. 125 | 126 | > git clone https://github.com/varyshare/easy_slam_tutorial.git 127 | 128 | > cd easy_slam_tutorial/ -------------------------------------------------------------------------------- /feature_extract/FAST_feature_extraction.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Features from Accelerated Segment Test (FAST) 3 | Python实现, 4 | 从0开始,最原始最简单的FAST特征点提取方法(无金字塔采样) 5 | 代码地址:https://github.com/varyshare/easy_slam_tutorial/tree/master/feature_extract 6 | 欢迎start这项目 7 | 教程地址:https://blog.csdn.net/varyshare/article/details/96430924 8 | 代码没有怎么经过优化,所以会有0.8s左右的卡顿 9 | ''' 10 | import numpy as np 11 | import matplotlib.pyplot as plt 12 | import cv2 # 用于读取图片 13 | 14 | 15 | # 1. 读取图片(为了简化问题我就直接构造一个数组作为图片) 16 | img = cv2.imread('feature.png',cv2.IMREAD_GRAYSCALE) 17 | 18 | # 2. 设置参数 19 | # 设置灰度值相差多大才算较大差异, 20 | # 以及周围点超过多少个高差异点才算当前中心像素点是关键点 21 | h_gray_scale = 20 # 在ORB特征提取中使用的FAST像素差阈值默认是20 22 | k_diff_point = 9 # 超过k_diff_point个差异那就认为是关键点(周围共16个点) 23 | r_pixel = 3 # 获取周围像素所用的圆的半径,单位:像素 24 | 25 | 26 | # 3. 遍历所有的像素进行检测关键点 27 | def bresenham_circle(): 28 | """ 29 | return: 圆周上所有的点相对圆心的坐标列表。 30 | 即,返回圆心在(0,0)处时圆周上各点的坐标。 31 | 返回一个r_pixel*r_pixel的矩阵,圆周上的点标记为1,其他地方标记为0 32 | """ 33 | 34 | _masked_canvas = np.zeros((2*r_pixel+1,2*r_pixel+1)) 35 | def save(x,y): 36 | """ 37 | 把(x,y)加入到结果列表中 38 | 注意:需要把(x,y)变换到数组坐标系(图形学坐标系) 39 | """ 40 | _masked_canvas[-y+r_pixel,x+r_pixel] = 1 41 | pass 42 | 43 | # 初始化,画第一个点,从水平最右边那个点开始画 44 | (x,y) = (r_pixel,0) 45 | 46 | """ 47 | 从定义来讲就是 48 | P_k=d1+d2 49 | d1 = 第1个下一步待选点离圆弧的距离(负数) 50 | d2 = 第2个下一步待选点离圆弧的距离(正数) 51 | 但是为了提高效率通常使用递推来求P_{k+1}=P_k + 一个数 52 | """ 53 | P_k = -2*r_pixel + 3 54 | 55 | # 迭代的求完1/8圆弧 56 | while x>=y: 57 | # 下一步有两个待选点,具体选哪个要看P_k>0 或 <0 58 | if P_k>=0:# 外侧候选点偏离圆弧更远 59 | P_k_next = P_k - 4*x + 4*y + 10 60 | (x_next,y_next) = (x-1, y+1) 61 | else:# 内侧候选点偏离圆弧更远 62 | P_k_next = P_k + 4*y + 6 63 | (x_next,y_next) = (x, y+1) 64 | # 对称法画这对称的8个地方 65 | save(x,y) 66 | save(-x,y) 67 | save(x,-y) 68 | save(-x,-y) 69 | 70 | save(y,x) 71 | save(y,-x) 72 | save(-y,x) 73 | save(-y,-x) 74 | # 更新当前坐标和P_k 75 | (x,y) = (int(x_next),int(y_next)) 76 | P_k = P_k_next 77 | pass 78 | 79 | return _masked_canvas 80 | 81 | # 先bresenham算法算出半径为r_pixel时圆周上的点相对圆心的坐标 82 | masked_canvas = bresenham_circle() 83 | def key_point_test(_row,_col): 84 | """ 85 | 检测像素点(_row,_col)是否是关键点。 86 | 满足关键点只有一个条件:周围16个像素点与中心像素点相比 87 | 差异度较大(>h_gray_scale)的像素点个数超过k_diff_point个 88 | return: boolean 89 | """ 90 | # 获取以(_row,_col)为几何中心的7x7正方形区域内的像素值 91 | surround_points = img[_row-3:_row+3+1,_col-3:_col+3+1] 92 | 93 | # 获取圆周上的点与圆心的像素差值的绝对值 94 | _dist = np.abs((surround_points - img[_row,_col])) * masked_canvas 95 | 96 | if (_dist>h_gray_scale).sum()> k_diff_point: 97 | return True 98 | else: 99 | return False 100 | 101 | 102 | key_point_list = [] 103 | 104 | for row in range(r_pixel,img.shape[0]-r_pixel): 105 | for col in range(r_pixel,img.shape[1]-r_pixel): 106 | 107 | if key_point_test(row,col): 108 | key_point_list.append(cv2.KeyPoint(x=row,y=col,_size=1)) 109 | else: 110 | continue 111 | 112 | pass 113 | pass 114 | 115 | 116 | img_with_keypoints = cv2.drawKeypoints(img,key_point_list,outImage=np.array([]),color=(0,0,255)) 117 | cv2.imshow("show key points",img_with_keypoints) 118 | cv2.waitKey(0) 119 | 120 | -------------------------------------------------------------------------------- /feature_extract/ORB_feature_extract.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 使用OpenCV实现ORB特征点提取 3 | 作者:知乎@Ai酱 4 | 代码地址:https://github.com/varyshare/easy_slam_tutorial/tree/master/feature_extract 5 | ''' 6 | 7 | # -*- coding: utf-8 -*- 8 | import cv2 9 | import numpy as np 10 | # 0. 读取图片 11 | img = cv2.imread('./right.png',cv2.IMREAD_GRAYSCALE) 12 | # 1. 创建一个ORB检测器实例 13 | orb = cv2.ORB_create() 14 | # 2. 检测关键点 15 | keypoint, descript = orb.detectAndCompute(img,None) 16 | # 3. 绘制关键点 17 | result_img = cv2.drawKeypoints(img,keypoint,None,color=(0,255,0),flags=cv2.IMREAD_GRAYSCALE) 18 | # 4. 显示含有关键点的图片 19 | cv2.imshow("ORB feature point extract",result_img) 20 | cv2.waitKey(0) 21 | 22 | 23 | -------------------------------------------------------------------------------- /feature_extract/bresenham_circle.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Jul 21 15:02:36 2019 4 | Bresenham画圆法实现 5 | 博客教程地址: 6 | https://blog.csdn.net/varyshare/article/details/96724103 7 | @author: 知乎@Ai酱 8 | """ 9 | import numpy as np 10 | import matplotlib.pyplot as plt 11 | 12 | img = np.zeros((105,105)) # 创建一个105x105的画布 13 | count = 0 14 | def draw(x,y): 15 | """ 16 | 绘制点(x,y) 17 | 注意:需要把(x,y)变换到数组坐标系(图形学坐标系) 18 | 因为数组(0,0)是左上,而原先坐标系(0,0)是中心点 19 | 而且数组行数向下是增加的。 20 | """ 21 | 22 | img[-y+int(img.shape[0]/2),x+int(img.shape[1]/2)] = 1 23 | 24 | pass 25 | 26 | r_pixel = 50 # 圆的半径,单位:像素 27 | # 初始化,画第一个点,从水平最右边那个点开始画 28 | (x,y) = (r_pixel,0) 29 | 30 | """ 31 | 从定义来讲就是 32 | P_k=d1+d2 33 | d1 = 第1个下一步待选点离圆弧的距离(负数) 34 | d2 = 第2个下一步待选点离圆弧的距离(正数) 35 | 但是为了提高效率通常使用递推来求P_{k+1}=P_k + 一个数 36 | """ 37 | P_k = -2*r_pixel + 3 38 | 39 | # 迭代的求完1/8圆弧 40 | while x>=y: 41 | # 下一步有两个待选点,具体选哪个要看P_k>0 或 <0 42 | if P_k>=0:# 外侧候选点偏离圆弧更远 43 | P_k_next = P_k - 4*x + 4*y + 10 44 | (x_next,y_next) = (x-1, y+1) 45 | else:# 内侧候选点偏离圆弧更远 46 | P_k_next = P_k + 4*y + 6 47 | (x_next,y_next) = (x, y+1) 48 | # 对称法画其他地方 49 | draw(x,y) 50 | draw(-x,y) 51 | draw(x,-y) 52 | draw(-x,-y) 53 | 54 | draw(y,x) 55 | draw(y,-x) 56 | draw(-y,x) 57 | draw(-y,-x) 58 | # 更新坐标和P_k 59 | (x,y) = (int(x_next),int(y_next)) 60 | P_k = P_k_next 61 | 62 | 63 | 64 | pass 65 | 66 | # 绘制图片 67 | plt.imshow(img) 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /feature_extract/feature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/feature_extract/feature.png -------------------------------------------------------------------------------- /feature_extract/right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/feature_extract/right.png -------------------------------------------------------------------------------- /feature_extract/从零开始实现FAST特征点提取算法教程.md: -------------------------------------------------------------------------------- 1 | # 从零开始实现FAST特征点提取算法教程Python代码实现 2 | 3 | FAST的全称是:Features from Accelerated Segment Test 4 | 5 | > 特征点提取与匹配在计算机视觉中是一个很重要的环节。比如人脸识别,目标跟踪,三维重建,等等都是先提取特征点然后匹配特征点最后执行后面的算法。因此学习特定点提取和匹配是计算机视觉中的基础。本文将介绍FAST特征点提取与匹配算法的原理,并使用Python不调用OpenCV包实现FAST特征点提取算法。 6 | 7 | # 特征点提取到底是提取的是什么? 8 | 答:**首先,提取的是角点,边缘**。提取角点可以进行跟踪,提取边就可以知道图像发生了怎样的旋转。反正都是提取的是那些周围发生颜色明显变化的那些地方。这个也很容易想通,要是它周围全一样的颜色那肯定是物体的内部,一来没必要跟踪。二来它发生了移动计算机也无法判断,因为它周围都一样颜色计算机咋知道有没有变化。**其次,提取的是周围信息(学术上叫做:描述子)**。我们**只要提到特征点提取就一定要想到提取完后我们是需要匹配的**。为了判断这个点有没有移动,我们需要比较前后两帧图片中相同特征点之间是否有位移。为了判断是否是相同特征点那就要进行比对(匹配)。**怎么比较两个特征点是否是同一个**?**这就需要比较这两个特征点周围信息是否一样。周围信息是一样那就认为是同一个特征点**。那么怎么比较周围信息呢?一般会把周围的像素通过一系列计算方式变成一个数字。然后比较这个数字是否相同来判断周围信息是否相同。 9 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190718145644785.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 10 | 11 | # 所有特征提取与匹配算法通用过程 12 | 1. 找到那些周围有明显变化的像素点作为特征点。如下图所示,那些角点和边缘这些地方明显颜色变化的那些像素点被作为特征点。 13 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190718152825942.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 14 | 2. **提取这些特征点周围信息。一般是在当前这个点周围随机采样选几个像素点作为当前特征点的周围信息,或者画个圈圈进行采样**。不同采样方法构成了不同算法。反正你想一个采样方法那你就创建了一种算法。 15 | 16 | 3. 特征点匹配。比如我要跟踪某个物体,我肯定是要先从这个物体提取一些特征点。然后看下一帧相同特征点的位置在哪,计算机就知道这个物体位置在哪了。怎么匹配?前面提到了我们第2步有提取当前特征点周围信息,只要周围信息一样那就是相同特征点。特征匹配也有很多种算法,最土的是前后两帧图片上的特征点一个一个的比对。 17 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190718152119359.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 18 | **记住学习任何特征提取与匹配算法都要时刻想起上面提到的三步骤。这样你不会太陷入那些书里面的细节中而学了很久都不懂。或者学完就忘。事实上那些算法非常简单,只不过你不知道他们各个步骤之间的联系是什么为什么这么设计。不知道这些当然就看不懂了。** 19 | # FAST特征点提取算法 20 | FAST (Features from Accelerated Segment Test)是一个特征点提取算法的缩写。这是一个点提取算法。它原理非常简单,**遍历所有的像素点,判断当前像素点是不是特征点的唯一标准就是在以当前像素点为圆心以3像素为半径画个圆(圆上有16个点),统计这16个点的像素值与圆心像素值相差比较大的点的个数。超过9个差异度很大的点那就认为圆心那个像素点是一个特征点**。那么什么叫做差异度很大呢?答:就是像素值相减取绝对值,然后我们设置一个数字只要前面那个绝对值大于这个数字,那就认为差异大。比如我**设置阈值是3。第1个像素点的像素值是4,中间圆心像素值是10,然后10-4=6,这是大于阈值3的。所以第1个像素点算所一个差异度较大的像素点**。就这样**统计1~16个中有多少个是和圆心相比差异度比较大的点。只要超过9个那就认为圆心是一个特征点。**是不是很简单?其实这些算法只要你知道他们想干嘛,你也可以设计一个不错的算法的。 21 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190718160218250.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 22 | 为了简化问题我们构造一个带有角点的7x7的小图片,注意下面坐标轴单位是像素。 23 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/2019072210335231.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 24 | 然后使用bresenham算法画圆(如下图所示),可以看到周围有超过9个点与中心那个像素点的像素值很大差异。因此程序会判断当前圆心所在的像素点是关键点。 25 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190722103253875.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 26 | 现在稍微有一丝丝难度的是怎么画圆。因为这个圆它是一个像素一个像素的画。这个圆其实你自己可以随便设计一个算法画圆。今天我们要讲FAST算法当然还是介绍下他是怎么画圆的。他就用了最普通的图形学画圆算法([Bresenham 画圆法](http://en.wikipedia.org/wiki/Midpoint_circle_algorithm) )。 27 | 28 | **其实到这里FAST算法我们就介绍完了。为了节省大家的时间(你的赞和关注是支持我分享的动力)**,我把Bresenham 画圆法也讲讲。 29 | ## Bresenham 布雷森汉姆算法画圆的原理与编程实现教程 30 | 注意:Bresenham的圆算法只是中点画圆算法的优化版本。区别在于Bresenham的算法只使用整数算术,而中点画圆法仍需要浮点数。**你不了解中点画圆法并没有任何影响**,因为他们只是思想一样,但是思路并不是一样。 31 | 32 | Bresenham也是根据待选的两个点哪个离圆弧近就下一步选哪个。它是怎么判断的呢?这两个点一定有一个在圆弧内一个在圆弧外。到底选哪个?Bresenham的方法就是直接计算两个点离圆弧之间的距离,然后判断哪个更近就选哪个。如下图所示: 33 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190719115849411.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 34 | 那么怎么用数学量化他们离圆弧的距离呢? 35 | 答:前面我们提到了,当前粉红色这个点坐标为$(x_k,y_k)$,下一步它有两种可能的走法绿色$(x_k-1,y_k+1)$,紫色坐标为$(x_k,y_k+1)$ 36 | $d1 = (x_k-1)^2+(y_k+1)^2-r^2$ 37 | $d2 = (x_k)^2+(y_k+1)^2-r^2$ 38 | 注意:$d1 = (x_k-1)^2+(y_k+1)^2-r^2$小于0的,因为绿色那个点一定在圆内侧。$d2 = (x_k)^2+(y_k+1)^2-r^2$一定是大于等于0的,因为紫色那个点一定在圆外侧。 39 | 40 | **所以我们只用比较$P_k = d1+d2$到底是大于0还是小于0就能确定选哪个点了。大于0选绿色$(x_k-1,y_k+1)$那个点(因为紫色那个点偏离圆弧程度更大)。小于0则选紫色$(x_k,y_k+1)$那个点**。 41 | 42 | **好了Bresenham画圆法我讲完了**。 43 | 44 | 你或许会问,不对啊。我在网上看到的关于Bresenham画圆法的博客还有其他公式。确实我还有一个小细节没讲。**你用上面的方法是已经可以画圆了,剩下的就是一些提高计算效率的小细节**。 45 | 46 | $P_k = d1+d2= (x_k-1)^2+(y_k+1)^2-r^2+(x_k)^2+(y_k+1)^2-r^2$这个公式走到下一步时候$P_{k+1} = d1+d2$又要重新计算。为了提高效率。人们就想能不能通过递推的方式来算$P_{k+1}$,即能不能找一个这样的公式$P_{k+1}=P_k+Z$提高计算效率。 47 | 48 | 这个也很简单,这个递推公式关键在于求Z。而我们变换下公式$P_{k+1}=P_k+Z$得到$Z=P_{k+1}-P_k$。 49 | 注意:$P_k= d1+d2= (x_k-1)^2+(y_k+1)^2-r^2+(x_k)^2+(y_k+1)^2-r^2$我们已知的,而$P_{k+1}$这个根据$P_k$大于0还是小于0也可以算出来。 50 | 1. 当$P_k>=0$则证明靠近外侧的那个待选点$(x_k,y_k+1)$离圆弧更远,所以我们下一步选的点是另外一个靠近内侧圆弧的那个点$(x_k-1,y_k+1)$。也就是说第k+1步那个点$(x_{k+1},y_{k+1})=(x_k-1,y_k+1)$。 51 | $Z=P_{k+1}-P_k= (x_{k+1}-1)^2+(y_{k+1}+1)^2-r^2+(x_{k+1})^2+(y_{k+1}+1)^2-r^2 52 | -[ (x_k-1)^2+(y_k+1)^2-r^2+(x_k)^2+(y_k+1)^2-r^2] \\ 53 | = (x_k-1-1)^2+(y_k+1+1)^2-r^2+(x_k-1)^2+(y_k+1+1)^2-r^2 54 | -[ (x_k-1)^2+(y_k+1)^2-r^2+(x_k)^2+(y_k+1)^2-r^2]\\ 55 | =-4x_k+4y_k+10$。所以$P_{k+1}=P_k-4x_k+4y_k+10$ 56 | 2. 当$P_k<0$时,们下一步选的点是另外一个靠近内侧圆弧的那个点是$(x_k,y_k+1)$。也就是说第k+1步那个点$(x_{k+1},y_{k+1})=(x_k,y_k+1)$。我们看看现在的Z是多少。 57 | $Z=P_{k+1}-P_k= (x_{k+1}-1)^2+(y_{k+1}+1)^2-r^2+(x_{k+1})^2+(y_{k+1}+1)^2-r^2 58 | -[ (x_k-1)^2+(y_k+1)^2-r^2+(x_k)^2+(y_k+1)^2-r^2] \\ 59 | = (x_k-1)^2+(y_k+1+1)^2-r^2+(x_k)^2+(y_k+1+1)^2-r^2 60 | -[ (x_k-1)^2+(y_k+1)^2-r^2+(x_k)^2+(y_k+1)^2-r^2]\\ 61 | =4y_k+6$。所以$P_{k+1}=P_k+4y_k+6$ 62 | 注意:根据初始点为(r,0)来计算$P_k$的初始值=$-2r+3$。 63 | ```python 64 | # -*- coding: utf-8 -*- 65 | """ 66 | Created on Sun Jul 21 15:02:36 2019 67 | Bresenham画圆法实现 68 | 博客教程地址: 69 | https://blog.csdn.net/varyshare/article/details/96724103 70 | @author: 知乎@Ai酱 71 | """ 72 | import numpy as np 73 | import matplotlib.pyplot as plt 74 | 75 | img = np.zeros((105,105)) # 创建一个105x105的画布 76 | 77 | def draw(x,y): 78 | """ 79 | 绘制点(x,y) 80 | 注意:需要把(x,y)变换到数组坐标系(图形学坐标系) 81 | 因为数组(0,0)是左上,而原先坐标系(0,0)是中心点 82 | 而且数组行数向下是增加的。 83 | """ 84 | # 平移原点 85 | x += int(img.shape[0]/2) 86 | y += int(img.shape[1]/2) 87 | # 88 | img[-y,x] = 1 89 | pass 90 | 91 | r_pixel = 50 # 圆的半径,单位:像素 92 | # 初始化,画第一个点,从水平最右边那个点开始画 93 | (x,y) = (r_pixel,0) 94 | 95 | """ 96 | 从定义来讲就是 97 | P_k=d1+d2 98 | d1 = 第1个下一步待选点离圆弧的距离(负数) 99 | d2 = 第2个下一步待选点离圆弧的距离(正数) 100 | 但是为了提高效率通常使用递推来求P_{k+1}=P_k + 一个数 101 | """ 102 | P_k = -2*r_pixel + 3 103 | 104 | # 迭代的求完1/8圆弧 105 | while x>=y: 106 | # 下一步有两个待选点,具体选哪个要看P_k>0 或 <0 107 | if P_k>=0:# 外侧候选点偏离圆弧更远 108 | P_k_next = P_k - 4*x + 4*y + 10 109 | (x_next,y_next) = (x-1, y+1) 110 | else:# 内侧候选点偏离圆弧更远 111 | P_k_next = P_k + 4*y + 6 112 | (x_next,y_next) = (x, y+1) 113 | # 对称法画其他地方 114 | draw(x,y) 115 | draw(-x,y) 116 | draw(x,-y) 117 | draw(-x,-y) 118 | 119 | draw(y,x) 120 | draw(y,-x) 121 | draw(-y,x) 122 | draw(-y,-x) 123 | # 更新坐标和P_k 124 | (x,y) = (int(x_next),int(y_next)) 125 | P_k = P_k_next 126 | pass 127 | # 绘制图片 128 | plt.imshow(img) 129 | ``` 130 | 131 | 程序运行的结果为: 132 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190721174835219.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 133 | Bresenham画圆github代码下载地址: https://gist.github.com/varyshare/adc2960a36da9571674861fb6cfea58a 134 | 上图的半径是40像素,当然FAST画圆的半径是3像素.我们只用修改一行设置半径的代码为`r_pixel = 3 # 圆的半径,单位:像素`。半径为3像素的圆如下图所示: 135 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190721190029188.png) 136 | 137 | 138 | 使用OpenCV库中的FAST特征点检测算法 139 | ```python 140 | ''' 141 | Features from Accelerated Segment Test (FAST) 142 | Python实现, 143 | 从0开始,最原始最简单的FAST特征点提取方法(无金字塔采样) 144 | 代码地址:https://github.com/varyshare/easy_slam_tutorial/tree/master/feature_extract 145 | 欢迎fork参与这个开源项目,star这项目 146 | 教程地址:https://blog.csdn.net/varyshare/article/details/96430924 147 | 代码没有怎么经过优化,所以会有0.8s左右的卡顿 148 | ''' 149 | import numpy as np 150 | import matplotlib.pyplot as plt 151 | import cv2 # 用于读取图片 152 | 153 | 154 | # 1. 读取图片(为了简化问题我就直接构造一个数组作为图片) 155 | img = cv2.imread('feature.png',cv2.IMREAD_GRAYSCALE) 156 | 157 | # 2. 设置参数 158 | # 设置灰度值相差多大才算较大差异, 159 | # 以及周围点超过多少个高差异点才算当前中心像素点是关键点 160 | h_gray_scale = 20 # 在ORB特征提取中使用的FAST像素差阈值默认是20 161 | k_diff_point = 9 # 超过k_diff_point个差异那就认为是关键点(周围共16个点) 162 | r_pixel = 3 # 获取周围像素所用的圆的半径,单位:像素 163 | 164 | 165 | # 3. 遍历所有的像素进行检测关键点 166 | def bresenham_circle(): 167 | """ 168 | return: 圆周上所有的点相对圆心的坐标列表。 169 | 即,返回圆心在(0,0)处时圆周上各点的坐标。 170 | 返回一个r_pixel*r_pixel的矩阵,圆周上的点标记为1,其他地方标记为0 171 | """ 172 | 173 | _masked_canvas = np.zeros((2*r_pixel+1,2*r_pixel+1)) 174 | def save(x,y): 175 | """ 176 | 把(x,y)加入到结果列表中 177 | 注意:需要把(x,y)变换到数组坐标系(图形学坐标系) 178 | """ 179 | _masked_canvas[-y+r_pixel,x+r_pixel] = 1 180 | pass 181 | 182 | # 初始化,画第一个点,从水平最右边那个点开始画 183 | (x,y) = (r_pixel,0) 184 | 185 | """ 186 | 从定义来讲就是 187 | P_k=d1+d2 188 | d1 = 第1个下一步待选点离圆弧的距离(负数) 189 | d2 = 第2个下一步待选点离圆弧的距离(正数) 190 | 但是为了提高效率通常使用递推来求P_{k+1}=P_k + 一个数 191 | """ 192 | P_k = -2*r_pixel + 3 193 | 194 | # 迭代的求完1/8圆弧 195 | while x>=y: 196 | # 下一步有两个待选点,具体选哪个要看P_k>0 或 <0 197 | if P_k>=0:# 外侧候选点偏离圆弧更远 198 | P_k_next = P_k - 4*x + 4*y + 10 199 | (x_next,y_next) = (x-1, y+1) 200 | else:# 内侧候选点偏离圆弧更远 201 | P_k_next = P_k + 4*y + 6 202 | (x_next,y_next) = (x, y+1) 203 | # 对称法画这对称的8个地方 204 | save(x,y) 205 | save(-x,y) 206 | save(x,-y) 207 | save(-x,-y) 208 | 209 | save(y,x) 210 | save(y,-x) 211 | save(-y,x) 212 | save(-y,-x) 213 | # 更新当前坐标和P_k 214 | (x,y) = (int(x_next),int(y_next)) 215 | P_k = P_k_next 216 | pass 217 | 218 | return _masked_canvas 219 | 220 | # 先bresenham算法算出半径为r_pixel时圆周上的点相对圆心的坐标 221 | masked_canvas = bresenham_circle() 222 | def key_point_test(_row,_col): 223 | """ 224 | 检测像素点(_row,_col)是否是关键点。 225 | 满足关键点只有一个条件:周围16个像素点与中心像素点相比 226 | 差异度较大(>h_gray_scale)的像素点个数超过k_diff_point个 227 | return: boolean 228 | """ 229 | # 获取以(_row,_col)为几何中心的7x7正方形区域内的像素值 230 | surround_points = img[_row-3:_row+3+1,_col-3:_col+3+1] 231 | 232 | # 获取圆周上的点与圆心的像素差值的绝对值 233 | _dist = np.abs((surround_points - img[_row,_col])) * masked_canvas 234 | 235 | if (_dist>h_gray_scale).sum()> k_diff_point: 236 | return True 237 | else: 238 | return False 239 | 240 | 241 | key_point_list = [] 242 | 243 | for row in range(r_pixel,img.shape[0]-r_pixel): 244 | for col in range(r_pixel,img.shape[1]-r_pixel): 245 | 246 | if key_point_test(row,col): 247 | key_point_list.append(cv2.KeyPoint(x=row,y=col,_size=1)) 248 | else: 249 | continue 250 | 251 | pass 252 | pass 253 | 254 | 255 | img_with_keypoints = cv2.drawKeypoints(img,key_point_list,outImage=np.array([]),color=(0,0,255)) 256 | cv2.imshow("show key points",img_with_keypoints) 257 | cv2.waitKey(0) 258 | ``` 259 | 下图是我们对一个小图片进行检测程序运行的结果,可以看到最明显的几个角点找到了,但是也有几个点漏掉了。这是因为我们设置16个点中超过9个点和中心点不同这个数值9对于这张图来说大了些。你如果设置为7那就都能检测到了。 260 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190722110318186.png) 261 | 参考文献: 262 | [1] https://medium.com/software-incubator/introduction-to-feature-detection-and-matching-65e27179885d 263 | [2] https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_feature2d/py_fast/py_fast.html#fast 264 | [3] https://medium.com/software-incubator/introduction-to-fast-features-from-accelerated-segment-test-4ed33dde6d65 265 | [4] https://www.youtube.com/watch?v=1Te8U_JR8SI -------------------------------------------------------------------------------- /finding_an_image_transform/book_A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/finding_an_image_transform/book_A.jpg -------------------------------------------------------------------------------- /finding_an_image_transform/book_B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/finding_an_image_transform/book_B.jpg -------------------------------------------------------------------------------- /image_smooth_blur/lenna.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/image_smooth_blur/lenna.jpg -------------------------------------------------------------------------------- /image_smooth_blur/图像光滑化处理与高斯模糊毛玻璃效果.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/image_smooth_blur/图像光滑化处理与高斯模糊毛玻璃效果.md -------------------------------------------------------------------------------- /image_smooth_blur/高斯模糊效果.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/image_smooth_blur/高斯模糊效果.png -------------------------------------------------------------------------------- /img/orb_效果图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/img/orb_效果图.png -------------------------------------------------------------------------------- /introduction to robotics/homework1/laserscan.dat: -------------------------------------------------------------------------------- 1 | 1.16 1.16 1.17 1.16 1.16 1.16 1.16 1.16 1.16 1.16 1.16 1.17 1.16 1.17 1.17 1.16 1.16 1.16 1.16 1.17 1.18 1.18 1.18 1.18 1.19 1.19 1.20 1.20 1.21 1.20 1.21 1.21 1.21 1.22 1.22 1.22 1.22 1.23 1.23 1.24 1.24 1.24 1.25 1.26 1.26 1.26 1.27 1.27 1.28 1.28 1.29 1.29 1.30 1.31 1.31 1.32 1.32 1.33 1.34 1.34 1.35 1.35 1.35 1.36 1.37 1.37 1.38 1.39 1.40 1.42 1.42 1.43 1.45 1.46 1.47 1.47 1.48 1.49 1.51 1.52 1.53 1.54 1.55 1.56 1.57 1.58 1.59 1.60 1.62 1.63 1.65 1.66 1.68 1.69 1.70 1.71 1.73 1.83 1.91 1.96 1.98 2.00 2.02 2.05 2.08 2.09 2.12 2.15 2.18 2.20 2.24 2.26 2.28 2.32 2.35 2.39 2.42 2.46 2.50 2.54 2.56 2.53 2.53 2.52 2.52 2.54 2.60 2.66 2.71 2.75 2.80 2.87 2.91 2.99 3.05 3.12 3.18 3.25 3.33 3.41 3.49 3.58 3.69 3.82 3.93 4.04 4.17 4.29 4.42 4.57 10.05 10.01 10.01 9.98 9.97 9.93 9.93 9.89 9.89 9.86 9.86 9.83 9.84 9.80 9.81 9.79 9.80 9.77 9.77 9.76 9.76 9.75 9.76 9.73 9.74 9.75 9.82 9.84 9.88 9.84 9.88 9.87 9.88 9.85 9.83 9.79 7.85 6.93 6.33 5.76 5.24 4.83 4.46 4.18 3.88 3.65 3.45 3.27 3.10 2.96 2.84 2.72 2.53 2.48 2.44 2.30 2.21 2.17 2.09 1.99 1.94 1.87 1.84 1.78 1.71 1.68 1.66 1.59 1.58 1.52 1.50 1.49 1.42 1.39 1.36 8.80 1.31 1.29 8.89 1.22 1.17 1.16 1.18 1.16 9.11 9.17 1.10 1.08 9.27 9.33 9.40 9.45 1.01 1.00 9.62 9.64 9.69 9.71 0.94 0.92 0.89 0.88 10.06 10.11 7.42 7.41 0.91 10.39 0.82 0.81 10.65 10.71 10.63 10.52 10.41 10.32 0.87 10.16 0.74 0.74 0.79 0.73 9.68 9.62 9.55 9.47 9.42 9.35 9.28 9.20 0.67 0.67 0.67 0.66 8.89 5.68 8.76 8.73 8.68 8.60 8.56 8.49 8.44 8.43 0.63 8.32 0.62 0.61 8.18 0.60 8.11 8.01 9.93 7.29 7.93 9.80 9.75 7.84 7.80 9.63 7.78 7.70 0.59 7.67 0.57 0.58 9.40 0.58 7.53 9.30 7.49 9.24 7.43 9.17 9.15 7.36 9.10 7.33 7.30 9.04 7.28 8.99 8.99 7.22 0.55 7.19 0.54 0.53 7.17 0.53 7.15 8.84 6.52 8.82 8.81 7.08 8.78 7.08 8.77 7.06 7.05 8.75 7.05 8.74 8.73 8.73 0.53 7.04 0.52 0.53 0.51 2 | -------------------------------------------------------------------------------- /introduction to robotics/homework1/sensing_solution.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | scan = np.loadtxt('laserscan.dat') 5 | angle = np.linspace(-math.pi/2,math.pi/2,np.shape(scan)[0],endpoint='true') 6 | 7 | 8 | 9 | def to_ref_laser(_scan,_angle): 10 | """ 11 | 将雷达采集到的数据转成相对雷达的坐标。 12 | 原理:雷达采集到的数据是极坐标,要转成直角坐标 13 | """ 14 | _x_ref_laser = _scan*np.cos(_angle) 15 | _y_ref_laser = _scan*np.sin(_angle) 16 | return _x_ref_laser,_y_ref_laser 17 | 18 | def to_ref_world(_scan,_angle): 19 | # 将雷达采样点的直角坐标转换为齐次坐标 20 | _x_ref_laser,_y_ref_laser =to_ref_laser(_scan,_angle) 21 | homopos_ref_laser = np.stack((_x_ref_laser,_y_ref_laser,np.ones(_x_ref_laser.shape[0]))) 22 | 23 | # 计算将雷达坐标系转换到机器人坐标系的变换矩阵 24 | angle_laser_robot = np.pi 25 | T_laser_robot = np.array([ 26 | [np.cos(angle_laser_robot),-np.sin(angle_laser_robot),0.2], 27 | [np.sin(angle_laser_robot),np.cos(angle_laser_robot),0.0], 28 | [0.0, 0.0, 1.0], 29 | ]) 30 | # 计算采样点在机器人坐标系下的坐标 31 | homopos_ref_robot = np.matmul(T_laser_robot,homopos_ref_laser) 32 | 33 | # 计算机器人坐标系变换到世界坐标系的变换矩阵 34 | angle_robot_world = np.pi/4 35 | T_robot_world = np.array([ 36 | [np.cos(angle_robot_world),-np.sin(angle_robot_world),1.0], 37 | [np.sin(angle_robot_world),np.cos(angle_robot_world),0.5], 38 | [0.0, 0.0, 1.0], 39 | ]) 40 | # 计算采样点在世界坐标系下的坐标 41 | homopos_ref_world = np.matmul(T_robot_world,homopos_ref_robot) 42 | return homopos_ref_world[0],homopos_ref_world[1] 43 | 44 | 45 | def plot_points(_point_cordinate): 46 | plt.scatter(_point_cordinate[0],_point_cordinate[1]) 47 | 48 | # 绘制扫描的各个点相对雷达的坐标 49 | # plot_points(to_ref_laser(scan,angle)) 50 | 51 | # 绘制机器人,雷达,和雷达扫描到的点 52 | # 绘制机器人中心点 53 | plot_points(np.array([1,0.5])) 54 | # 绘制雷达所在点 55 | plot_points(to_ref_world(np.array([0]),np.array([0]))) 56 | # 绘制雷达扫到的点 57 | plot_points(to_ref_world(scan,angle)) 58 | 59 | plt.gca().set_aspect('equal', adjustable='box') 60 | plt.show() -------------------------------------------------------------------------------- /introduction to robotics/homework2/sheet03.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/introduction to robotics/homework2/sheet03.pdf -------------------------------------------------------------------------------- /introduction to robotics/homework3/sheet04.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/introduction to robotics/homework3/sheet04.pdf -------------------------------------------------------------------------------- /introduction to robotics/教科书_textbook/贝叶斯推理好书非常多的例子bayesian reasoning and maching learning.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/introduction to robotics/教科书_textbook/贝叶斯推理好书非常多的例子bayesian reasoning and maching learning.pdf -------------------------------------------------------------------------------- /joint_robot_simulation/two_joint_arm_robot.py: -------------------------------------------------------------------------------- 1 | """ 2 | @author 李韬——知乎@Ai酱 3 | 教程地址:https://blog.csdn.net/varyshare/article/details/96885179 4 | """ 5 | import numpy as np 6 | from numpy import cos, sin, arccos, arctan2, sqrt 7 | import matplotlib.pyplot as plt 8 | 9 | (target_x,target_y) = (1,1) # 机器人要到达的目标点 10 | class TwoLinkArm: 11 | """ 12 | 两连杆手臂模拟。 13 | 所使用的变量与模拟实体对应关系如下所示: 14 | (joint0)——连杆0——(joint1)——连杆1——[joint2] 15 | 注意:joint0是基座也是坐标原点(0,0) 16 | """ 17 | 18 | def __init__(self, _joint_angles=[0, 0]): 19 | # 第0个关节是基座所以坐标固定是原点(0,0) 20 | self.joint0 = np.array([0, 0]) 21 | # 机器人两段连杆(手臂)的长度 22 | self.link_lengths = [1, 1] 23 | self.update_joints(_joint_angles) 24 | 25 | def update_joints(self, _joint_angles): 26 | self.joint_angles = _joint_angles 27 | self.forward_kinematics() 28 | 29 | def forward_kinematics(self): 30 | """ 31 | 根据各个关节角计算各个关节的位置. 32 | 注意:所使用的变量与模拟实体对应关系如下所示: 33 | (joint0)——连杆0——(joint1)——连杆1——[joint2] 34 | """ 35 | 36 | # 计算关节1的位置 37 | # q0,q1分别是第0和第1个关节的关节角 38 | q0 = self.joint_angles[0] 39 | a0 = self.link_lengths[0] 40 | self.joint1 = self.joint0 + [a0 * cos(q0), a0 * sin(q0)] 41 | # 计算关节2的位置 42 | q1 = self.joint_angles[1] 43 | a1 = self.link_lengths[1] 44 | # 注意:q1是杆1相对于杆0的延长线的转角,而杆0相对水平线的转角是q0 45 | # 所以杆1相对水平线的转角是(q0+q1), 而joint2是杆1的末端 46 | self.joint2 = self.joint1 + [a1 * cos(q0 + q1), a1 * sin(q0 + q1)] 47 | 48 | def plot(self): 49 | """ 50 | 绘制当前状态下的机械臂 51 | """ 52 | 53 | # 清理坐标系中的内容 54 | plt.cla() 55 | 56 | # 三个关节的坐标 57 | x = [self.joint0[0], self.joint1[0], self.joint2[0]] 58 | y = [self.joint0[1], self.joint1[1], self.joint2[1]] 59 | # 绘制这样的一条线——连杆0————连杆1—— 60 | plt.plot(x, y, c="red", zorder=1) 61 | # 绘制三个黑圆点代表关节,zorder=2是为了让绘制的点盖在直线上面 62 | plt.scatter(x, y, c="black", zorder=2) 63 | # 绘制目标点 64 | global target_x,target_y 65 | plt.scatter(target_x,target_y,c='blue',marker='*') 66 | # 固定住坐标系, 67 | # 不让它乱变,不让我点击的坐标和它显示的坐标不是一个坐标 68 | plt.xlim(-2, 2) 69 | plt.ylim(-2, 2) 70 | 71 | 72 | 73 | def inverse_kinematic(self, x, y): 74 | """ 75 | 逆运动学求解要达到(x,y)需要转动的角度, 76 | 返回机器人各关节需要转动的角度 77 | """ 78 | a0 = self.link_lengths[0] 79 | a1 = self.link_lengths[1] 80 | q1 = arccos((x ** 2 + y ** 2 - a0 ** 2 - a1 ** 2) / (2 * a0 * a1)) 81 | q0 = arctan2(y, x) - arctan2(a1 * sin(q1), a1 * cos(q1) + a0) 82 | return [q0, q1] 83 | 84 | def animation(self,x,y): 85 | _joint_angles = self.inverse_kinematic(x, y) 86 | 87 | # 将这个角度变化过程分解成一个1s内的执行15步的慢动作 88 | duration_time_seconds = 1 89 | actions_num = 15 90 | angles_per_action = (np.array(_joint_angles) - np.array(self.joint_angles))/actions_num 91 | plt.ion() # 开启交互模式不然没有动画效果 92 | for action_i in range(actions_num): 93 | 94 | self.joint_angles = np.array(self.joint_angles) + angles_per_action 95 | self.update_joints(self.joint_angles) 96 | self.plot() 97 | dt = duration_time_seconds/actions_num 98 | plt.pause(dt) 99 | 100 | 101 | 102 | def to_mouse_posi(self,event): 103 | """ 104 | 鼠标点击事件处理函数:记录鼠标在坐标系中的位置(x,y) 105 | 然后将其设置为机器人要到达的目标点 106 | """ 107 | global target_x, target_y 108 | if event.xdata == None or event.ydata == None: 109 | print("请在坐标系内选择一个点") 110 | return 111 | target_x = event.xdata 112 | target_y = event.ydata 113 | self.animation(target_x,target_y) 114 | 115 | 116 | # --------------------------------- 117 | def main(): 118 | fig = plt.figure() 119 | arm_robot = TwoLinkArm() 120 | arm_robot.animation(target_x,target_y) 121 | fig.canvas.mpl_connect("button_press_event", arm_robot.to_mouse_posi) 122 | plt.ioff() # 一定要在plt.show()之前终止交互模式不然会一闪而过 123 | plt.show() 124 | 125 | 126 | if __name__ == "__main__": 127 | main() 128 | pass 129 | -------------------------------------------------------------------------------- /joint_robot_simulation/two_link_arm_robot_result.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/joint_robot_simulation/two_link_arm_robot_result.gif -------------------------------------------------------------------------------- /joint_robot_simulation/两连杆关节机械臂机器人给定位置求解各关节转动角度教程模拟Python实现.md: -------------------------------------------------------------------------------- 1 | # 两连杆关节机械臂机器人给定位置求解各关节转动角度教程模拟Python实现 2 | 我们要解决的问题是**已知一个目标点坐标(x,y),已知两个连杆的长度a1,a2,我们的目标是求q1,q2这两个关节角**.如下图所示: 3 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190723152329992.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 4 | 因为已知坐标(x,y)即我们已知下图中的三角形的两个直角边。根据勾股定理可以得到斜边的长度为$r=\sqrt{x^2+y^2}$. 5 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190723152504849.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 6 | 因此下面这个三角形所有的边都是已知的了。 7 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190723152813954.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 8 | 高中的几何学告诉我们三条边已知的话那就可以根据余弦定理求出一个角。因此我们是计划把那个大角$\alpha$求出来。为什么?因为求出$\alpha$那么我们就可以求出关节角q2,因为它们是互为补角。$q2=180-\alpha$。现在我们已经求出一个关节角了。 9 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190723153338783.png) 10 | 现在我们知道了角度q2.而且知道第2个杆的长度a2. 因此我们可以解出下面这个三角形的两条边. 11 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190723153448942.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 12 | 于是乎,我们现在已知下面这个直角三角形的两条直角边。根据反切公式可以求出$\beta$这个锐角。为什么要求$\beta$这个锐角?请看后面的分析。 13 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190723153617365.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 14 | 下面这个图的三角形的两个直角边就是目标点的横坐标和纵坐标x,y。那么我们是可以求出边y对着的那个角,并且$\beta$我们已经求出了。因此我们可以求出我们想要的关节角q1. 15 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190723154206689.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 16 | # 总结: 17 | 现在我们得到了两个关节角的求解方式。 18 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190723154341456.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 19 | 但是由于cos函数是一个对称函数,所以同一个值会对应两个可能的角度。**这也符合我们的预期,因为除非两个杆完全在同一条线上,否则任意给定一个目标位置就一定可以给出两种不同的弯曲方式**。 20 | **情况1**: 21 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190723154526445.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 22 | **情况2**: 23 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190723154712884.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 24 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190723154813450.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 25 | # Python编程实践模拟一个二连杆机器人 26 | ## 前向运动可视化 27 | ```python 28 | """ 29 | @author 李韬——知乎@Ai酱 30 | 教程地址:https://blog.csdn.net/varyshare/article/details/96885179 31 | """ 32 | import numpy as np 33 | from math import sin,cos,pi 34 | import matplotlib.pyplot as plt 35 | class TwoLinkArm: 36 | """ 37 | 两连杆手臂模拟。 38 | 所使用的变量与模拟实体对应关系如下所示: 39 | (joint0)——连杆0——(joint1)——连杆1——[joint2] 40 | 注意:joint0是基座也是坐标原点(0,0) 41 | """ 42 | def __init__(self,_joint_angles=[0,0]): 43 | # 第0个关节是基座所以坐标固定是原点(0,0) 44 | self.joint0 = np.array([0,0]) 45 | # 机器人两段连杆(手臂)的长度 46 | self.link_lengths = [1,1] 47 | self.update_joints(_joint_angles) 48 | self.forward_kinematics() 49 | 50 | 51 | def update_joints(self, _joint_angles): 52 | self.joint_angles = _joint_angles 53 | 54 | def forward_kinematics(self): 55 | """ 56 | 根据各个关节角计算各个关节的位置. 57 | 注意:所使用的变量与模拟实体对应关系如下所示: 58 | (joint0)——连杆0——(joint1)——连杆1——[joint2] 59 | """ 60 | 61 | # 计算关节1的位置 62 | # q0,q1分别是第0和第1个关节的关节角 63 | q0 = self.joint_angles[0] 64 | a0 = self.link_lengths[0] 65 | self.joint1 = self.joint0 + [a0*cos(q0), a0*sin(q0)] 66 | 67 | # 计算关节2的位置 68 | q1 = self.joint_angles[1] 69 | a1 = self.link_lengths[1] 70 | # 注意:q1是杆1相对于杆0的延长线的转角,而杆0相对水平线的转角是q0 71 | # 所以杆1相对水平线的转角是(q0+q1), 而joint2是杆1的末端 72 | self.joint2 = self.joint1 + [a1*cos(q0+q1), a1*sin(q0+q1)] 73 | 74 | def plot(self): 75 | """ 76 | 绘制当前状态下的机械臂 77 | """ 78 | # 三个关节的坐标 79 | x = [self.joint0[0],self.joint1[0],self.joint2[0]] 80 | y = [self.joint0[1],self.joint1[1],self.joint2[1]] 81 | # 绘制这样的一条线——连杆0————连杆1—— 82 | plt.plot(x, y,c='red',zorder=1) 83 | # 绘制三个黑圆点代表关节,zorder=2是为了让绘制的点盖在直线上面 84 | plt.scatter(x,y,c='black',zorder=2) 85 | plt.show() 86 | def transform(_points,_theta,_origin): 87 | """ 88 | 求这些点_points绕_origin这个点旋转_theta度后相对世界坐标系的坐标。 89 | 注意_points的坐标是相对以_origin为原点的坐标系下的坐标。 90 | _origin: 旋转中心点在世界坐标系下的坐标 91 | _points: 相对旋转中心这个 92 | _theta: 旋转的角度 93 | """ 94 | T = np.array([ 95 | [cos(_theta), -sin(_theta), _origin[0]], 96 | [sin(_theta), cos(_theta), _origin[1]], 97 | [0, 0, 1] 98 | ]) 99 | 100 | 101 | arm_robot = TwoLinkArm([pi/6,pi/4]) 102 | arm_robot.plot() 103 | ``` 104 | 运行截图: 105 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/2019072320201481.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZhcnlzaGFyZQ==,size_16,color_FFFFFF,t_70) 106 | ## 鼠标选定屏幕上一点,然后求逆解进行运动Python实现代码 107 | 108 | 代码地址(同一个文件夹):[two_joint_arm_robot.py](two_joint_arm_robot.py) 109 | 110 | 下面是效果图,**打开你的编辑器跟着我写的代码实践吧,你的赞和关注是我持续分享的动力**。 111 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190724160425592.gif) 112 | 113 | 114 | 115 | 参考文献: 116 | [1] https://robotacademy.net.au/lesson/inverse-kinematics-for-a-2-joint-robot-arm-using-geometry/ -------------------------------------------------------------------------------- /slam_book_list/前50页介绍李群李代数An elementary introduction to groups and representations.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/slam_book_list/前50页介绍李群李代数An elementary introduction to groups and representations.pdf -------------------------------------------------------------------------------- /solve_least_square/.ipynb_checkpoints/最小二乘法有什么用_如何用最小二乘法求解方程-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 最小二乘法到底是什么?有什么用?\n", 8 | "答:**最小二乘法一般用来求解线性方程组用的**。\n", 9 | "# 如何用最小二乘法求解线性方程组?\n", 10 | "比如现在有一组方程组,其中$a_i,b_i$是一个数字,$x_i$是我们需要求的变量。\n", 11 | "\n", 12 | "$a_1x_1 + a_2x_2 =b_1 \\\\\n", 13 | "a_3x_1 + a_4x_2 =b_2$\n", 14 | "\n", 15 | "我们为了方便编程我们先将上面这个方程组凑成矩阵相乘的形式:\n", 16 | "\n", 17 | "令$A= \\begin{bmatrix}\n", 18 | " a_1 & a_2 \\\\\n", 19 | " a_3 & a_4 \\\\\n", 20 | " \\end{bmatrix}$\n", 21 | " \n", 22 | " 令$X=\\begin{bmatrix}\n", 23 | " x_1 \\\\ x_2\n", 24 | " \\end{bmatrix}$\n", 25 | " \n", 26 | " 令$B=\\begin{bmatrix}\n", 27 | " b_1 \\\\ b_2\n", 28 | " \\end{bmatrix}$\n", 29 | " 于是前面提到的方程组可以写成$AX=B$。我们的目标就是求得$X$。很明显我们可以直接计算$X=A^{-1}B$。\n", 30 | " \n", 31 | " **但是通常数据是有噪声的,很可能AX=B这个方程组是无解的。但是就这样放弃了吗**?\n", 32 | " 在计算机界中一个很重要的思想就是虽然无法求得一个解,但是我们可以求得一个尽可能理想的解。**于是“最小二乘法”应运而生**。\n", 33 | " \n", 34 | " # 最小二乘法与解方程之间的联系是什么?\n", 35 | " \n", 36 | "前面提到了现实世界中可能无法求解$AX=B$,但是我们可以通过求$(AX-B)^2$这个东西的最小值时对应的$X$。为什么?因为$(AX-B)^2$的最小值是0.这意味着此时的$AX-B$会尽可能的接近0.这意味着此时的$X$满足$AX \\approx B$。所以此时的X是一个比较理想的解。我想现在你应该懂了最小二乘法中的“最小”“二乘”的含义了。\n", 37 | "\n", 38 | "由于$(AX-B)^2$它是一个关于$X$的二次函数。根据高中学的知识,因此我们可以就$(AX-B)^2$对$X$进行求导,当导数等于0时$(AX-B)^2$取得最小值。此时的X就是方程$AX=B$的一个比较理想的解。\n", 39 | "\n", 40 | "\n", 41 | " 现在我们开始看看如何就$(AX-B)^2$对$X$求导并且让它等于0(注意这里A,X,B是矩阵)。\n", 42 | " \n", 43 | "于是我们得到\n", 44 | " $A^TAX=A^TB$,所以$X=(A^TA)^{-1}A^TB$。\n" 45 | ] 46 | } 47 | ], 48 | "metadata": { 49 | "kernelspec": { 50 | "display_name": "Python 3", 51 | "language": "python", 52 | "name": "python3" 53 | }, 54 | "language_info": { 55 | "codemirror_mode": { 56 | "name": "ipython", 57 | "version": 3 58 | }, 59 | "file_extension": ".py", 60 | "mimetype": "text/x-python", 61 | "name": "python", 62 | "nbconvert_exporter": "python", 63 | "pygments_lexer": "ipython3", 64 | "version": "3.7.0" 65 | } 66 | }, 67 | "nbformat": 4, 68 | "nbformat_minor": 2 69 | } 70 | -------------------------------------------------------------------------------- /solve_least_square/最小二乘法有什么用_如何用最小二乘法求解方程.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 最小二乘法到底是什么?有什么用?\n", 8 | "答:**最小二乘法一般用来求解线性方程组用的**。\n", 9 | "# 如何用最小二乘法求解线性方程组?\n", 10 | "比如现在有一组方程组,其中$a_i,b_i$是一个数字,$x_i$是我们需要求的变量。\n", 11 | "\n", 12 | "$a_1x_1 + a_2x_2 =b_1 \\\\\n", 13 | "a_3x_1 + a_4x_2 =b_2$\n", 14 | "\n", 15 | "我们为了方便编程我们先将上面这个方程组凑成矩阵相乘的形式:\n", 16 | "\n", 17 | "令$A= \\begin{bmatrix}\n", 18 | " a_1 & a_2 \\\\\n", 19 | " a_3 & a_4 \\\\\n", 20 | " \\end{bmatrix}$\n", 21 | " \n", 22 | " 令$X=\\begin{bmatrix}\n", 23 | " x_1 \\\\ x_2\n", 24 | " \\end{bmatrix}$\n", 25 | " \n", 26 | " 令$B=\\begin{bmatrix}\n", 27 | " b_1 \\\\ b_2\n", 28 | " \\end{bmatrix}$\n", 29 | " 于是前面提到的方程组可以写成$AX=B$。我们的目标就是求得$X$。很明显我们可以直接计算$X=A^{-1}B$。\n", 30 | " \n", 31 | " **但是通常数据是有噪声的,很可能AX=B这个方程组是无解的。但是就这样放弃了吗**?\n", 32 | " 在计算机界中一个很重要的思想就是虽然无法求得一个解,但是我们可以求得一个尽可能理想的解。**于是“最小二乘法”应运而生**。\n", 33 | " \n", 34 | " # 最小二乘法与解方程之间的联系是什么?\n", 35 | " \n", 36 | "前面提到了现实世界中可能无法求解$AX=B$,但是我们可以通过求$(AX-B)^2$这个东西的最小值时对应的$X$。为什么?因为$(AX-B)^2$的最小值是0.这意味着此时的$AX-B$会尽可能的接近0.这意味着此时的$X$满足$AX \\approx B$。所以此时的X是一个比较理想的解。我想现在你应该懂了最小二乘法中的“最小”“二乘”的含义了。\n", 37 | "\n", 38 | "由于$(AX-B)^2$它是一个关于$X$的二次函数。根据高中学的知识,因此我们可以就$(AX-B)^2$对$X$进行求导,当导数等于0时$(AX-B)^2$取得最小值。此时的X就是方程$AX=B$的一个比较理想的解。\n", 39 | "\n", 40 | "\n", 41 | " 现在我们开始看看如何就$(AX-B)^2$对$X$求导并且让它等于0(注意这里A,X,B是矩阵)。\n", 42 | " \n", 43 | "于是我们得到\n", 44 | " $A^TAX=A^TB$,所以$X=(A^TA)^{-1}A^TB$。\n" 45 | ] 46 | } 47 | ], 48 | "metadata": { 49 | "kernelspec": { 50 | "display_name": "Python 3", 51 | "language": "python", 52 | "name": "python3" 53 | }, 54 | "language_info": { 55 | "codemirror_mode": { 56 | "name": "ipython", 57 | "version": 3 58 | }, 59 | "file_extension": ".py", 60 | "mimetype": "text/x-python", 61 | "name": "python", 62 | "nbconvert_exporter": "python", 63 | "pygments_lexer": "ipython3", 64 | "version": "3.7.0" 65 | } 66 | }, 67 | "nbformat": 4, 68 | "nbformat_minor": 2 69 | } 70 | -------------------------------------------------------------------------------- /前50页介绍李群李代数An elementary introduction to groups and representations.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varyshare/easy_slam_tutorial/58a9172b09c34249b8f7d055d53256f29a44b8dd/前50页介绍李群李代数An elementary introduction to groups and representations.pdf --------------------------------------------------------------------------------