ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

基于uArm 的视觉分拣方案--代码分析--2021-02-24

2021-02-25 00:00:10  阅读:223  来源: 互联网

标签:02 Serial2 -- write uArm blob 串行 Serial openmv


以下代码为了做笔记方便,希望有错误的地方大家能给予修改意见,感谢!
代码均来自https://github.com/uArm-Developer/Vision-Pick-and-Place
Arduino参考链接:https://www.arduino.cc/reference/en/
使用 ArduinoMega
1. Arduino 函数简要介绍

  • Serial相关函数
    Arduino串行端口(也称为UART或USART),ArduinoMega有多个:
    在这里插入图片描述
//指示指定的串行端口是否已就绪。如果指定的串行端口可用,则返回true
if(Serial) 
//获取可用于从串行端口读取的字节数(字符)。这是已经到达并存储在串行接收缓冲区(包含64个字节)中的数据。返回可读取的字节数。
Serial.available()
//获取可用于在串行缓冲区中进行写入而不阻止写入操作的字节数(字符)。返回可以写入的字节数。
Serial.availableForWrite()
//设置以每秒比特数(波特)为单位的串行数据传输的数据速率。speed:以每秒位数(波特)为单位。允许的数据类型:long;config:设置数据,奇偶校验和停止位。无返回值。如:Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
Serial.begin(speed)
Serial.begin(speed, config)
//禁用串行通信,允许将RX和TX引脚用于常规输入和输出。无返回值。
Serial.end()
//从串行缓冲区读取数据,直到找到目标为止。如果找到目标,该函数将返回true;如果目标超时返回false。target:要搜索的字符串。允许的数据类型:char。length:目标的长度。允许的数据类型:size_t。
Serial.find(target)
Serial.find(target, length)
//从串行缓冲区读取数据,直到找到给定长度的目标字符串或终止符字符串。如果找到目标字符串,该函数将返回true,如果超时则返回false。target:要搜索的字符串。允许的数据类型:char。terminal:搜索中的终端字符串。允许的数据类型:char。
Serial.findUntil(target, terminal)
//等待输出串行数据的传输完成。无返回值。
Serial.flush()
//从串行缓冲区返回第一个有效的浮点数。parseFloat()以不是浮点数的第一个字符终止。返回浮点数据。ignore:用于跳过搜索中指示的字符。详细参见:https://www.arduino.cc/reference/en/language/functions/communication/serial/parsefloat/
Serial.parseFloat()
Serial.parseFloat(lookahead)
Serial.parseFloat(lookahead, ignore)
//在输入的序列中查找下一个有效整数。如果超时,该函数终止。返回long类型的下一个有效整数的
Serial.parseInt()
Serial.parseInt(lookahead)
Serial.parseInt(lookahead, ignore)
//返回传入串行数据的下一个字节(字符),而不将其从内部串行缓冲区中删除。也就是说,对的后续调用peek()将返回相同的字符,而对的下一个调用也将返回相同的字符read()。
Serial.peek()
//将数据作为人类可读的ASCII文本打印到串行端口。返回写入的字节数,尽管读取该数字是可选的。详细参见:https://www.arduino.cc/reference/en/language/functions/communication/serial/print/
Serial.print(val)
Serial.print(val, format)
//将数据作为人类可读的ASCII文本打印到串行端口,后跟回车符(ASCII 13,或'\ r')和换行符(ASCII 10,或'\ n')。此命令的格式与Serial.print()相同。返回写入的字节数,尽管读取该数字是可选的。
Serial.println(val)
Serial.println(val, format)
//读取传入的串行数据。输入的串行数据的第一个字节的可用数据(如果没有可用数据,则为-1)。数据类型:int
Serial.read()
//从串行端口读取字符到缓冲区。返回放置在缓冲区中的字节数。
Serial.readBytes(buffer, length)
//将来自串行缓冲区的字符读取到数组中。返回读入缓冲区的字符数。0表示length参数<= 0,在任何其他输入之前发生超时,或者在任何其他输入之前发现终止字符。character:要搜索的字符。允许的数据类型:char。buffer:用于存储字节的缓冲区。允许的数据类型:char或的数组byte。length:要读取的字节数。允许的数据类型:int。
Serial.readBytesUntil(character, buffer, length)
//从串行缓冲区读取字符到字符串。返回从串行缓冲器读的一个String。
Serial.readString()
//从串行缓冲区读取字符到字符串。返回从串行缓冲区读取的整个String,直到终止符。terminator:要搜索的字符。允许的数据类型:char。
Serial.readStringUntil(terminator)
//设置等待串行数据的最大毫秒数。默认值为1000毫秒。没有返回值。
Serial.setTimeout(time)
//将二进制数据写入串行端口。该数据以字节或一系列字节的形式发送;要发送代表数字数字的字符,请改用print()函数。val:要作为单个字节发送的值。str:作为一系列字节发送的字符串。buf:要作为一系列字节发送的数组。len:要从数组发送的字节数。返回写入的字节数。
Serial.write(val)
Serial.write(str)
Serial.write(buf, len)
//有数据时调用。使用Serial.read()捕捉到了这个数据。没有返回值。
serialEvent()
  • Digital I/O相关函数
