{"id":2667,"date":"2018-12-12T14:27:53","date_gmt":"2018-12-12T13:27:53","guid":{"rendered":"https:\/\/www.macerobotics.com\/?p=2667"},"modified":"2018-12-12T14:31:53","modified_gmt":"2018-12-12T13:31:53","slug":"mrpiz-suivie-de-ligne-avec-opencv-python","status":"publish","type":"post","link":"https:\/\/www.macerobotics.com\/?p=2667","title":{"rendered":"MRPiZ &#8211; suivie de ligne avec openCV &#038; python"},"content":{"rendered":"<p>Ce tutorial pr\u00e9sente l&#8217;impl\u00e9mentation d&#8217;un suivi de ligne pour le robot mobile MRPiZ.<\/p>\n<div class=\"ttfmake-embed-wrapper aligncenter\" style=\"max-width: 960px;\"><iframe loading=\"lazy\" title=\"MRPiZ - raspberry pi robot - line following with openCV\" width=\"960\" height=\"540\" src=\"https:\/\/www.youtube.com\/embed\/aivx8BPpwwQ?start=4&#038;feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen><\/iframe><\/div>\n<h2>Mat\u00e9riel n\u00e9cessaire<\/h2>\n<ul>\n<li>Un robot MRPiZ<\/li>\n<li>Une cam\u00e9ra compatible, id\u00e9alement grand angle.<\/li>\n<\/ul>\n<div>&nbsp;<\/div>\n<h2>Logiciels n\u00e9cessaire<\/h2>\n<ul>\n<li>Python 2.7 (d\u00e9ja install\u00e9 par d\u00e9faut),<\/li>\n<li>La biblioth\u00e8que Python MRPiZ (d\u00e9ja install\u00e9e par d\u00e9faut),<\/li>\n<li>OpenCV pour python<\/li>\n<\/ul>\n<h1>&nbsp;<\/h1>\n<h2>Activation de video4linux<\/h2>\n<p>Deux m\u00e9thodes sont possibles pour acc\u00e9der \u00e0 la cam\u00e9ra:<\/p>\n<ol>\n<li>PiCamera: la m\u00e9thode la plus r\u00e9pandue, mais lente car il est n\u00e9cessaire de transformer l&#8217;image pour la mettre au bon format,<\/li>\n<li>v4l: qui s&#8217;interface directement avec OpenCV, c&#8217;est la m\u00e9thode choisie pour ce tutorial.<\/li>\n<\/ol>\n<p>Il nous faut donc activer v4l:<\/p>\n<div class=\"highlight highlight-source-shell\">\n<pre>$ sudo modprobe bcm2835-v4l2<\/pre>\n<\/div>\n<div>\n<p><span style=\"color: #ff0000;\"><strong>Warning<\/strong><\/span><\/p>\n<p>Cette commande est a effectuer \u00e0 chaque red\u00e9marrage.<a name=\"user-content-le-suivi-de-ligne\"><\/a><\/p>\n<\/div>\n<h2>&nbsp;<\/h2>\n<h2>Le suivi de ligne<\/h2>\n<p>Le fichier complet se trouve dans Software\/Python\/tutorials\/line_follower\/line.py.<\/p>\n<div>\n<p><span style=\"color: #ff0000;\"><strong>Warning<\/strong><\/span><\/p>\n<p>Utilisez CTRL+C pour arr\u00eater le robot.<\/p>\n<\/div>\n<p><a name=\"user-content-importation-des-modules\"><\/a><\/p>\n<h3>Importation des modules<\/h3>\n<div class=\"highlight highlight-source-python\">\n<pre><span class=\"pl-k\">import<\/span> numpy <span class=\"pl-k\">as<\/span> np\r\n<span class=\"pl-k\">import<\/span> cv2\r\n<span class=\"pl-k\">import<\/span> sys\r\n<span class=\"pl-k\">from<\/span> mrpiZ_lib <span class=\"pl-k\">import<\/span> <span class=\"pl-k\">*<\/span><\/pre>\n<\/div>\n<p><a name=\"user-content-parametres-globaux\"><\/a><\/p>\n<h3>Param\u00e9tr\u00e9s globaux<\/h3>\n<div class=\"highlight highlight-source-python\">\n<pre><span class=\"pl-c\"># image size<\/span>\r\n<span class=\"pl-c1\">WIDTH<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-c1\">640<\/span>\r\n<span class=\"pl-c1\">HEIGHT<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-c1\">480<\/span>\r\n\r\n<span class=\"pl-c\"># turn coeff<\/span>\r\n<span class=\"pl-c1\">COEFF<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-c1\">0.05<\/span>\r\n<span class=\"pl-c\"># base robot speed in straight line<\/span>\r\n<span class=\"pl-c1\">SPEED<\/span> <span class=\"pl-k\">=<\/span> <span class=\"pl-c1\">30<\/span><\/pre>\n<\/div>\n<p><a name=\"user-content-activation-de-la-camera\"><\/a><\/p>\n<h3>Activation de la cam\u00e9ra<\/h3>\n<p>Pour am\u00e9liorer les performances, la r\u00e9solution est r\u00e9duite \u00e0 640 pixels en largeur et 480 en hauteur.<\/p>\n<div class=\"highlight highlight-source-python\">\n<pre>video_capture <span class=\"pl-k\">=<\/span> cv2.VideoCapture(<span class=\"pl-c1\">0<\/span>)\r\nvideo_capture.set(<span class=\"pl-c1\">3<\/span>, <span class=\"pl-c1\">WIDTH<\/span>)\r\nvideo_capture.set(<span class=\"pl-c1\">4<\/span>, <span class=\"pl-c1\">HEIGHT<\/span>)<\/pre>\n<\/div>\n<p><a name=\"user-content-boucle-principale\"><\/a><\/p>\n<h3>Boucle principale<\/h3>\n<p>La boucle principale va fonctionner \u00e0 l&#8217;infini, pour l&#8217;arr\u00eater il faudra appuyer sur CTRL+C.<\/p>\n<div class=\"highlight highlight-source-python\">\n<pre><span class=\"pl-k\">try<\/span>:\r\n    <span class=\"pl-k\">while<\/span>(<span class=\"pl-c1\">True<\/span>):<\/pre>\n<\/div>\n<p><a name=\"user-content-capture-de-l-image\"><\/a><\/p>\n<h3>Capture de l&#8217;image<\/h3>\n<p>Premi\u00e8re \u00e9tape, on commence par capturer une image.<\/p>\n<div class=\"highlight highlight-source-python\">\n<pre><span class=\"pl-c\"># Capture the frames<\/span>\r\nret, frame <span class=\"pl-k\">=<\/span> video_capture.read()<\/pre>\n<\/div>\n<p>Voici un exemple d&#8217;image captur\u00e9e:<\/p>\n<p><a href=\"https:\/\/github.com\/macerobotics\/MRPiZ\/blob\/master\/doc\/fr\/source\/tutorials\/line_follower\/0_init.jpg\" target=\"_blank\" rel=\"noopener noreferrer\"><img decoding=\"async\" src=\"https:\/\/github.com\/macerobotics\/MRPiZ\/raw\/master\/doc\/fr\/source\/tutorials\/line_follower\/0_init.jpg\" alt=\"0_init.jpg\"><\/a><\/p>\n<h3>Suppression de la partie haute<\/h3>\n<p>Pour am\u00e9liorer les performances, on ne va garder que la partie basse de l&#8217;image:<\/p>\n<div class=\"highlight highlight-source-python\">\n<pre><span class=\"pl-c\"># Crop the image<\/span>\r\n<span class=\"pl-c\"># Keep the 100 lower pixels<\/span>\r\ncrop_img <span class=\"pl-k\">=<\/span> frame[<span class=\"pl-c1\">379<\/span>:<span class=\"pl-c1\">480<\/span>, <span class=\"pl-c1\">0<\/span>:<span class=\"pl-c1\">640<\/span>]<\/pre>\n<\/div>\n<p><a href=\"https:\/\/github.com\/macerobotics\/MRPiZ\/blob\/master\/doc\/fr\/source\/tutorials\/line_follower\/1_crop.jpg\" target=\"_blank\" rel=\"noopener noreferrer\"><img decoding=\"async\" src=\"https:\/\/github.com\/macerobotics\/MRPiZ\/raw\/master\/doc\/fr\/source\/tutorials\/line_follower\/1_crop.jpg\" alt=\"1_crop.jpg\"><\/a><\/p>\n<h2>Niveaux de gris<\/h2>\n<p>Ensuite on passe l&#8217;image en niveaux de gris:<\/p>\n<div class=\"highlight highlight-source-python\">\n<pre><span class=\"pl-c\"># Convert to grayscale<\/span>\r\ngray <span class=\"pl-k\">=<\/span> cv2.cvtColor(crop_img, cv2.<span class=\"pl-c1\">COLOR_BGR2GRAY<\/span>)<\/pre>\n<\/div>\n<p><a href=\"https:\/\/github.com\/macerobotics\/MRPiZ\/blob\/master\/doc\/fr\/source\/tutorials\/line_follower\/2_gray.jpg\" target=\"_blank\" rel=\"noopener noreferrer\"><img decoding=\"async\" src=\"https:\/\/github.com\/macerobotics\/MRPiZ\/raw\/master\/doc\/fr\/source\/tutorials\/line_follower\/2_gray.jpg\" alt=\"2_gray.jpg\"><\/a><\/p>\n<h2>Flou<\/h2>\n<p><a name=\"user-content-flou\"><\/a><\/p>\n<p>Un filtre afin de rendre flou les lignes de l&#8217;image est appliqu\u00e9, il permet de rendre plus efficace les \u00e9tapes suivantes:<\/p>\n<div class=\"highlight highlight-source-python\">\n<pre><span class=\"pl-c\"># Gaussian blur<\/span>\r\nblur <span class=\"pl-k\">=<\/span> cv2.GaussianBlur(gray,(<span class=\"pl-c1\">5<\/span>,<span class=\"pl-c1\">5<\/span>),<span class=\"pl-c1\">0<\/span>)<\/pre>\n<\/div>\n<p><a href=\"https:\/\/github.com\/macerobotics\/MRPiZ\/blob\/master\/doc\/fr\/source\/tutorials\/line_follower\/3_blur.jpg\" target=\"_blank\" rel=\"noopener noreferrer\"><img decoding=\"async\" src=\"https:\/\/github.com\/macerobotics\/MRPiZ\/raw\/master\/doc\/fr\/source\/tutorials\/line_follower\/3_blur.jpg\" alt=\"3_blur.jpg\"><\/a><\/p>\n<p><a name=\"user-content-seuillage\"><\/a><\/p>\n<h3>Seuillage<\/h3>\n<p>Ensuite on va filtrer les parties claires de l&#8217;image pour ne garder les parties noires, pour cela, un filtre de seuillage est appliqu\u00e9:<\/p>\n<div class=\"highlight highlight-source-python\">\n<pre><span class=\"pl-c\"># Color thresholding<\/span>\r\nret,thresh <span class=\"pl-k\">=<\/span> cv2.threshold(blur,<span class=\"pl-c1\">60<\/span>,<span class=\"pl-c1\">255<\/span>,cv2.<span class=\"pl-c1\">THRESH_BINARY_INV<\/span>)<\/pre>\n<\/div>\n<p><a href=\"https:\/\/github.com\/macerobotics\/MRPiZ\/blob\/master\/doc\/fr\/source\/tutorials\/line_follower\/4_thresh.jpg\" target=\"_blank\" rel=\"noopener noreferrer\"><img decoding=\"async\" src=\"https:\/\/github.com\/macerobotics\/MRPiZ\/raw\/master\/doc\/fr\/source\/tutorials\/line_follower\/4_thresh.jpg\" alt=\"4_thresh.jpg\"><\/a><\/p>\n<p><a name=\"user-content-detection-de-contours\"><\/a><\/p>\n<h3>D\u00e9tection de contours<\/h3>\n<p>Ensuite, on va utiliser openCV pour d\u00e9tecter les contours:<\/p>\n<div class=\"highlight highlight-source-python\">\n<pre><span class=\"pl-c\"># Find the contours of the frame<\/span>\r\ncontours,hierarchy <span class=\"pl-k\">=<\/span> cv2.findContours(thresh.copy(), <span class=\"pl-c1\">1<\/span>, cv2.<span class=\"pl-c1\">CHAIN_APPROX_NONE<\/span>)<\/pre>\n<\/div>\n<p><a href=\"https:\/\/github.com\/macerobotics\/MRPiZ\/blob\/master\/doc\/fr\/source\/tutorials\/line_follower\/5_contour.jpg\" target=\"_blank\" rel=\"noopener noreferrer\"><img decoding=\"async\" src=\"https:\/\/github.com\/macerobotics\/MRPiZ\/raw\/master\/doc\/fr\/source\/tutorials\/line_follower\/5_contour.jpg\" alt=\"5_contour.jpg\"><\/a><\/p>\n<p><a name=\"user-content-extraction-du-plus-gros-contour\"><\/a><\/p>\n<h3>Extraction du plus gros contour<\/h3>\n<p>Il nous faut ensuite extraire la ligne la plus large trouv\u00e9e afin d&#8217;\u00e9liminer les fausses d\u00e9tections:<\/p>\n<div class=\"highlight highlight-source-python\">\n<pre><span class=\"pl-c\"># Find the biggest contour (if detected)<\/span>\r\n<span class=\"pl-k\">if<\/span> <span class=\"pl-c1\">len<\/span>(contours) <span class=\"pl-k\">&gt;<\/span> <span class=\"pl-c1\">0<\/span>:\r\n    c <span class=\"pl-k\">=<\/span> <span class=\"pl-c1\">max<\/span>(contours, <span class=\"pl-v\">key<\/span><span class=\"pl-k\">=<\/span>cv2.contourArea)\r\n    M <span class=\"pl-k\">=<\/span> cv2.moments(c)\r\n\r\n    <span class=\"pl-c\"># Skip to avoid div by zero<\/span>\r\n    <span class=\"pl-k\">if<\/span> <span class=\"pl-c1\">int<\/span>(M[<span class=\"pl-s\"><span class=\"pl-pds\">'<\/span>m00<span class=\"pl-pds\">'<\/span><\/span>]) <span class=\"pl-k\">==<\/span> <span class=\"pl-c1\">0<\/span>:\r\n        <span class=\"pl-k\">continue<\/span><\/pre>\n<\/div>\n<p><a name=\"user-content-calcul-du-milieu-de-la-ligne\"><\/a><\/p>\n<h3>Calcul du milieu de la ligne<\/h3>\n<p>Une fois les contours de la ligne d\u00e9tect\u00e9e, on calcul le centre de la ligne, c&#8217;est la que l&#8217;on veut que le robot aille:<\/p>\n<div class=\"highlight highlight-source-python\">\n<pre><span class=\"pl-c\"># Get the line center<\/span>\r\ncx <span class=\"pl-k\">=<\/span> <span class=\"pl-c1\">int<\/span>(M[<span class=\"pl-s\"><span class=\"pl-pds\">'<\/span>m10<span class=\"pl-pds\">'<\/span><\/span>]<span class=\"pl-k\">\/<\/span>M[<span class=\"pl-s\"><span class=\"pl-pds\">'<\/span>m00<span class=\"pl-pds\">'<\/span><\/span>])\r\ncy <span class=\"pl-k\">=<\/span> <span class=\"pl-c1\">int<\/span>(M[<span class=\"pl-s\"><span class=\"pl-pds\">'<\/span>m01<span class=\"pl-pds\">'<\/span><\/span>]<span class=\"pl-k\">\/<\/span>M[<span class=\"pl-s\"><span class=\"pl-pds\">'<\/span>m00<span class=\"pl-pds\">'<\/span><\/span>])<\/pre>\n<\/div>\n<p><a name=\"user-content-controle-des-moteurs\"><\/a><\/p>\n<h3>Contr\u00f4le des moteurs<\/h3>\n<p>Une correction proportionnelle \u00e0 la diff\u00e9rence entre la position de la ligne et le milieu de l&#8217;image est calcul\u00e9e. Les moteurs sont ensuite command\u00e9s pour ralentir un des moteurs et acc\u00e9l\u00e9rer l&#8217;autre, ceci afin de faire tourner le robot en direction du centre de la ligne.<\/p>\n<div class=\"highlight highlight-source-python\">\n<pre>delta <span class=\"pl-k\">=<\/span> <span class=\"pl-c1\">COEFF<\/span> <span class=\"pl-k\">*<\/span> (cx <span class=\"pl-k\">-<\/span> <span class=\"pl-c1\">320<\/span>)\r\nmotorRight(<span class=\"pl-c1\">0<\/span>, <span class=\"pl-c1\">SPEED<\/span> <span class=\"pl-k\">-<\/span> delta)\r\nmotorLeft(<span class=\"pl-c1\">0<\/span>, <span class=\"pl-c1\">SPEED<\/span> <span class=\"pl-k\">+<\/span> delta)<\/pre>\n<\/div>\n<p><a name=\"user-content-clavier\"><\/a><\/p>\n<h3>Clavier<\/h3>\n<p>Enfin, deux lignes de code permettent d&#8217;arr\u00eater le robot quand on appuie sur CTRL+C.<\/p>\n<div class=\"highlight highlight-source-python\">\n<pre><span class=\"pl-k\">except<\/span> <span class=\"pl-c1\">KeyboardInterrupt<\/span>:\r\n    stop()<\/pre>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Ce tutorial pr\u00e9sente l&#8217;impl\u00e9mentation d&#8217;un suivi de ligne pour le robot mobile MRPiZ. Mat\u00e9riel n\u00e9cessaire Un robot MRPiZ Une cam\u00e9ra compatible, id\u00e9alement grand angle. &nbsp; Logiciels n\u00e9cessaire Python 2.7 (d\u00e9ja install\u00e9 par d\u00e9faut), La biblioth\u00e8que Python MRPiZ (d\u00e9ja install\u00e9e par d\u00e9faut), OpenCV pour python &nbsp; Activation de video4linux Deux m\u00e9thodes sont possibles pour acc\u00e9der \u00e0 &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[55,45,46,26],"tags":[],"class_list":["post-2667","post","type-post","status-publish","format-standard","hentry","category-mrpiz","category-raspberry-pi","category-robot","category-tutoriel"],"_links":{"self":[{"href":"https:\/\/www.macerobotics.com\/index.php?rest_route=\/wp\/v2\/posts\/2667","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.macerobotics.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.macerobotics.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.macerobotics.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.macerobotics.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2667"}],"version-history":[{"count":4,"href":"https:\/\/www.macerobotics.com\/index.php?rest_route=\/wp\/v2\/posts\/2667\/revisions"}],"predecessor-version":[{"id":2671,"href":"https:\/\/www.macerobotics.com\/index.php?rest_route=\/wp\/v2\/posts\/2667\/revisions\/2671"}],"wp:attachment":[{"href":"https:\/\/www.macerobotics.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2667"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.macerobotics.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2667"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.macerobotics.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2667"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}