"Come, refuse, go, stay" is a small car based on Openmv ranging to identify objects and maintain a constant distance

Recently, a classmate came to me because his graduation project was about to be concluded. His project was to make a car that could recognize the QR code and realize ranging. At the same time, he also hoped that the car would keep a constant distance from the QR code. That is, when the QR code approaches the car, the car will retreat, and when the QR code moves away from the car, the car will move forward. (there is a feeling of hard to get)
To achieve this function, I have made the following tutorials for your reference. The source code is put in the article.
Part 1
Materials and modules required: L298n motor, two L298n motor drive boards (which are practical, cheap and powerful), one Openmv, one mcnamu wheel, one arduino and expansion board, one 12v power supply), and several DuPont lines
00:29
VID_20210507_120807

"Come, refuse, go, stay" is a small car based on Openmv ranging to identify objects and maintain a constant distance

The scheme is as follows
1. Drive the motor and the trolley can travel
2.Openmv keeps ranging and constantly perceives the distance from Apriltag
3.Openmv communicates the perceived distance with arduino through serial port, and arduino judges
4. After Arduino's judgment, give instructions to the motor. If the distance is small, the trolley will move backward. If the distance is large, the trolley will move forward
After sorting out the ideas, is it very simple?
Part 2
Background: AprilTag is a visual reference system that can be used for various tasks, including AR, robot and camera calibration. This tag can be printed directly by the printer, and the AprilTag detection program can calculate the precise 3D position, direction and id relative to the camera. This is particularly useful for OpenMV! It looks like this:

A very powerful tag

In short, openmv can recognize the x, y, z coordinates and angular rotation of Apriltag, which is a very cow tag. The establishment system is as follows. Openmv is the center of the origin, so in general, z has always been negative. (because openmv faces forward)

Official code

# AprilTags Example
#
# This example shows the power of the OpenMV Cam to detect April Tags
# on the OpenMV Cam M7. The M4 versions cannot detect April Tags.
import sensor, image, time, math

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA) # we run out of memory if the resolution is much bigger...
sensor.skip_frames(30)
sensor.set_auto_gain(False)  # must turn this off to prevent image washout...
sensor.set_auto_whitebal(False)  # must turn this off to prevent image washout...
clock = time.clock()

# be careful! And find_ Different from qrcodes, find_apriltags can work without software to correct distortion.

# Note that the unit of output posture is radian, which can be converted into angle, but the unit of position is related to your size, so it needs to be converted in equal proportion

# f_x is the focal length in pixels of x. For standard OpenMV, it should be equal to 2.8 / 3.984 * 656. This value is the focal length in millimeters divided by the length of the photosensitive element in the x direction and multiplied by the pixel of the photosensitive element in the x direction (OV7725)
# f_y is the focal length in pixels of y. For standard OpenMV, it should be equal to 2.8 / 2.952 * 488. This value is the focal length in millimeters divided by the length of the photosensitive element in the y direction and multiplied by the pixel of the photosensitive element in the y direction (OV7725)

# c_x is the x center of the image
# c_y is the y center of the image

f_x = (2.8 / 3.984) * 160 # Default value
f_y = (2.8 / 2.952) * 120 # Default value
c_x = 160 * 0.5 # Default (image.w * 0.5)
c_y = 120 * 0.5 # Default (image.h * 0.5)

def degrees(radians):
    return (180 * radians) / math.pi

while(True):
    clock.tick()
    img = sensor.snapshot()
    for tag in img.find_apriltags(fx=f_x, fy=f_y, cx=c_x, cy=c_y): # The default is TAG36H11
        img.draw_rectangle(tag.rect(), color = (255, 0, 0))
        img.draw_cross(tag.cx(), tag.cy(), color = (0, 255, 0))
        print_args = (tag.x_translation(), tag.y_translation(), tag.z_translation(), \
            degrees(tag.x_rotation()), degrees(tag.y_rotation()), degrees(tag.z_rotation()))
        # The unit of position is unknown, and the unit of rotation is angle
        print("Tx: %f, Ty %f, Tz %f, Rx %f, Ry %f, Rz %f" % print_args)
    print(clock.fps())

In the official code, the X, y and Z coordinates of tag and the rotation angle of Rx, Ry and Rz are finally printed. For this project, we don't need to know the angle, so only x, y and Z are related to us. Whether the trolley moves forward or backward, we only need to care about whether the distance in the z-axis direction changes. Therefore, X and y have nothing to do with the project. We only need to pay attention to the distance in the z-axis.
Therefore, in the code I give at last, only the coordinates of Z are printed on the serial port. During serial communication, I only transmit the z-axis coordinates.
The Openmv code is as follows

import sensor, image, time, math
import json
from pyb import UART
uart = UART(3, 9600)#Serial communication must be defined

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA) # we run out of memory if the resolution is much bigger...
sensor.skip_frames(30)
sensor.set_auto_gain(False)  # must turn this off to prevent image washout...
sensor.set_auto_whitebal(False)  # must turn this off to prevent image washout...
clock = time.clock()

