基于Raspberry Pi的番茄分类机设计

描述

  手动分类项目是最费力和最耗时的任务之一。手动分拣,无论是水果还是蔬菜或其他任何东西,都需要大量的人力和时间。因此,在本教程中,我们尝试构建一个能够区分红番茄和绿番茄的番茄分选机。

  分拣机所需组件

  硬件

  树莓派

  Pi 相机模块

  2×伺服电机

  软件

  边缘脉冲工作室

  Edge Impulse 入门

  要使用Edge Impulse Raspberry Pi训练机器学习模型,请创建一个 Edge Impulse 帐户,验证您的帐户,然后开始一个新项目。

分类机

  在 Raspberry Pi 上安装 Edge Impulse

  现在要在 Raspberry Pi 上使用 Edge Impulse,您首先必须在 Raspberry Pi 上安装 Edge Impulse 及其依赖项。使用以下命令在 Raspberry 上安装 Edge Impulse:

  curl -sL https://deb.nodesource.com/setup_12.x | 须藤重击 -

  sudo apt install -y gcc g++ make build-essential nodejs sox gstreamer1.0-tools gstreamer1.0-plugins-good gstreamer1.0-plugins-base gstreamer1.0-plugins-base-apps

  sudo npm install edge-impulse-linux -g --unsafe-perm

  现在使用以下命令运行 Edge Impulse:

  边缘脉冲Linux

  您将被要求登录您的 Edge Impulse 帐户。然后系统会要求您选择一个项目,最后选择一个麦克风和摄像头以连接到该项目。

分类机

  现在由于 Edge Impulse 在 Raspberry Pi 上运行,我们必须将 Pi 相机模型与 Pi 连接以进行图像采集。如下图所示连接 Pi 相机:

分类机

  创建数据集

  如前所述,我们使用 Edge Impulse Studio 来训练我们的图像分类模型。为此,我们必须收集一个数据集,其中包含我们希望使用 Pi 相机进行分类的对象样本。由于目标是对红番茄和绿番茄进行分类,因此您需要收集一些红番茄和绿番茄的样本图像,以便区分两者。

  您可以通过手机、树莓派板子采集样本,也可以将数据集导入边缘脉冲账户。将样本加载到 Edge Impulse 中的最简单方法是使用您的手机。为此,您必须将您的手机与 Edge Impulse 连接。

  要连接您的手机,请单击“设备”,然后单击“连接新设备”。

分类机

  现在在下一个窗口中单击“使用您的手机”,将出现一个二维码。使用您的手机使用 Google Lens 或其他 QR 码扫描仪应用程序扫描 QR 码。这会将您的手机与 Edge Impulse studio 连接起来。

分类机

  将手机与 Edge Impulse Studio 连接后,您现在可以加载样本。要加载样本,请单击“数据采集”。现在在数据采集页面上输入标签名称并选择“相机”作为传感器。点击“开始采样”。

分类机

  这会将番茄图像保存到 Edge Impulse 云中。从不同角度拍摄 50 到 60 张图像。上传样本后,现在将标签设置为“Green Tomato”并收集另外 50 到 60 张图像。除了绿色和红色番茄的样本外,还要收集一些不确定情况的样本,以防框架中没有任何东西。

分类机

  这些样本用于训练模块,在接下来的步骤中,我们将收集测试数据。测试数据应至少占训练数据的 20%。

  训练模型

  当我们的数据集准备好后,现在我们将为我们的数据创建一个脉冲。为此,请访问“创造冲动”页面。

分类机

  现在在“创建脉冲”页面上,单击“添加处理块”,然后单击“图像”块旁边的“添加”按钮添加一个处理块,该处理块将标准化图像数据并减少颜色深度。之后,单击“迁移学习(图像) ”块以获取用于图像分类的预训练模型,我们将在该模型上执行迁移学习以针对我们的番茄识别任务对其进行调整。然后点击“保存冲动”。

分类机

  接下来,转到“脉冲设计”菜单项下的“图像”子项,然后单击“生成特征”选项卡,然后点击绿色的“生成特征”按钮。