//将指定的引脚配置为充当输入或输出。pin:用于设置模式的Arduino引脚号。mode:INPUT,OUTPUT,或INPUT_PULLUP。无返回值。
pinMode(pin, mode)
//从指定的数字引脚读取值,HIGH或LOW,返回HIGH 或者 LOW。
digitalRead(pin)
//将HIGH或LOW值写入数字引脚。pin:Arduino引脚号。value:HIGH或LOW。无返回值。
digitalWrite(pin, value)

2. Vision.ino代码解析

//After finishing the wiring, press the D5 button to run the code

int inByte = 0,//serial buf
    num = 0;//buf counter
int x_openmv=0, y_openmv=0;
int x_uarm=0, y_uarm=0;
unsigned long times;
char buf[20],
     flag=0;
char color_sel=1;// 0:yellow   1:red   2:green
     
unsigned char get_openmv_data();
void pick_and_palce();

void wait_for_finish_moving()
{
  inByte=0;//clear the buffer
  while(inByte!='@'){
     if (Serial2.available() > 0) {
        inByte = Serial2.read();
     }
  }
}

void setup() {
  pinMode(5,INPUT);//button
  pinMode(A3,OUTPUT);//orange led,A3也是一个引脚编号,十六进制形式
  digitalWrite(A3,LOW);
  //设置数据传输速率
  Serial.begin(115200);//usb or xbee
  Serial1.begin(115200);//openmv
  Serial2.begin(115200);//uarm
  
  //Move to XYZ(mm), F is speed(mm/min),参见链接:http://download.ufactory.cc/docs/en/Swift-Quick-Start-Guide.pdf
  Serial2.write("G0 X200 Y0 Z160 F10000\n");
//if button is pressed, then start the program
  while(digitalRead(5)==HIGH);
  digitalWrite(A3,HIGH);
  
  Serial.write("V2 START!\n");
  Serial2.write("M2400 S0\n");//set the mode of uarm
  delay(4000);
  Serial2.write("M2400 S0\n");//set the mode of uarm
  Serial2.write("M2122 V1\n");//report when finish the movemnet

  //返回自Arduino开发板开始运行当前程序以来经过的毫秒数。 大约50天后,该数字将溢出(返回零)。
  times = millis();
}

void loop() {
  if(flag == 0)
  {
    digitalWrite(A3,HIGH);
    Serial2.write("G0 X200 Y0 Z159 F10000\n");// in order to trig the report of finish movement '@'
    wait_for_finish_moving();
    //Serial2.write("G2202 N0 V90\n");
    //wait_for_finish_moving();
    Serial2.write("G0 X200 Y0 Z160 F10000\n");

    delay(100);//wait for the uarm to finish the moving then start the vision tracking
    wait_for_finish_moving();
    
    flag = 1;//vision start
    switch(color_sel){
      case 0: Serial1.write('y');break;
      case 1: Serial1.write('r');break;
      case 2: Serial1.write('g');break;
      default: break;
    }
    Serial1.write('S');//send vision start command
    Serial.write("vision start for finding the cube\n");//send vision start command
    times = millis();
    
  }
  digitalWrite(A3,LOW);
  //get commands from pc,读取openMv的数据,并传给uArm
  if (Serial.available() > 0) 
  {
    inByte = Serial.read(); //读取openMv的数据

    Serial2.write(inByte); //传给uArm
  }
  
  //get object coordinates from openmv
  if(get_openmv_data()==1)
  {
    flag = 0;//vision end
    Serial.write("move\n");//confirm the openmv data

//new algorithm
    x_uarm = y_openmv*(-0.7035)-3.635 + 88 + 70 + 200;
    y_uarm = x_openmv*(-0.7488)+12.391 + 107.5 + 15 +0;
    
    String commands="G0 X"; 
    commands.concat(x_uarm);
    commands+=" Y";
    commands.concat(y_uarm);
    commands+=" Z100 F10000\n";
    Serial2.print(commands);
    
    Serial.print(commands);

    pick_and_palce();    
  }
}

