【方法】树莓派小车自动循迹(摄像头)

布满荆棘的人生 2022-04-10 06:24 1068阅读 0赞

Github上有更多代码,按需自取

文章目录

  • 说明
  • 注意
  • 代码

今天我们来介绍一下树莓派小车的循迹教程
首先看一个效果视频
在这里插入图片描述

说明

该小车的硬件是:树莓派+L298N,其实用Arduino也是一样的,下位机只提供一个车轮的控制,视觉识别都是通过树莓派完成的

视觉利用Opencv来实现,关于如何安装Opencv以及使用摄像头请参看我另一篇博客。

算法逻辑:看到这样一个赛道,赛道是白色的,其余部分是我们都看成非白色,这样很自然而然地就想到了二值化,将赛道的白色单独显示出来,通过一个二值化就能够很好的区分开赛道与背景。(二值化参考博客)
我们不需要分析整个摄像头范围,其实只需要选取图像中的一行像素,类似于一个线阵摄像头,中间部分是白色的,左右是黑色,所以只需要找到白色部分的中点,然后让小车一直朝着这个中点矫正就好了,如果偏左了,就让左轮加快速度,另一边同理。重点是过弯的时候,需要仔细调整这个速度,否则容易转不过去。

主要是GPIO口的一些操控,可以看我的另一篇博客

注意

尽量自己学会调试,查找问题,我的代码不是万能的,但你是万能的

一、腐蚀膨胀问题,原理我不在这里多解释了,自己选择是否腐蚀膨胀/先后顺序/迭代次数,会满足你不同的使用场景

二、转弯速度问题
30 + direction …
这些速度参数设置自己调节,甚至都可以采用缩放倍率

三、检查摄像头拍的图片
有发现拍出来的图最右边多了一列全白,解决办法有俩:1、先腐蚀,把白色去掉。2、别膨胀,把最后一列去掉,怎么去掉呢?white_index[0][white_count - 1] 把这个1改为2,相当于不去考虑最后一列

代码

  1. # coding:utf-8
  2. # 加入摄像头模块,让小车实现自动循迹行驶
  3. # 思路为:摄像头读取图像,进行二值化,将白色的赛道凸显出来
  4. # 选择下方的一行像素,黑色为0,白色为255
  5. # 找到白色值的中点
  6. # 目标中点与标准中点(320)进行比较得出偏移量
  7. # 根据偏移量来控制小车左右轮的转速
  8. # 考虑了偏移过多失控->停止;偏移量在一定范围内->高速直行(这样会速度不稳定,已删)
  9. import RPi.GPIO as gpio
  10. import time
  11. import cv2
  12. import numpy as np
  13. # 定义引脚
  14. pin1 = 12
  15. pin2 = 16
  16. pin3 = 18
  17. pin4 = 22
  18. # 设置GPIO口为BOARD编号规范
  19. gpio.setmode(gpio.BOARD)
  20. # 设置GPIO口为输出
  21. gpio.setup(pin1, gpio.OUT)
  22. gpio.setup(pin2, gpio.OUT)
  23. gpio.setup(pin3, gpio.OUT)
  24. gpio.setup(pin4, gpio.OUT)
  25. # 设置PWM波,频率为500Hz
  26. pwm1 = gpio.PWM(pin1, 500)
  27. pwm2 = gpio.PWM(pin2, 500)
  28. pwm3 = gpio.PWM(pin3, 500)
  29. pwm4 = gpio.PWM(pin4, 500)
  30. # pwm波控制初始化
  31. pwm1.start(0)
  32. pwm2.start(0)
  33. pwm3.start(0)
  34. pwm4.start(0)
  35. # center定义
  36. center = 320
  37. # 打开摄像头,图像尺寸640*480(长*高),opencv存储值为480*640(行*列)
  38. cap = cv2.VideoCapture(0)
  39. while (1):
  40. ret, frame = cap.read()
  41. # 转化为灰度图
  42. gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  43. # 大津法二值化
  44. retval, dst = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)
  45. # 膨胀,白区域变大
  46. dst = cv2.dilate(dst, None, iterations=2)
  47. # # 腐蚀,白区域变小
  48. # dst = cv2.erode(dst, None, iterations=6)
  49. # 单看第400行的像素值
  50. color = dst[400]
  51. # 找到白色的像素点个数
  52. white_count = np.sum(color == 255)
  53. # 找到白色的像素点索引
  54. white_index = np.where(color == 255)
  55. # 防止white_count=0的报错
  56. if white_count == 0:
  57. white_count = 1
  58. # 找到白色像素的中心点位置
  59. center = (white_index[0][white_count - 1] + white_index[0][0]) / 2
  60. # 计算出center与标准中心点的偏移量
  61. direction = center - 320
  62. print(direction)
  63. # 停止
  64. if abs(direction) > 250:
  65. pwm1.ChangeDutyCycle(0)
  66. pwm2.ChangeDutyCycle(0)
  67. pwm3.ChangeDutyCycle(0)
  68. pwm4.ChangeDutyCycle(0)
  69. # 右转
  70. elif direction >= 0:
  71. # 限制在70以内
  72. if direction > 70:
  73. direction = 70
  74. pwm1.ChangeDutyCycle(30 + direction)
  75. pwm2.ChangeDutyCycle(0)
  76. pwm3.ChangeDutyCycle(30)
  77. pwm4.ChangeDutyCycle(0)
  78. # 左转
  79. elif direction < 0:
  80. if direction < -70:
  81. direction = -70
  82. pwm1.ChangeDutyCycle(30)
  83. pwm2.ChangeDutyCycle(0)
  84. pwm3.ChangeDutyCycle(30 - direction)
  85. pwm4.ChangeDutyCycle(0)
  86. if cv2.waitKey(1) & 0xFF == ord('q'):
  87. break
  88. # 释放清理
  89. cap.release()
  90. cv2.destroyAllWindows()
  91. pwm1.stop()
  92. pwm2.stop()
  93. pwm3.stop()
  94. pwm4.stop()
  95. gpio.cleanup()

发表评论

表情:
评论列表 (有 0 条评论,1068人围观)

还没有评论,来说两句吧...

相关阅读