Added entirety of CAD files, Software and the translated documentation

This commit is contained in:
Raphael Maenle
2019-11-12 19:55:35 +01:00
commit 134990e892
266 changed files with 48020 additions and 0 deletions

View File

@ -0,0 +1,178 @@
# import the necessary package
from picamera.array import PiRGBArray
from picamera import PiCamera
from image_process.colorlabler import ColorLabler
from image_process.coordtransform import CoordinateTransform
from time import sleep
import argparse
import imutils
import numpy as np
import cv2
import math
import time
class DetectColor:
#values for crop_image
y = 130
h = 210
x = 140
w = 340
radius = 86.4 #distance between robot center and camera center in mm
z = 239 #distance between camera and tray in mm
#camera information
focallength = 3.04 #mm
pixelsize = 0.00112 #mm
def __init__(self):
# define the list of boundaries
# blue (dark), red, yellow, cyan, green
self.boundaries = [
([17, 15, 100], [50, 56, 200]),
([86, 31, 4], [220, 88, 50]),
([204, 204, 50], [255, 255, 102]),
([5, 200, 200], [125, 255, 255]),
([100, 192, 0], [150, 255, 150])]
#create Matrix to save position and color
index = np.array([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58])
colorindex = np.full(59,-1, dtype=int)
self.Matrix = np.column_stack((index,colorindex))
xcoordinates = np.array([24.68,45.72,54.91,56.98,17.48,11.97,48.78,93.34,82.2,54.32,24.68,24.68,32.56,40.44,77.03,111.44,105.08,100.96,11.97,39.8,70.35,105.08,123.76,131.86,108.29,92.25,58.33,23.34,7.66,0,-24.68,-45.72,-54.91,-56.98,-17.48,-11.97,-48.78,-93.34,-82.2,-54.32,-24.68,-24.68,-32.56,-40.44,-77.03,-111.44,-105.08,-100.96,-11.97,-39.8,-70.35,-105.08,-123.76,-131.86,-108.29,-92.25,-58.33,-23.34,-7.66])
ycoordinates = np.array([-61.88,-49.5,-24.16,19.79,59.11,-86.1,-79.83,-60.72,70.62,72.05,85.12,-106.88,-102.18,-97.47,-84.32,-39.84,-6.32,28.98,-130.68,-121.61,-110.27,-79.83,-12.15,11.8,52.11,90.76,109.74,109.74,130.48,0,61.88,49.5,24.16,-19.79,-59.11,86.1,79.83,60.72,-70.62,-72.05,-85.12,106.88,102.18,97.47,84.32,39.84,6.32,-28.98,130.68,121.61,110.27,79.83,12.15,-11.8,-52.11,-90.76,-109.74,-109.74,-130.48])
self.Matrix = np.column_stack((self.Matrix,xcoordinates))
self.Matrix = np.column_stack((self.Matrix,ycoordinates))
#camera settings
self.camera = PiCamera()
self.camera.resolution = (640,480)
self.rawCapture = PiRGBArray(self.camera, size=(640,480))
#self.rawCapture = PiRGBArray(self.camera)
time.sleep(0.1)
def startDetection(self, angle):
#take picture
self.camera.capture(self.rawCapture, format="bgr")
image = self.rawCapture.array
#
#
#angle = 90
#if angle > 360:
# angle = angle - 360
#
#
i = 1
#crop the image
crop_image = image[self.y : self.y + self.h, self.x : self.x + self.w]
#crop_image = cv2.imread("/home/pi/Pictures/90.jpg")
cv2.imwrite("original.png", crop_image)
#
#
crop_imagehsv = cv2.cvtColor(crop_image,cv2.COLOR_BGR2HSV)
lower_red1 = np.array([0,150,50])
upper_red1 = np.array([10,255,255])
lower_red2 = np.array([178,75,75])
upper_red2 = np.array([179,255,255])
lower_blue = np.array([110,100,100])
upper_blue = np.array([130,255,255])
lower_green = np.array([40,100,100])
upper_green = np.array([70,255,255])
lower_yellow = np.array([25,75,75])
upper_yellow = np.array([30,255,255])
lower_cyan = np.array([87,75,75])
upper_cyan = np.array([91,255,255])
#red
mas1 = cv2.inRange(crop_imagehsv, lower_red1, upper_red1)
cv2.imwrite("mas1.png", mas1)
mas2 = cv2.inRange(crop_imagehsv, lower_red2, upper_red2)
cv2.imwrite("mas2.png", mas2)
#blue
mas3 = cv2.inRange(crop_imagehsv, lower_blue, upper_blue)
cv2.imwrite("mas3.png", mas3)
#green
mas4 = cv2.inRange(crop_imagehsv, lower_green, upper_green)
cv2.imwrite("mas4.png", mas4)
#yellow
mas5 = cv2.inRange(crop_imagehsv, lower_yellow, upper_yellow)
cv2.imwrite("mas5.png", mas5)
#cyan
mas6 = cv2.inRange(crop_imagehsv, lower_cyan, upper_cyan)
cv2.imwrite("mas6.png", mas6)
#
mas = mas1 | mas2 | mas3 | mas4 | mas5 | mas6
cv2.imwrite("HSV.png", mas)
#
# loop over the boundarie
for (lower, upper) in self.boundaries:
# create NumPy arrays from the boundaries
lower = np.array(lower, dtype = "uint8")
upper = np.array(upper, dtype = "uint8")
# find the colors within the specified boundaries and apply
# the mask
mask = cv2.inRange(crop_image, lower, upper)
if(i == 1):
mask2 = mask | mas;
i = 0;
else:
mask2 = mask2 | mask;
outputall = cv2.bitwise_and(crop_image,crop_image,mask=mask2)
# show the images -> Debug
#cv2.imshow("images", outputall)
#cv2.waitKey(0)
# load the image, convert it to grayscale, blur it slightly,
# and threshold it
cv2.imwrite("MaskAll.png", outputall)
blurred = cv2.GaussianBlur(outputall, (5, 5), 0)
gray = cv2.cvtColor(blurred, cv2.COLOR_BGR2GRAY)
#cv2.imwrite("Graustufenbild.png", gray)
lab = cv2.cvtColor(blurred, cv2.COLOR_BGR2LAB)
cv2.imwrite("lab.png", lab)
thresh = cv2.threshold(gray, 1, 255, cv2.THRESH_BINARY)[1]
cv2.imwrite("thresh.png", thresh)
#cv2.waitKey(0)
# find contours in the thresholded image
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if imutils.is_cv2() else cnts[1]
#initialize color labeler
cl = ColorLabler();
tr = CoordinateTransform(self.h,self.w, self.radius, self.focallength, self.pixelsize, self.z);
# loop over the contours
for c in cnts:
print("---------------------------------------")
temp = 10000
saveindex = -1
# compute the center of the contour
M = cv2.moments(c)
cX = int((M["m10"] / M["m00"]))
cY = int((M["m01"] / M["m00"]))
#label color
color_name, colorId = cl.label(lab,c)
#transform coordinate
xnew, ynew = tr.transform(angle,cX,cY)
print(cX, cY)
print(xnew, ynew,math.sqrt((xnew-self.Matrix[0][2])**2+(ynew-self.Matrix[0][3])**2))
for j in range(59):
if (math.sqrt((xnew-self.Matrix[j][2])**2+(ynew-self.Matrix[j][3])**2)) < temp:
temp = math.sqrt((xnew-self.Matrix[j][2])**2+(ynew-self.Matrix[j][3])**2)
saveindex = self.Matrix[j][0]
print(saveindex)
#save colorid for selected placeId
self.Matrix[int(saveindex)][1] = colorId
print colorId
self.rawCapture.truncate(0)
print self.Matrix
if __name__ == "__main__":
ballColors = DetectColor()
ballColors.startDetection(10)