//get object coordinates from openmv
unsigned char get_openmv_data()
{
  if (Serial1.available() > 0) 
  {
    inByte = Serial1.read();
    buf[num++] = inByte;
    Serial.write(inByte);
    if((inByte=='\n')&&(buf[0]=='x'))
    {
      Serial.write("get openmv data\n");
      int counters=1;//jump the letter x
      x_openmv=0;
      do{
        x_openmv = x_openmv*10;
        x_openmv += buf[counters++] - 48;
      }while((buf[counters]>=0x30)&&(buf[counters]<=0x39));
      
      y_openmv=0;
      counters++;//jump the letter y
      do{
        y_openmv = y_openmv*10;
        y_openmv += buf[counters++] - 48;
      }while(counters+1<num);

      num = 0;
      return 1;
    }
    //Serial.println(x_openmv,DEC);
    //Serial.println(y_openmv,DEC);

  }
  if((millis()-times>10000)&&(flag==1))//if no object detected, reset the flag every 10s
  {
    //clear the uart buffers
    while(Serial1.available() > 0)
    {
      inByte = Serial1.read();
    }
    //reset the count of uart
    num = 0;
     times = millis();
     flag = 0;
     Serial.write("status 1\n");//NO OBJECT IN CAMERA
  }
  return 0;
}
//move the detected object to the fixed position
void pick_and_palce()
{
  Serial2.write("G0 Z23 F10000\n");
  Serial2.write("M2231 V1\n");
  Serial2.write("G0 Z120 F10000\n");
  delay(500);
  Serial2.write("G2202 N0 V15\n");
  Serial2.write("G0 Z50 F10000\n");
  Serial2.write("M2231 V0\n");
  Serial2.write("G0 Z80 F10000\n");
  Serial2.write("G2202 N0 V90\n");
  delay(8000);
  //change the color of tracking
  //color_sel++;
  //color_sel = color_sel%3;
}

3. color_tracking_test.py代码解析
参考链接:
https://docs.singtown.com/micropython/zh/latest/openmvcam/index.html
https://docs.singtown.com/micropython/zh/latest/openmvcam/library/pyb.UART.html#pyb-uart
https://docs.singtown.com/micropython/zh/latest/openmvcam/library/pyb.LED.html#pyb-led
https://docs.singtown.com/micropython/zh/latest/openmvcam/library/omv.image.html?highlight=blob%20rect#blob.rect

# Single Color Code Tracking Example
#
# This example shows off single color code tracking using the OpenMV Cam.
#
# A color code is a blob composed of two or more colors. The example below will
# only track colored objects which have both the colors below in them.

import sensor, image, time
from pyb import UART
from pyb import LED
blue_led  = LED(3)    # LED(3) -> 蓝色 RGB LED Segment
green_led  = LED(2)   # LED(2) -> 绿色 RGB LED Segment
red_led  = LED(1)     # LED(1) -> 红色 RGB LED Segment

uart = UART(3, 115200, timeout_char = 1000)  # 使用给定波特率初始化
blue_led.on()   # 打开LED,达到最大强度
# Color Tracking Thresholds (L Min, L Max, A Min, A Max, B Min, B Max)
# The below thresholds track in general red/green things. You may wish to tune them...
#thresholds = [(30, 100, 15, 127, 15, 127), # generic_red_thresholds -> index is 0 so code == (1 << 0)
#              (30, 100, -64, -8, -32, 32)] # generic_green_thresholds -> index is 1 so code == (1 << 1)

# 创建颜色编码元组
thresholds = [(55, 100,-24, 11, 32, 86),     #1#yellow
              (27, 100, 42, 80, 30, 64),     #2#red
              (39, 100,-51,-12, 10, 57)]      #4#green
# Codes are or'ed together when "merge=True" for "find_blobs".

sensor.reset()     # 初始化相机传感器
sensor.set_pixformat(sensor.RGB565)  # 初始化相机传感器-sensor.GRAYSCALE: 8-bits per pixel
sensor.set_framesize(sensor.QVGA)    # 设置相机模块的帧大小
sensor.skip_frames(time = 2000)   # 通过关键字参数 time 来跳过几毫秒的帧数,让相机图像在改变相机设置后稳定下来
sensor.set_auto_gain(False) # 若您想追踪颜色,则需关闭白平衡
sensor.set_auto_whitebal(False) # 若您想追踪颜色,则需关闭白平衡
clock = time.clock()   