分类机

  之后,点击“Impulse design”菜单项下的“Transfer learning”子项,点击页面底部的“Start training”按钮。这里我们使用了默认的 MobileNetV2。如果需要,您可以使用不同的训练模型。

分类机

  训练模型需要一些时间。训练模型后,它将显示训练性能。对我来说,准确率是 75%,损失是 0.58。我们现在可以测试我们训练好的模型。为此,单击左侧菜单中的“实时分类”选项卡,然后您可以使用 Raspberry Pi 相机拍摄样本图像。

  在 Raspberry Pi 上部署经过训练的模型

  训练过程完成后,我们可以将训练好的 Edge 脉冲图像分类模型部署到 Raspberry Pi。有两种方法可以做到这一点,一种是使用边缘脉冲 linux runner 命令。这将通过全硬件加速自动编译训练好的模型,将模型下载到树莓派,然后开始分类,无需编写任何代码,另一种方法是下载模型文件,然后使用 python SDK 示例进行图像分类。

  第一种方法很简单,进入终端窗口输入以下命令:

  edge-impulse-linux-runner

  如果 edge-impulse-linux 命令已在运行,则按 Control-C 将其停止,然后输入上述命令。如果您已经分配了一个项目并想清除它以启动一个新项目,请使用以下命令:

  边缘脉冲跑步者--清洁

  这会将 Raspberry Pi 连接到 Edge Impulse 云并下载最近训练的模型,并启动视频流。结果将显示在终端窗口中。

分类机

  您还可以使用 Raspberry Pi IP 地址在浏览器上打开视频流。但由于我们的目标是构建一个红绿番茄分拣机,我们必须使用第二种方法,即使用 python SDK 示例。通过以下方式下载模型文件:

  edge-impulse-linux-runner --下载modelfile.eim

  现在克隆此存储库以获取用于对象分类、语音识别等的 python 示例:

  git 克隆 https://github.com/edgeimpulse/linux-sdk-python

  在这里,我们将对红色和绿色西红柿进行分类,因此我们将使用此存储库的示例文件夹中的分类.py 示例。使用以下命令运行此代码:

  python3 分类.py 模型文件.eim

  其中modefile.eim是经过训练的模型文件名。确保此文件与代码位于同一文件夹中。

  运行代码后,它将打印检测到的对象的概率,如下图所示:

分类机

  现在我们必须对代码进行一些调整,以便我们可以根据检测移动伺服系统。预测的标签和分数存储在标签和分数变量中,因此我们将这些分数值存储在一个数组中,然后将这三个值分配给三个不同的值,以便我们可以轻松地比较它们并相应地移动舵机。文档末尾还提供了包含所有更改的完整代码。

  对于标签中的标签:

  score = res[‘result’][‘classification’][label]

  print(‘%s: %.2f\t’ % (label, score), end=‘’)

  data.append(分数)

  打印(‘’,冲洗=真)

  绿色=圆形(数据[0],2)

  红色 = 圆形(数据 [1],2)

  不确定=轮(数据[2],2)

  如果(绿色 》=0.45 和 framee_count%10 ==0):

  而(绿色》 = 0.35):

  pwm1.ChangeDutyCycle(12.0)

  time.sleep(0.500)

  pwm1.ChangeDutyCycle(2.0) #close

  时间。睡眠(0.250)

  pwm.ChangeDutyCycle(7.0)

  time.sleep(0.450)

  pwm.ChangeDutyCycle(2.0)

  绿色=0.01

  如果(红色 》=0.50 和 framee_count%10 ==0):

  而(红色》 = 0.50):

  pwm1.ChangeDutyCycle(7.0)

  time.sleep(0.500)

  pwm1.ChangeDutyCycle(2.0)

  时间。睡眠(0.250)

  pwm.ChangeDutyCycle(7.0)

  time.sleep(0.450)

  pwm.ChangeDutyCycle(2.0)

  红色=0.01

  树莓派分拣机电路图

  为了移动西红柿,我们将两个伺服电机连接到 Raspberry Pi。一个伺服器用于一个接一个地移动西红柿,第二个伺服器用于将西红柿放入各自的盒子中。