View File

@ -0,0 +1,80 @@
# Creates instructions for the robot to move 360 degrees around, make photos of the ball-plate and write the ball_colors to the database
import MySQLdb
import random
import serial
import time
from struct import pack
def sendBin( dec ):
x = pack('i', dec)
ser.write(x[1])
ser.write(x[0])
ser = serial.Serial('/dev/ttyACM0', 9600) # Change name if necessary. Find out name with ls /dev/tty* (1x without, 1x with USB connected)
time.sleep(2) # Arduino resets, so wait for some time
# Create instructions to turn around
signature = chr(0b01010010) # from raspberry
priority0 = chr(0b00000000) # add to end of queue
priority1 = chr(0b00000001)
moveJ = chr(0b00000001) # binary 1
moveA = chr(0b00000100) # binary 4
pause = chr(0b00000111) # binary 7
dontChange = chr(0b01111111) + chr(0b11111111)
eot = chr(0b11111111) + chr(0b11111111) # end of transmission
magOff = chr(0b00000000)
magOn = chr(0b00000001)
# a, r, h
if 1 == 1:
msg = signature + priority0 + moveJ
ser.write(msg)
sendBin(120)
sendBin(100)
sendBin(10)
ser.write(eot)
msg = signature + priority0 + pause
ser.write(msg)
sendBin(5000)
ser.write(eot)
msg = signature + priority0 + moveJ
ser.write(msg)
sendBin(80)
sendBin(110)
sendBin(20)
ser.write(eot)
if 1 == 0:
msg = signature + priority0 + moveA
ser.write(msg)
sendBin(50)
sendBin(128)
sendBin(90)
msg = magOff + eot
ser.write(msg)
msg = signature + priority0 + moveA
ser.write(msg)
sendBin(30)
sendBin(180)
sendBin(100)
msg = magOff + eot
ser.write(msg)
msg = signature + priority1 + pause
ser.write(msg)
sendBin(2000)
ser.write(eot)
while True:
print(ser.readline())
ser.close() # close serial connection
# For debugging
#file = open("testfile.txt", "w")
#file.write("update")
#file.close

View File

