A
Arduino4mo ago
IsakS

How do I communicate between Arduino and OpenCV?

Hi, I've made python program using OpenCV that tracks my robot and creates a destination for the robot to move to. How do I bridge the python file into my Arduino code, such that my robot moves to the destination? My idea was feeding my motors the x and y coordinates for where my robot is, and where it needs to go. BUT, how can I achieve this? I realize this is not an OpenCV discord, but perhaps anyone here has had some experience with this before? any help is greatly appreciated, thanks in advance!:)
9 Replies
IsakS
IsakSOP4mo ago
import cv2
import sys
import numpy as np
import random as rand


def random_position():
return (rand.randint(30,620), rand.randint(30,460))

s = 0
if len(sys.argv) > 1:
s = sys.argv[1]

source = cv2.VideoCapture(s)
win_name = 'Camera Preview'
cv2.namedWindow(win_name, cv2.WINDOW_NORMAL)
(x, y, windowWidth, windowHeight) = cv2.getWindowImageRect(win_name)

rand_circle_pos = random_position()
rand_circle_radius = 10

def euclideanDistance(p1,p2):
return np.linalg.norm(np.array(p1) - np.array(p2))

while cv2.waitKey(1) != 27: # Escape
has_frame, frame = source.read()
if not has_frame:
break

hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
lower_red = np.array([0, 130, 120])
upper_red = np.array([20, 255, 255])
red_mask = cv2.inRange(hsv, lower_red, upper_red)

red_mask = cv2.erode(red_mask, None, iterations=2)
red_mask = cv2.dilate(red_mask, None, iterations=2)