分类机

  如电路图所示,第一个舵机连接到 GPIO 25,第二个舵机连接到 Raspberry Pi 的 GPIO 17。两个舵机均由 Raspberry Pi 的 5V 和 GND 引脚供电。

  构建分拣机设置

  现在,随着训练和编码部分的完成,让我们进入下一个部分,即为西红柿分类进行完整设置。我们使用了 2mm 厚的白色 Sunboard 和两个伺服电机。第一个伺服电机用于一个接一个地移动西红柿,第二个伺服电机用于根据颜色将西红柿放入盒子中。将所有部件连接在一起后,这台分拣机将如下所示:

分类机

  现在要测试设置,将一些西红柿放入托盘中,在摄像头下方放一个西红柿,然后在 Raspberry Pi 上启动代码。

分类机

  该项目的完整工作显示在下面给出的视频中。除了根据颜色对番茄进行分类外,我们还可以根据番茄是否腐烂的状态对其进行分类。如果您有任何问题,请将它们放在评论部分,或者您可以使用我们的论坛开始讨论。

  代码

    #!/usr/bin/env python

  导入简历2

  导入操作系统

  导入系统,getopt

  进口信号

  进口时间

  从 edge_impulse_linux.image 导入 ImageImpulseRunner

  导入 RPi.GPIO 作为 GPIO

  跑步者=无

  show_camera = 假

  framee_count=0

  伺服销 = 25

  伺服1 = 17

  GPIO.setmode(GPIO.BCM)

  GPIO.setup(servo_pin,GPIO.OUT)

  GPIO.setup(servo1, GPIO.OUT)

  # 设置 PWM 进程

  pwm = GPIO.PWM(servo_pin,50) # 50 Hz(20 ms PWM 周期)

  pwm1 = GPIO.PWM(servo1,50)

  pwm.start(7) # 旋转 90 度启动 PWM

  pwm1.start(7)

  pwm1.ChangeDutyCycle(2.0)

  pwm.ChangeDutyCycle(2.0) #close

  现在定义():

  返回回合(时间。时间()* 1000)

  def get_webcams():

  port_ids = []

  对于范围内的端口(5):

  print(“正在端口 %s 中寻找摄像头:” %port)

  相机 = cv2.VideoCapture(端口)

  如果 camera.isOpened():

  ret = camera.read()

  如果重新:

  backendName =camera.getBackendName()

  w = camera.get(3)

  h = camera.get(4)

  print(“在端口 %s 中找到相机 %s (%sx %s) ” %(backendName,h,w, port))

  port_ids.append(端口)

  相机.release()

  返回 port_ids

  def sigint_handler(sig, frame):

  打印(‘中断’)

  如果(跑步者):

  runner.stop()

  系统退出(0)

  signal.signal(signal.SIGINT, sigint_handler)

  定义帮助():

  print(‘python分类.py 《path_to_model.eim》 《摄像头端口ID,仅当存在多于1个摄像头时才需要》’)

  定义主(argv):

  framee_count=0

  尝试:

  opts, args = getopt.getopt(argv, “h”, [“--help”])

  除了 getopt.GetoptError:

  帮助()

  系统退出(2)

  对于 opt,在 opts 中的 arg:

  如果选择加入(‘-h’,‘--help’):

  帮助()

  sys.exit()

  如果 len(args) == 0:

  帮助()

  系统退出(2)

  模型 = 参数 [0]

  dir_path = os.path.dirname(os.path.realpath(__file__))

  modelfile = os.path.join(dir_path, 模型)

  打印(‘模型:’ + 模型文件)

  以 ImageImpulseRunner(modelfile) 作为跑步者:

  尝试:

  model_info = runner.init()

  print(‘为 “’ + model_info[‘project’][‘owner’] + ‘ / ’ + model_info[‘project’][‘name’] + ‘”’ 加载了跑步者

  标签 = model_info[‘model_parameters’][‘labels’]

  如果 len(args)》= 2:

  videoCaptureDeviceId = int(args[1])

  别的:

  port_ids = get_webcams()

  如果 len(port_ids) == 0:

  raise Exception(‘找不到任何网络摄像头’)

  如果 len(args)《= 1 和 len(port_ids)》 1:

  raise Exception(“找到多个摄像头。将摄像头端口 ID 作为第二个参数添加到此脚本中”)

  videoCaptureDeviceId = int(port_ids[0])

  相机 = cv2.VideoCapture(videoCaptureDeviceId)

  ret = camera.read()[0]

  如果重新:

  backendName = camera.getBackendName()

  w = camera.get(3)

  h = camera.get(4)

  print(“已选择端口 %s 中的摄像机 %s (%sx %s)。” %(backendName,h,w, videoCaptureDeviceId))

  相机.release()

  别的:

  raise Exception(“无法初始化选定的相机。”)

  next_frame = 0 # 此处限制为 ~10 fps

  对于 runner.classifier(videoCaptureDeviceId) 中的 res、img:

  如果(下一个帧》现在()):

  time.sleep((next_frame - now()) / 1000)

  # print(‘分类运行响应’, res)

  数据 = []

  帧数 = 帧数 +1

  print(“帧数:”, framee_count)

  如果 res[“result”].keys() 中的“分类”:

  print(‘结果 (%d ms.) ’ % (res[‘timing’][‘dsp’] + res[‘timing’][‘classification’]), end=‘’)

  对于标签中的标签:

  score = res[‘result’][‘classification’][label]

  # 打印(分数)

  print(‘%s: %.2f\t’ % (label, score), end=‘’)

  data.append(分数)

  打印(‘’,冲洗=真)

  绿色=圆形(数据[0],2)

  红色 = 圆形(数据 [1],2)

  不确定=轮(数据[2],2)

  打印(绿色,红色,不确定)

  如果(绿色 》=0.25 和 framee_count%10 ==0):

  而(绿色》 = 0.25):

  pwm1.ChangeDutyCycle(12.0)

  print(“检测到绿色番茄”)

  time.sleep(0.500)

  pwm1.ChangeDutyCycle(2.0) #close

  时间。睡眠(0.250)

  pwm.ChangeDutyCycle(7.0)

  time.sleep(0.450)

  pwm.ChangeDutyCycle(2.0)

  绿色=0.01

  # time.sleep(2)

  如果(红色 》=0.50 和 framee_count%10 ==0):

  而(红色》 = 0.50):

  pwm1.ChangeDutyCycle(7.0)

  print(“检测到红番茄”)

  time.sleep(0.500)

  pwm1.ChangeDutyCycle(2.0)

  时间。睡眠(0.250)

  pwm.ChangeDutyCycle(7.0)

  time.sleep(0.450)

  pwm.ChangeDutyCycle(2.0)

  红色=0.01

  # time.sleep(2)

  别的:

  time.sleep(0.01)

  # print(‘%s: %.2f\t’ % (Green,Red,Uncertain), end =‘’)

  如果(show_camera):

  cv2.imshow(‘edgeimpulse’, img)

  如果 cv2.waitKey(1) == ord(‘q’):

  休息

  res[“result”].keys() 中的 elif “bounding_boxes”:

  print(‘找到 %d 个边界框 (%d ms.)’ % (len(res[“result”][“bounding_boxes”]), res[‘timing’][‘dsp’] + res[‘timing’] [‘分类’]))

  对于 res[“result”][“bounding_boxes”] 中的 bb:

  print(‘\t%s (%.2f): x=%dy=%dw=%dh=%d’ % (bb[‘label’], bb[‘value’], bb[‘x’], bb[‘y’], bb[‘width’], bb[‘height’]))

  next_frame = now() + 100

  最后:

  如果(跑步者):

  runner.stop()

  # framee_count=0

  如果 __name__ == “__main__”:

  主要(sys.argv[1:])

  帽释放()

  cv2.destroyAllWindows()

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
评论(0)
发评论
jf_50322138 2023-12-07
0 回复 举报
大佬,最后的代码可以发一下吗,谢谢 收起回复

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分