@ -0,0 +1,180 @@
import math
import sys
import serial
import time
from struct import pack
# Transforming x, y, z to r, a, h
def xyz2arh( x, y, z ):
r = int(round(math.sqrt(pow(x, 2) + pow(y, 2))))
a = int(round(math.degrees(math.atan2(y, x))))
# atan2 gives negative results for -y --> subtract it from 360
if a < 0:
a = 360 + a
print a
h = z
return [a, r, h]
def xyz2a( x, y ):
a = int(round(math.degrees(math.atan2(y, x))))
# atan2 gives negative results for -y --> subtract it from 360
if a < 0:
a = 360 + a
return a
def xyz2r( x, y ):
r = int(round(math.sqrt(pow(x, 2) + pow(y, 2))))
return r
def xyz2h( z ):
h = z
return h
def sendBin( dec , ser):
print ("SENT POSITION: " + str(dec))
x = pack('i', dec)
ser.write(x[1])
ser.write(x[0])
def sendPositions( pos , ser):
sendBin(pos[0], ser)
sendBin(pos[1], ser)
sendBin(pos[2], ser)
# Calculate the shelf-number (1. digit of track)
def shelfNr( track ):
return int(track/10)
# Calculate the track-number (2. digit of track)
def trackNr( track ):
return track % 10
# Create and send instructions to move a ball from given x, y to track
def create_instr( x, y, track):
ser = serial.Serial(port='/dev/ttyACM0', # Change name if necessary. Find out name with ls /dev/tty* (1x without, 1x with USB connected)
baudrate=9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1)
time.sleep(1) # TODO DELETE Arduino resets, so wait for some time
signature = chr(0b01010010) # from raspberry
priority = chr(0b00000000) # add to end of queue
moveJ = chr(0b00000001) # binary 1
moveL = chr(0b00000010) # binary 2
moveL = moveJ
moveA = chr(0b00000100) # binary 4
pause = chr(0b00000111) # binary 7
dontChange = 32767
eot = chr(0b11111110) + chr(0b11111110) # end of transmission
magOn = chr(0b00000001)
magOff = chr(0b00000000)
heights = (80, 258, 187, 56) # heights of shelfs in mm (1. = Vorposition-Hoehe; 2. = Unterstes Brett Hoehe)
angles = (90, 63, 55, 47, 40, 32, 0) # angles of tracks in degrees + 0/6 = prePos
trackPosR = (170, 160, 173, 170) # radius of final pos in track in mm (2. = Oberste)
trackPrePosR = (trackPosR[0]-10, trackPosR[1]-10, trackPosR[2]-10, trackPosR[3]-10) # radius of pre pos of track
trackPreShelfR = trackPrePosR # radius of pre pos of shelf
shelfPrePosR = 155 # save bottom-position
shelfPrePosHOffset = 5
ballPosPreZOffset = 40 # height of pre pos of ballpos in mm (above the ball)
ballPosFinH = 10
magnetPauseTime = 1000
speedSlow = 20
speedFast = 50
higherOffsetBalls = [[-17.48, -59.11], # 34
[-54.32, -72.05], # 39
[-24.68, -85.12], # 40
[-58.33, -109.74]] # 56
higherhigherOffsetBalls = [[-131.86, -11.8]] # 53
# Create header
header = signature + priority
# Transform the given arguments so the program can work with them
x, y = y, x # change x and y to suit robot coordinate system
x = float(x)
y = float(y)
track = int(track)
#for i in range(len(higherOffsetBalls)):
# if x == higherOffsetBalls[i][1] and y == higherOffsetBalls[i][0]:
# ballPosFinH = 13
# break
#for i in range(len(higherhigherOffsetBalls)):
# if x == higherhigherOffsetBalls[i][1] and y == higherhigherOffsetBalls[i][0]:
# ballPosFinH = 15
# break
actShelfNr = shelfNr(track)
instr =[[moveJ, xyz2a(x, y), xyz2r(x, y), xyz2h(ballPosPreZOffset), speedFast], # To Ball-Pre-Position
[moveL, xyz2a(x, y), xyz2r(x, y), xyz2h(ballPosFinH), speedSlow], # To Ball-Position
[moveA, dontChange, dontChange, dontChange, magOn], # Switch on Magnet
[pause, magnetPauseTime],
[moveL, xyz2a(x, y), xyz2r(x, y), xyz2h(ballPosPreZOffset), speedSlow], # To Ball-Pre-Position again
[moveJ, angles[0], shelfPrePosR, heights[0], speedFast], # To Shelf-Pre-Pre-Position
# TODO Left or right pre-position?
[moveJ, angles[0], trackPreShelfR[actShelfNr], heights[actShelfNr]+shelfPrePosHOffset, speedFast], # To Shelf-Pre-Position
[moveJ, angles[trackNr(track)], trackPrePosR[actShelfNr], heights[actShelfNr]+shelfPrePosHOffset, speedSlow], # To Track-Pre-Position
[moveL, angles[trackNr(track)], trackPosR[actShelfNr], heights[actShelfNr], speedSlow], # To Track-Goal-Position
[moveA, dontChange, dontChange, dontChange, magOff], # Switch off Magnet
[pause, magnetPauseTime],
[moveL, angles[trackNr(track)], trackPrePosR[actShelfNr], heights[actShelfNr]+shelfPrePosHOffset, speedSlow], # To Track-Pre-Position
[moveJ, angles[0], trackPreShelfR[actShelfNr], heights[actShelfNr]+shelfPrePosHOffset, speedSlow], # To Shelf-Pre-Position
[moveJ, angles[0], shelfPrePosR, heights[0], speedFast] # To Shelf-Pre-Pre-Position
]
# MoveJ, MoveL: A, R, H
# MoveA: R1, P, R2
# TODO: Is robot in safe mode?
# Send instructions
#time.sleep(5)
for i in range(len(instr)):
ser.write(header)
ser.write(instr[i][0]) # send move-type
if instr[i][0] == pause:
sendBin(instr[i][1], ser)
if instr[i][0] == moveJ or instr[i][0] == moveL:
sendBin(instr[i][1], ser)
sendBin(instr[i][2], ser)
sendBin(instr[i][3], ser)
#sendBin(instr[i][4], ser) # send speed
if instr[i][0] == moveA:
sendBin(instr[i][1], ser)
sendBin(instr[i][2], ser)
sendBin(instr[i][3], ser)
ser.write(instr[i][4]) # send magOn / magOff
ser.write(eot)
# DEBUG
print("SENT INSTR" + str(i))
time.sleep(1.0)
# Debug: Send pause
# ser.write(header)
# ser.write(pause)
# sendBin(1500,ser)
# ser.write(eot)
while ser.inWaiting():
print(ser.readline())
while 1 == 1:
print(ser.readline())
ser.close() # close serial connection
# Only for debugging
#file = open("testfile.txt", "w")
#file.write(str(x) + str(y) + str(track));
# Get the information from php with arguments -> if the file create_instructions is called directly with arguments
if __name__ == "__main__":
create_instr(sys.argv[1], sys.argv[2], sys.argv[3])

View File

@ -0,0 +1,17 @@
<?php
$host = "localhost";
$username = "root";
$password = "root";
$dbname = "rpr_robot";
$result_array = array();
/* Create connection */
$conn = new mysqli ($host, $username, $password, $dbname);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
?>

View File