blue_led.off()
green_led.off()
red_led.off()

# 色块(int)的中心x、y位置
object_x_old = 0
object_y_old = 0

code = 2 ## 1:yellow   2:red    4:green
buf = "00"
# Only blobs that with more pixels than "pixel_threshold" and more area than "area_threshold" are
# returned by "find_blobs" below. Change "pixels_threshold" and "area_threshold" if you change the
# camera resolution. "merge=True" must be set to merge overlapping color blobs for color codes.

while(True):
    clock.tick()   # 更新图像的帧率

    # 关闭所有的灯
    blue_led.off()  
    green_led.off()
    red_led.off()



    img = sensor.snapshot() # 使用相机拍摄一张照片,并返回 image 对象
    
    # img.find_blobs:查找图像中所有色块,并返回一个包括每个色块的色块对象的列表
    # thresholds 必须是元组列表。 [(lo, hi), (lo, hi), ..., (lo, hi)] 定义你想追踪的颜色范围。 对于灰度图像,每个元组需要包含两个值 - 最小灰度值和最大灰度值。 仅考虑落在这些阈值之间的像素区域。 对于RGB565图像,每个元组需要有六个值(l_lo,l_hi,a_lo,a_hi,b_lo,b_hi) - 分别是LAB L,A和B通道的最小值和最大值。
    # 若一个色块的边界框区域小于 area_threshold ,则会被过滤掉。
    # 若一个色块的像素数小于 pixel_threshold ,则会被过滤掉。
    # merge 若为True,则合并所有没有被过滤掉的色块,这些色块的边界矩形互相交错重叠。 margin 可在相交测试中用来增大或减小色块边界矩形的大小。例如:边缘为1、相互间边界矩形为1的色块将被合并。
    for blob in img.find_blobs(thresholds, pixels_threshold=100, area_threshold=100, merge=False):
#check with color should be detect
        if uart.any()>0 :   # 检查是否有内容有待读取
            buf=uart.read() # 读取字符,返回值:包含读入字节的bytes对象
            print (buf[0])
            if buf[0]==ord('y') : #ord()函数主要用来返回对应字符的ascii码
                code = 1
            if buf[0]==ord('r') :
                code = 2
            if buf[0]==ord('g') :
                code = 4

#check if there is object with right color
#Blob 类 – 色块对象
#blob.code()返回一个32位的二进制数字,其中为每个颜色阈值设置一个位,这是色块的一部分。
#如果您通过 image.find_blobs 来寻找三个颜色阈值,这个色块可以设置为0/1/2位。 注意:除非以 merge=True 调用 image.find_blobs ,否则每个色块只能设置一位。
        if blob.code() == code: 
#blob.rect()返回一个矩形元组(x, y, w, h) ,用于如色块边界框的 image.draw_rectangle 等 其他的 image 方法。
#blob.cx()返回色块(int)的中心x位置,可以通过索引 [5] 取得这个值
# blob.cy()返回色块(int)的中心y位置。您也可以通过索引 [6] 取得这个值。
            img.draw_rectangle(blob.rect())#在图像上绘制一个矩形
            img.draw_cross(blob.cx(), blob.cy())#在图像上绘制一个十字
            #print(blob.cx(), blob.cy(),blob.w())

#make sure the detected object is stable and print the coordinates
#first it detect if the coordinates of blob is available
#second compared with the last position to make sure if the object is not moving
#third reduce the affect of anbience
            if blob.cx()!=None and (
                abs(object_x_old - int(blob.cx())) < 8 and
                abs(object_y_old - int(blob.cy())) < 8) and (
                blob.w()>35 and
                blob.h()>35):
               #just detect the objects. turn on the blue only
               blue_led.on()
               red_led.off()
               green_led.off()
               #print("stable!")
               #print (buf)
#check if the uart got any command and response
               #if uart.any()>0 :

                    #buf=uart.read(1)
               if buf[1]==ord('S') :
                    #print("command\n")
                    #detect both the objects and the vision command from mega2560. turn on the red only
                    blue_led.off()
                    red_led.on()
                    green_led.off()

                    uart.write('x'+str(blob.cx())+'y'+str(blob.cy())+'\n')

                    #finish the sending. turn on the green only
                    blue_led.off()
                    red_led.off()
                    green_led.on()
                    #clear the flag
                    buf = "00"


            object_x_old = int(blob.cx())
            object_y_old = int(blob.cy())

标签:02,Serial2,--,write,uArm,blob,串行,Serial,openmv
来源: https://blog.csdn.net/weixin_42656213/article/details/113924864

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有