# be careful! And find_ Different from qrcodes, find_apriltags can work without software to correct distortion.
# Note that the unit of output posture is radian, which can be converted into angle, but the unit of position is related to your size, so it needs to be converted in equal proportion
# f_x is the focal length in pixels of x. For standard OpenMV, it should be equal to 2.8 / 3.984 * 656. This value is the focal length in millimeters divided by the length of the photosensitive element in the x direction and multiplied by the pixel of the photosensitive element in the x direction (OV7725)
# f_y is the focal length in pixels of y. For standard OpenMV, it should be equal to 2.8 / 2.952 * 488. This value is the focal length in millimeters divided by the length of the photosensitive element in the y direction and multiplied by the pixel of the photosensitive element in the y direction (OV7725)
# c_x is the x center of the image
# c_y is the y center of the image

f_x = (2.8 / 3.984) * 160 # Default value
f_y = (2.8 / 2.952) * 120 # Default value
c_x = 160 * 0.5 # Default (image.w * 0.5)
c_y = 120 * 0.5 # Default (image.h * 0.5)

def degrees(radians):
    return (180 * radians) / math.pi

while(True):
    clock.tick()
    img = sensor.snapshot()
    for tag in img.find_apriltags(fx=f_x, fy=f_y, cx=c_x, cy=c_y):
        img.draw_rectangle(tag.rect(), color = (255, 0, 0))
        img.draw_cross(tag.cx(), tag.cy(), color = (0, 255, 0))
        a= int(- tag.z_translation())#As mentioned above, z has always been negative. Here we force tag z becomes positive and a becomes int
        print_args = str(a)#Change a into string type to facilitate the data transmission in json format
        data_out = json.dumps(set(print_args))#Print of string type_ Args becomes json data format
        uart.write(data_out +'\n')#Output in serial port buffer
        print('you send:',data_out)
        print("Tz: %f" % tag.z_translation())

In this code, we have transformed the data format many times because we use tag z_ The data obtained by the translations function is of float type, which is first converted to int type to facilitate the conversion to string type, while the conversion to string type is to facilitate the conversion to json data format and data transmission.
However, the conversion from float to int has errors to some extent, but it is small. In pursuit of high precision, you can try to convert float directly to string. (Note: in this case, there will be more data transmitted and communication delay.)
Both schemes will have delays. If only the effect is pursued, the integer can be passed directly.
Arduino partial code

```c
#include <SoftwareSerial.h>
#include "Arduino.h"
#include "Motor_drive.h"

#define A_1  36
#define A_2  37
#define B_1  35
#define B_2  34
#define C_1  32
#define C_2  33
#define D_1  31
#define D_2  30
#define conA  4
#define conB  5
#define conC  6
#define conD  7

SoftwareSerial softSerial(10, 11); // RX, TX
Motor_drive motor_drive=Motor_drive(A_1,A_2,B_1,B_2,C_1,C_2,D_1,D_2,conA,conB,conC,conD);   


void setup() 
{
  softSerial.begin(9600);
  Serial.begin(9600);
}
void loop() 
{
  if(softSerial.available())
 {
    String s = detectString();
    int a=s[1];
     Serial.print(a);
      Serial.print('\n');
    Serial.print(s[1]);
    Serial.print('\n');
    Servo(s[1]);
    
  }
}

String detectString()
{
  while(softSerial.read() != '{');
  return(softSerial.readStringUntil('}'));
}

void Servo(int a)
{
    if(a<55)
     {
    motor_drive.Forward(128);
    delay(10);
     Serial.print('0');
     Serial.print('\n');
     }
    if(a>55)
     {
     motor_drive.Backward(128);
     delay(10);
     Serial.print('1');
     Serial.print('\n');
     }
      
}

``

Note that I wrote a motor drive library here, so in the function, after I judge the distance, I directly use the function in the library. If I don't import and store, I can't compile successfully. Because Baidu online disk can't log in temporarily, if you need the library, you can send me a private letter.
Part 3
Summary, attention points
1. When transmitting data, tag z_ The result of translations is a variable of float type, such as 7.238493. I convert it to int type, that is, 7.38493 At this time, an error is caused, but it is small, no more than 1. This will save me the time of serial communication. For example, if I transmit 7, I only need to transmit {"7"}. But if I transmit 7.238493, I need to transmit {"7". " "2" "3" "8" "4" "9" "3"} in the past, this was the limitation of json data format. Finally, it is inconvenient for me to judge.
2. After Arduino receives data, I use int type A to receive string type data. s[1] should be the order of magnitude of 7, 8, 9 and 10. After I assign it to int type A, a becomes the order of magnitude of 54, 55 and 56.

Keywords: Python C++ Computer Vision Robot arduino

Added by SnakeO on Fri, 18 Feb 2022 12:04:56 +0200