@ -0,0 +1,194 @@
var actPrio = 0;
var numBallsPlaced = new Array();
for (var i = 0; i < 55; i++) {numBallsPlaced[i] = 0;}
$( function() {
// Let the plate items be draggable
$( "li", "#ballBox" ).draggable({
revert: "invalid",
containment: "document",
helper: "clone",
cursor: "move"
});
refreshPlate();
refreshShelf();
});
// Send request for scanning of new balls and writing the data to the database
function updateBallPos() {
if (window.XMLHttpRequest) {xmlhttp = new XMLHttpRequest();
} else {xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");}
xmlhttp.open("GET","updateBallDatabase.php?q=5",true);
xmlhttp.send();
document.getElementById('unclickableCircle').style.visibility = "visible";
document.getElementById('stareBalls').style.visibility = "visible";
setTimeout(function(){
document.getElementById('stareBalls').style.visibility = "hidden";
document.getElementById('unclickableCircle').style.visibility = "hidden";
refreshPlate();
}, 60000);
}
// Loads all balls from the database and displays them on the plate
function refreshPlate() {
var myObj, x, txt = "", offsetX, offsetY, scale;
document.getElementById("ballBox").innerHTML = ""; // Clear plate first
if (window.XMLHttpRequest) {xmlhttp = new XMLHttpRequest();
} else {xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");}
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
myObj = JSON.parse(this.responseText);
offsetX = 300+51;
offsetY = 300+51;
scale = 2.62;
for (x in myObj) {
// Calculate ball-position on plate in website
myObj[x].pos_x = offsetX + myObj[x].pos_x * scale;
myObj[x].pos_y = offsetY + myObj[x].pos_y * scale;
$("<div id='" + myObj[x].pos_id + "' class='ball' style='top: " + myObj[x].pos_y + "px; left: " + myObj[x].pos_x +"px; background-color: " + myObj[x].ball_color + ";'></div>")
.appendTo('#ballBox').attr('ballID', myObj[x].pos_id).draggable({
revert: "invalid",
containment: "document",
});
document.getElementById(myObj[x].pos_id).style.cursor = "grab";
}
}
};
xmlhttp.open("GET","updateBallDatabase.php?q=0",true);
xmlhttp.send();
}
// Loads all balls from the database and displays them in the shelf
function refreshShelf() {
var myObj, x;
var id = 0;
document.getElementById("shelfBox").innerHTML = ""; // Clear shelf first
for (var i = 0; i < 35; i++) {numBallsPlaced[i] = 0;} // Init array on refresh
for (var i = 10; i < 35; i++) {
if(i%5 == 0 && i%10 != 0) { // Display 5 traces per shelf
i = i + 5;
$('<br>').appendTo('#shelfBox');
}
id = i + 1;
$('<div class="shelf" id="shelf' + id + '" ></div>').data('number', i).attr('section', id).appendTo('#shelfBox').droppable({
accept: "#ballBox > div",
classes: { "ui-droppable-active": "ui-state-highlight" },
drop: handleBallDrop
});
}
if (window.XMLHttpRequest) {xmlhttp = new XMLHttpRequest();
} else {xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");}
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
myObj = JSON.parse(this.responseText);
for (x in myObj) {
$("<div id='" + myObj[x].pos_id + "' class='ball' style='position: relative; left: 13px; background-color: " + myObj[x].ball_color + ";'></div>")
.appendTo('#shelf'+ myObj[x].ball_targetPos + '').attr('ballID', myObj[x].pos_id);
numBallsPlaced[myObj[x].ball_targetPos]++;
if(numBallsPlaced[myObj[x].ball_targetPos] >= 5) {
$("#shelf" + myObj[x].ball_targetPos).droppable({accept: "",}); //Prevent balls from being dropped if shelf is already full
}
}
}
};
xmlhttp.open("GET","updateBallDatabase.php?q=3",true);
xmlhttp.send();
}
// Puts all balls from the shelf back on the plate (in the database)
function emptyShelf(str) {
if (window.XMLHttpRequest) {xmlhttp = new XMLHttpRequest();
} else {xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");}
xmlhttp.open("GET","updateBallDatabase.php?q=2",true);
xmlhttp.send();
var i = 0;
xmlhttp.onreadystatechange = function() {
i++;
if (i == 3) {
refreshShelf();
refreshPlate();
}
};
}
// If a ball is dropped, it is replaced and that is wirtten to the database
function handleBallDrop(event, ui) {
$item = ui.draggable;
var ballID = $item.attr('ballID');
var dropID = $(this).attr('section');
var live = 0;
//if (dropID > 10 && dropID < 20) dropID + 20;
//if (dropID > 30 && dropID < 40) dropID - 20;
// Drop ball and change behaviour and style
$item.appendTo(this).fadeIn();
ui.draggable.draggable('disable');
document.getElementById(ballID).style.cursor = "context-menu";
document.getElementById(ballID).style.position = "relative";
document.getElementById(ballID).style.top = "0px";
document.getElementById(ballID).style.left = "13px";
numBallsPlaced[dropID] = numBallsPlaced[dropID] + 1;
if (window.XMLHttpRequest) {xmlhttp = new XMLHttpRequest();
} else {xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");}
if (document.getElementById("myonoffswitch").checked == true) live = 1;
xmlhttp.open("GET","updateBallDatabase.php?q=1&ballID=" + ballID + "&shelfID=" + dropID + "&prio=" + actPrio + "&live=" + live,true);
actPrio++;
if(numBallsPlaced[dropID] >= 5) {
$("#shelf" + dropID).droppable({accept: "",}); //Prevent balls from being dropped if shelf is already full
}
xmlhttp.send();
document.getElementById('unclickableCircle').style.visibility = "visible";
document.getElementById('playBalls').style.visibility = "visible";
setTimeout(function(){
document.getElementById('unclickableCircle').style.visibility = "hidden";
document.getElementById('playBalls').style.visibility = "hidden";
}, 15000);
}
function autoSort(){
emptyShelf();
setTimeout(function(){
if (window.XMLHttpRequest) {xmlhttp = new XMLHttpRequest();
} else {xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");}
xmlhttp.open("GET","updateBallDatabase.php?q=4",true);
xmlhttp.send();
document.getElementById('unclickableCircle').style.visibility = "visible";
document.getElementById('playBalls').style.visibility = "visible";
var i = 0;
xmlhttp.onreadystatechange = function() {
i++;
if (i == 3){
refreshPlate();
refreshShelf();
document.getElementById('unclickableCircle').style.visibility = "hidden";
document.getElementById('playBalls').style.visibility = "hidden";
//location.reload();
//console.log("A");
}
};
}, 1000);
}
function startSorting(){
if (window.XMLHttpRequest) {xmlhttp = new XMLHttpRequest();
} else {xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");}
xmlhttp.open("GET","updateBallDatabase.php?q=6",true);
xmlhttp.send();
alert("Your data has been sent successfully. The robot will start sorting soon.");
}

View File

@ -0,0 +1,73 @@
from scipy.spatial import distance as dist
from collections import OrderedDict
import numpy as np
import cv2
class ColorLabler:
def __init__(self):
# initialize the colors dictionary, containing the color
# name as the key and the RGB tuple as the value
# colors = ({
# "Blue": (33.5, 35.5, 150),
# "Red": (153, 59.5, 27),
# "Yellow": (206, 217.5, 50),
# "Cyan": (43, 255, 255),
# "Green": (61, 165, 85)})
colors = ({
"Blue": (28, 70, 152),
"Red": (126, 34, 47),
"Yellow": (86, 83, 42),
"Cyan": (109, 131, 180),
"Green": (2, 77, 54)})
# allocate memory for the L*a*b* image, then initialize
# the color names list
self.lab = np.zeros((len(colors), 1, 3), dtype = "uint8")
self.colorNames = []
# loop over the colors dictionary
for (i, (name,rgb)) in enumerate(colors.items()):
# update the L*a*b* array and the color names list
self.lab[i] = rgb
self.colorNames.append(name)
# convert the L*a*b* array from the RGB color space
# to L*a*b*
self.lab = cv2.cvtColor(self.lab, cv2.COLOR_RGB2LAB)
def label(self, image, c):
# construct a mask for the contour, then compute the
# average L*a*b* value for the masked region
mask = np.zeros(image.shape[:2], dtype = "uint8")
cv2.drawContours(mask, [c], -1, 255, -1)
#cv2.imwrite("mask1.png",mask)
mask = cv2.erode(mask, None, iterations = 2)
#cv2.imwrite("mask2.png", mask)
mean = cv2.mean(image, mask = mask)[:3]
# initialize the minimum distance found thus far
minDist = (np.inf, None)
# loop over the known L*a*b* color values
for (i, row) in enumerate(self.lab):
# compute the distance between the current L*a*b*
# color value and the mean of the image
d = dist.euclidean(row[0], mean)
# if the distance is smaller than the current distance,
# then update the bookkeeping variable
if d < minDist[0]:
minDist = (d, i)
if self.colorNames[minDist[1]] == "Blue":
color_index = 0
elif self.colorNames[minDist[1]] == "Red":
color_index = 1
elif self.colorNames[minDist[1]] == "Yellow":
color_index = 2
elif self.colorNames[minDist[1]] == "Cyan":
color_index = 3
elif self.colorNames[minDist[1]] == "Green":
color_index = 4
# return the name of the color with the smallest distance
return self.colorNames[minDist[1]], color_index

View File

@ -0,0 +1,109 @@
import numpy as np
import cv2
import math
class CoordinateTransform:
def __init__(self,h,w,Radius,foc,pixsize,z):
self.w = w
self.h = h
#a = np.array([[w],[h],[1]])
#self.high = z;
self.R = Radius #mm
#focallengthinpixel=float(foc)/pixsize
#self.inverseCalib_matrix = np.array([[float(1)/focallengthinpixel,0,0],[0,float(1)/focallengthinpixel,0],[0,0,1]])
#print(a)
#b = np.dot(self.inverseCalib_matrix,a)
#print(b)
#b = b*z
#inverse calibration matrix without cx and cy
self.xresolution = 156
self.yresolution = 96
#self.xresolution = b[0][0]
#self.yresolution = b[1][0]
#print(b)
#print(self.xresolution)
#print(self.yresolution)
def transform(self, angle, x, y):
angledeg = angle
anglerad = float(angle*math.pi)/180
beta = (math.pi/2) - anglerad
Rx = self.R*math.cos(anglerad)
Ry = self.R*math.sin(anglerad)
OldPositioninPixel = np.array([[x],[y],[1]])
#print(self.xresolution, self.w,x)
m = float(self.xresolution*x)/self.w
n = float(self.yresolution*y)/self.h
OldPosition = np.array([[m],[n],[1.0]])
#print(OldPositioninPixel)
#OldPosition = np.dot(self.inverseCalib_matrix,OldPositioninPixel)
#print(OldPosition)
#OldPosition = OldPosition * self.high
#print(OldPosition)
Rotate = np.array([[math.cos(beta),-math.sin(beta),0],[math.sin(beta),math.cos(beta),0],[0,0,1]]) #rotate coordinate
Translation1 = np.array([[1,0,Rx],[0,1,-Ry],[0,0,1]]) #transform to camera center
if angledeg < 90 and angledeg > 0:
gamma = (math.pi/2) - anglerad
k = (float(self.xresolution)/2) - (float(self.yresolution)/2)*math.tan(gamma)
l = (float(self.yresolution)/2)/math.cos(gamma)
TranslationQ1 = np.array([[1,0,-(math.cos(gamma)*k)],[0,1,-(math.sin(gamma)*k+l)],[0,0,1]])
M1 = np.dot(TranslationQ1,Translation1)
#print(TranslationQuadrant1)
#print(Translation1)
#print(M1)
M2 = np.dot(M1,Rotate)
#print(M2)
M3 = np.dot(M2,OldPosition)
print(M3)
if angledeg > 90 and angledeg < 180:
gamma = (math.pi) - anglerad
k = (float(self.xresolution)/2) - (float(self.yresolution)/2)*math.tan(gamma)
l = (float(self.yresolution)/2)/math.cos(gamma)
TranslationQ2 = np.array([[1,0,-(math.cos(gamma)*k+l)],[0,1,-(math.sin(gamma)*k)],[0,0,1]])
M1 = np.dot(TranslationQ2,Translation1)
M2 = np.dot(M1,Rotate)
M3 = np.dot(M2,OldPosition)
if angledeg > 180 and angledeg < 270:
gamma = ((3*math.pi)/2) - anglerad
k = (float(self.xresolution)/2) - (float(self.yresolution)/2)*math.tan(gamma)
l = (float(self.yresolution)/2)/math.cos(gamma)
TranslationQ3 = np.array([[1,0,math.cos(gamma)*k],[0,1,math.sin(gamma)*k+l],[0,0,1]])
M1 = np.dot(TranslationQ3,Translation1)
M2 = np.dot(M1,Rotate)
M3 = np.dot(M2,OldPosition)
if angledeg > 270 and angledeg < 360:
gamma = (2*math.pi) - anglerad
k = (float(self.xresolution)/2) - (float(self.yresolution)/2)*math.tan(gamma)
l = (float(self.yresolution)/2)/math.cos(gamma)
TranslationQ4 = np.array([[1,0,math.sin(gamma)*k+l],[0,1,-(math.cos(gamma)*k)],[0,0,1]])
M1 = np.dot(TranslationQ4,Translation1)
M2 = np.dot(M1,Rotate)
M3 = np.dot(M2,OldPosition)
if angledeg == 0:
TranslationA0 = np.array([[1,0,-(float(self.xresolution)/2)],[0,1,-(float(self.yresolution)/2)],[0,0,1]])
M1 = np.dot(TranslationA0,Translation1)
M2 = np.dot(M1,Rotate)
M3 = np.dot(M2,OldPosition)
if angledeg == 90:
TranslationA90= np.array([[1,0,-(float(self.xresolution)/2)],[0,1,-(float(self.yresolution)/2)],[0,0,1]])
M1 = np.dot(TranslationA90,Translation1)
M2 = np.dot(M1,Rotate)
M3 = np.dot(M2,OldPosition)
if angledeg == 180:
TranslationA180= np.array([[1,0,-(float(self.yresolution)/2)],[0,1,(float(self.xresolution)/2)],[0,0,1]])
M1 = np.dot(TranslationA180,Translation1)
M2 = np.dot(M1,Rotate)
M3 = np.dot(M2,OldPosition)
if angledeg == 270:
TranslationA270= np.array([[1,0,(float(self.xresolution)/2)],[0,1,(float(self.yresolution)/2)],[0,0,1]])
M1 = np.dot(TranslationA270,Translation1)
M2 = np.dot(M1,Rotate)
M3 = np.dot(M2,OldPosition)
# return position with base orientation
return M3[0][0], M3[1][0]

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,35 @@
# Reads the positions of all sorted balls from the database and calls create_instr so the robot sorts them
import MySQLdb
from create_instructions import create_instr
db = MySQLdb.connect("localhost","root","root","rpr_robot" ) # Open database connection
cursor = db.cursor() # Prepare a cursor object using cursor() method
# Write the sql-query for later proceeding
# Get the initial and final position as well as the id of all balls which are placed and not sorted by the robot yet, ordered by the priority ascending
sql = "SELECT pos_x, pos_y, ball_targetPos, pos_id FROM ball_positions WHERE ball_targetPos!=0 AND sorted_by_robot=0 ORDER BY ball_prio ASC"
try:
cursor.execute(sql) # Execute the SQL command
results = cursor.fetchall() # Fetch all the rows
for row in results:
posX = row[0]
posY = row[1]
ball_targetPos = row[2]
id = row[3]
print ball_targetPos
create_instr(posX, posY, ball_targetPos) # Create movement-instructions for every sorted ball
sql = "UPDATE ball_positions SET sorted_by_robot=1 WHERE pos_id = %i" % (id) # Write to database, that the robot moved this ball in real life
cursor.execute(sql) # Execute the SQL command
db.commit() # Commit your changes in the database
except:
print "Error: unable to fetch data"
db.rollback() # Rollback in case there is any error
db.close() # disconnect from server
# For debugging
file = open("testfile.txt", "w")
file.write("readBalls")
file.close

View File

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8"/>
<link rel="stylesheet" type="text/css" href="style.css">
<link rel="stylesheet" type="text/css" href="style_button.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="handleBalls.js"></script>
<script>
function checkBoxAutoSort() {
/*if (document.getElementById("myonoffswitch").checked == false){
document.getElementById("autosortButton").disabled = false;
document.getElementById("startSortingButton").disabled = false;
} else {
document.getElementById("autosortButton").disabled = true;
document.getElementById("startSortingButton").disabled = true;
}*/
alert("You have to buy a premium-account to switch Live-Mode off")
//$("#myonoffswitch").prop("checked", true);
//$("#myonoffswitch").attr("checked", true);
document.getElementById("myonoffswitch").checked = true;
}
</script>
</head>
<body>
<div class="msg" id="playBalls" style="visibility: hidden;">
Please wait<br>while I'm playing<br>with your balls!
</div>
<div class="msg" id="stareBalls" style="visibility: hidden;">
Please wait while I'm having an intense look at your balls!
</div>
<div id="nav">
<form>
<div id="navL">
<button type="button" name="empty" class="button" onclick="emptyShelf(this.value)">Empty Shelf</button> <br>
<button type="button" name="refresh" class="button" onclick="updateBallPos()">Refresh Positions</button> <br>
</div>
<div id="navM">
<span style="margin-left: 8px; font-size: 16px;"> Live-Sort </span>
<div class="onoffswitch">
<input type="checkbox" name="onoffswitch" class="onoffswitch-checkbox" id="myonoffswitch" onclick="checkBoxAutoSort()" checked>
<label class="onoffswitch-label" for="myonoffswitch">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
<div id="navR">
<button type="button" name="autosort" class="button" onclick="autoSort()" id="autosortButton" disabled="">Auto Sort</button>
<button type="button" name="start" class="button" onclick="startSorting()" id="startSortingButton" disabled="">Start Sorting</button>
</div>
</form>
</div>
<div id="unclickableCircle" style="visibility: hidden;"></div>
<div id="ballBox">
<!-- Balls on Plate are created dynamically -->
</div>
<div id="shelfBox" class="ui-widget-content ui-state-default">
<!-- Balls in Shelf are created dynamically -->
</div>
</body>
</html>

View File

@ -0,0 +1,114 @@
body {
background-color: lightblue;
font-family: verdana;
}
div.ball{
width: 30px;
height: 30px;
border-radius: 15px;
position: absolute;
/*opacity: 0.5;*/
z-index: 999;
/*cursor: grab;*/
}
#ballBox{
background-image: url("img/Aufnahmeplatte.png");
background-repeat: no-repeat;
width: 800px;
height: 800px;
position: absolute;
top: 90px;
left: 10px;
}
.msg{
position: absolute;
font-weight: bold;
font-size: 20pt;
top: 150px;
left: 175px;
width: 400px;
height: 150px;
float: left;
z-index: 9999999;
padding-top: 60px;
text-align: center;
background-color: #000;
border-radius: 10px;
opacity: .6;
color: #fff;
}
#unclickableCircle{
background-color: black;
position: absolute;
opacity: 0.4;
width: 732px;
height: 732px;
margin-top: 2px;
margin-left: 2px;
border-radius: 366px;
z-index: 99999;
}
#backgroundMsg{
background-color: red;
position: absolute;
opacity: 0.2;
width: 100%;
height: 100vh;
z-index: 99999;
}
#nav{
/*background-color: red;*/
height: 80px;
}
#navL{
/*background-color: yellow;*/
width: 200px;
float: left;
}
#navM{
/*background-color: green;*/
width: 90px;
float: left;
margin-left: 30px;
margin-right: 30px;
}
#navR{
/*background-color: green;*/
width: 200px;
float: left;
}
#shelfBox{
/*background-color: red;*/
background-image: url("img/Shelf.png");
background-repeat: repeat-y;
background-size: contain;
float: right;
position: absolute;
top: 100px;
left: 750px;
padding-right: 5px;
}
.shelf{
/*background-color: yellow;
opacity: 0.5;*/
width: 60px;
height: 150px;
margin-top: 4px;
margin-bottom: 10px;
margin-left: 5px;
float: left;
}
ul {
list-style-type: none;
}