contours, _ = cv2.findContours(red_mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

robot_pos = None
robot_radius = 0

if contours:
largest = max(contours, key=cv2.contourArea)
((x, y), radius) = cv2.minEnclosingCircle(largest)

if radius > 10:
robot_pos = (int(x), int(y))
robot_radius = int(radius)

cv2.circle(frame, robot_pos, robot_radius, (255, 0, 255), 3)
cv2.circle(frame, robot_pos, 2, (0, 255, 255), -1)

rand_circle = cv2.circle(frame, rand_circle_pos, rand_circle_radius, (255, 155, 0), 4)

if robot_pos is not None:
distance = euclideanDistance(robot_pos, rand_circle_pos)
if distance < robot_radius + rand_circle_radius:
rand_circle_pos = random_position()

cv2.imshow(win_name, frame)


print("Origin Coordinates(x,y): ", x, y)
print(robot_pos)

source.release()
cv2.destroyWindow(win_name)
import cv2
import sys
import numpy as np
import random as rand


def random_position():
return (rand.randint(30,620), rand.randint(30,460))

s = 0
if len(sys.argv) > 1:
s = sys.argv[1]

source = cv2.VideoCapture(s)
win_name = 'Camera Preview'
cv2.namedWindow(win_name, cv2.WINDOW_NORMAL)
(x, y, windowWidth, windowHeight) = cv2.getWindowImageRect(win_name)

rand_circle_pos = random_position()
rand_circle_radius = 10

def euclideanDistance(p1,p2):
return np.linalg.norm(np.array(p1) - np.array(p2))

while cv2.waitKey(1) != 27: # Escape
has_frame, frame = source.read()
if not has_frame:
break

hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
lower_red = np.array([0, 130, 120])
upper_red = np.array([20, 255, 255])
red_mask = cv2.inRange(hsv, lower_red, upper_red)

red_mask = cv2.erode(red_mask, None, iterations=2)
red_mask = cv2.dilate(red_mask, None, iterations=2)

contours, _ = cv2.findContours(red_mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

robot_pos = None
robot_radius = 0

if contours:
largest = max(contours, key=cv2.contourArea)
((x, y), radius) = cv2.minEnclosingCircle(largest)

if radius > 10:
robot_pos = (int(x), int(y))
robot_radius = int(radius)

cv2.circle(frame, robot_pos, robot_radius, (255, 0, 255), 3)
cv2.circle(frame, robot_pos, 2, (0, 255, 255), -1)

rand_circle = cv2.circle(frame, rand_circle_pos, rand_circle_radius, (255, 155, 0), 4)

if robot_pos is not None:
distance = euclideanDistance(robot_pos, rand_circle_pos)
if distance < robot_radius + rand_circle_radius:
rand_circle_pos = random_position()

cv2.imshow(win_name, frame)


print("Origin Coordinates(x,y): ", x, y)
print(robot_pos)

source.release()
cv2.destroyWindow(win_name)
C++
#include <Arduino_PMIC.h>
#include <ArduinoMotorCarrier.h>
#include <Arduino.h>

void encoderReadings();
float batteryVoltage;


void setup()
{
//Serial port initialization
Serial.begin(115200);

//Establishing the communication with the Motor Carrier
if (controller.begin()){
Serial.print("Motor Carrier connected, firmware version ");
Serial.println(controller.getFWVersion());
}
else {
Serial.println("Couldn't connect! Is the red LED blinking? You may need to update the firmware with FWUpdater sketch");
while (1);
}

// Reboot the motor controller; brings every value back to default
Serial.println("reboot");
controller.reboot();
delay(500);

// Reset the encoders internal counter to zero
Serial.println("reset counters");
encoder1.resetCounter(0);
encoder2.resetCounter(0);

// PWM for motors 0 MIN, 100 MAX
M1.setDuty(0); // Negative for CW direction
M2.setDuty(0); // Positive for CW direction

if (!PMIC.enableBoostMode()) {
Serial.println("Error enabling Boost Mode");
}

batteryVoltage = battery.getRaw()/236.0; //236 for Nano, 77 for MKR.
Serial.print("Battery voltage: ");
Serial.print(batteryVoltage, 3);
Serial.print("V, Raw ");
Serial.println(battery.getRaw());
}

void loop() {
controller.ping();
delay(30);
encoderReadings();
delay(1000);
}

void encoderReadings(){
Serial.print("Encoder1 Pos [counts]: ");
Serial.print(encoder1.getRawCount());
Serial.print(" Encoder1 vel [count/sec]:");
Serial.println(encoder1.getCountPerSecond());
Serial.print("Encoder2 pos [counts]: ");
Serial.print(encoder2.getRawCount());
Serial.print(" Encoder2 vel [count/sec]:");
Serial.println(encoder2.getCountPerSecond());
Serial.println("");
}
C++
#include <Arduino_PMIC.h>
#include <ArduinoMotorCarrier.h>
#include <Arduino.h>

void encoderReadings();
float batteryVoltage;


void setup()
{
//Serial port initialization
Serial.begin(115200);

//Establishing the communication with the Motor Carrier
if (controller.begin()){
Serial.print("Motor Carrier connected, firmware version ");
Serial.println(controller.getFWVersion());
}
else {
Serial.println("Couldn't connect! Is the red LED blinking? You may need to update the firmware with FWUpdater sketch");
while (1);
}

// Reboot the motor controller; brings every value back to default
Serial.println("reboot");
controller.reboot();
delay(500);

// Reset the encoders internal counter to zero
Serial.println("reset counters");
encoder1.resetCounter(0);
encoder2.resetCounter(0);

// PWM for motors 0 MIN, 100 MAX
M1.setDuty(0); // Negative for CW direction
M2.setDuty(0); // Positive for CW direction

if (!PMIC.enableBoostMode()) {
Serial.println("Error enabling Boost Mode");
}

batteryVoltage = battery.getRaw()/236.0; //236 for Nano, 77 for MKR.
Serial.print("Battery voltage: ");
Serial.print(batteryVoltage, 3);
Serial.print("V, Raw ");
Serial.println(battery.getRaw());
}

void loop() {
controller.ping();
delay(30);
encoderReadings();
delay(1000);
}

void encoderReadings(){
Serial.print("Encoder1 Pos [counts]: ");
Serial.print(encoder1.getRawCount());
Serial.print(" Encoder1 vel [count/sec]:");
Serial.println(encoder1.getCountPerSecond());
Serial.print("Encoder2 pos [counts]: ");
Serial.print(encoder2.getRawCount());
Serial.print(" Encoder2 vel [count/sec]:");
Serial.println(encoder2.getCountPerSecond());
Serial.println("");
}
MaderDash
MaderDash4mo ago
@IsakS you can do that. Send both data values, then have the arduino extrapolate. Or you can do all that on the python side and just send move commands... Up to you
IsakS
IsakSOP4mo ago
Is there an article/video on this, worth to watch? I tried looking myself, but I could not find for the specific I wanted, unless I'm in that beginner area where if something is not specifically what I want, I look away:p
MaderDash
MaderDash4mo ago
Most likely, it doesn't directly cover what you're trying to do, but the process is kind of the same. If you have a computer and you want to transfer data to an Arduino, you just use a comport. It doesn't care if it's a Python script or a C# script; it doesn't matter what script is running on the PC side. It's just a comport from the Arduino's perspective.
IsakS
IsakSOP4mo ago
I'll look into it
MaderDash
MaderDash4mo ago
Oh, remember. So the best example, like an offer, is, let's say, I were to make a Python sketch on my computer that I don't know tells the CPU temperature. And I wanted it to go to the Arduino in the Python script. What I would do is, since I would grab the comport the Arduino is connected to, and then just send the data to the comport. On the Arduino side, it would grab the data, did it receive it? And then put it on a display or a bar graph, or whatever it is I'm trying to do with the Arduino side. So, by my understanding, whatever you have that's running the OpenCV, let's just say it's a Raspberry Pi. You would then use that code to capture the comport to the Yardwino, and then you have the same as a serial monitor: you have direct communication between the two of them, and you can go ahead and code it to do whatever you want.
IsakS
IsakSOP4mo ago
If we’re including the raspberry pi, how necessary from 1 to 10 (10 being high necessity) would it be to try implement ros2? But, I think get the message! So it does not really matter as long as the COM port, and serial is equal in both scripts? Such that the serial would just «work». So theoretically if I were to name something x_global in my python script, then in my Arduino code, could I call that variable, and use it, freely? Or is there still some trickery behind it. I assume it is not like a library .. unless it is, but highly unlikely 😅
MaderDash
MaderDash4mo ago
Well, kinda. You have two options available to you. One is called the pull structure. That means that the areduino, will send a request basically to the computer and tell it, hey, I want some data. then the computer receives that, and sends the data packet. The next one is called a push system, in which case the Python script just spits out data forever down the pipe, and it's just sending data, sending data, sending data. The arduino, just capturing it and using what it wants to use from it. There is a third option.It's called an acknowledgment option.The python script will santa pisa data.The ardreno receives it and acknowledges of valid packet, and then that tells the python script.Okay, i'm ready to send a new packet of data. With option number one, you would have the arduino sin.Data to the p c and then the pc send data back to the r, adreno, and it'll just sit there and listen to the port until the art we know requests more data. Option two, you're just streaming, live data directly out to comport, no matter what if anything's listening or not and the arduino, just captures it and then when it finds a good valid packet, then it goes ahead and does something. Option three is a smart system.There are doinno requests, a packet.The python sends exactly when packet the arduino.Verifies, the depakit was received to python verifies that it was a valid packet.If not, it resends it again, and if it is a valid packet, then it gets ready for a new packet, and then the whole process is repeated. Note the last one is how your pc's internet works.
IsakS
IsakSOP4mo ago
Oh damn, alright. I'll see my options and figure something out, thank you again for helping!:)

Did you find this page helpful?