View File

@ -0,0 +1,116 @@
/*.button {
background-color:#ffebba;
-moz-border-radius:5px;
-webkit-border-radius:5px;
border-radius:5px;
border:1px solid #f5b427;
display:inline-block;
cursor:pointer;
color:#030303;
font-family:Arial;
font-size: 16px;
padding: 2px 32px;
margin-top: 5px;
text-decoration:none;
width: 90%;
}
.button:hover {
background-color:#f5b427;
}
.button:active {
top:1px;
}
.button:disabled{
background-color: white;
cursor: default;
}*/
.button {
background-color: #34A7C1;
border: 1px solid #999999;
border-radius:5px;
-moz-border-radius:5px;
-webkit-border-radius:5px;
color: #EEEEEE;
padding: 2px 32px;
margin-top: 5px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
cursor: pointer;
width: 100%;
}
.button:hover {
background-color: #EEEEEE;
color: #34A7C1;
}
.button:active {
background-color: #34A7C1;
color: #EEEEEE;
}
.button:disabled{
background-color: #EEEEEE;
color: #34A7C1;
cursor: default;
}
.onoffswitch {
position: relative;
padding-top: 5px;
width: 90px;
-webkit-user-select:none; -moz-user-select:none; -ms-user-select: none;
}
.onoffswitch-checkbox {
display: none;
}
.onoffswitch-label {
display: block;
overflow: hidden;
cursor: pointer;
border: 2px solid #999999;
border-radius: 20px;
}
.onoffswitch-inner {
display: block;
width: 200%;
margin-left: -100%;
transition: margin 0.3s ease-in 0s;
}
.onoffswitch-inner:before, .onoffswitch-inner:after {
display: block; float: left; width: 50%; height: 30px; padding: 0; line-height: 30px;
font-size: 14px; color: white; font-family: Trebuchet, Arial, sans-serif; font-weight: bold;
box-sizing: border-box;
}
.onoffswitch-inner:before {
content: "ON";
padding-left: 10px;
background-color: #34A7C1; color: #FFFFFF;
}
.onoffswitch-inner:after {
content: "OFF";
padding-right: 10px;
background-color: #EEEEEE; color: #999999;
text-align: right;
}
.onoffswitch-switch {
display: block; width: 18px; margin: 6px;
background: #FFFFFF;
position: absolute; top: 5px; bottom: 0;
right: 56px;
border: 2px solid #999999; border-radius: 20px;
transition: all 0.3s ease-in 0s;
}
.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner {
margin-left: 0;
}
.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch {
right: 0px;
}

View File

@ -0,0 +1 @@
140.05.044

View File

@ -0,0 +1,103 @@
<?php
include('db.inc.php');
$q = intval($_GET['q']);
// Load Plate with balls and pass the result to html-file
if($q == 0) {
$result = $conn->query("SELECT pos_id, pos_x, pos_y, ball_color FROM ball_positions WHERE pos_isEmpty = 0 AND ball_targetPos = 0");
$outp = array();
$outp = $result->fetch_all(MYSQLI_ASSOC);
echo json_encode($outp);
$conn->close();
}
// Load Shelf with balls and pass the result to html-file
if($q == 3) {
$result = $conn->query("SELECT pos_id, ball_color, ball_targetPos FROM ball_positions WHERE ball_targetPos != 0 ORDER BY ball_prio");
$outp = array();
$outp = $result->fetch_all(MYSQLI_ASSOC);
echo json_encode($outp);
$conn->close();
}
// Write to database, if a ball has been moved to the shelf
if($q == 1) {
$ballID = intval($_GET['ballID']);
$shelfID = intval($_GET['shelfID']);
$prio = intval($_GET['prio']);
$live = intval($_GET['live']);
// Create instructions only if webside is in live-mode
if ($live == 1) {
$conn->query("UPDATE ball_positions SET ball_targetPos = $shelfID, ball_prio = $prio, sorted_by_robot = 1 WHERE pos_id = $ballID");
//Get infos for python-script
$result = $conn->query("SELECT pos_x, pos_y, ball_targetPos FROM ball_positions WHERE pos_id = $ballID");
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
$posX = $row['pos_x'];
$posY = $row['pos_y'];
$target = $row['ball_targetPos'];
passthru('python create_instructions.py '.$posX.' '.$posY.' '.$target.'');
} else {
$conn->query("UPDATE ball_positions SET ball_targetPos = $shelfID, ball_prio = $prio, sorted_by_robot = 0 WHERE pos_id = $ballID");
}
$conn->close();
}
// Empty the shelf -> Reset target-positions and priorities
if($q == 2) {
$conn->query("UPDATE ball_positions SET pos_isEmpty = 0, ball_targetPos = 0, ball_prio = -1, sorted_by_robot = 0");
$conn->query("UPDATE ball_positions SET pos_isEmpty = 1 WHERE pos_id = 29");
$conn->close();
echo json_encode("done");
}
// Autosort balls in Database
if($q == 4) {
$result = $conn->query("SELECT pos_id, ball_color FROM ball_positions WHERE pos_isEmpty = 0 AND ball_targetPos = 0 ORDER BY ball_color, pos_id");
$numColor = array(0, 0, 0, 0); // Store Blue, Red, Green, Yellow
$actColor = "undefined";
$i = 0;
$prio = 0;
$shelfID = 10;
while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
$actID = $row['pos_id'];
$actColor = $row['ball_color'];
if ($i == 0) $lastColor = $actColor; // Don't go to next shelf at first run
if (($i % 5 == 0 && $i != 0) || $actColor != $lastColor) { // If the shelf is full or color changes
$shelfID++;
$i = 0;
}
if ($shelfID % 5 == 0 && $shelfID % 10 != 0 && $shelfID != 10) {// If the last shelf in a row is reached
$shelfID = $shelfID + 5;
$i = 0;
}
$conn->query("UPDATE ball_positions SET ball_targetPos = $shelfID, ball_prio = $prio WHERE pos_id = $actID");
$lastColor = $actColor;
$i++;
$prio++;
}
$conn->close();
echo json_encode("done");
}
// Call python-script for refreshing the plate (scanning the positions)
if($q == 5) {
passthru('python update_VarAngle.py');
}
// Call python-script for doing the sorting of the balls which are in the shelf in Not-Live-Mode
if($q == 6) {
passthru('python readBalls.py');
}
?>

View File

@ -0,0 +1,133 @@
# Creates instructions for the robot to move 360 degrees around, make photos of the ball-plate and write the ball_colors to the database
import MySQLdb
import random
import serial
import time
from color_detection_v4 import DetectColor
from struct import pack
def sendBin( dec ):
x = pack('i', dec)
ser.write(x[1])
ser.write(x[0])
#ser = serial.Serial('/dev/ttyACM0', 9600) # Change name if necessary. Find out name with ls /dev/tty* (1x without, 1x with USB connected)
ser = serial.Serial(port='/dev/ttyACM0', # Change name if necessary. Find out name with ls /dev/tty* (1x without, 1x with USB connected)
baudrate=9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1)
time.sleep(2) # Arduino resets, so wait for some time
db = MySQLdb.connect("localhost","root","root","rpr_robot" ) # Open database connection
cursor = db.cursor() # prepare a cursor object using cursor() method
# Get the number of rows in the database for the loop
cursor.execute('select * from ball_positions')
cursor.fetchall()
num_entries = cursor.rowcount - 1
# Define the possible colors of the balls
colors = ["blue", "red", "yellow", "cyan", "green"]
is_empty = 0;
# Create instructions to turn around
signature = chr(0b01010010) # from raspberry
priority0 = chr(0b00000000) # add to end of queue
priority1 = chr(0b00000001)
moveJ = chr(0b00000001) # binary 1
moveA = chr(0b00000100) # binary 4
pause = chr(0b00000111) # binary 7
dontChange = chr(0b01111111) + chr(0b11111111)
eot = chr(0b11111110) + chr(0b11111110) # end of transmission
magOff = chr(0b00000000)
magOn = chr(0b00000001)
angle = (0, 45, 90, 135, 180, 225, 270, 315)
# Predefine messages
msgPause = signature + priority0 + pause
msgTurn = signature + priority0 + moveA
# Initialize color detection
ballColors = DetectColor()
# TODO: Check if robot is in save mode or move it to!
# Set startposition
ser.write(msgTurn)
sendBin(0) # Keep R1 as it is
sendBin(110) # Put P to this height
sendBin(90) # Put R2 to this angle
ser.write(eot)
print("TO START")
time.sleep(10)
for i in angle:
ser.write(msgTurn)
sendBin(i) # Turn R1 to actual angle
ser.write(dontChange) # Keep P as it is
ser.write(dontChange) # Keep R2 as it is
ser.write(eot)
print("TO "+str(i))
ser.write(msgPause)
sendBin(2000) # Send Wait for 2 seconds
ser.write(eot)
print("pause at "+str(i))
# TODO Wait time or wait for "I'm there" from robot
time.sleep(3)
i = i + 90
if i >= 360:
i = i - 360
ballColors.startDetection(i)
# TODO Wait time
time.sleep(3)
ser.write(msgTurn)
sendBin(0) # Keep R1 as it is
sendBin(110) # Put P to this height
sendBin(90) # Put R2 to this angle
ser.write(eot)
print("TO START")
# Go through array and write detected positions to database
for i in range(0, num_entries + 1):
# Ball-Position 29 must be empty at any time!
if i == 29 or ballColors.Matrix[i][1] == -1:
is_empty = 1
color = "invalid"
else:
is_empty = 0
# Set random color -> Just for offline-programming!
#color = random.choice(colors);
color = colors[int(ballColors.Matrix[i][1])];
# Prepare SQL query to UPDATE required records
sql = "UPDATE ball_positions SET pos_isEmpty = %i, ball_color = '%s', ball_targetPos = 0, ball_prio = -1 WHERE pos_id = %i" % (is_empty, color, i)
try:
cursor.execute(sql) # Execute the SQL command
db.commit() # Commit your changes in the database
except:
print "Error: unable to update records"
db.rollback() # Rollback in case there is any error
db.close() # disconnect from server
# Only for debugging
while True:
print(ser.readline())
file = open("testfile.txt", "w")
file.write(ballColors.Matrix);
file.close
#print ballColors.Matrix
ser.close() # close serial connection

View File

@ -0,0 +1,39 @@
# Creates instructions for the robot to move 360 degrees around, make photos of the ball-plate and write the ball_colors to the database
import MySQLdb
import random
db = MySQLdb.connect("localhost","root","root","rpr_robot" ) # Open database connection
cursor = db.cursor() # prepare a cursor object using cursor() method
# Get the number of rows in the database for the loop
cursor.execute('select * from ball_positions')
cursor.fetchall()
num_entries = cursor.rowcount - 1
# Define the possible colors of the balls
colors = ["blue", "red", "yellow", "cyan", "green"]
is_empty = 0;
# Go through array and write detected positions to database
for i in range(0, num_entries + 1):
# Ball-Position 29 must be empty at any time!
if i == 29:
is_empty = 1
color = "invalid"
else:
is_empty = 0
# Set random color -> Just for offline-programming!
color = random.choice(colors);
# Prepare SQL query to UPDATE required records
sql = "UPDATE ball_positions SET pos_isEmpty = %i, ball_color = '%s', ball_targetPos = 0, ball_prio = -1 WHERE pos_id = %i" % (is_empty, color, i)
try:
cursor.execute(sql) # Execute the SQL command
db.commit() # Commit your changes in the database
except:
print "Error: unable to update records"
db.rollback() # Rollback in case there is any error
db.close() # disconnect from server