diff --git a/ros2_ws/build/.built_by b/ros2_ws/build/.built_by new file mode 100644 index 0000000000000000000000000000000000000000..06e74acb63e6917bd1f0f8853213d49f0c5978e4 --- /dev/null +++ b/ros2_ws/build/.built_by @@ -0,0 +1 @@ +colcon diff --git a/ros2_ws/build/COLCON_IGNORE b/ros2_ws/build/COLCON_IGNORE new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/GameWavefront.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/GameWavefront.py new file mode 100644 index 0000000000000000000000000000000000000000..aae934fa4259ad8bf8e9b6554fc34074aadac6ed --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/GameWavefront.py @@ -0,0 +1,375 @@ +# Wavefront Planning +# Sajad Saeedi, Andrew Davison 2017 +# Implementation is based on the following reference +# +# H. Choset, K. M. Lynch, S. Hutchinson, G. Kantor, W. Burgard, L. E. Kavraki and S. Thrun, +# Principles of Robot Motion: Theory, Algorithms, and Implementations, +# MIT Press, Boston, 2005. +# http://www.cs.cmu.edu/afs/cs/Web/People/motionplanning/ +# +# From Chapter 4.5 - Wave-Front Planner +# This planner determines a path via gradient descent on the grid starting from the start. +# Essentially, the planner determines the path one pixel at a time. +# The wave-front planner essentially forms a potential function on the grid which has one local minimum and thus is resolution complete. +# The planner also determines the shortest path, but at the cost of coming dangerously close to obstacles. +# The major drawback of this method is that the planner has to search the entire space for a path + +import pygame, os, math, time, random +import numpy as np +try: + import pygame + from pygame import surfarray + from pygame.locals import * +except ImportError: + raise ImportError('Error Importing Pygame/surfarray') + +pygame.init() + +SCALE = 1 + +# set the width and height of the screen +WIDTH = 1500/SCALE +HEIGHT = 1000/SCALE + +# The region we will fill with obstacles +PLAYFIELDCORNERS = (-3.0, -3.0, 3.0, 3.0) + +# Barrier locations +barriers = [] +# barrier contents are (bx, by, visibilitymask) +for i in range(100): + (bx, by) = (random.uniform(PLAYFIELDCORNERS[0], PLAYFIELDCORNERS[2]), random.uniform(PLAYFIELDCORNERS[1], PLAYFIELDCORNERS[3])) + barrier = [bx, by, 0] + barriers.append(barrier) + +BARRIERRADIUS = 0.1 + + +ROBOTRADIUS = 0.15 +W = 2 * ROBOTRADIUS +SAFEDIST = 0.2 +BARRIERINFILATE = ROBOTRADIUS + +MAXVELOCITY = 0.5 #ms^(-1) max speed of each wheel +MAXACCELERATION = 0.5 #ms^(-2) max rate we can change speed of each wheel + +target = (PLAYFIELDCORNERS[2] + 1.0, 0) + +k = 160/SCALE # pixels per metre for graphics +u0 = WIDTH / 2 +v0 = HEIGHT / 2 + +x = PLAYFIELDCORNERS[0] - 0.5 +y = 0.0 +theta = 0.0 + +vL = 0.00 +vR = 0.00 + +size = [int(WIDTH), int(HEIGHT)] +screen = pygame.display.set_mode(size) +black = (20,20,40) + +# This makes the normal mouse pointer invisible in graphics window +pygame.mouse.set_visible(0) + +# time delta +dt = 0.1 + + +def surfdemo_show(array_img, name): + "displays a surface, waits for user to continue" + screen = pygame.display.set_mode(array_img.shape[:2], 0, 32) + surfarray.blit_array(screen, array_img) + pygame.display.flip() + pygame.display.set_caption(name) + while 1: + e = pygame.event.wait() + if e.type == MOUSEBUTTONDOWN: break + if e.type == KEYDOWN: break + + +def predictPosition(vL, vR, x, y, theta, dt): + + # Position update in time dt + + # Special cases + # Straight line motion + if (vL == vR): + xnew = x + vL * dt * math.cos(theta) + ynew = y + vL * dt * math.sin(theta) + thetanew = theta + # Pure rotation motion + elif (vL == -vR): + xnew = x + ynew = y + thetanew = theta + ((vR - vL) * dt / W) + else: + # Rotation and arc angle of general circular motion + R = W / 2.0 * (vR + vL) / (vR - vL) + deltatheta = (vR - vL) * dt / W + xnew = x + R * (math.sin(deltatheta + theta) - math.sin(theta)) + ynew = y - R * (math.cos(deltatheta + theta) - math.cos(theta)) + thetanew = theta + deltatheta + + return (xnew, ynew, thetanew) + + +def drawBarriers(barriers): + for barrier in barriers: + if(barrier[2] == 0): + pygame.draw.circle(screen, (0,20,80), (int(u0 + k * barrier[0]), int(v0 - k * barrier[1])), int(k * BARRIERRADIUS), 0) + else: + pygame.draw.circle(screen, (0,120,255), (int(u0 + k * barrier[0]), int(v0 - k * barrier[1])), int(k * BARRIERRADIUS), 0) + return + +def dialtebarrieres(imgin, barriers): + imgout = imgin + for barrier in barriers: + if(barrier[2] == 0): + center_u = int(u0 + k * barrier[0]) + center_v = int(v0 - k * barrier[1]) + RAD = BARRIERRADIUS+BARRIERINFILATE + radius = int(k * (RAD)) + radius2 = int(k * (BARRIERRADIUS)) + points = [(x,y) for x in range(center_u-radius, center_u+radius) for y in range(center_v-radius, center_v+radius)] + for pt in points: + vu = center_u - pt[0] + vv = center_v - pt[1] + distance = math.sqrt(vv*vv + vu*vu) + if (distance < radius and distance > radius2 and pt[0] < WIDTH-1 and pt[1] < HEIGHT-1): + imgout[pt[0],pt[1],0] = 0 + imgout[pt[0],pt[1],1] = 40 + imgout[pt[0],pt[1],2] = 80 + return imgout + +def MakeWaveFront(imgarray, start_uv, target_uv): + print ("building wavefront, please wait ... ") + heap = [] + newheap = [] + + u = target_uv[0] + v = target_uv[1] + + imgarray[:,:,0] = 0 # use channel 0 for planning + imgarray[u, v, 0] = 2 + + lastwave = 3 # start by setting nodes around target to 3 + moves = [(u + 1, v), (u - 1, v), (u, v - 1), (u, v + 1)] + for move in moves: + imgarray[move[0], move[1], 0] = 3 + heap.append(move) + + path = False + max_search = int(np.sqrt(WIDTH*WIDTH + HEIGHT*HEIGHT)) + for currentwave in range(4, max_search): + lastwave = lastwave + 1 + while(heap != []): + position = heap.pop() + (u, v) = position + moves = [(u + 1, v), (u - 1, v), (u, v + 1), (u, v - 1)] + for move in moves: + if(imgarray[move[0], move[1], 2] != 80): + if(imgarray[move[0], move[1], 0] == 0 and imgarray[position[0], position[1], 0] == currentwave - 1 and move[0] < WIDTH-1 and move[1] < HEIGHT-1 and move[0]>2 and move[1]>2): + imgarray[move[0], move[1], 0] = currentwave + newheap.append(move) + if(move == start_uv): + path = True + break + if(path == True): + break + if(path == True): + break + heap = newheap + newheap = [] + return imgarray, currentwave + + +def FindPath(imgarray, start_uv, currentwave): + trajectory = [] + nextpt = start_uv + path = [] + for backwave in range(currentwave-1,2,-1): + path.append(nextpt) + (u,v) = nextpt + values = [] + val = [] + moves = [(u + 1, v), (u - 1, v), (u, v + 1), (u, v - 1), (u + 1, v + 1), (u - 1, v - 1), (u + 1, v - 1), (u - 1, v - 1)] + for move in moves: + val.append(imgarray[move[0], move[1], 0]) + if(imgarray[move[0], move[1], 0] == backwave): + values.append(imgarray[move[0], move[1], 0]) + minimum = min(values) + indices = [i for i, v in enumerate(val) if v == minimum] + nextid = random.choice(indices) + nextpt = moves[nextid] + return path + +def GetControl(x,y,theta, waypoint): + wpu = waypoint[0] + wpv = waypoint[1] + + vt = 0.2 + u = u0 + k * x + v = v0 - k * y + vector = (wpu - u, -wpv + v) + vectorangle = math.atan2(vector[1], vector[0]) + psi = vectorangle - theta + + if(abs(psi) > (2*3.14/180)): + if(psi > 0): + vR = vt/2 + vL = -vt/2 + else: + vR = -vt/2 + vL = vt/2 + else: + wlx = x - (W/2.0) * math.sin(theta) + wly = y + (W/2.0) * math.cos(theta) + ulx = u0 + k * wlx + vlx = v0 - k * wly + dl = math.sqrt((ulx-wpu)*(ulx-wpu) + (vlx-wpv)*(vlx-wpv)) + + wrx = x + (W/2.0) * math.sin(theta) + wry = y - (W/2.0) * math.cos(theta) + urx = u0 + k * wrx + vrx = v0 - k * wry + dr = math.sqrt((urx-wpu)*(urx-wpu) + (vrx-wpv)*(vrx-wpv)) + + vR = 1*(2*vt)/(1+(dl/dr)) + vL = 1*(2*vt - vR) + + return vL, vR + +def AnimateRobot(x,y,theta): + # Draw robot + u = u0 + k * x + v = v0 - k * y + pygame.draw.circle(screen, (255,255,255), (int(u), int(v)), int(k * ROBOTRADIUS), 3) + # Draw wheels + # left wheel centre + wlx = x - (W/2.0) * math.sin(theta) + wly = y + (W/2.0) * math.cos(theta) + ulx = u0 + k * wlx + vlx = v0 - k * wly + WHEELBLOB = 0.04 + pygame.draw.circle(screen, (0,0,255), (int(ulx), int(vlx)), int(k * WHEELBLOB)) + # right wheel centre + wrx = x + (W/2.0) * math.sin(theta) + wry = y - (W/2.0) * math.cos(theta) + urx = u0 + k * wrx + vrx = v0 - k * wry + pygame.draw.circle(screen, (0,0,255), (int(urx), int(vrx)), int(k * WHEELBLOB)) + + time.sleep(dt / 5) + pygame.display.flip() + #time.sleep(0.2) + +def AnimateNavigation(barriers, waypint, path): + screen.fill(black) + drawBarriers(barriers) + + for pt in path: + screen.set_at(pt, (255,255,255)) + + pygame.draw.circle(screen, (255,100,0), target_uv, int(k * ROBOTRADIUS), 0) + pygame.draw.circle(screen, (255,100,0), (int(u0 + k * target[0]), int(v0 - k * target[1])), int(k * ROBOTRADIUS), 0) + pygame.draw.circle(screen, (255,0,255), (int(waypint[0]), int(waypint[1])), int(5)) + +def AnimatePath(imgarray, path, start_uv): + for pt in path: + imgarray[pt[0], pt[1], 0] = 0 + imgarray[pt[0], pt[1], 1] = 255 + imgarray[pt[0], pt[1], 2] = 255 + + #imgarray[:,:,0] /= imgarray[:,:,0].max()/255.0 + scalefactor = imgarray[:,:,0].max()/255.0 + imgarray[:,:,0] = imgarray[:,:,0]/scalefactor + imgarray[:,:,0].astype(int) + imgarray[start_uv[0], start_uv[1], 0] = 0 + imgarray[start_uv[0], start_uv[1], 1] = 255 + imgarray[start_uv[0], start_uv[1], 2] = 255 + + surfdemo_show(imgarray, 'Wavefront Path Planning') + + +def GetWaypoint(x,y,theta, path, waypointIndex, waypointSeperation, target): + reset = False + waypoint = path[waypointIndex] + u = u0 + k * x + v = v0 - k * y + distance_to_wp = math.sqrt((waypoint[0]-u)**2+(waypoint[1]-v)**2) # todo compare distance in metrics + if(distance_to_wp < 3): + waypointIndex = waypointSeperation + waypointIndex + if(waypointIndex > len(path)): + waypointIndex = len(path) - 1 + + return waypoint, reset, waypointIndex + +def IsAtTarget(x,y,target): + disttotarget = math.sqrt((x - target[0])**2 + (y - target[1])**2) + if (disttotarget < 0.04): + return True + else: + return False + + +while(1): + start_uv = (int(u0 + k * x), int(v0 - k * y)) + target_uv = (int(u0 + k * target[0]), int(v0 - k * target[1])) + print ("start is: ", start_uv) + print ("goal is: ", target_uv) + + # prepare map of the world for plannign + screen.fill(black) + drawBarriers(barriers) + pygame.draw.circle(screen, (255,100,0), target_uv, int(k * ROBOTRADIUS), 0) + pygame.draw.circle(screen, (255,255,0), start_uv, int(k * ROBOTRADIUS), 0) + imgscreen8 = pygame.surfarray.array3d(screen) + imgscreen16 = np.array(imgscreen8, dtype=np.uint16) + drawBarriers(barriers) + imgarray = dialtebarrieres(imgscreen16, barriers) + pygame.display.flip() + pygame.display.set_caption('Wavefront Path Planning') + + # build wavefront, given the map, start point, and target point + imgarray, currentwave = MakeWaveFront(imgarray, start_uv, target_uv) + print ("press a key to start navaigation ... ") + + # find the path using the wavefront + path = FindPath(imgarray, start_uv, currentwave) + + # normlaize and show the path on the map + AnimatePath(imgarray, path, start_uv) + + # set start point + x = PLAYFIELDCORNERS[0] - 0.5 + y = 0 + theta = 0 + waypointSeperation = 40; + waypointIndex = waypointSeperation; + + # loop to navigate the path from start to target + while(1): + # get a waypoint to follow the path + (waypoint, reset, waypointIndex)= GetWaypoint(x,y,theta, path, waypointIndex, waypointSeperation, target) + + # reset the simulation, if robot is at target + if (IsAtTarget(x,y,target)): + target = (target[0], random.uniform(PLAYFIELDCORNERS[1], PLAYFIELDCORNERS[3])) + x = PLAYFIELDCORNERS[0] - 0.5 + y = 0.0 + theta = 0.0 + break + + + # calculate control signals to get to the next waypoint + (vL, vR) = GetControl(x,y,theta, waypoint) + + # Actually now move and update position based on chosen vL and vR + (x, y, theta) = predictPosition(vL, vR, x, y, theta, dt) + + # animate + AnimateNavigation(barriers, waypoint, path) + AnimateRobot(x,y,theta) + \ No newline at end of file diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/WavefrontChatGpt.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/WavefrontChatGpt.py new file mode 100644 index 0000000000000000000000000000000000000000..a358dfa0282ce93f94817eca2941d5fa2e991539 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/WavefrontChatGpt.py @@ -0,0 +1,53 @@ +import numpy as np +import cv2 +import matplotlib.pyplot as plt +import time + +class WaveFrontPlanner: + __wall = 999 + __goal = 1 + __nothing = '000' + __path = 'PATH' + + def __init__(self, mask_list): + if not isinstance(mask_list, np.ndarray): + raise ValueError("Invalid input type. 'mask_list' must be a numpy array.") + self.__mapOfWorld = np.array([['999' if j > 200 else '000' for j in row] for row in mask_list]) + + def minSurroundingNodeValue(self, x, y): + values = [self.__mapOfWorld[x + 1][y], self.__mapOfWorld[x - 1][y], + self.__mapOfWorld[x][y + 1], self.__mapOfWorld[x][y - 1]] + min_value = min(value for value in values if value != self.__nothing) + min_index = values.index(min_value) + 1 + return min_value, min_index + + def waveFrontAlgorithm(self): + goal_coordinates = np.where(self.__mapOfWorld == self.__goal) + goal_x, goal_y = goal_coordinates[0][0], goal_coordinates[1][0] + queue = [(goal_x, goal_y)] + + while queue: + x, y = queue.pop(0) + + if self.__mapOfWorld[x][y] == self.__nothing: + min_value, min_index = self.minSurroundingNodeValue(x, y) + self.__mapOfWorld[x][y] = min_value + 1 + queue.append((x + 1, y)) # Move down + queue.append((x - 1, y)) # Move up + queue.append((x, y + 1)) # Move right + queue.append((x, y - 1)) # Move left + + self.__mapOfWorld[goal_x][goal_y] = self.__goal + + def visualizeMap(self): + plt.imshow(cv2.flip(self.__mapOfWorld, 0), cmap='hot', interpolation='nearest') + plt.colorbar() + plt.show() + +# Beispielverwendung: +mask_list = np.zeros((200, 200)) +mask_list[50:150, 50:150] = 1 + +planner = WaveFrontPlanner(mask_list) +planner.waveFrontAlgorithm() +planner.visualizeMap() diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/WavefrontPlanner.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/WavefrontPlanner.py new file mode 100644 index 0000000000000000000000000000000000000000..13cebc92bac4ee4b8096a05c1e3007b8f8741a69 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/WavefrontPlanner.py @@ -0,0 +1,397 @@ +############################################################################ +# WAVEFRONT ALGORITHM +# Adapted to Python Code By Darin Velarde +# Fri Jan 29 13:56:53 PST 2010 +# from C code from John Palmisano +# (www.societyofrobots.com) +############################################################################ +import cv2 +import numpy as np +import matplotlib.pyplot as plt +import time + +class waveFrontPlanner: + ############################################################################ + # WAVEFRONT ALGORITHM + # Adapted to Python Code By Darin Velarde + # Fri Jan 29 13:56:53 PST 2010 + # from C code from John Palmisano + # (www.societyofrobots.com) + ############################################################################ + + def __init__(self, mapOfWorld, slow=False): + self.__slow = slow + self.__mapOfWorld = mapOfWorld + if str(type(mapOfWorld)).find("numpy") != -1: + #If its a numpy array + self.__height, self.__width = self.__mapOfWorld.shape + else: + self.__height, self.__width = len(self.__mapOfWorld), len(self.__mapOfWorld[0]) + + self.__nothing = 000 + self.__wall = 999 + self.__goal = 1 + self.__path = "PATH" + + self.__finalPath = [] + + #Robot value + self.__robot = 254 + #Robot default Location + self.__robot_x = 0 + self.__robot_y = 0 + + #default goal location + self.__goal_x = 8 + self.__goal_y = 9 + + #temp variables + self.__temp_A = 0 + self.__temp_B = 0 + self.__counter = 0 + self.__steps = 0 #determine how processor intensive the algorithm was + + #when searching for a node with a lower value + self.__minimum_node = 250 + self.__min_node_location = 250 + self.__new_state = 1 + self.__old_state = 1 + self.__reset_min = 250 #above this number is a special (wall or robot) + ########################################################################### + + def setRobotPosition(self, x, y): + """ + Sets the robot's current position + + """ + + self.__robot_x = x + self.__robot_y = y + ########################################################################### + + def setGoalPosition(self, x, y): + """ + Sets the goal position. + + """ + + self.__goal_x = x + self.__goal_y = y + ########################################################################### + + def robotPosition(self): + return (self.__robot_x, self.__robot_y) + ########################################################################### + + def goalPosition(self): + return (self.__goal_x, self.__goal_y) + ########################################################################### + + def run(self, prnt=False): + """ + The entry point for the robot algorithm to use wavefront propagation. + + """ + + path = [] + while self.__mapOfWorld[self.__robot_x][self.__robot_y] != self.__goal: + if self.__steps > 10000: + print ("Cannot find a path.") + return + #find new location to go to + self.__new_state = self.propagateWavefront() + #update location of robot + if self.__new_state == 1: + self.__robot_x -= 1 + if prnt: + print("Move to x=%d y=%d\n\n" % (self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + if self.__new_state == 2: + self.__robot_y += 1 + if prnt: + print("Move to x=%d y=%d\n\n" %(self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + if self.__new_state == 3: + self.__robot_x += 1 + if prnt: + print("Move to x=%d y=%d\n\n" %(self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + if self.__new_state == 4: + self.__robot_y -= 1 + if prnt: + print("Move to x=%d y=%d\n\n" %(self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + self.__old_state = self.__new_state + msg = "Found the goal in %i steps:\n" % self.__steps + msg += "mapOfWorld size= %i %i\n\n" % (self.__height, self.__width) + if prnt: + print(msg) + self.printMap() + return path + ########################################################################### + + def propagateWavefront(self, prnt=False): + self.unpropagate() + #Old robot location was deleted, store new robot location in mapOfWorld + self.__mapOfWorld[self.__robot_x][self.__robot_y] = self.__robot + self.__path = self.__robot + #start location to begin scan at goal location + self.__mapOfWorld[self.__goal_x][self.__goal_y] = self.__goal + counter = 0 + while counter < 200: #allows for recycling until robot is found + x = 0 + y = 0 + #time.sleep(0.00001) + #while the mapOfWorld hasnt been fully scanned + while x < self.__height and y < self.__width: + #if this location is a wall or the goal, just ignore it + if self.__mapOfWorld[x][y] != self.__wall and \ + self.__mapOfWorld[x][y] != self.__goal: + #a full trail to the robot has been located, finished! + minLoc = self.minSurroundingNodeValue(x, y) + if minLoc < self.__reset_min and \ + self.__mapOfWorld[x][y] == self.__robot: + if prnt: + print("Finished Wavefront:\n") + self.printMap() + # Tell the robot to move after this return. + return self.__min_node_location + #record a value in to this node + elif self.__minimum_node != self.__reset_min: + #if this isnt here, 'nothing' will go in the location + self.__mapOfWorld[x][y] = self.__minimum_node + 1 + #go to next node and/or row + y += 1 + if y == self.__width and x != self.__height: + x += 1 + y = 0 + #print self.__robot_x, self.__robot_y + if prnt: + print("Sweep #: %i\n" % (counter + 1)) + self.printMap() + self.__steps += 1 + counter += 1 + return 0 + ########################################################################### + + def unpropagate(self): + """ + clears old path to determine new path + stay within boundary + + """ + + for x in range(0, self.__height): + for y in range(0, self.__width): + if self.__mapOfWorld[x][y] != self.__wall and \ + self.__mapOfWorld[x][y] != self.__goal and \ + self.__mapOfWorld[x][y] != self.__path: + #if this location is a wall or goal, just ignore it + self.__mapOfWorld[x][y] = self.__nothing #clear that space + ########################################################################### + + def minSurroundingNodeValue(self, x, y): + """ + this method looks at a node and returns the lowest value around that + node. + + """ + + #reset minimum + self.__minimum_node = self.__reset_min + #down + if x < self.__height -1: + if self.__mapOfWorld[x + 1][y] < self.__minimum_node and \ + self.__mapOfWorld[x + 1][y] != self.__nothing: + #find the lowest number node, and exclude empty nodes (0's) + self.__minimum_node = self.__mapOfWorld[x + 1][y] + self.__min_node_location = 3 + #up + if x > 0: + if self.__mapOfWorld[x-1][y] < self.__minimum_node and \ + self.__mapOfWorld[x-1][y] != self.__nothing: + self.__minimum_node = self.__mapOfWorld[x-1][y] + self.__min_node_location = 1 + #right + if y < self.__width -1: + if self.__mapOfWorld[x][y + 1] < self.__minimum_node and \ + self.__mapOfWorld[x][y + 1] != self.__nothing: + self.__minimum_node = self.__mapOfWorld[x][y + 1] + self.__min_node_location = 2 + #left + if y > 0: + if self.__mapOfWorld[x][y - 1] < self.__minimum_node and \ + self.__mapOfWorld[x][y - 1] != self.__nothing: + self.__minimum_node = self.__mapOfWorld[x][y-1] + self.__min_node_location = 4 + return self.__minimum_node + ########################################################################### + + def printMap(self): + """ + Prints out the map of this instance of the class. + + """ + + msg = '' + for temp_B in range(0, self.__height): + for temp_A in range(0, self.__width): + if self.__mapOfWorld[temp_B][temp_A] == self.__wall: + msg += "%04s" % "[#]" + elif self.__mapOfWorld[temp_B][temp_A] == self.__robot: + msg += "%04s" % "-" + elif self.__mapOfWorld[temp_B][temp_A] == self.__goal: + msg += "%04s" % "G" + else: + msg += "%04s" % str(self.__mapOfWorld[temp_B][temp_A]) + msg += "\n\n" + msg += "\n\n" + print(msg) + # + if self.__slow == True: + time.sleep(0.05) +############################################################################ + +class mapCreate: + ############################################################################ + + def create_map(self, scale,img): + + # Map erstellen + + # Img Objekt + #cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + #success, img = cap.read() + + #Karte von Bild + #succ, img = cv2.imread(2) + print('Original Dimensions : ',img.shape) + + scale_percent = 100-scale # percent of original size + width = int(img.shape[1] * scale_percent / 100) + height = int(img.shape[0] * scale_percent / 100) + dim = (width, height) + + # resize image + resized = cv2.resize(img, dim, interpolation = cv2.INTER_AREA) + print('Resized Dimensions : ',resized.shape) + + gray = cv2.cvtColor(resized, cv2.COLOR_RGB2GRAY) + mask = cv2.inRange(gray, np.array([90]), np.array([255])) + + mapOfWorld = [[0]*gray.shape[1]]*gray.shape[0] + mask_list= np.ndarray.tolist(mask) + + #Markiere alle leeren Zellen mit 000 und alle Zellen mit Hinderniss mit 999 + for i in range(0,mask.shape[0]): + mapOfWorld[i] = ['999' if j > 200 else '000' for j in mask_list[i]] + + mapOfWorld = np.array(mapOfWorld, dtype = int) + mapOfWorldSized = mapOfWorld.copy() + + e = 3 + #Hindernisse Größer machen + for i in range(0,mapOfWorld.shape[0]): + for j in range(0,mapOfWorld.shape[1]): + for r in range(0,e): + try: + if (mapOfWorldSized[i][j] == 999): + mapOfWorld[i][j+e] = 999 + mapOfWorld[i+e][j] = 999 + mapOfWorld[i-e][j] = 999 + mapOfWorld[i][j-e] = 999 + mapOfWorld[i+e][j+e] = 999 + mapOfWorld[i+e][j-e] = 999 + mapOfWorld[i-e][j+e] = 999 + mapOfWorld[i-e][j-e] = 999 + except Exception: + continue + + return mapOfWorld + ############################################################################ + + def scale_koord(self, sx, sy, gx, gy,scale): + scale_percent = 100-scale # percent of original size + + sx = int(sx*scale_percent/100) + sy = int(sy*scale_percent/100) + gx = int(gx*scale_percent/100) + gy = int(gy*scale_percent/100) + + return sx,sy,gx,gy + ############################################################################ + + def rescale_koord(self, path,scale): + scale_percent = 100-scale # percent of original size100 + + path = np.array(path) + + for i in range(len(path)): + path[i] = path[i]*100/scale_percent + + return path + ############################################################################ + + def calc_trans(self, x, y, height): + yTrans = height - y + xTrans = x + + return xTrans, yTrans +############################################################################### + +if __name__ == "__main__": + """ + X is vertical, Y is horizontal + + """ + + Map = mapCreate() #Map Objekt erzeugen + + #Verkleinerungsfaktor in % + scale = 85 + + img = cv2.imread("D:/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/karte.jpg", cv2.COLOR_RGB2GRAY) + #cap = cv2.VideoCapture(2) + mapWorld = Map.create_map(scale, img) + height, width = img.shape[:2] + + #Angaben in Weltkoordinaten + sy = 300 #X-Koordinate im Weltkoordinaten System + sx = 200 #Y-Koordinate im Weltkoordinaten System + + gy = 700 #X-Koordinate im Weltkordinaten System + gx = 240 #Y-Koordinate im Weltkoordinaten System + + sx,sy,gx,gy = Map.scale_koord(sx,sy,gx,gy,scale) #Kordinaten Tauschen X,Y + + start = time.time() + planner = waveFrontPlanner(mapWorld) + planner.setGoalPosition(gx,gy) + planner.setRobotPosition(sx,sy) + path = planner.run(False) + end = time.time() + print("Took %f seconds to run wavefront simulation" % (end - start)) + + path = Map.rescale_koord(path, scale) + #print(path) +#%% Plot + #Programm Koordinaten + imgTrans = cv2.transpose(img) # X und Y-Achse im Bild tauschen + imgPlot, ax1 = plt.subplots() + + ax1.set_title('Programm Koordinaten') + ax1.imshow(imgTrans) + ax1.set_xlabel('[px]') + ax1.set_ylabel('[px]') + ax1.scatter(path[:,0], path[:,1], color='r') + + #Bild Koordinaten + imgPlot2, ax2 = plt.subplots() + ax2.set_title('Bild Koordinaten') + ax2.set_xlabel('[px]') + ax2.set_ylabel('[px]') + ax2.imshow(img) + ax2.scatter(path[:,1], path[:,0], color='r') + +#%% Sphero Pfad + pathWeltX, pathWeltY = Map.calc_trans(path[:,1], path[:,0], height) \ No newline at end of file diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/__init__.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/_constants.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/_constants.py new file mode 100644 index 0000000000000000000000000000000000000000..fa8a0e875eed9f484945c1eeaf2fda3fd98503a4 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/_constants.py @@ -0,0 +1,72 @@ +''' +Known Peripheral UUIDs, obtained by querying using the Bluepy module: +===================================================================== +Anti DOS Characteristic <00020005-574f-4f20-5370-6865726f2121> +Battery Level Characteristic <Battery Level> +Peripheral Preferred Connection Parameters Characteristic <Peripheral Preferred Connection Parameters> +API V2 Characteristic <00010002-574f-4f20-5370-6865726f2121> +DFU Control Characteristic <00020002-574f-4f20-5370-6865726f2121> +Name Characteristic <Device Name> +Appearance Characteristic <Appearance> +DFU Info Characteristic <00020004-574f-4f20-5370-6865726f2121> +Service Changed Characteristic <Service Changed> +Unknown1 Characteristic <00020003-574f-4f20-5370-6865726f2121> +Unknown2 Characteristic <00010003-574f-4f20-5370-6865726f2121> + +The rest of the values saved in the dictionaries below, were borrowed from +@igbopie's javacript library, which is available at https://github.com/igbopie/spherov2.js + +''' + +deviceID = {"apiProcessor": 0x10, # 16 + "systemInfo": 0x11, # 17 + "powerInfo": 0x13, # 19 + "driving": 0x16, # 22 + "animatronics": 0x17, # 23 + "sensor": 0x18, # 24 + "something": 0x19, # 25 + "userIO": 0x1a, # 26 + "somethingAPI": 0x1f} # 31 + +SystemInfoCommands = {"mainApplicationVersion": 0x00, # 00 + "bootloaderVersion": 0x01, # 01 + "something": 0x06, # 06 + "something2": 0x13, # 19 + "something6": 0x12, # 18 + "something7": 0x28} # 40 + +sendPacketConstants = {"StartOfPacket": 0x8d, # 141 + "EndOfPacket": 0xd8} # 216 + +userIOCommandIDs = {"allLEDs": 0x0e} # 14 + +flags= {"isResponse": 0x01, # 0x01 + "requestsResponse": 0x02, # 0x02 + "requestsOnlyErrorResponse": 0x04, # 0x04 + "resetsInactivityTimeout": 0x08} # 0x08 + +powerCommandIDs={"deepSleep": 0x00, # 0 + "sleep": 0x01, # 01 + "batteryVoltage": 0x03, # 03 + "wake": 0x0D, # 13 + "something": 0x05, # 05 + "something2": 0x10, # 16 + "something3": 0x04, # 04 + "something4": 0x1E} # 30 + +drivingCommands={"rawMotor": 0x01, # 1 + "resetHeading": 0x06, # 6 + "driveAsSphero": 0x04, # 4 + "driveAsRc": 0x02, # 2 + "driveWithHeading": 0x07, # 7 + "stabilization": 0x0C} # 12 + +sensorCommands={'sensorMask': 0x00, # 00 + 'sensorResponse': 0x02, # 02 + 'configureCollision': 0x11, # 17 + 'collisionDetectedAsync': 0x12, # 18 + 'resetLocator': 0x13, # 19 + 'enableCollisionAsync': 0x14, # 20 + 'sensor1': 0x0F, # 15 + 'sensor2': 0x17, # 23 + 'configureSensorStream': 0x0C} # 12 diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/a_star.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/a_star.py new file mode 100644 index 0000000000000000000000000000000000000000..6d203504327dd356614b08d9d8f169f63aa2ad55 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/a_star.py @@ -0,0 +1,282 @@ +""" + +A* grid planning + +author: Atsushi Sakai(@Atsushi_twi) + Nikos Kanargias (nkana@tee.gr) + +See Wikipedia article (https://en.wikipedia.org/wiki/A*_search_algorithm) + +""" + +import math + +import matplotlib.pyplot as plt + +show_animation = True + + +class AStarPlanner: + + def __init__(self, ox, oy, resolution, rr): + """ + Initialize grid map for a star planning + + ox: x position list of Obstacles [m] + oy: y position list of Obstacles [m] + resolution: grid resolution [m] + rr: robot radius[m] + """ + + self.resolution = resolution + self.rr = rr + self.min_x, self.min_y = 0, 0 + self.max_x, self.max_y = 0, 0 + self.obstacle_map = None + self.x_width, self.y_width = 0, 0 + self.motion = self.get_motion_model() + self.calc_obstacle_map(ox, oy) + + class Node: + def __init__(self, x, y, cost, parent_index): + self.x = x # index of grid + self.y = y # index of grid + self.cost = cost + self.parent_index = parent_index + + def __str__(self): + return str(self.x) + "," + str(self.y) + "," + str( + self.cost) + "," + str(self.parent_index) + + def planning(self, sx, sy, gx, gy): + """ + A star path search + + input: + s_x: start x position [m] + s_y: start y position [m] + gx: goal x position [m] + gy: goal y position [m] + + output: + rx: x position list of the final path + ry: y position list of the final path + """ + + start_node = self.Node(self.calc_xy_index(sx, self.min_x), + self.calc_xy_index(sy, self.min_y), 0.0, -1) + goal_node = self.Node(self.calc_xy_index(gx, self.min_x), + self.calc_xy_index(gy, self.min_y), 0.0, -1) + + open_set, closed_set = dict(), dict() + open_set[self.calc_grid_index(start_node)] = start_node + + while True: + if len(open_set) == 0: + print("Open set is empty..") + break + + c_id = min( + open_set, + key=lambda o: open_set[o].cost + self.calc_heuristic(goal_node, + open_set[ + o])) + current = open_set[c_id] + + # show graph + if show_animation: # pragma: no cover + plt.plot(self.calc_grid_position(current.x, self.min_x), + self.calc_grid_position(current.y, self.min_y), "xc") + # for stopping simulation with the esc key. + plt.gcf().canvas.mpl_connect('key_release_event', + lambda event: [exit( + 0) if event.key == 'escape' else None]) + if len(closed_set.keys()) % 10 == 0: + plt.pause(0.001) + + if current.x == goal_node.x and current.y == goal_node.y: + print("Find goal") + goal_node.parent_index = current.parent_index + goal_node.cost = current.cost + break + + # Remove the item from the open set + del open_set[c_id] + + # Add it to the closed set + closed_set[c_id] = current + + # expand_grid search grid based on motion model + for i, _ in enumerate(self.motion): + node = self.Node(current.x + self.motion[i][0], + current.y + self.motion[i][1], + current.cost + self.motion[i][2], c_id) + n_id = self.calc_grid_index(node) + + # If the node is not safe, do nothing + if not self.verify_node(node): + continue + + if n_id in closed_set: + continue + + if n_id not in open_set: + open_set[n_id] = node # discovered a new node + else: + if open_set[n_id].cost > node.cost: + # This path is the best until now. record it + open_set[n_id] = node + + rx, ry = self.calc_final_path(goal_node, closed_set) + + return rx, ry + + def calc_final_path(self, goal_node, closed_set): + # generate final course + rx, ry = [self.calc_grid_position(goal_node.x, self.min_x)], [ + self.calc_grid_position(goal_node.y, self.min_y)] + parent_index = goal_node.parent_index + while parent_index != -1: + n = closed_set[parent_index] + rx.append(self.calc_grid_position(n.x, self.min_x)) + ry.append(self.calc_grid_position(n.y, self.min_y)) + parent_index = n.parent_index + + return rx, ry + + @staticmethod + def calc_heuristic(n1, n2): + w = 1.0 # weight of heuristic + d = w * math.hypot(n1.x - n2.x, n1.y - n2.y) + return d + + def calc_grid_position(self, index, min_position): + """ + calc grid position + + :param index: + :param min_position: + :return: + """ + pos = index * self.resolution + min_position + return pos + + def calc_xy_index(self, position, min_pos): + return round((position - min_pos) / self.resolution) + + def calc_grid_index(self, node): + return (node.y - self.min_y) * self.x_width + (node.x - self.min_x) + + def verify_node(self, node): + px = self.calc_grid_position(node.x, self.min_x) + py = self.calc_grid_position(node.y, self.min_y) + + if px < self.min_x: + return False + elif py < self.min_y: + return False + elif px >= self.max_x: + return False + elif py >= self.max_y: + return False + + # collision check + if self.obstacle_map[node.x][node.y]: + return False + + return True + + def calc_obstacle_map(self, ox, oy): + + self.min_x = round(min(ox)) + self.min_y = round(min(oy)) + self.max_x = round(max(ox)) + self.max_y = round(max(oy)) + print("min_x:", self.min_x) + print("min_y:", self.min_y) + print("max_x:", self.max_x) + print("max_y:", self.max_y) + + self.x_width = round((self.max_x - self.min_x) / self.resolution) + self.y_width = round((self.max_y - self.min_y) / self.resolution) + print("x_width:", self.x_width) + print("y_width:", self.y_width) + + # obstacle map generation + self.obstacle_map = [[False for _ in range(self.y_width)] + for _ in range(self.x_width)] + for ix in range(self.x_width): + x = self.calc_grid_position(ix, self.min_x) + for iy in range(self.y_width): + y = self.calc_grid_position(iy, self.min_y) + for iox, ioy in zip(ox, oy): + d = math.hypot(iox - x, ioy - y) + if d <= self.rr: + self.obstacle_map[ix][iy] = True + break + + @staticmethod + def get_motion_model(): + # dx, dy, cost + motion = [[1, 0, 1], + [0, 1, 1], + [-1, 0, 1], + [0, -1, 1], + [-1, -1, math.sqrt(2)], + [-1, 1, math.sqrt(2)], + [1, -1, math.sqrt(2)], + [1, 1, math.sqrt(2)]] + + return motion + + +def main(): + print(__file__ + " start!!") + + # start and goal position + sx = 10.0 # [m] + sy = 10.0 # [m] + gx = 50.0 # [m] + gy = 50.0 # [m] + grid_size = 2.0 # [m] + robot_radius = 1.0 # [m] + + # set obstacle positions + ox, oy = [], [] + for i in range(-10, 60): + ox.append(i) + oy.append(-10.0) + for i in range(-10, 60): + ox.append(60.0) + oy.append(i) + for i in range(-10, 61): + ox.append(i) + oy.append(60.0) + for i in range(-10, 61): + ox.append(-10.0) + oy.append(i) + for i in range(-10, 40): + ox.append(20.0) + oy.append(i) + for i in range(0, 40): + ox.append(40.0) + oy.append(60.0 - i) + + if show_animation: # pragma: no cover + plt.plot(ox, oy, ".k") + plt.plot(sx, sy, "og") + plt.plot(gx, gy, "xb") + plt.grid(True) + plt.axis("equal") + + a_star = AStarPlanner(ox, oy, grid_size, robot_radius) + rx, ry = a_star.planning(sx, sy, gx, gy) + + if show_animation: # pragma: no cover + plt.plot(rx, ry, "-r") + plt.pause(0.001) + plt.show() + + +if __name__ == '__main__': + main() diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/blobErkennung.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/blobErkennung.py new file mode 100644 index 0000000000000000000000000000000000000000..20036f56c3bb1f237bb1ec84a06c18afa70b5308 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/blobErkennung.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + +# Standard imports +import cv2 +import numpy as np; + +# Img Objekt +#cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") +cap = cv2.VideoCapture(4) + +# Set up the detector with default parameters. +detector = cv2.SimpleBlobDetector() + +# Setup SimpleBlobDetector parameters. +params = cv2.SimpleBlobDetector_Params() + +# Change thresholds +#params.minThreshold = 5 +#params.maxThreshold = 500 + +#FIlter by Color +params.filterByColor = True +params.blobColor = 255 + +# Filter by Area. +params.filterByArea = True +params.minArea = 20 +params.maxArea = 200 + +# Filter by Circularity +#params.filterByCircularity = True +#params.minCircularity = 0.5 +#params.maxCircularity = 1 + +# Filter by Convexity +#params.filterByConvexity = True +#params.minConvexity = 0.7 +#params.maxConvexity = 1 + +# Filter by Inertia +params.filterByInertia = True +params.minInertiaRatio = 0.5 + +# Create a detector with the parameters +detector = cv2.SimpleBlobDetector_create(params) + +success, img = cap.read() +height, width = img.shape[:2] + +def calc_trans(x,y): + yTrans = height - y + xTrans = x + + return xTrans, yTrans + +while True: + + success, img = cap.read() + gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY ) + + # Detect blobs. + keypoints = detector.detect(gray) + + #print(keypoints) + for keyPoint in keypoints: + x = keyPoint.pt[0] + y = keyPoint.pt[1] + #s = keyPoint.sizekeyPoints + + # Draw detected blobs as red circles. + # cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures + # the size of the circle corresponds to the size of blob + + im_with_keypoints = cv2.drawKeypoints(gray, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) + + # Show blobs + cv2.imshow("Keypoints", im_with_keypoints) + + if cv2.waitKey(10) & 0xFF == ord('q'): + break \ No newline at end of file diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/dynamicPathPlanning.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/dynamicPathPlanning.py new file mode 100644 index 0000000000000000000000000000000000000000..6607c85a415e48eac5483d8ecc329954fbf76051 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/dynamicPathPlanning.py @@ -0,0 +1,504 @@ +import numpy as np +import matplotlib.pyplot as plt +import robotController as rc +import mapGeneration as mg +import random +from controller import Robot + +#======[Scenario Control functions]============================================ + +#This function causes the robot to wait some time steps before starting it's operation +#DO NOT CHANGE THIS FUNCTION OR IT'S CALL IN THE CODE +def waitRandomTimeSteps(turtle, stop=50): + initialPose = np.zeros(7) + initialPose[0:2] = findRobotPosition(turtle) + turtle.setPoseGoal(initialPose) + turtle.drive_goalPose() + nRand = random.randint(1, stop) + for i in range(nRand): + turtle.robot.step(turtle.TIME_STEP) + turtle.update() + +#=========[Example functions for finding objects in the map]=================== + +def findTargetPosition(turtlebot): + mapInd = turtlebot.findInMap_FinalTarget() + if(len(mapInd) != 0): + pos = turtlebot.getPositionFromMapIndex(mapInd[0]) + else: + pos = [] + return np.array(pos, dtype=float).tolist() + +def findRobotPosition(turtlebot): + mapInds = turtlebot.findInMap_Robot() + robotPos = np.zeros((len(mapInds), 2)) + for i in range(len(mapInds)): + pos = turtlebot.getPositionFromMapIndex(mapInds[i]) + robotPos[i, ...] = pos + XYInd = np.average(robotPos, axis=0) + return np.array(XYInd, dtype=float).tolist() + +def findObstaclePositions(turtlebot): + mapInds = turtlebot.findInMap_Obstacles() + obsPos = np.zeros((len(mapInds), 2)) + for i in range(len(mapInds)): + pos = turtlebot.getPositionFromMapIndex(mapInds[i]) + obsPos[i, ...] = pos + return np.array(obsPos, dtype=float).tolist() + +#=========[Framework for own implementations of path tracking and planning]==== + +def checkForPossibleCollisions(turtlebot, turtlePos, collisionCntr): + #Fill this with your collision detection between the planed path and + #moving obstacles + hitBox = 0 + + faktor = 0.1 + + turtlePos1 = [turtlePos[0] + faktor, turtlePos[1] + faktor] + turtlePos2 = [turtlePos[0] + faktor, turtlePos[1] - faktor] + turtlePos3 = [turtlePos[0] - faktor, turtlePos[1] + faktor] + turtlePos4 = [turtlePos[0] - faktor, turtlePos[1] - faktor] + + turtlePos5 = [turtlePos[0] + faktor, turtlePos[1] ] + turtlePos6 = [turtlePos[0] , turtlePos[1] + faktor] + turtlePos7 = [turtlePos[0] - faktor, turtlePos[1] ] + turtlePos8 = [turtlePos[0] , turtlePos[1] - faktor] + + turtlePosBool = bool(turtlebot.isMapAtPosition_Obstacle(turtlePos)) + turtlePos1Bool = bool(turtlebot.isMapAtPosition_Obstacle(turtlePos1)) + turtlePos2Bool = bool(turtlebot.isMapAtPosition_Obstacle(turtlePos2)) + turtlePos3Bool = bool(turtlebot.isMapAtPosition_Obstacle(turtlePos3)) + turtlePos4Bool = bool(turtlebot.isMapAtPosition_Obstacle(turtlePos4)) + turtlePos5Bool = bool(turtlebot.isMapAtPosition_Obstacle(turtlePos5)) + turtlePos6Bool = bool(turtlebot.isMapAtPosition_Obstacle(turtlePos6)) + turtlePos7Bool = bool(turtlebot.isMapAtPosition_Obstacle(turtlePos7)) + turtlePos8Bool = bool(turtlebot.isMapAtPosition_Obstacle(turtlePos8)) + + hitBox = turtlePosBool | turtlePos1Bool | turtlePos2Bool | turtlePos3Bool | turtlePos4Bool | turtlePos5Bool | turtlePos6Bool | turtlePos7Bool | turtlePos8Bool + + if(hitBox): + collisionCntr += 1 + return True + + if(~hitBox): + return False + +def staticPathTracking(turtlebot, path, indCntr): + #This delta value determines the precision of the position control during path tracking + #A high value leads towards a larger curve radius at corners in the path, + # which might result in a smoothing of the path and keeping the velocity high. + #A lower value sets a higher precision for the precision control and thus, + # the robot might slow down when reaching a path point, but it stays very + # close to the original path + delta = 0.05 + if(indCntr == (len(path)-1)): + delta = 0.01 + #Control the turtlebot to drive towards the target position + isNearGoal = turtlebot.drive_goalPose(delta=delta) + #If the robot is close to the goal select the next goal position + if (isNearGoal): + indCntr += 1 + i = min(indCntr, len(path)) + if i == len(path): + indCntr = -1 + else: + #print("Aktuelle Position Pfad: ", path[i]) + turtlebot.setPoseGoal(path[i]) + return indCntr + +def planInitialPath(turtle): + + initialObstacles = findObstaclePositions(turtle) + initialPose = findRobotPosition(turtle) + initialFinalTarget = findTargetPosition(turtle) + + plt.scatter(np.array(initialObstacles)[..., 0], np.array(initialObstacles)[..., 1]) + plt.scatter(np.array(initialPose)[..., 0], np.array(initialPose)[..., 1] ) + plt.scatter(np.array(initialFinalTarget)[..., 0], np.array(initialFinalTarget)[..., 1]) + plt.show() + plt.pause(0.001) + + from matplotlib import pyplot as ppl + from matplotlib import cm + import random, sys, math, os.path + from matplotlib.pyplot import imread + + #Implementation of RRT + + MAP_IMG = './karte.png' #Black and white image for a map + MIN_NUM_VERT = 20 # Minimum number of vertex in the graph + MAX_NUM_VERT = 1500 # Maximum number of vertex in the graph + STEP_DISTANCE = 20 # Maximum distance between two vertex + SEED = None # For random numbers + + def rapidlyExploringRandomTree(ax, img, start, goal, seed=None): + hundreds = 100 + seed = random.seed(seed) + #print("Zufallsseed: ", seed) + points = [] + graph = [] + points.append(start) + graph.append((start, [])) + print('Generating and conecting random points') + occupied = True + phaseTwo = False + + # Phase two values (points 5 step distances around the goal point) + minX = max(goal[0] - 5 * STEP_DISTANCE, 0) + maxX = min(goal[0] + 5 * STEP_DISTANCE, len(img[0]) - 1) + minY = max(goal[1] - 5 * STEP_DISTANCE, 0) + maxY = min(goal[1] + 5 * STEP_DISTANCE, len(img) - 1) + + i = 0 + while (goal not in points) and (len(points) < MAX_NUM_VERT): + if (i % 100) == 0: + print(i, 'points randomly generated') + + if (len(points) % hundreds) == 0: + print(len(points), 'vertex generated') + hundreds = hundreds + 100 + + while (occupied): + if phaseTwo and (random.random() > 0.8): + point = [random.randint(minX, maxX), random.randint(minY, maxY)] + else: + point = [random.randint(0, len(img[0]) - 1), random.randint(0, len(img) - 1)] + + if (img[point[1]][point[0]][0] * 255 == 255): + occupied = False + + occupied = True + + nearest = findNearestPoint(points, point) + newPoints = connectPoints(point, nearest, img) + addToGraph(ax, graph, newPoints, point) + newPoints.pop(0) # The first element is already in the points list + points.extend(newPoints) + ppl.draw() + i = i + 1 + + if len(points) >= MIN_NUM_VERT: + if not phaseTwo: + print('Phase Two') + phaseTwo = True + + if phaseTwo: + nearest = findNearestPoint(points, goal) + newPoints = connectPoints(goal, nearest, img) + addToGraph(ax, graph, newPoints, goal) + newPoints.pop(0) + points.extend(newPoints) + ppl.draw() + + if goal in points: + print('Goal found, total vertex in graph:', len(points), 'total random points generated:', i) + + path = searchPath(graph, start, [start]) + + for i in range(len(path) - 1): + ax.plot([path[i][0], path[i + 1][0]], [path[i][1], path[i + 1][1]], color='g', linestyle='-', + linewidth=2) + ppl.draw() + + print('Showing resulting map') + print('Final path:', path) + print('The final path is made from:', len(path), 'connected points') + else: + path = None + print('Reached maximum number of vertex and goal was not found') + print('Total vertex in graph:', len(points), 'total random points generated:', i) + print('Showing resulting map') + + ppl.show() + return path + + def searchPath(graph, point, path): + for i in graph: + if point == i[0]: + p = i + + if p[0] == graph[-1][0]: + return path + + for link in p[1]: + path.append(link) + finalPath = searchPath(graph, link, path) + + if finalPath != None: + return finalPath + else: + path.pop() + + def addToGraph(ax, graph, newPoints, point): + if len(newPoints) > 1: # If there is anything to add to the graph + for p in range(len(newPoints) - 1): + nearest = [nearest for nearest in graph if (nearest[0] == [newPoints[p][0], newPoints[p][1]])] + nearest[0][1].append(newPoints[p + 1]) + graph.append((newPoints[p + 1], [])) + + if not p == 0: + ax.plot(newPoints[p][0], newPoints[p][1], '+k') # First point is already painted + ax.plot([newPoints[p][0], newPoints[p + 1][0]], [newPoints[p][1], newPoints[p + 1][1]], color='k', + linestyle='-', linewidth=1) + + if point in newPoints: + ax.plot(point[0], point[1], '.g') # Last point is green + else: + ax.plot(newPoints[p + 1][0], newPoints[p + 1][1], '+k') # Last point is not green + + def connectPoints(a, b, img): + newPoints = [] + newPoints.append([b[0], b[1]]) + step = [(a[0] - b[0]) / float(STEP_DISTANCE), (a[1] - b[1]) / float(STEP_DISTANCE)] + + # Set small steps to check for walls + pointsNeeded = int(math.floor(max(math.fabs(step[0]), math.fabs(step[1])))) + + if math.fabs(step[0]) > math.fabs(step[1]): + if step[0] >= 0: + step = [1, step[1] / math.fabs(step[0])] + else: + step = [-1, step[1] / math.fabs(step[0])] + + else: + if step[1] >= 0: + step = [step[0] / math.fabs(step[1]), 1] + else: + step = [step[0] / math.fabs(step[1]), -1] + + blocked = False + for i in range(pointsNeeded + 1): # Creates points between graph and solitary point + for j in range(STEP_DISTANCE): # Check if there are walls between points + coordX = round(newPoints[i][0] + step[0] * j) + coordY = round(newPoints[i][1] + step[1] * j) + + if coordX == a[0] and coordY == a[1]: + break + if coordY >= len(img) or coordX >= len(img[0]): + break + if img[int(coordY)][int(coordX)][0] * 255 < 255: + blocked = True + if blocked: + break + + if blocked: + break + if not (coordX == a[0] and coordY == a[1]): + newPoints.append( + [newPoints[i][0] + (step[0] * STEP_DISTANCE), newPoints[i][1] + (step[1] * STEP_DISTANCE)]) + + if not blocked: + newPoints.append([a[0], a[1]]) + return newPoints + + def findNearestPoint(points, point): + best = (sys.maxsize, sys.maxsize, sys.maxsize) + for p in points: + if p == point: + continue + dist = math.sqrt((p[0] - point[0]) ** 2 + (p[1] - point[1]) ** 2) + if dist < best[2]: + best = (p[0], p[1], dist) + return (best[0], best[1]) + + karte = np.ones([80, 80, 3], dtype=int) + + # Koordinatentransformation + initialObstacles = [[int(x[0] * 10), int(x[1] * 10)] for x in initialObstacles] + initialPose = [initialPose[0] * 10, initialPose[1] * 10] + initialFinalTarget = [initialFinalTarget[0] * 10, initialFinalTarget[1] * 10] + + initialObstacles = [[int(x[0] + 40), int(x[1] - 40)] for x in initialObstacles] + initialPose = [initialPose[0] + 40, initialPose[1] - 40] + initialFinalTarget = [initialFinalTarget[0] + 40, initialFinalTarget[1] - 40] + + initialObstacles = [[int(x[0]), int(x[1] * (-1))] for x in initialObstacles] + initialPose = [int(initialPose[0]), int(initialPose[1] * -1)] + initialFinalTarget = [int(initialFinalTarget[0]), int(initialFinalTarget[1] * -1)] + + # Karte Hindernisse einzeichnen + rot = np.array(0, dtype=int) + gruen = np.array(0, dtype=int) + blau = np.array(0, dtype=int) + + for k in initialObstacles: + karte[k[1], k[0], 0] = rot; + karte[k[1], k[0], 1] = gruen; + karte[k[1], k[0], 2] = blau; + + karte[k[1] + 1, k[0] + 0, 0] = rot; + karte[k[1] + 1, k[0] + 0, 1] = gruen; + karte[k[1] + 1, k[0] + 0, 2] = blau; + + karte[k[1] - 0, k[0] + 1, 0] = rot; + karte[k[1] - 0, k[0] + 1, 1] = gruen; + karte[k[1] - 0, k[0] + 1, 2] = blau; + + karte[k[1] + 0, k[0] - 1, 0] = rot; + karte[k[1] + 0, k[0] - 1, 1] = gruen; + karte[k[1] + 0, k[0] - 1, 2] = blau; + + karte[k[1] - 1, k[0] - 0, 0] = rot; + karte[k[1] - 1, k[0] - 0, 1] = gruen; + karte[k[1] - 1, k[0] - 0, 2] = blau; + + #Plotten als PNG speichern + from PIL import Image + im = Image.fromarray((karte * 255).astype(np.uint8)) + im.save('karte.png') + + print('Loading map... with file \'', MAP_IMG, '\'') + img = imread(MAP_IMG) + fig = ppl.gcf() + fig.clf() + ax = fig.add_subplot(1, 1, 1) + ax.imshow(img, cmap=cm.Greys_r) + ax.axis('image') + ppl.draw() + print('Map is', len(img[0]), 'x', len(img)) + start = initialPose + goal = initialFinalTarget + + path = rapidlyExploringRandomTree(ax, img, start, goal, seed=SEED) + + # Ruecktransformation + path1 = [[x[0], x[1] * (-1)] for x in path] + path2 = [[(x[0] - 40), (x[1] + 40)] for x in path1] + path3 = [[(x[0] / 10), (x[1] / 10)] for x in path2] + + path3 = [[(x[0]), x[1], 0, 0, 0, 0, 0] for x in path3] + + print("Umgerechneter Pfad", path3) + + if len(sys.argv) > 2: + print('Only one argument is needed') + elif len(sys.argv) > 1: + if os.path.isfile(sys.argv[1]): + MAP_IMG = sys.argv[1] + else: + print(sys.argv[1], 'is not a file') + + pathIndCntr = 0 + + return path3, pathIndCntr + +#=============[Evaluation functions]=========================================== +collisionCntr = 0 +recalcCntr = 0 +def updateEval_RobotCollisions(detector): + global collisionCntr + collision = np.nan_to_num(detector.getValue(), nan=0) + collisionCntr += int(collision) + return bool(collision) + +#======================================[MAIN Execution]======================== +if __name__ == "__main__": + plotting = False + nPlot = 80 + festgefahren = 0 + #Only increase this value if the simulation is running slowly + TIME_STEP = 64 + + #%%=============[DO NOT CHANGE THIS PART OF THE SCRIPT]================== + loopCntr = 0 # + turtle = rc.turtlebotController(TIME_STEP, + poseGoal_tcs=np.array([-0.863, -2.0, 0.0, 0.0, 0.0, 1.0, 3.14159])) # + detector = turtle.robot.getDevice("collisionDetector") # + detector.enable(TIME_STEP) # + # + #The map will be initialized after the first timestep (Transmission) # + turtle.robot.step(turtle.TIME_STEP) # + turtle.update() # + + #The map will have the dynamic objects after the second timestep # + turtle.robot.step(turtle.TIME_STEP) # + turtle.update() # + ScenarioFinalTarget = findTargetPosition(turtle) # + # + if plotting: + mg.plotting_GM_plotly(turtle.mapGenerator) + #DO NOT CHANGE OR REMOVE THIS FUNCTION CALL AND POSITION # + waitRandomTimeSteps(turtle) # + #===============[From here you can change the script]==================== + #%% + #here you should insert a function, which plans a path; the functions has to return a path and related index + try: + path, pathIndCntr = planInitialPath(turtle) + except TypeError: + print("Erste Pfadplanung nicht erfolgreich, neustart...") + try: + path, pathIndCntr = planInitialPath(turtle) + except TypeError: + print("Zweite Pfadplanung nicht erfolgreich, neustart...") + try: + path, pathIndCntr = planInitialPath(turtle) + except TypeError: + print("Dritte Pfadplanung nicht erfolgreich, neustart...") + exit() + + turtle.setPoseGoal(path[pathIndCntr]) + startTime = turtle.robot.getTime() + + inhibitCntr = 0 + while turtle.robot.step(turtle.TIME_STEP) != -1: + #%%===[DO NOT CHANGE THIS PART OF THE LOOP]========================== + loopCntr += 1 # + turtle.update() + + collision = checkForPossibleCollisions(turtle, findRobotPosition(turtle), collisionCntr) + + if ( (plotting) & (loopCntr % nPlot == 0)): # + mg.plotting_GM_plotly(turtle.mapGenerator) # + #====[From here you can change the loop]============================= + #%% + #Follow the static path towards the index pathIndCntr in the path + inhibitCntr = max(0, inhibitCntr - 1) + + if(collision == False): + pathIndCntr = staticPathTracking(turtle, path, pathIndCntr) + + if(collision == True): + print("Knallt") + turtle.stop() + festgefahren += 1 + + if(festgefahren == 10): + #Berechnen neue Pos + vorherigePos = path[pathIndCntr-1] + neueRichtung = [vorherigePos[0] / 2, vorherigePos[1] / 2, 0, 0, 0, 0, 0] + path.insert(pathIndCntr, neueRichtung) + + #Fahre neue Pos an bis du da bist + #Wenn du da bist festgefahren 0 und hoffentlich keine Collsion mehr + recalcCntr += 1 + inhibitCntr = 15 + festgefahren = 0 + collision = False + + #This is the exit condition. Changing this is not recommended + if(pathIndCntr == -1): + break + #End while + #%% + print("Simulation ended") + print("Timesteps with collisions: ", collisionCntr) + print("Recalculations done: ", recalcCntr) + print("Simulationtime: ", turtle.robot.getTime() - startTime) + print("") + print("Recalculation") + print("Timesteps with collisions: ", collisionCntr) + print("Recalculations done: ", recalcCntr) + print("Simulationtime: ", turtle.robot.getTime() - startTime) + + lastStateObstacles = np.array(findObstaclePositions(turtle)) + lastStateRobotPose = np.array(findRobotPosition(turtle)) + + trackRobot = np.array(turtle.trackRobot) + plt.scatter(lastStateObstacles[..., 0], lastStateObstacles[..., 1]) + plt.scatter(lastStateRobotPose[..., 0], lastStateRobotPose[..., 1]) + + plt.plot(trackRobot[..., 0], trackRobot[..., 1]) + plt.scatter(turtle.poseGoal[0], turtle.poseGoal[1]) + plt.scatter(turtle.mapGenerator.finalTarget[0], turtle.mapGenerator.finalTarget[1]) + plt.show() diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/map.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/map.py new file mode 100644 index 0000000000000000000000000000000000000000..379645de42c7898a6d3e846c21e3c8bf996ac70a --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/map.py @@ -0,0 +1,24 @@ +#Map erstellen + +# Standard imports +import cv2 +import numpy as np; + +# Img Objekt +cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + +img = cap.read() +#height, width = img.shape[:2] + +success, img = cap.read() +gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) +mask = cv2.inRange(gray, np.array([90]), np.array([255])) + +mask[mask == 255] = 87 + +mask = np.array(mask, dtype=np.uint8).view('S2').squeeze() + +mask[mask == b'WW'] = 'W' + +print(mask) + diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/maperstellen.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/maperstellen.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node copy.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node copy.py new file mode 100644 index 0000000000000000000000000000000000000000..29f2473b20a9c782704e15851518da8ec90ee823 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node copy.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 + +import rclpy +from rclpy.node import Node +import json +from sensor_msgs.msg import Imu +import threading +from std_msgs.msg import String +from geometry_msgs.msg import Quaternion, Vector3 +from box import Box +import numpy as np +from sphero_mini_controller.treiber import SpheroMini +import datetime + + +class MyNode(Node): + + def __init__(self): + super().__init__("sphero_mini") + self.publisher_ = self.create_publisher(String,"Imu",5) + #self.get_logger().info("Hello from ROS2") + + def connect(self): + #conf_file_path = file("/sphero_conf.json") + #with open("sphero_conf.json", "r") as f: + # cfg = Box(json.load(f)) + + MAC_ADDRESS = "C6:69:72:CD:BC:6D" + + # Connect: + sphero = SpheroMini(MAC_ADDRESS, verbosity = 4) + # battery voltage + sphero.getBatteryVoltage() + print(f"Bettery voltage: {sphero.v_batt}v") + + # firmware version number + sphero.returnMainApplicationVersion() + print(f"Firmware version: {'.'.join(str(x) for x in sphero.firmware_version)}") + return sphero + + + def create_quaternion(self, roll, pitch, yaw): + q = Quaternion() + cy, sy = np.cos(yaw * 0.5), np.sin(yaw * 0.5) + cp, sp = np.cos(pitch * 0.5), np.sin(pitch * 0.5) + cr, sr = np.cos(roll * 0.5), np.sin(roll * 0.5) + + q.w = cr * cp * cy + sr * sp * sy + q.x = sr * cp * cy - cr * sp * sy + q.y = cr * sp * cy + sr * cp * sy + q.z = cr * cp * sy - sr * sp * cy + + return q + + + def create_angular_veolocity_vector3(self, groll, gpitch, gyaw): + v = Vector3() + v.x = groll + v.y = gpitch + v.z = float(gyaw) + + return v + + + def create_linear_acc_vector3(self, xacc, yacc, zacc): + v = Vector3() + v.x = xacc + v.y = yacc + v.z = zacc + + return v + + + def get_sensors_data(self, sphero): + return { + "roll": sphero.IMU_roll, + "pitch": sphero.IMU_pitch, + "yaw": sphero.IMU_yaw, + "groll": sphero.IMU_gyro_x, + "gpitch": sphero.IMU_gyro_y, + "xacc": sphero.IMU_acc_x, + "yacc": sphero.IMU_acc_y, + "zacc": sphero.IMU_acc_z + } + + + def publish_imu(self, sensors_values,node): + i = Imu() + + i.header.stamp = node.get_clock().now().to_msg() + + i.orientation = self.create_quaternion( + roll=sensors_values["roll"], + pitch=sensors_values["pitch"], + yaw=sensors_values["yaw"] + ) + i.angular_velocity = self.create_angular_veolocity_vector3( + groll=sensors_values["groll"], + gpitch=sensors_values["gpitch"], + gyaw=0 # We don't have the IMU_gyro_z + ) + i.linear_acceleration = self.create_linear_acc_vector3( + xacc=sensors_values["xacc"], + yacc=sensors_values["yacc"], + zacc=sensors_values["zacc"] + ) + + #print(i) + + #self.publisher_.publish(i) + + + +def main(args = None): + + try: + rclpy.init(args=args) #Kommunikation Starten + node = MyNode() #Node erstellen + sphero = node.connect() + except Exception as e: # rclpy.ROSInterruptException + print("Konnte nicht verbinden") + raise e + + thread = threading.Thread(target=rclpy.spin, args=(node, ), daemon=True) + thread.start() + rate = node.create_rate(2) + + try: + while rclpy.ok(): + + sphero.configureSensorMask( + IMU_yaw=True, + IMU_pitch=True, + IMU_roll=True, + IMU_acc_y=True, + IMU_acc_z=True, + IMU_acc_x=True, + IMU_gyro_x=True, + IMU_gyro_y=True, + #IMU_gyro_z=True + ) + sphero.configureSensorStream() + + sensors_values = node.get_sensors_data(sphero) + #rclpy.logdebug(sensors_values) + node.publish_imu(sensors_values, node) + sphero.setLEDColor(red = 0, green = 255, blue = 0) # Turn LEDs green + + # Aiming: + sphero.setLEDColor(red = 0, green = 0, blue = 0) # Turn main LED off + sphero.stabilization(False) # Turn off stabilization + sphero.setBackLEDIntensity(255) # turn back LED on + sphero.wait(3) # Non-blocking pau`se + sphero.resetHeading() # Reset heading + sphero.stabilization(True) # Turn on stabilization + sphero.setBackLEDIntensity(0) # Turn back LED off + + # Move around: + sphero.setLEDColor(red = 0, green = 0, blue = 255) # Turn main LED blue + sphero.roll(100, 0) # roll forwards (heading = 0) at speed = 50 + + sphero.wait(3) # Keep rolling for three seconds + + sphero.roll(0, 0) # stop + sphero.wait(1) # Allow time to stop + + sphero.setLEDColor(red = 0, green = 255, blue = 0) # Turn main LED green + sphero.roll(-100, 0) # Keep facing forwards but roll backwards at speed = 50 + sphero.wait(3) # Keep rolling for three seconds + + sphero.roll(0, 0) # stop + sphero.wait(1) # Allow time to stop + + rate.sleep() + + except KeyboardInterrupt: + pass + + rclpy.shutdown() + thread.join() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node.py new file mode 100644 index 0000000000000000000000000000000000000000..8906b23876454b93bf060523bda262e61363cdd2 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node.py @@ -0,0 +1,766 @@ +#!/usr/bin/env python3 +############################################################################ +#%% Imports +import rclpy +from rclpy.node import Node +from sensor_msgs.msg import Imu +import threading +from geometry_msgs.msg import Quaternion, Vector3 +import numpy as np +from sphero_mini_controller.treiber import SpheroMini +import cv2 +import time +import queue +import matplotlib.pyplot as plt +############################################################################ +#%% Blob Detector / Globale Variablen +detector = cv2.SimpleBlobDetector() + +# Setup SimpleBlobDetector parameters. +params = cv2.SimpleBlobDetector_Params() + +# Change thresholds +#params.minThreshold = 5 +#params.maxThreshold = 500 + +#FIlter by Color +params.filterByColor = True +params.blobColor = 255 + +# Filter by Area. +params.filterByArea = True +params.minArea = 10 +params.maxArea = 200 + +# Filter by Circularity +#params.filterByCircularity = True +#params.minCircularity = 0.5 +#params.maxCircularity = 1 + +# Filter by Convexity +#params.filterByConvexity = True +#params.minConvexity = 0.7 +#params.maxConvexity = 1 + +# Filter by Inertia +#params.filterByInertia = True +#params.minInertiaRatio = 0.01 + +# Create a detector with the parameters +detector = cv2.SimpleBlobDetector_create(params) +############################################################################ +#%% Class Video Capture +# Bufferless VideoCapture +# Quelle ? +class VideoCapture: + def __init__(self, name): + self.cap = cv2.VideoCapture(name) + self.q = queue.Queue() + t = threading.Thread(target=self._reader) + t.daemon = True + t.start() + # read frames as soon as they are available, keeping only most recent one + def _reader(self): + while True: + ret, frame = self.cap.read() + #cv2.imshow("Cap", frame) + + if not ret: + break + if not self.q.empty(): + try: + self.q.get_nowait() # discard previous (unprocessed) frame + except queue.Empty: + pass + self.q.put(frame) + + def read(self): + return self.q.get() +############################################################################ +#%% Class Sphero Node +class MyNode(Node): + def __init__(self): + super().__init__("sphero_mini") + + def connect(self): + #Automatisch MAC-Adresse laden + #conf_file_path = file("/sphero_conf.json") + #with open("sphero_conf.json", "r") as f: + # cfg = Box(json.load(f)) + + MAC_ADDRESS = "C6:69:72:CD:BC:6D" + + # Connect: + sphero = SpheroMini(MAC_ADDRESS, verbosity = 4) + # battery voltage + sphero.getBatteryVoltage() + print(f"Bettery voltage: {sphero.v_batt}v") + + # firmware version number + sphero.returnMainApplicationVersion() + print(f"Firmware version: {'.'.join(str(x) for x in sphero.firmware_version)}") + return sphero + + def create_quaternion(self, roll, pitch, yaw): + q = Quaternion() + cy, sy = np.cos(yaw * 0.5), np.sin(yaw * 0.5) + cp, sp = np.cos(pitch * 0.5), np.sin(pitch * 0.5) + cr, sr = np.cos(roll * 0.5), np.sin(roll * 0.5) + + q.w = cr * cp * cy + sr * sp * sy + q.x = sr * cp * cy - cr * sp * sy + q.y = cr * sp * cy + sr * cp * sy + q.z = cr * cp * sy - sr * sp * cy + + return q + + def create_angular_veolocity_vector3(self, groll, gpitch, gyaw): + v = Vector3() + v.x = groll + v.y = gpitch + v.z = float(gyaw) + + return v + + def create_linear_acc_vector3(self, xacc, yacc, zacc): + v = Vector3() + v.x = xacc + v.y = yacc + v.z = zacc + + return v + + def get_sensors_data(self, sphero): + return { + "roll": sphero.IMU_roll, + "pitch": sphero.IMU_pitch, + "yaw": sphero.IMU_yaw, + "groll": sphero.IMU_gyro_x, + "gpitch": sphero.IMU_gyro_y, + "xacc": sphero.IMU_acc_x, + "yacc": sphero.IMU_acc_y, + "zacc": sphero.IMU_acc_z + } + + def publish_imu(self, sensors_values,node): + i = Imu() + + i.header.stamp = node.get_clock().now().to_msg() + + i.orientation = self.create_quaternion( + roll=sensors_values["roll"], + pitch=sensors_values["pitch"], + yaw=sensors_values["yaw"] + ) + i.angular_velocity = self.create_angular_veolocity_vector3( + groll=sensors_values["groll"], + gpitch=sensors_values["gpitch"], + gyaw=0 # We don't have the IMU_gyro_z + ) + i.linear_acceleration = self.create_linear_acc_vector3( + xacc=sensors_values["xacc"], + yacc=sensors_values["yacc"], + zacc=sensors_values["zacc"] + ) + + def get_pos(self, cap, height): + #zeitanfang = time.time() + + success = False + + while(success == False): + try: + img = cap.read() + + gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY ) + keypoints = detector.detect(gray) + + for keyPoint in keypoints: + x = keyPoint.pt[0] + y = keyPoint.pt[1] + success = True + + xTrans, yTrans = self.calc_trans(x,y, height) + except Exception: + continue + + #print("Blob X: %f Blob Y: %f" %(x,y)) + #zeitende = time.time() + #print("Dauer Programmausführung:",(zeitende-zeitanfang)) + #print("Get_Pos: X,Y:", xTrans,yTrans) + + return xTrans,yTrans + + def calc_offset(self, sphero, cap, height): + sphero.roll(100,0) + sphero.wait(1) + sphero.roll(0,0) + sphero.wait(0.5) + + ref = self.get_pos(cap, height) + print("calc_offset: Ref Punkt x,y", ref) + + sphero.wait(1) + sphero.roll(100,180) + sphero.wait(1) + sphero.roll(0,0) + + startpunkt = self.get_pos(cap, height) + print("calc_offset: Startpunkt x,y", startpunkt) + + ref = np.array(ref) + startpunkt = np.array(startpunkt) + + start_ref = ref-startpunkt + + phi = np.arctan2(start_ref[1],start_ref[0]) + phi = np.degrees(phi) + phi = int(-phi) + + print("Phi ohne alles:", phi) + + phi = self.phi_in_range(phi) + + print("Calc_Offset: ", phi) + + return phi + + def calc_av(self,istPos, sollPos, cap, height): + startPos = istPos + + startPos = np.array(startPos) + sollPos = np.array(sollPos) + + pktSpheroKord = sollPos - startPos + + phi = np.arctan2(pktSpheroKord[1], pktSpheroKord[0]) + phi = np.degrees(phi) + + phiZiel = int(phi) + + phiZiel = self.phi_in_range(phiZiel) + + v = 100 + + #print("Calc_AV: a,v", phiZiel, v) + + return phiZiel, v + + def drive_to(self,sphero, targetPos, aOffset, cap, height): + dmin = 20 + pos = self.get_pos(cap, height) + pos = np.array(pos) + + diff = targetPos - pos + + d = np.linalg.norm(diff, ord = 2) + + ar = [] + ar.append(0) + + i = 1 + + while d > dmin: + a,v = self.calc_av(pos,targetPos, cap, height) + + aR = -aOffset - a #Fallunterscheidung? + ar.append((self.phi_in_range(aR))) + + #Fahrbefehl + if(ar[i] != ar[i-1]): + sphero.roll(v, ar[i]) + sphero.wait(0.05) + else: + sphero.wait(0.05) + + #Aktuelle Pos + pos = self.get_pos(cap, height) + pos = np.array(pos) + + #Abweichung + diff = targetPos - pos + diff = np.array(diff) + d = np.linalg.norm(diff, ord = 2) + + i = i + 1 + + sphero.roll(0,0) + print("Ziel Erreicht") + + return + + def calc_trans(self,x,y, height): + yTrans = height - y + xTrans = x + + return xTrans, yTrans + + def phi_in_range(self,phi): + while(phi < 0): + phi = phi + 360 + while(phi > 360): + phi = phi - 360 + return phi +############################################################################ +#%% Class Wavefrontplanner +class waveFrontPlanner: + ############################################################################ + # WAVEFRONT ALGORITHM + # Adapted to Python Code By Darin Velarde + # Fri Jan 29 13:56:53 PST 2010 + # from C code from John Palmisano + # (www.societyofrobots.com) + ############################################################################ + + def __init__(self, mapOfWorld, slow=False): + self.__slow = slow + self.__mapOfWorld = mapOfWorld + if str(type(mapOfWorld)).find("numpy") != -1: + #If its a numpy array + self.__height, self.__width = self.__mapOfWorld.shape + else: + self.__height, self.__width = len(self.__mapOfWorld), len(self.__mapOfWorld[0]) + + self.__nothing = 000 + self.__wall = 999 + self.__goal = 1 + self.__path = "PATH" + + self.__finalPath = [] + + #Robot value + self.__robot = 254 + #Robot default Location + self.__robot_x = 0 + self.__robot_y = 0 + + #default goal location + self.__goal_x = 8 + self.__goal_y = 9 + + #temp variables + self.__temp_A = 0 + self.__temp_B = 0 + self.__counter = 0 + self.__steps = 0 #determine how processor intensive the algorithm was + + #when searching for a node with a lower value + self.__minimum_node = 250 + self.__min_node_location = 250 + self.__new_state = 1 + self.__old_state = 1 + self.__reset_min = 250 #above this number is a special (wall or robot) + ########################################################################### + + def setRobotPosition(self, x, y): + """ + Sets the robot's current position + + """ + + self.__robot_x = x + self.__robot_y = y + ########################################################################### + + def setGoalPosition(self, x, y): + """ + Sets the goal position. + + """ + + self.__goal_x = x + self.__goal_y = y + ########################################################################### + + def robotPosition(self): + return (self.__robot_x, self.__robot_y) + ########################################################################### + + def goalPosition(self): + return (self.__goal_x, self.__goal_y) + ########################################################################### + + def run(self, prnt=False): + """ + The entry point for the robot algorithm to use wavefront propagation. + + """ + + path = [] + while self.__mapOfWorld[self.__robot_x][self.__robot_y] != self.__goal: + if self.__steps > 10000: + print ("Cannot find a path.") + return + #find new location to go to + self.__new_state = self.propagateWavefront() + #update location of robot + if self.__new_state == 1: + self.__robot_x -= 1 + if prnt: + print("Move to x=%d y=%d\n\n" % (self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + if self.__new_state == 2: + self.__robot_y += 1 + if prnt: + print("Move to x=%d y=%d\n\n" %(self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + if self.__new_state == 3: + self.__robot_x += 1 + if prnt: + print("Move to x=%d y=%d\n\n" %(self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + if self.__new_state == 4: + self.__robot_y -= 1 + if prnt: + print("Move to x=%d y=%d\n\n" %(self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + self.__old_state = self.__new_state + msg = "Found the goal in %i steps:\n" % self.__steps + msg += "mapOfWorld size= %i %i\n\n" % (self.__height, self.__width) + if prnt: + print(msg) + self.printMap() + return path + ########################################################################### + + def propagateWavefront(self, prnt=False): + self.unpropagate() + #Old robot location was deleted, store new robot location in mapOfWorld + self.__mapOfWorld[self.__robot_x][self.__robot_y] = self.__robot + self.__path = self.__robot + #start location to begin scan at goal location + self.__mapOfWorld[self.__goal_x][self.__goal_y] = self.__goal + counter = 0 + while counter < 200: #allows for recycling until robot is found + x = 0 + y = 0 + #time.sleep(0.00001) + #while the mapOfWorld hasnt been fully scanned + while x < self.__height and y < self.__width: + #if this location is a wall or the goal, just ignore it + if self.__mapOfWorld[x][y] != self.__wall and \ + self.__mapOfWorld[x][y] != self.__goal: + #a full trail to the robot has been located, finished! + minLoc = self.minSurroundingNodeValue(x, y) + if minLoc < self.__reset_min and \ + self.__mapOfWorld[x][y] == self.__robot: + if prnt: + print("Finished Wavefront:\n") + self.printMap() + # Tell the robot to move after this return. + return self.__min_node_location + #record a value in to this node + elif self.__minimum_node != self.__reset_min: + #if this isnt here, 'nothing' will go in the location + self.__mapOfWorld[x][y] = self.__minimum_node + 1 + #go to next node and/or row + y += 1 + if y == self.__width and x != self.__height: + x += 1 + y = 0 + #print self.__robot_x, self.__robot_y + if prnt: + print("Sweep #: %i\n" % (counter + 1)) + self.printMap() + self.__steps += 1 + counter += 1 + return 0 + ########################################################################### + + def unpropagate(self): + """ + clears old path to determine new path + stay within boundary + + """ + + for x in range(0, self.__height): + for y in range(0, self.__width): + if self.__mapOfWorld[x][y] != self.__wall and \ + self.__mapOfWorld[x][y] != self.__goal and \ + self.__mapOfWorld[x][y] != self.__path: + #if this location is a wall or goal, just ignore it + self.__mapOfWorld[x][y] = self.__nothing #clear that space + ########################################################################### + + def minSurroundingNodeValue(self, x, y): + """ + this method looks at a node and returns the lowest value around that + node. + + """ + + #reset minimum + self.__minimum_node = self.__reset_min + #down + if x < self.__height -1: + if self.__mapOfWorld[x + 1][y] < self.__minimum_node and \ + self.__mapOfWorld[x + 1][y] != self.__nothing: + #find the lowest number node, and exclude empty nodes (0's) + self.__minimum_node = self.__mapOfWorld[x + 1][y] + self.__min_node_location = 3 + #up + if x > 0: + if self.__mapOfWorld[x-1][y] < self.__minimum_node and \ + self.__mapOfWorld[x-1][y] != self.__nothing: + self.__minimum_node = self.__mapOfWorld[x-1][y] + self.__min_node_location = 1 + #right + if y < self.__width -1: + if self.__mapOfWorld[x][y + 1] < self.__minimum_node and \ + self.__mapOfWorld[x][y + 1] != self.__nothing: + self.__minimum_node = self.__mapOfWorld[x][y + 1] + self.__min_node_location = 2 + #left + if y > 0: + if self.__mapOfWorld[x][y - 1] < self.__minimum_node and \ + self.__mapOfWorld[x][y - 1] != self.__nothing: + self.__minimum_node = self.__mapOfWorld[x][y-1] + self.__min_node_location = 4 + return self.__minimum_node + ########################################################################### + + def printMap(self): + """ + Prints out the map of this instance of the class. + + """ + + msg = '' + for temp_B in range(0, self.__height): + for temp_A in range(0, self.__width): + if self.__mapOfWorld[temp_B][temp_A] == self.__wall: + msg += "%04s" % "[#]" + elif self.__mapOfWorld[temp_B][temp_A] == self.__robot: + msg += "%04s" % "-" + elif self.__mapOfWorld[temp_B][temp_A] == self.__goal: + msg += "%04s" % "G" + else: + msg += "%04s" % str(self.__mapOfWorld[temp_B][temp_A]) + msg += "\n\n" + msg += "\n\n" + print(msg) + # + if self.__slow == True: + time.sleep(0.05) + + def plotMap(self,img,path): + #Programm Koordinaten + print("Plotten") + + imgTrans = cv2.transpose(img) # X und Y-Achse im Bild tauschen + imgPlot, ax1 = plt.subplots() + + ax1.set_title('Programm Koordinaten') + ax1.imshow(imgTrans) + ax1.set_xlabel('[px]') + ax1.set_ylabel('[px]') + ax1.scatter(path[:,0], path[:,1], color='r') + + #Bild Koordinaten + imgPlot2, ax2 = plt.subplots() + ax2.set_title('Bild Koordinaten') + ax2.set_xlabel('[px]') + ax2.set_ylabel('[px]') + ax2.imshow(img) + ax2.scatter(path[:,1], path[:,0], color='r') + + plt.show() + + return + + def getPathWelt(self,path,height,Mapobj): + + pathWeltX, pathWeltY = Mapobj.calc_trans_welt(path[:,1], path[:,0], height) + pathEckenX = [] + pathEckenY = [] + + for i,px in enumerate(pathWeltX): + if (i < len(pathWeltX)-1) & (i > 0): + if px != pathWeltX[i+1]: + if pathWeltY[i]!=pathWeltY[i-1]: + pathEckenX.append(px) + pathEckenY.append(pathWeltY[i]) + if pathWeltY[i] != pathWeltY[i+1]: + if pathWeltX[i]!=pathWeltX[i-1]: + pathEckenX.append(px) + pathEckenY.append(pathWeltY[i]) + + pathEckenX.append(pathWeltX[-1]) + pathEckenY.append(pathWeltY[-1]) + + + uebergabePath = [] + for i in range(0,len(pathEckenX)): + uebergabePath.append((pathEckenX[i],pathEckenY[i])) + + print("Ecken: ", uebergabePath) + + return uebergabePath + +############################################################################ +#%% Class Map +class mapCreate: + ############################################################################ + + def create_map(self, scale, cap): + + # Map erstellen + + # Img Objekt + #cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + img = cap.read() + + #Karte von Bild + #img = cv2.imread("D:/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/map/map.jpg") + print('Original Dimensions : ',img.shape) + + scale_percent = 100-scale # percent of original size + width = int(img.shape[1] * scale_percent / 100) + height = int(img.shape[0] * scale_percent / 100) + dim = (width, height) + + # resize image + resized = cv2.resize(img, dim, interpolation = cv2.INTER_AREA) + print('Resized Dimensions : ',resized.shape) + + gray = cv2.cvtColor(resized, cv2.COLOR_RGB2GRAY) + mask = cv2.inRange(gray, np.array([50]), np.array([255])) + + plt.plot(mask) + + mapOfWorld = [[0]*gray.shape[1]]*gray.shape[0] + mask_list= np.ndarray.tolist(mask) + + #Markiere alle leeren Zellen mit 000 und alle Zellen mit Hinderniss mit 999 + for i in range(0,mask.shape[0]): + mapOfWorld[i] = ['999' if j > 200 else '000' for j in mask_list[i]] + + mapOfWorld = np.array(mapOfWorld, dtype = int) + mapOfWorldSized = mapOfWorld.copy() + + e = 1 + #Hindernisse Größer machen + for i in range(0,mapOfWorld.shape[0]): + for j in range(0,mapOfWorld.shape[1]): + for r in range(0,e): + try: + if (mapOfWorldSized[i][j] == 999): + mapOfWorld[i][j+e] = 999 + mapOfWorld[i+e][j] = 999 + mapOfWorld[i-e][j] = 999 + mapOfWorld[i][j-e] = 999 + mapOfWorld[i+e][j+e] = 999 + mapOfWorld[i+e][j-e] = 999 + mapOfWorld[i-e][j+e] = 999 + mapOfWorld[i-e][j-e] = 999 + except Exception: + continue + + return mapOfWorld,img + ############################################################################ + + def scale_koord(self, sx, sy, gx, gy,scale): + scale_percent = 100-scale # percent of original size + + sx = int(sx*scale_percent/100) + sy = int(sy*scale_percent/100) + gx = int(gx*scale_percent/100) + gy = int(gy*scale_percent/100) + + return sx,sy,gx,gy + ############################################################################ + + def rescale_koord(self, path,scale): + scale_percent = 100-scale # percent of original size100 + + path = np.array(path) + + for i in range(len(path)): + path[i] = path[i]*100/scale_percent + + return path + ############################################################################ + + def calc_trans_welt(self, x, y, height): + yTrans = height - y + xTrans = x + + return xTrans, yTrans +############################################################################### +#%% Main Funktion +def main(args = None): + #Verbindung herstellen, Node erstellen + try: + rclpy.init(args=args) #Kommunikation Starten + node = MyNode() #Node erstellen + sphero = node.connect() + thread = threading.Thread(target=rclpy.spin, args=(node, ), daemon=True) + thread.start() + #rate = node.create_rate(5) + + except Exception as e: # rclpy.ROSInterruptException + print("Konnte nicht verbinden") + raise e + + #cap = VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + cap = VideoCapture(4) + Map = mapCreate() + + img = cap.read() + height, width = img.shape[:2] + + scale = 90 + + sphero.setLEDColor(255,0,0) + sphero.wait(1) + sphero.setBackLEDIntensity(0) + sphero.stabilization(True) + + aOffset = node.calc_offset(sphero, cap, height) + + sphero.wait(1) + + #while(True): + + mapWorld,img = Map.create_map(scale, cap) + height, width = img.shape[:2] + + #Befehle für WaveFrontPlanner/Maperstellung + sy,sx = node.get_pos(cap,height) #Aktuelle Roboter Pos in Welt + sy, sx = node.calc_trans(sy,sx,height) + + gy = int(input("Bitte geben Sie die X-Zielkoordinate im Bild Koordinatensystem ein:")) #X-Koordinate im Weltkoordinaten System + gx = int(input("Bitte geben Sie die Y-Zielkoordinate im Bild Koordinatensystem ein:")) #Y-Koordinate im Weltkordinaten System + + sx,sy,gx,gy = Map.scale_koord(sx,sy,gx,gy,scale) #Kordinaten Tauschen X,Y + + start = time.time() + planner = waveFrontPlanner(mapWorld) + planner.setGoalPosition(gx,gy) + planner.setRobotPosition(sx,sy) + path = planner.run(False) + end = time.time() + print("Took %f seconds to run wavefront simulation" % (end - start)) + + path = Map.rescale_koord(path, scale) + + planner.plotMap(img,path) + + pathWelt = planner.getPathWelt(path,height,Map) + + for p in pathWelt: + node.drive_to(sphero,p,aOffset,cap, height) + sphero.wait(1) + + print("Geschafft") + + #if cv2.waitKey(10) & 0xFF == ord('q'): + # break + + rclpy.shutdown() +############################################################################ +#%% Main +if __name__ == '__main__': + main() +############################################################################ diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node_alt.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node_alt.py new file mode 100644 index 0000000000000000000000000000000000000000..3b4ea7911e77fbc716fc3c714888fe11f6c6d3ab --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node_alt.py @@ -0,0 +1,323 @@ +#!/usr/bin/env python3 + +import rclpy +from rclpy.node import Node +import json +from sensor_msgs.msg import Imu +import threading +from std_msgs.msg import String +from geometry_msgs.msg import Quaternion, Vector3 +from box import Box +import numpy as np +from sphero_mini_controller.treiber import SpheroMini +import datetime +import cv2 +import time +import queue, threading, time + +# Set up the detector with default parameters. +detector = cv2.SimpleBlobDetector() + +# Setup SimpleBlobDetector parameters. +params = cv2.SimpleBlobDetector_Params() + +# Change thresholds +#params.minThreshold = 5 +#params.maxThreshold = 500 + +#FIlter by Color +params.filterByColor = True +params.blobColor = 255 + +# Filter by Area. +params.filterByArea = True +params.minArea = 10 +params.maxArea = 200 + +# Filter by Circularity +#params.filterByCircularity = True +#params.minCircularity = 0.5 +#params.maxCircularity = 1 + +# Filter by Convexity +#params.filterByConvexity = True +#params.minConvexity = 0.7 +#params.maxConvexity = 1 + +# Filter by Inertia +#params.filterByInertia = True +#params.minInertiaRatio = 0.01 + +# Create a detector with the parameters +detector = cv2.SimpleBlobDetector_create(params) + +# bufferless VideoCapture +class VideoCapture: + def __init__(self, name): + self.cap = cv2.VideoCapture(name) + self.q = queue.Queue() + t = threading.Thread(target=self._reader) + t.daemon = True + t.start() + # read frames as soon as they are available, keeping only most recent one + def _reader(self): + while True: + ret, frame = self.cap.read() + if not ret: + break + if not self.q.empty(): + try: + self.q.get_nowait() # discard previous (unprocessed) frame + except queue.Empty: + pass + self.q.put(frame) + + def read(self): + return self.q.get() + +class MyNode(Node): + + def __init__(self): + super().__init__("sphero_mini") + + def connect(self): + #Automatisch MAC-Adresse laden + #conf_file_path = file("/sphero_conf.json") + #with open("sphero_conf.json", "r") as f: + # cfg = Box(json.load(f)) + + MAC_ADDRESS = "C6:69:72:CD:BC:6D" + + # Connect: + sphero = SpheroMini(MAC_ADDRESS, verbosity = 4) + # battery voltage + sphero.getBatteryVoltage() + print(f"Bettery voltage: {sphero.v_batt}v") + + # firmware version number + sphero.returnMainApplicationVersion() + print(f"Firmware version: {'.'.join(str(x) for x in sphero.firmware_version)}") + return sphero + + def create_quaternion(self, roll, pitch, yaw): + q = Quaternion() + cy, sy = np.cos(yaw * 0.5), np.sin(yaw * 0.5) + cp, sp = np.cos(pitch * 0.5), np.sin(pitch * 0.5) + cr, sr = np.cos(roll * 0.5), np.sin(roll * 0.5) + + q.w = cr * cp * cy + sr * sp * sy + q.x = sr * cp * cy - cr * sp * sy + q.y = cr * sp * cy + sr * cp * sy + q.z = cr * cp * sy - sr * sp * cy + + return q + + def create_angular_veolocity_vector3(self, groll, gpitch, gyaw): + v = Vector3() + v.x = groll + v.y = gpitch + v.z = float(gyaw) + + return v + + def create_linear_acc_vector3(self, xacc, yacc, zacc): + v = Vector3() + v.x = xacc + v.y = yacc + v.z = zacc + + return v + + def get_sensors_data(self, sphero): + return { + "roll": sphero.IMU_roll, + "pitch": sphero.IMU_pitch, + "yaw": sphero.IMU_yaw, + "groll": sphero.IMU_gyro_x, + "gpitch": sphero.IMU_gyro_y, + "xacc": sphero.IMU_acc_x, + "yacc": sphero.IMU_acc_y, + "zacc": sphero.IMU_acc_z + } + + def publish_imu(self, sensors_values,node): + i = Imu() + + i.header.stamp = node.get_clock().now().to_msg() + + i.orientation = self.create_quaternion( + roll=sensors_values["roll"], + pitch=sensors_values["pitch"], + yaw=sensors_values["yaw"] + ) + i.angular_velocity = self.create_angular_veolocity_vector3( + groll=sensors_values["groll"], + gpitch=sensors_values["gpitch"], + gyaw=0 # We don't have the IMU_gyro_z + ) + i.linear_acceleration = self.create_linear_acc_vector3( + xacc=sensors_values["xacc"], + yacc=sensors_values["yacc"], + zacc=sensors_values["zacc"] + ) + + def get_pos(self, cap, height): + zeitanfang = time.time() + + img = cap.read() + + gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY ) + keypoints = detector.detect(gray) + + for keyPoint in keypoints: + x = keyPoint.pt[0] + y = keyPoint.pt[1] + + #print("Blob X: %f Blob Y: %f" %(x,y)) + + zeitende = time.time() + #print("Dauer Programmausführung:",(zeitende-zeitanfang)) + + xTrans, yTrans = self.calc_trans(x,y, height) + + #print("Get_Pos: X,Y:", xTrans,yTrans) + + return xTrans,yTrans + + def calc_offset(self, sphero, cap, height): + sphero.roll(100,0) + sphero.wait(2) + sphero.roll(0,0) + sphero.wait(0.5) + + ref = self.get_pos(cap, height) + print("calc_offset: Ref Punkt x,y", ref) + + sphero.wait(1) + sphero.roll(100,180) + sphero.wait(2) + sphero.roll(0,0) + + startpunkt = self.get_pos(cap, height) + print("calc_offset: Startpunkt x,y", startpunkt) + + ref = np.array(ref) + startpunkt = np.array(startpunkt) + + start_ref = ref-startpunkt + + phi = np.arctan2(start_ref[1],start_ref[0]) + phi = np.degrees(phi) + phi = int(phi) + + print("Phi ohne alles:", phi) + + if(phi < 0): + phi = phi + 360 + + if(phi > 360): + phi = phi - 360 + + print("Calc_Offset: ", phi) + + return phi + + def calc_av(self, sollPos, cap, height): + + startPos = self.get_pos(cap, height) + + startPos = np.array(startPos) + sollPos = np.array(sollPos) + + pktSpheroKord = sollPos - startPos + + phi = np.arctan2(pktSpheroKord[1], pktSpheroKord[0]) + phi = np.degrees(phi) + + phiZiel = int(phi) + + if(phiZiel < 0): + phiZiel = phiZiel + 360 + + if(phi > 360): + phi = phi - 360 + + v = 100 + + print("Calc_AV: a,v", phiZiel, v) + + return phiZiel, v + + def drive_to(self,sphero, targetPos, aOffset, cap, height): + dmin = 50 + pos = self.get_pos(cap, height) + pos = np.array(pos) + + diff = targetPos - pos + + d = np.linalg.norm(diff, ord = 2) + + while d > dmin: + a,v = self.calc_av(targetPos, cap, height) + ar = aOffset + a #Fallunterscheidung? + + pos = self.get_pos(cap, height) + pos = np.array(pos) + diff = targetPos - pos + diff = np.array(diff) + d = np.linalg.norm(diff, ord = 2) + + sphero.roll(v, ar) + time.sleep(0.1) + + sphero.roll(0,ar) + + return + + def calc_trans(self,x,y, height): + yTrans = height - y + xTrans = x + + return xTrans, yTrans + +def main(args = None): + #Verbindung herstellen, Node erstellen + try: + rclpy.init(args=args) #Kommunikation Starten + node = MyNode() #Node erstellen + sphero = node.connect() + thread = threading.Thread(target=rclpy.spin, args=(node, ), daemon=True) + thread.start() + rate = node.create_rate(5) + + except Exception as e: # rclpy.ROSInterruptException + print("Konnte nicht verbinden") + raise e + + cap = VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + + img = cap.read() + height, width = img.shape[:2] + + sphero.setLEDColor(255,0,0) + sphero.wait(1) + sphero.setBackLEDIntensity(100) + sphero.stabilization(True) + + aOffset = node.calc_offset(sphero, cap, height) + + sphero.roll(100,0+aOffset) + sphero.wait(2) + sphero.roll(0,0) + + sphero.wait(5) + + targetPos = 117,430 + + node.drive_to(sphero,targetPos,aOffset,cap, height) + + rclpy.shutdown() + +if __name__ == '__main__': + main() + diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node_komisch.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node_komisch.py new file mode 100644 index 0000000000000000000000000000000000000000..7c5dc3e024792433ecfc8652b43a8c8b3281bc30 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node_komisch.py @@ -0,0 +1,305 @@ +#!/usr/bin/env python3 + +import rclpy +from rclpy.node import Node +import json +from sensor_msgs.msg import Imu +import threading +from std_msgs.msg import String +from geometry_msgs.msg import Quaternion, Vector3 +from box import Box +import numpy as np +from sphero_mini_controller.treiber import SpheroMini +import datetime +import cv2 +import time + + +# Img Objekt +cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + +# Set up the detector with default parameters. +detector = cv2.SimpleBlobDetector() + +# Setup SimpleBlobDetector parameters. +params = cv2.SimpleBlobDetector_Params() + +# Change thresholds +#params.minThreshold = 5 +#params.maxThreshold = 500 + +#FIlter by Color +params.filterByColor = True +params.blobColor = 255 + +# Filter by Area. +#params.filterByArea = True +#params.minArea = 100 +#params.maxArea = 1000 + +# Filter by Circularity +#params.filterByCircularity = True +#params.minCircularity = 0.5 +#params.maxCircularity = 1 + +# Filter by Convexity +#params.filterByConvexity = True +#params.minConvexity = 0.7 +#params.maxConvexity = 1 + +# Filter by Inertia +#params.filterByInertia = True +#params.minInertiaRatio = 0.01 + +# Create a detector with the parameters +detector = cv2.SimpleBlobDetector_create(params) + +class MyNode(Node): + #Konstrukter Node Objekt + def __init__(self): + super().__init__("sphero_mini") + #self.publisher_ = self.create_publisher(String,"Imu",5) + + def connect(self): + #Automatisch MAC-Adresse laden + #conf_file_path = file("/sphero_conf.json") + #with open("sphero_conf.json", "r") as f: + # cfg = Box(json.load(f)) + + MAC_ADDRESS = "C6:69:72:CD:BC:6D" + + # Connect: + sphero = SpheroMini(MAC_ADDRESS, verbosity = 4) + # battery voltage + sphero.getBatteryVoltage() + print(f"Bettery voltage: {sphero.v_batt}v") + + # firmware version number + sphero.returnMainApplicationVersion() + print(f"Firmware version: {'.'.join(str(x) for x in sphero.firmware_version)}") + return sphero + + def create_quaternion(self, roll, pitch, yaw): + q = Quaternion() + cy, sy = np.cos(yaw * 0.5), np.sin(yaw * 0.5) + cp, sp = np.cos(pitch * 0.5), np.sin(pitch * 0.5) + cr, sr = np.cos(roll * 0.5), np.sin(roll * 0.5) + + q.w = cr * cp * cy + sr * sp * sy + q.x = sr * cp * cy - cr * sp * sy + q.y = cr * sp * cy + sr * cp * sy + q.z = cr * cp * sy - sr * sp * cy + + return q + + def create_angular_veolocity_vector3(self, groll, gpitch, gyaw): + v = Vector3() + v.x = groll + v.y = gpitch + v.z = float(gyaw) + + return v + + def create_linear_acc_vector3(self, xacc, yacc, zacc): + v = Vector3() + v.x = xacc + v.y = yacc + v.z = zacc + + return v + + def get_sensors_data(self, sphero): + return { + "roll": sphero.IMU_roll, + "pitch": sphero.IMU_pitch, + "yaw": sphero.IMU_yaw, + "groll": sphero.IMU_gyro_x, + "gpitch": sphero.IMU_gyro_y, + "xacc": sphero.IMU_acc_x, + "yacc": sphero.IMU_acc_y, + "zacc": sphero.IMU_acc_z + } + + def publish_imu(self, sensors_values,node): + i = Imu() + + i.header.stamp = node.get_clock().now().to_msg() + + i.orientation = self.create_quaternion( + roll=sensors_values["roll"], + pitch=sensors_values["pitch"], + yaw=sensors_values["yaw"] + ) + i.angular_velocity = self.create_angular_veolocity_vector3( + groll=sensors_values["groll"], + gpitch=sensors_values["gpitch"], + gyaw=0 # We don't have the IMU_gyro_z + ) + i.linear_acceleration = self.create_linear_acc_vector3( + xacc=sensors_values["xacc"], + yacc=sensors_values["yacc"], + zacc=sensors_values["zacc"] + ) + + def positionauslesen(self): + zeitanfang = time.time() + + x = (-1) + y = (-1) + + while x < 0 and y < 0: + + success, img = cap.read() + gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY ) + # Detect blobs. + keypoints = detector.detect(gray) + + #print(keypoints) + for keyPoint in keypoints: + x = keyPoint.pt[0] + y = keyPoint.pt[1] + + #print("Blob X: %f Blob Y: %f" %(x,y)) + zeitende = time.time() + print("Dauer Programmausführung:",(zeitende-zeitanfang)) + return (x,y) + + def ref_winkel(self, sphero): + + startpunkt = self.positionauslesen() + + sphero.roll(100,0) + sphero.wait(2) + sphero.roll(0,0) + sphero.wait(0.5) + + ref = self.positionauslesen() + + sphero.wait(0.5) + sphero.roll(100,180) + sphero.wait(2) + sphero.roll(0,0) + + soll = (startpunkt[0]+100,startpunkt[1]) + + soll = np.array(soll) + ref = np.array(ref) + startpunkt = np.array(startpunkt) + + start_ref = ref-startpunkt + start_soll = soll-startpunkt + + y = start_ref[0] * start_soll[1] - start_ref[1] * start_soll[0] + x = start_ref[0] * start_soll[0] + start_ref[1] * start_soll[1] + + phi = np.arctan2(y,x) + phi = np.degrees(phi) + phi = int(phi) + + if(phi < 0 ): + phi = phi + 360 + + return phi + + def fahre_ziel(self, sphero, x,y, phiAbw): + + startPos = self.positionauslesen() + sollPos = (x,y) + + startPos = np.array(startPos) + sollPos = np.array(sollPos) + + pktSpheroKord = sollPos - startPos + + phi = np.arctan2(pktSpheroKord[1], pktSpheroKord[0]) + phi = np.degrees(phi) + + phi = int(phi) + + phiZiel = phiAbw - phi + + if(phiZiel < 0 ): + phiZiel = phiZiel + 360 + + print("Winkel Sphero System ", phi) + + #sollPos = (x,y) + #startPos = self.positionauslesen() + #refPos = (startPos[0]+100,startPos[1]) + + #sollPos = np.array(sollPos) + #refPos = np.array(refPos) + #startPos = np.array(startPos) + + #start_ref = refPos-startPos + #start_soll = sollPos-startPos + + #y = start_ref[0] * start_soll[1] - start_ref[1] * start_soll[0] + #x = start_ref[0] * start_soll[0] + start_ref[1] * start_soll[1] + + #phi = np.arctan2(y,x) + #phi = np.degrees(phi) + #phiZiel = phiAbw - (int(phi) * -1) + + print("Zielwinkel: ", phiZiel) + + while(not(np.allclose(startPos, sollPos, atol = 50))): + + sphero.roll(100, (phiZiel)) + + aktPos = self.positionauslesen() + aktPos = np.array(aktPos) + + time.sleep(0.1) + + print("Ziel erreicht") + + sphero.roll(0, (0)) + + return + + +def main(args = None): + #Verbindung herstellen, Node erstellen + try: + rclpy.init(args=args) #Kommunikation Starten + node = MyNode() #Node erstellen + sphero = node.connect() + thread = threading.Thread(target=rclpy.spin, args=(node, ), daemon=True) + thread.start() + rate = node.create_rate(5) + + except Exception as e: # rclpy.ROSInterruptException + print("Konnte nicht verbinden") + raise e + + sphero.setLEDColor(255,0,0) + sphero.wait(1) + sphero.setBackLEDIntensity(100) + sphero.stabilization(True) + + #Differenzwinkel auslesen + phiAbw = node.ref_winkel(sphero) + print("Abweichender Winkel Initialfahrt:", phiAbw) + + sphero.wait(5) + sphero.roll(100, phiAbw) + + sphero.wait(5) + node.fahre_ziel(sphero, 94,94, phiAbw) + + sphero.wait(5) + node.fahre_ziel(sphero, 134,462, phiAbw) + + sphero.wait(5) + node.fahre_ziel(sphero, 581,372, phiAbw) + + sphero.wait(5) + node.fahre_ziel(sphero, 331,308, phiAbw) + + rclpy.spin() + rclpy.shutdown() + +if __name__ == '__main__': + main() + diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node_refwinkel.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node_refwinkel.py new file mode 100644 index 0000000000000000000000000000000000000000..78066a4c2fa4da3a7a78b3faca6b5427ae979e42 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node_refwinkel.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 + +import rclpy +from rclpy.node import Node +import json +from sensor_msgs.msg import Imu +import threading +from std_msgs.msg import String +from geometry_msgs.msg import Quaternion, Vector3 +from box import Box +import numpy as np +from sphero_mini_controller.treiber import SpheroMini +import datetime + +class MyNode(Node): + #Konstrukter Node Objekt + def __init__(self): + super().__init__("sphero_mini") + #self.publisher_ = self.create_publisher(String,"Imu",5) + + def connect(self): + #Automatisch MAC-Adresse laden + #conf_file_path = file("/sphero_conf.json") + #with open("sphero_conf.json", "r") as f: + # cfg = Box(json.load(f)) + + MAC_ADDRESS = "C6:69:72:CD:BC:6D" + + # Connect: + sphero = SpheroMini(MAC_ADDRESS, verbosity = 4) + # battery voltage + sphero.getBatteryVoltage() + print(f"Bettery voltage: {sphero.v_batt}v") + + # firmware version number + sphero.returnMainApplicationVersion() + print(f"Firmware version: {'.'.join(str(x) for x in sphero.firmware_version)}") + return sphero + + def create_quaternion(self, roll, pitch, yaw): + q = Quaternion() + cy, sy = np.cos(yaw * 0.5), np.sin(yaw * 0.5) + cp, sp = np.cos(pitch * 0.5), np.sin(pitch * 0.5) + cr, sr = np.cos(roll * 0.5), np.sin(roll * 0.5) + + q.w = cr * cp * cy + sr * sp * sy + q.x = sr * cp * cy - cr * sp * sy + q.y = cr * sp * cy + sr * cp * sy + q.z = cr * cp * sy - sr * sp * cy + + return q + + def create_angular_veolocity_vector3(self, groll, gpitch, gyaw): + v = Vector3() + v.x = groll + v.y = gpitch + v.z = float(gyaw) + + return v + + def create_linear_acc_vector3(self, xacc, yacc, zacc): + v = Vector3() + v.x = xacc + v.y = yacc + v.z = zacc + + return v + + def get_sensors_data(self, sphero): + return { + "roll": sphero.IMU_roll, + "pitch": sphero.IMU_pitch, + "yaw": sphero.IMU_yaw, + "groll": sphero.IMU_gyro_x, + "gpitch": sphero.IMU_gyro_y, + "xacc": sphero.IMU_acc_x, + "yacc": sphero.IMU_acc_y, + "zacc": sphero.IMU_acc_z + } + + def publish_imu(self, sensors_values,node): + i = Imu() + + i.header.stamp = node.get_clock().now().to_msg() + + i.orientation = self.create_quaternion( + roll=sensors_values["roll"], + pitch=sensors_values["pitch"], + yaw=sensors_values["yaw"] + ) + i.angular_velocity = self.create_angular_veolocity_vector3( + groll=sensors_values["groll"], + gpitch=sensors_values["gpitch"], + gyaw=0 # We don't have the IMU_gyro_z + ) + i.linear_acceleration = self.create_linear_acc_vector3( + xacc=sensors_values["xacc"], + yacc=sensors_values["yacc"], + zacc=sensors_values["zacc"] + ) + + def positionauslesen(self): + # Standard imports + import cv2 + import numpy as np; + + # Img Objekt + cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + + # Set up the detector with default parameters. + detector = cv2.SimpleBlobDetector() + + # Setup SimpleBlobDetector parameters. + params = cv2.SimpleBlobDetector_Params() + + # Change thresholds + #params.minThreshold = 5 + #params.maxThreshold = 500 + + #FIlter by Color + params.filterByColor = True + params.blobColor = 255 + + # Filter by Area. + #params.filterByArea = True + #params.minArea = 100 + #params.maxArea = 1000 + + # Filter by Circularity + #params.filterByCircularity = True + #params.minCircularity = 0.5 + #params.maxCircularity = 1 + + # Filter by Convexity + #params.filterByConvexity = True + #params.minConvexity = 0.7 + #params.maxConvexity = 1 + + # Filter by Inertia + #params.filterByInertia = True + #params.minInertiaRatio = 0.01 + + # Create a detector with the parameters + detector = cv2.SimpleBlobDetector_create(params) + + success, img = cap.read() + gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY ) + + # Detect blobs. + keypoints = detector.detect(gray) + + #print(keypoints) + for keyPoint in keypoints: + x = keyPoint.pt[0] + y = keyPoint.pt[1] + #keypointss = keyPoint.sizekeyPoints + + #print("Blob X: %f Blob Y: %f" %(x,y)) + + # Draw detected blobs as red circles. + # cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures + # the size of the circle corresponds to the size of blob + + im_with_keypoints = cv2.drawKeypoints(gray, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) + + # Show blobs + cv2.imshow("Keypoints", im_with_keypoints) + + return (x,y) + + def ref_winkel(self, sphero, x,y): + + startpunkt = self.positionauslesen() + + sphero.roll(100,0) + sphero.wait(2) + sphero.roll(0,0) + sphero.wait(0.5) + + ref = self.positionauslesen() + + #print(ref) + + sphero.wait(0.5) + sphero.roll(100,180) + sphero.wait(2) + sphero.roll(0,0) + + soll = (x,y) + + soll = np.array(soll) + ref = np.array(ref) + startpunkt = np.array(startpunkt) + + start_ref = ref-startpunkt + start_soll = soll-startpunkt + phi = np.arccos(np.dot(start_ref,start_soll) / (np.linalg.norm(start_ref)*np.linalg.norm(start_soll))) + return phi + +def main(args = None): + #Verbindung herstellen, Node erstellen + try: + rclpy.init(args=args) #Kommunikation Starten + node = MyNode() #Node erstellen + sphero = node.connect() + thread = threading.Thread(target=rclpy.spin, args=(node, ), daemon=True) + thread.start() + rate = node.create_rate(5) + + except Exception as e: # rclpy.ROSInterruptException + print("Konnte nicht verbinden") + raise e + + #Simple Moves, Erste Bewegung erkennen + #try:oll = (0,0) + # while rclpy.ok(): + + #sphero.positionauslesen() + #soll = 0,0 + phi = node.ref_winkel(sphero, 0,0) + + phi = np.degrees(phi) + + phi = int(phi) + + print("Abweihender Winkel:", phi) + + sphero.wait(0.5) + sphero.roll(100, (0 + phi)) + sphero.wait(2) + sphero.roll(0,0) + +# except KeyboardInterrupt: + # pass + + rclpy.shutdown() + # thread.join() + +if __name__ == '__main__': + main() + + #!/usr/bin/env python3 + diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node_testfahrt.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node_testfahrt.py new file mode 100644 index 0000000000000000000000000000000000000000..29f2473b20a9c782704e15851518da8ec90ee823 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node_testfahrt.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 + +import rclpy +from rclpy.node import Node +import json +from sensor_msgs.msg import Imu +import threading +from std_msgs.msg import String +from geometry_msgs.msg import Quaternion, Vector3 +from box import Box +import numpy as np +from sphero_mini_controller.treiber import SpheroMini +import datetime + + +class MyNode(Node): + + def __init__(self): + super().__init__("sphero_mini") + self.publisher_ = self.create_publisher(String,"Imu",5) + #self.get_logger().info("Hello from ROS2") + + def connect(self): + #conf_file_path = file("/sphero_conf.json") + #with open("sphero_conf.json", "r") as f: + # cfg = Box(json.load(f)) + + MAC_ADDRESS = "C6:69:72:CD:BC:6D" + + # Connect: + sphero = SpheroMini(MAC_ADDRESS, verbosity = 4) + # battery voltage + sphero.getBatteryVoltage() + print(f"Bettery voltage: {sphero.v_batt}v") + + # firmware version number + sphero.returnMainApplicationVersion() + print(f"Firmware version: {'.'.join(str(x) for x in sphero.firmware_version)}") + return sphero + + + def create_quaternion(self, roll, pitch, yaw): + q = Quaternion() + cy, sy = np.cos(yaw * 0.5), np.sin(yaw * 0.5) + cp, sp = np.cos(pitch * 0.5), np.sin(pitch * 0.5) + cr, sr = np.cos(roll * 0.5), np.sin(roll * 0.5) + + q.w = cr * cp * cy + sr * sp * sy + q.x = sr * cp * cy - cr * sp * sy + q.y = cr * sp * cy + sr * cp * sy + q.z = cr * cp * sy - sr * sp * cy + + return q + + + def create_angular_veolocity_vector3(self, groll, gpitch, gyaw): + v = Vector3() + v.x = groll + v.y = gpitch + v.z = float(gyaw) + + return v + + + def create_linear_acc_vector3(self, xacc, yacc, zacc): + v = Vector3() + v.x = xacc + v.y = yacc + v.z = zacc + + return v + + + def get_sensors_data(self, sphero): + return { + "roll": sphero.IMU_roll, + "pitch": sphero.IMU_pitch, + "yaw": sphero.IMU_yaw, + "groll": sphero.IMU_gyro_x, + "gpitch": sphero.IMU_gyro_y, + "xacc": sphero.IMU_acc_x, + "yacc": sphero.IMU_acc_y, + "zacc": sphero.IMU_acc_z + } + + + def publish_imu(self, sensors_values,node): + i = Imu() + + i.header.stamp = node.get_clock().now().to_msg() + + i.orientation = self.create_quaternion( + roll=sensors_values["roll"], + pitch=sensors_values["pitch"], + yaw=sensors_values["yaw"] + ) + i.angular_velocity = self.create_angular_veolocity_vector3( + groll=sensors_values["groll"], + gpitch=sensors_values["gpitch"], + gyaw=0 # We don't have the IMU_gyro_z + ) + i.linear_acceleration = self.create_linear_acc_vector3( + xacc=sensors_values["xacc"], + yacc=sensors_values["yacc"], + zacc=sensors_values["zacc"] + ) + + #print(i) + + #self.publisher_.publish(i) + + + +def main(args = None): + + try: + rclpy.init(args=args) #Kommunikation Starten + node = MyNode() #Node erstellen + sphero = node.connect() + except Exception as e: # rclpy.ROSInterruptException + print("Konnte nicht verbinden") + raise e + + thread = threading.Thread(target=rclpy.spin, args=(node, ), daemon=True) + thread.start() + rate = node.create_rate(2) + + try: + while rclpy.ok(): + + sphero.configureSensorMask( + IMU_yaw=True, + IMU_pitch=True, + IMU_roll=True, + IMU_acc_y=True, + IMU_acc_z=True, + IMU_acc_x=True, + IMU_gyro_x=True, + IMU_gyro_y=True, + #IMU_gyro_z=True + ) + sphero.configureSensorStream() + + sensors_values = node.get_sensors_data(sphero) + #rclpy.logdebug(sensors_values) + node.publish_imu(sensors_values, node) + sphero.setLEDColor(red = 0, green = 255, blue = 0) # Turn LEDs green + + # Aiming: + sphero.setLEDColor(red = 0, green = 0, blue = 0) # Turn main LED off + sphero.stabilization(False) # Turn off stabilization + sphero.setBackLEDIntensity(255) # turn back LED on + sphero.wait(3) # Non-blocking pau`se + sphero.resetHeading() # Reset heading + sphero.stabilization(True) # Turn on stabilization + sphero.setBackLEDIntensity(0) # Turn back LED off + + # Move around: + sphero.setLEDColor(red = 0, green = 0, blue = 255) # Turn main LED blue + sphero.roll(100, 0) # roll forwards (heading = 0) at speed = 50 + + sphero.wait(3) # Keep rolling for three seconds + + sphero.roll(0, 0) # stop + sphero.wait(1) # Allow time to stop + + sphero.setLEDColor(red = 0, green = 255, blue = 0) # Turn main LED green + sphero.roll(-100, 0) # Keep facing forwards but roll backwards at speed = 50 + sphero.wait(3) # Keep rolling for three seconds + + sphero.roll(0, 0) # stop + sphero.wait(1) # Allow time to stop + + rate.sleep() + + except KeyboardInterrupt: + pass + + rclpy.shutdown() + thread.join() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/pfadplanung.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/pfadplanung.py new file mode 100644 index 0000000000000000000000000000000000000000..d07a2a00e56b7626e47d9ae9ba004c28b8fa3a94 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/pfadplanung.py @@ -0,0 +1,223 @@ + +def planInitialPath(start, goal): + + from matplotlib import pyplot as ppl + from matplotlib import cm + import random, sys, math, os.path + from matplotlib.pyplot import imread + + #Implementation of RRT + + MAP_IMG = './map.jpg' #Black and white image for a map + MIN_NUM_VERT = 20 # Minimum number of vertex in the graph + MAX_NUM_VERT = 1500 # Maximum number of vertex in the graph + STEP_DISTANCE = 10 # Maximum distance between two vertex + SEED = None # For random numbers + + def rapidlyExploringRandomTree(ax, img, start, goal, seed=None): + hundreds = 100 + seed = random.seed(seed) + #print("Zufallsseed: ", seed) + points = [] + graph = [] + points.append(start) + graph.append((start, [])) + print('Generating and conecting random points') + occupied = True + phaseTwo = False + + # Phase two values (points 5 step distances around the goal point) + minX = max(goal[0] - 5 * STEP_DISTANCE, 0) + maxX = min(goal[0] + 5 * STEP_DISTANCE, len(img[0]) - 1) + minY = max(goal[1] - 5 * STEP_DISTANCE, 0) + maxY = min(goal[1] + 5 * STEP_DISTANCE, len(img) - 1) + + i = 0 + while (goal not in points) and (len(points) < MAX_NUM_VERT): + if (i % 100) == 0: + print(i, 'points randomly generated') + + if (len(points) % hundreds) == 0: + print(len(points), 'vertex generated') + hundreds = hundreds + 100 + + while (occupied): + if phaseTwo and (random.random() > 0.8): + point = [random.randint(minX, maxX), random.randint(minY, maxY)] + else: + point = [random.randint(0, len(img[0]) - 1), random.randint(0, len(img) - 1)] + + if (img[point[1]][point[0]][0] * 255 == 255): + occupied = False + + occupied = True + + nearest = findNearestPoint(points, point) + newPoints = connectPoints(point, nearest, img) + addToGraph(ax, graph, newPoints, point) + newPoints.pop(0) # The first element is already in the points list + points.extend(newPoints) + ppl.draw() + i = i + 1 + + if len(points) >= MIN_NUM_VERT: + if not phaseTwo: + print('Phase Two') + phaseTwo = True + + if phaseTwo: + nearest = findNearestPoint(points, goal) + newPoints = connectPoints(goal, nearest, img) + addToGraph(ax, graph, newPoints, goal) + newPoints.pop(0) + points.extend(newPoints) + ppl.draw() + + if goal in points: + print('Goal found, total vertex in graph:', len(points), 'total random points generated:', i) + + path = searchPath(graph, start, [start]) + + for i in range(len(path) - 1): + ax.plot([path[i][0], path[i + 1][0]], [path[i][1], path[i + 1][1]], color='g', linestyle='-', + linewidth=2) + ppl.draw() + + print('Showing resulting map') + print('Final path:', path) + print('The final path is made from:', len(path), 'connected points') + else: + path = None + print('Reached maximum number of vertex and goal was not found') + print('Total vertex in graph:', len(points), 'total random points generated:', i) + print('Showing resulting map') + + ppl.show() + return path + + def searchPath(graph, point, path): + for i in graph: + if point == i[0]: + p = i + + if p[0] == graph[-1][0]: + return path + + for link in p[1]: + path.append(link) + finalPath = searchPath(graph, link, path) + + if finalPath != None: + return finalPath + else: + path.pop() + + def addToGraph(ax, graph, newPoints, point): + if len(newPoints) > 1: # If there is anything to add to the graph + for p in range(len(newPoints) - 1): + nearest = [nearest for nearest in graph if (nearest[0] == [newPoints[p][0], newPoints[p][1]])] + nearest[0][1].append(newPoints[p + 1]) + graph.append((newPoints[p + 1], [])) + + if not p == 0: + ax.plot(newPoints[p][0], newPoints[p][1], '+k') # First point is already painted + ax.plot([newPoints[p][0], newPoints[p + 1][0]], [newPoints[p][1], newPoints[p + 1][1]], color='k', + linestyle='-', linewidth=1) + + if point in newPoints: + ax.plot(point[0], point[1], '.g') # Last point is green + else: + ax.plot(newPoints[p + 1][0], newPoints[p + 1][1], '+k') # Last point is not green + + def connectPoints(a, b, img): + newPoints = [] + newPoints.append([b[0], b[1]]) + step = [(a[0] - b[0]) / float(STEP_DISTANCE), (a[1] - b[1]) / float(STEP_DISTANCE)] + + # Set small steps to check for walls + pointsNeeded = int(math.floor(max(math.fabs(step[0]), math.fabs(step[1])))) + + if math.fabs(step[0]) > math.fabs(step[1]): + if step[0] >= 0: + step = [1, step[1] / math.fabs(step[0])] + else: + step = [-1, step[1] / math.fabs(step[0])] + + else: + if step[1] >= 0: + step = [step[0] / math.fabs(step[1]), 1] + else: + step = [step[0] / math.fabs(step[1]), -1] + + blocked = False + for i in range(pointsNeeded + 1): # Creates points between graph and solitary point + for j in range(STEP_DISTANCE): # Check if there are walls between points + coordX = round(newPoints[i][0] + step[0] * j) + coordY = round(newPoints[i][1] + step[1] * j) + + if coordX == a[0] and coordY == a[1]: + break + if coordY >= len(img) or coordX >= len(img[0]): + break + if img[int(coordY)][int(coordX)][0] * 255 < 255: + blocked = True + if blocked: + break + + if blocked: + break + if not (coordX == a[0] and coordY == a[1]): + newPoints.append( + [newPoints[i][0] + (step[0] * STEP_DISTANCE), newPoints[i][1] + (step[1] * STEP_DISTANCE)]) + + if not blocked: + newPoints.append([a[0], a[1]]) + return newPoints + + def findNearestPoint(points, point): + best = (sys.maxsize, sys.maxsize, sys.maxsize) + for p in points: + if p == point: + continue + dist = math.sqrt((p[0] - point[0]) ** 2 + (p[1] - point[1]) ** 2) + if dist < best[2]: + best = (p[0], p[1], dist) + return (best[0], best[1]) + + # Standard imports + import cv2 + import numpy as np; + + # Img Objekt + cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + val, frame = cap.read() + inverted_image = np.invert(frame) + cv2.imwrite("map.jpg", inverted_image) + + print('Loading map... with file \'', MAP_IMG, '\'') + img = imread(MAP_IMG) + fig = ppl.gcf() + fig.clf() + ax = fig.add_subplot(1, 1, 1) + ax.imshow(img, cmap=cm.Greys_r) + ax.axis('image') + ppl.draw() + print('Map is', len(img[0]), 'x', len(img)) + + path = rapidlyExploringRandomTree(ax, img, start, goal, seed=SEED) + + if len(sys.argv) > 2: + print('Only one argument is needed') + elif len(sys.argv) > 1: + if os.path.isfile(sys.argv[1]): + MAP_IMG = sys.argv[1] + else: + print(sys.argv[1], 'is not a file') + + pathIndCntr = 0 + + return path, pathIndCntr + +start = (200,200) +ziel = (450, 450) +planInitialPath(start, ziel) \ No newline at end of file diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/potential_field_planning (1).py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/potential_field_planning (1).py new file mode 100644 index 0000000000000000000000000000000000000000..8f136b5ee3fbee7f9cd0d40fd368a4e10822030b --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/potential_field_planning (1).py @@ -0,0 +1,199 @@ +""" + +Potential Field based path planner + +author: Atsushi Sakai (@Atsushi_twi) + +Ref: +https://www.cs.cmu.edu/~motionplanning/lecture/Chap4-Potential-Field_howie.pdf + +""" + +from collections import deque +import numpy as np +import matplotlib.pyplot as plt + +# Parameters +KP = 5.0 # attractive potential gain +ETA = 100.0 # repulsive potential gain +AREA_WIDTH = 30.0 # potential area width [m] +# the number of previous positions used to check oscillations +OSCILLATIONS_DETECTION_LENGTH = 3 + +show_animation = True + + +def calc_potential_field(gx, gy, ox, oy, reso, rr, sx, sy): + minx = min(min(ox), sx, gx) - AREA_WIDTH / 2.0 + miny = min(min(oy), sy, gy) - AREA_WIDTH / 2.0 + maxx = max(max(ox), sx, gx) + AREA_WIDTH / 2.0 + maxy = max(max(oy), sy, gy) + AREA_WIDTH / 2.0 + xw = int(round((maxx - minx) / reso)) + yw = int(round((maxy - miny) / reso)) + + # calc each potential + pmap = [[0.0 for i in range(yw)] for i in range(xw)] + + for ix in range(xw): + x = ix * reso + minx + + for iy in range(yw): + y = iy * reso + miny + ug = calc_attractive_potential(x, y, gx, gy) + uo = calc_repulsive_potential(x, y, ox, oy, rr) + uf = ug + uo + pmap[ix][iy] = uf + + return pmap, minx, miny + + +def calc_attractive_potential(x, y, gx, gy): + return 0.5 * KP * np.hypot(x - gx, y - gy) + + +def calc_repulsive_potential(x, y, ox, oy, rr): + # search nearest obstacle + minid = -1 + dmin = float("inf") + for i, _ in enumerate(ox): + d = np.hypot(x - ox[i], y - oy[i]) + if dmin >= d: + dmin = d + minid = i + + # calc repulsive potential + dq = np.hypot(x - ox[minid], y - oy[minid]) + + if dq <= rr: + if dq <= 0.1: + dq = 0.1 + + return 0.5 * ETA * (1.0 / dq - 1.0 / rr) ** 2 + else: + return 0.0 + + +def get_motion_model(): + # dx, dy + motion = [[1, 0], + [0, 1], + [-1, 0], + [0, -1], + [-1, -1], + [-1, 1], + [1, -1], + [1, 1]] + + return motion + + +def oscillations_detection(previous_ids, ix, iy): + previous_ids.append((ix, iy)) + + if (len(previous_ids) > OSCILLATIONS_DETECTION_LENGTH): + previous_ids.popleft() + + # check if contains any duplicates by copying into a set + previous_ids_set = set() + for index in previous_ids: + if index in previous_ids_set: + return True + else: + previous_ids_set.add(index) + return False + + +def potential_field_planning(sx, sy, gx, gy, ox, oy, reso, rr): + + # calc potential field + pmap, minx, miny = calc_potential_field(gx, gy, ox, oy, reso, rr, sx, sy) + + # search path + d = np.hypot(sx - gx, sy - gy) + ix = round((sx - minx) / reso) + iy = round((sy - miny) / reso) + gix = round((gx - minx) / reso) + giy = round((gy - miny) / reso) + + if show_animation: + draw_heatmap(pmap) + # for stopping simulation with the esc key. + plt.gcf().canvas.mpl_connect('key_release_event', + lambda event: [exit(0) if event.key == 'escape' else None]) + plt.plot(ix, iy, "*k") + plt.plot(gix, giy, "*m") + + rx, ry = [sx], [sy] + motion = get_motion_model() + previous_ids = deque() + + while d >= reso: + minp = float("inf") + minix, miniy = -1, -1 + for i, _ in enumerate(motion): + inx = int(ix + motion[i][0]) + iny = int(iy + motion[i][1]) + if inx >= len(pmap) or iny >= len(pmap[0]) or inx < 0 or iny < 0: + p = float("inf") # outside area + print("outside potential!") + else: + p = pmap[inx][iny] + if minp > p: + minp = p + minix = inx + miniy = iny + ix = minix + iy = miniy + xp = ix * reso + minx + yp = iy * reso + miny + d = np.hypot(gx - xp, gy - yp) + rx.append(xp) + ry.append(yp) + + if (oscillations_detection(previous_ids, ix, iy)): + print("Oscillation detected at ({},{})!".format(ix, iy)) + break + + if show_animation: + plt.plot(ix, iy, ".r") + plt.pause(0.01) + + print("Goal!!") + + return rx, ry + + +def draw_heatmap(data): + data = np.array(data).T + plt.pcolor(data, vmax=100.0, cmap=plt.cm.Blues) + + +def main(): + print("potential_field_planning start") + + sx = 0.0 # start x position [m] + sy = 10.0 # start y positon [m] + gx = 30.0 # goal x position [m] + gy = 30.0 # goal y position [m] + grid_size = 0.5 # potential grid size [m] + robot_radius = 5.0 # robot radius [m] + + ox = [15.0, 5.0, 20.0, 25.0] # obstacle x position list [m] + oy = [25.0, 15.0, 26.0, 25.0] # obstacle y position list [m] + + if show_animation: + plt.grid(True) + plt.axis("equal") + + # path generation + _, _ = potential_field_planning( + sx, sy, gx, gy, ox, oy, grid_size, robot_radius) + + if show_animation: + plt.show() + + +if __name__ == '__main__': + print(__file__ + " start!!") + main() + print(__file__ + " Done!!") diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/potential_field_planning.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/potential_field_planning.py new file mode 100644 index 0000000000000000000000000000000000000000..c28279d83d3ea64eaee8592afabe70a8fe5372d1 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/potential_field_planning.py @@ -0,0 +1,248 @@ +""" + +Potential Field based path planner + +author: Atsushi Sakai (@Atsushi_twi) + +Ref: +https://www.cs.cmu.edu/~motionplanning/lecture/Chap4-Potential-Field_howie.pdf + +""" + +from collections import deque +import numpy as np +import matplotlib.pyplot as plt + +# Parameters +KP = 10.0 # attractive potential gain +ETA = 300.0 # repulsive potential gain +AREA_WIDTH = 704.0 # potential area width [m] +# the number of previous positions used to check oscillations +OSCILLATIONS_DETECTION_LENGTH = 3 + +show_animation = True + +px = [] +py = [] + +def calc_potential_field(gx, gy, ox, oy, reso, rr, sx, sy): + minx = min(min(ox), sx, gx) - AREA_WIDTH / 2.0 + miny = min(min(oy), sy, gy) - AREA_WIDTH / 2.0 + maxx = max(max(ox), sx, gx) + AREA_WIDTH / 2.0 + maxy = max(max(oy), sy, gy) + AREA_WIDTH / 2.0 + xw = int(round((maxx - minx) / reso)) + yw = int(round((maxy - miny) / reso)) + + # calc each potential + pmap = [[0.0 for i in range(yw)] for i in range(xw)] + + for ix in range(xw): + x = ix * reso + minx + + for iy in range(yw): + y = iy * reso + miny + ug = calc_attractive_potential(x, y, gx, gy) + uo = calc_repulsive_potential(x, y, ox, oy, rr) + uf = ug + uo + pmap[ix][iy] = uf + + return pmap, minx, miny + + +def calc_attractive_potential(x, y, gx, gy): + return 0.5 * KP * np.hypot(x - gx, y - gy) + + +def calc_repulsive_potential(x, y, ox, oy, rr): + # search nearest obstacle + minid = -1 + dmin = float("inf") + for i, _ in enumerate(ox): + d = np.hypot(x - ox[i], y - oy[i]) + if dmin >= d: + dmin = d + minid = i + + # calc repulsive potential + dq = np.hypot(x - ox[minid], y - oy[minid]) + + if dq <= rr: + if dq <= 0.1: + dq = 0.1 + + return 0.5 * ETA * (1.0 / dq - 1.0 / rr) ** 2 + else: + return 0.0 + + +def get_motion_model(): + # dx, dy + motion = [[1, 0], + [0, 1], + [-1, 0], + [0, -1], + [-1, -1], + [-1, 1], + [1, -1], + [1, 1]] + + return motion + + +def oscillations_detection(previous_ids, ix, iy): + previous_ids.append((ix, iy)) + + if (len(previous_ids) > OSCILLATIONS_DETECTION_LENGTH): + previous_ids.popleft() + + # check if contains any duplicates by copying into a set + previous_ids_set = set() + for index in previous_ids: + if index in previous_ids_set: + return True + else: + previous_ids_set.add(index) + return False + + +def potential_field_planning(sx, sy, gx, gy, ox, oy, reso, rr): + + # calc potential field + pmap, minx, miny = calc_potential_field(gx, gy, ox, oy, reso, rr, sx, sy) + + # search path + d = np.hypot(sx - gx, sy - gy) + ix = round((sx - minx) / reso) + iy = round((sy - miny) / reso) + gix = round((gx - minx) / reso) + giy = round((gy - miny) / reso) + + if show_animation: + draw_heatmap(pmap) + # for stopping simulation with the esc key. + plt.gcf().canvas.mpl_connect('key_release_event', + lambda event: [exit(0) if event.key == 'escape' else None]) + plt.plot(ix, iy, "*k") + plt.plot(gix, giy, "*m") + + rx, ry = [sx], [sy] + motion = get_motion_model() + previous_ids = deque() + + while d >= reso: + minp = float("inf") + minix, miniy = -1, -1 + for i, _ in enumerate(motion): + inx = int(ix + motion[i][0]) + iny = int(iy + motion[i][1]) + if inx >= len(pmap) or iny >= len(pmap[0]) or inx < 0 or iny < 0: + p = float("inf") # outside area + print("outside potential!") + else: + p = pmap[inx][iny] + if minp > p: + minp = p + minix = inx + miniy = iny + ix = minix + iy = miniy + xp = ix * reso + minx + yp = iy * reso + miny + d = np.hypot(gx - xp, gy - yp) + rx.append(xp) + ry.append(yp) + + if (oscillations_detection(previous_ids, ix, iy)): + print("Oscillation detected at ({},{})!".format(ix, iy)) + break + + if show_animation: + px.append(ix) + py.append(iy) + plt.plot(ix, iy, ".r") + plt.pause(0.01) + + print("Goal!!") + + return rx, ry + + +def draw_heatmap(data): + data = np.array(data).T + plt.pcolor(data, vmax=100.0, cmap=plt.cm.Blues) + + +def create_map(): + # Map erstellen + # Standard imports + import cv2 + import numpy as np; + + # Img Objekt + #cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + + #success, img = cap.read() + + gray = cv2.imread("/home/ubuntu/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/map/map.jpg",cv2.IMREAD_GRAYSCALE) + + #gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) + mask = cv2.inRange(gray, np.array([90]), np.array([255])) + + mapOfWorld = [[0]*gray.shape[1]]*gray.shape[0] + + mask_list= np.ndarray.tolist(mask) + + for i in range(0,mask.shape[0]): + mapOfWorld[i] = ['W' if j > 200 else ' ' for j in mask_list[i]] + + #mapOfWorld[200][200] = 'R' #Start Pos + #mapOfWorld[300][300] = 'G' #Ziel Pos + + return mapOfWorld + + +def create_obsticles(mapOfWorld): + ox = [] + oy = [] + + mapOfWorld = np.array(mapOfWorld) + + for i in range(0,mapOfWorld.shape[0]): + for j in range(0,mapOfWorld.shape[1]): + if mapOfWorld[i][j] == 'W': + ox.append(i) + oy.append(j) + + return ox,oy + +def main(): + print("potential_field_planning start") + + mapOfWorld = create_map() + + sx = 50 # start x position [m] + sy = 400 # start y positon [m] + gx = 600 # goal x position [m] + gy = 100 # goal y position [m] + grid_size = 10 # potential grid size [m] + robot_radius = 10 # robot radius [m] + + ox, oy = create_obsticles(mapOfWorld) + + if show_animation: + plt.grid(True) + plt.axis("equal") + + # path generation + _, _ = potential_field_planning(sx, sy, gx, gy, ox, oy, grid_size, robot_radius) + + if show_animation: + plt.show() + + print("X_Pos", px) + print("Y_Pos", py) + +if __name__ == '__main__': + print(__file__ + " start!!") + main() + print(__file__ + " Done!!") diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/sphero.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/sphero.py new file mode 100644 index 0000000000000000000000000000000000000000..b5138d1b23a13a1117038f444cc8a0de0dbc0078 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/sphero.py @@ -0,0 +1,22 @@ +import numpy as np +def ref_winkel(sphero, soll): + + startpunkt = sphero.positionsauslesen + + sphero.roll(100,0) + sphero.wait(2) + sphero.roll(0,0) + sphero.wait(0.5) + + ref = sphero.positionsauslesen + + sphero.wait(0.5) + sphero.roll(100,0) + sphero.wait(2) + sphero.roll(0,0) + + start_ref = ref-startpunkt + start_soll = soll-startpunkt + phi = np.arccos(start_ref*start_soll / (np.linalg.norm(start_ref)*np.linalg.norm(start_soll))) + return phi + diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/test.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/test.py new file mode 100644 index 0000000000000000000000000000000000000000..b5c4cbbe779a35653aad3fe176e1a0b9802319fd --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/test.py @@ -0,0 +1,23 @@ +#Map erstellen +# Standard imports +import cv2 +import numpy as np; + +# Img Objekt +cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + +success, img = cap.read() + +gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) +mask = cv2.inRange(gray, np.array([90]), np.array([255])) + +mapOfWorld = [[0]*gray.shape[1]]*gray.shape[0] + +mask_list= np.ndarray.tolist(mask) + +for i in range(0,mask.shape[0]): + mapOfWorld[i] = ['W' if j > 200 else ' ' for j in mask_list[i]] + +print(mapOfWorld) + + diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/treiber.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/treiber.py new file mode 100644 index 0000000000000000000000000000000000000000..ec3435051de5aa72fc393d224b0fe1a53927b8b0 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/treiber.py @@ -0,0 +1,638 @@ +from bluepy.btle import Peripheral +from bluepy import btle +from sphero_mini_controller._constants import * +import struct +import time +import sys + +class SpheroMini(): + def __init__(self, MACAddr, verbosity = 4, user_delegate = None): + ''' + initialize class instance and then build collect BLE sevices and characteristics. + Also sends text string to Anti-DOS characteristic to prevent returning to sleep, + and initializes notifications (which is what the sphero uses to send data back to + the client). + ''' + self.verbosity = verbosity # 0 = Silent, + # 1 = Connection/disconnection only + # 2 = Init messages + # 3 = Recieved commands + # 4 = Acknowledgements + self.sequence = 1 + self.v_batt = None # will be updated with battery voltage when sphero.getBatteryVoltage() is called + self.firmware_version = [] # will be updated with firware version when sphero.returnMainApplicationVersion() is called + + if self.verbosity > 0: + print("[INFO] Connecting to ", MACAddr) + self.p = Peripheral(MACAddr, "random") #connect + + if self.verbosity > 1: + print("[INIT] Initializing") + + # Subscribe to notifications + self.sphero_delegate = MyDelegate(self, user_delegate) # Pass a reference to this instance when initializing + self.p.setDelegate(self.sphero_delegate) + + if self.verbosity > 1: + print("[INIT] Read all characteristics and descriptors") + # Get characteristics and descriptors + self.API_V2_characteristic = self.p.getCharacteristics(uuid="00010002-574f-4f20-5370-6865726f2121")[0] + self.AntiDOS_characteristic = self.p.getCharacteristics(uuid="00020005-574f-4f20-5370-6865726f2121")[0] + self.DFU_characteristic = self.p.getCharacteristics(uuid="00020002-574f-4f20-5370-6865726f2121")[0] + self.DFU2_characteristic = self.p.getCharacteristics(uuid="00020004-574f-4f20-5370-6865726f2121")[0] + self.API_descriptor = self.API_V2_characteristic.getDescriptors(forUUID=0x2902)[0] + self.DFU_descriptor = self.DFU_characteristic.getDescriptors(forUUID=0x2902)[0] + + # The rest of this sequence was observed during bluetooth sniffing: + # Unlock code: prevent the sphero mini from going to sleep again after 10 seconds + if self.verbosity > 1: + print("[INIT] Writing AntiDOS characteristic unlock code") + self.AntiDOS_characteristic.write("usetheforce...band".encode(), withResponse=True) + + # Enable DFU notifications: + if self.verbosity > 1: + print("[INIT] Configuring DFU descriptor") + self.DFU_descriptor.write(struct.pack('<bb', 0x01, 0x00), withResponse = True) + + # No idea what this is for. Possibly a device ID of sorts? Read request returns '00 00 09 00 0c 00 02 02': + if self.verbosity > 1: + print("[INIT] Reading DFU2 characteristic") + _ = self.DFU2_characteristic.read() + + # Enable API notifications: + if self.verbosity > 1: + print("[INIT] Configuring API dectriptor") + self.API_descriptor.write(struct.pack('<bb', 0x01, 0x00), withResponse = True) + + self.wake() + + # Finished initializing: + if self.verbosity > 1: + print("[INIT] Initialization complete\n") + + def disconnect(self): + if self.verbosity > 0: + print("[INFO] Disconnecting") + + self.p.disconnect() + + def wake(self): + ''' + Bring device out of sleep mode (can only be done if device was in sleep, not deep sleep). + If in deep sleep, the device should be connected to USB power to wake. + ''' + if self.verbosity > 2: + print("[SEND {}] Waking".format(self.sequence)) + + self._send(characteristic=self.API_V2_characteristic, + devID=deviceID['powerInfo'], + commID=powerCommandIDs["wake"], + payload=[]) # empty payload + + self.getAcknowledgement("Wake") + + def sleep(self, deepSleep=False): + ''' + Put device to sleep or deep sleep (deep sleep needs USB power connected to wake up) + ''' + if deepSleep: + sleepCommID=powerCommandIDs["deepSleep"] + if self.verbosity > 0: + print("[INFO] Going into deep sleep. Connect USB power to wake.") + else: + sleepCommID=powerCommandIDs["sleep"] + self._send(characteristic=self.API_V2_characteristic, + devID=deviceID['powerInfo'], + commID=sleepCommID, + payload=[]) #empty payload + + def setLEDColor(self, red = None, green = None, blue = None): + ''' + Set device LED color based on RGB vales (each can range between 0 and 0xFF) + ''' + if self.verbosity > 2: + print("[SEND {}] Setting main LED colour to [{}, {}, {}]".format(self.sequence, red, green, blue)) + + self._send(characteristic = self.API_V2_characteristic, + devID = deviceID['userIO'], # 0x1a + commID = userIOCommandIDs["allLEDs"], # 0x0e + payload = [0x00, 0x0e, red, green, blue]) + + self.getAcknowledgement("LED/backlight") + + def setBackLEDIntensity(self, brightness=None): + ''' + Set device LED backlight intensity based on 0-255 values + + NOTE: this is not the same as aiming - it only turns on the LED + ''' + if self.verbosity > 2: + print("[SEND {}] Setting backlight intensity to {}".format(self.sequence, brightness)) + + self._send(characteristic = self.API_V2_characteristic, + devID = deviceID['userIO'], + commID = userIOCommandIDs["allLEDs"], + payload = [0x00, 0x01, brightness]) + + self.getAcknowledgement("LED/backlight") + + def roll(self, speed=None, heading=None): + ''' + Start to move the Sphero at a given direction and speed. + heading: integer from 0 - 360 (degrees) + speed: Integer from 0 - 255 + + Note: the zero heading should be set at startup with the resetHeading method. Otherwise, it may + seem that the sphero doesn't honor the heading argument + ''' + if self.verbosity > 2: + print("[SEND {}] Rolling with speed {} and heading {}".format(self.sequence, speed, heading)) + + if abs(speed) > 255: + print("WARNING: roll speed parameter outside of allowed range (-255 to +255)") + + if speed < 0: + speed = -1*speed+256 # speed values > 256 in the send packet make the spero go in reverse + + speedH = (speed & 0xFF00) >> 8 + speedL = speed & 0xFF + headingH = (heading & 0xFF00) >> 8 + headingL = heading & 0xFF + self._send(characteristic = self.API_V2_characteristic, + devID = deviceID['driving'], + commID = drivingCommands["driveWithHeading"], + payload = [speedL, headingH, headingL, speedH]) + + self.getAcknowledgement("Roll") + + def resetHeading(self): + ''' + Reset the heading zero angle to the current heading (useful during aiming) + Note: in order to manually rotate the sphero, you need to call stabilization(False). + Once the heading has been set, call stabilization(True). + ''' + if self.verbosity > 2: + print("[SEND {}] Resetting heading".format(self.sequence)) + + self._send(characteristic = self.API_V2_characteristic, + devID = deviceID['driving'], + commID = drivingCommands["resetHeading"], + payload = []) #empty payload + + self.getAcknowledgement("Heading") + + def returnMainApplicationVersion(self): + ''' + Sends command to return application data in a notification + ''' + if self.verbosity > 2: + print("[SEND {}] Requesting firmware version".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID = deviceID['systemInfo'], + commID = SystemInfoCommands['mainApplicationVersion'], + payload = []) # empty + + self.getAcknowledgement("Firmware") + + def getBatteryVoltage(self): + ''' + Sends command to return battery voltage data in a notification. + Data printed to console screen by the handleNotifications() method in the MyDelegate class. + ''' + if self.verbosity > 2: + print("[SEND {}] Requesting battery voltage".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID=deviceID['powerInfo'], + commID=powerCommandIDs['batteryVoltage'], + payload=[]) # empty + + self.getAcknowledgement("Battery") + + def stabilization(self, stab = True): + ''' + Sends command to turn on/off the motor stabilization system (required when manually turning/aiming the sphero) + ''' + if stab == True: + if self.verbosity > 2: + print("[SEND {}] Enabling stabilization".format(self.sequence)) + val = 1 + else: + if self.verbosity > 2: + print("[SEND {}] Disabling stabilization".format(self.sequence)) + val = 0 + self._send(self.API_V2_characteristic, + devID=deviceID['driving'], + commID=drivingCommands['stabilization'], + payload=[val]) + + self.getAcknowledgement("Stabilization") + + def wait(self, delay): + ''' + This is a non-blocking delay command. It is similar to time.sleep(), except it allows asynchronous + notification handling to still be performed. + ''' + start = time.time() + while(1): + self.p.waitForNotifications(0.001) + if time.time() - start > delay: + break + + def _send(self, characteristic=None, devID=None, commID=None, payload=[]): + ''' + A generic "send" method, which will be used by other methods to send a command ID, payload and + appropriate checksum to a specified device ID. Mainly useful because payloads are optional, + and can be of varying length, to convert packets to binary, and calculate and send the + checksum. For internal use only. + + Packet structure has the following format (in order): + + - Start byte: always 0x8D + - Flags byte: indicate response required, etc + - Virtual device ID: see _constants.py + - Command ID: see _constants.py + - Sequence number: Seems to be arbitrary. I suspect it is used to match commands to response packets (in which the number is echoed). + - Payload: Could be varying number of bytes (incl. none), depending on the command + - Checksum: See below for calculation + - End byte: always 0xD8 + + ''' + sendBytes = [sendPacketConstants["StartOfPacket"], + sum([flags["resetsInactivityTimeout"], flags["requestsResponse"]]), + devID, + commID, + self.sequence] + payload # concatenate payload list + + self.sequence += 1 # Increment sequence number, ensures we can identify response packets are for this command + if self.sequence > 255: + self.sequence = 0 + + # Compute and append checksum and add EOP byte: + # From Sphero docs: "The [checksum is the] modulo 256 sum of all the bytes + # from the device ID through the end of the data payload, + # bit inverted (1's complement)" + # For the sphero mini, the flag bits must be included too: + checksum = 0 + for num in sendBytes[1:]: + checksum = (checksum + num) & 0xFF # bitwise "and to get modulo 256 sum of appropriate bytes + checksum = 0xff - checksum # bitwise 'not' to invert checksum bits + sendBytes += [checksum, sendPacketConstants["EndOfPacket"]] # concatenate + + # Convert numbers to bytes + output = b"".join([x.to_bytes(1, byteorder='big') for x in sendBytes]) + + #send to specified characteristic: + characteristic.write(output, withResponse = True) + + def getAcknowledgement(self, ack): + #wait up to 10 secs for correct acknowledgement to come in, including sequence number! + start = time.time() + while(1): + self.p.waitForNotifications(1) + if self.sphero_delegate.notification_seq == self.sequence-1: # use one less than sequence, because _send function increments it for next send. + if self.verbosity > 3: + print("[RESP {}] {}".format(self.sequence-1, self.sphero_delegate.notification_ack)) + self.sphero_delegate.clear_notification() + break + elif self.sphero_delegate.notification_seq >= 0: + print("Unexpected ACK. Expected: {}/{}, received: {}/{}".format( + ack, self.sequence, self.sphero_delegate.notification_ack.split()[0], + self.sphero_delegate.notification_seq) + ) + if time.time() > start + 10: + print("Timeout waiting for acknowledgement: {}/{}".format(ack, self.sequence)) + break + +# ======================================================================= +# The following functions are experimental: +# ======================================================================= + + def configureCollisionDetection(self, + xThreshold = 50, + yThreshold = 50, + xSpeed = 50, + ySpeed = 50, + deadTime = 50, # in 10 millisecond increments + method = 0x01, # Must be 0x01 + callback = None): + ''' + Appears to function the same as other Sphero models, however speed settings seem to have no effect. + NOTE: Setting to zero seems to cause bluetooth errors with the Sphero Mini/bluepy library - set to + 255 to make it effectively disabled. + + deadTime disables future collisions for a short period of time to avoid repeat triggering by the same + event. Set in 10ms increments. So if deadTime = 50, that means the delay will be 500ms, or half a second. + + From Sphero docs: + + xThreshold/yThreshold: An 8-bit settable threshold for the X (left/right) and Y (front/back) axes + of Sphero. + + xSpeed/ySpeed: An 8-bit settable speed value for the X and Y axes. This setting is ranged by the + speed, then added to xThreshold, yThreshold to generate the final threshold value. + ''' + + if self.verbosity > 2: + print("[SEND {}] Configuring collision detection".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['configureCollision'], + payload=[method, xThreshold, xSpeed, yThreshold, ySpeed, deadTime]) + + self.collision_detection_callback = callback + + self.getAcknowledgement("Collision") + + def configureSensorStream(self): # Use default values + ''' + Send command to configure sensor stream using default values as found during bluetooth + sniffing of the Sphero Edu app. + + Must be called after calling configureSensorMask() + ''' + bitfield1 = 0b00000000 # Unknown function - needs experimenting + bitfield2 = 0b00000000 # Unknown function - needs experimenting + bitfield3 = 0b00000000 # Unknown function - needs experimenting + bitfield4 = 0b00000000 # Unknown function - needs experimenting + + if self.verbosity > 2: + print("[SEND {}] Configuring sensor stream".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['configureSensorStream'], + payload=[bitfield1, bitfield1, bitfield1, bitfield1]) + + self.getAcknowledgement("Sensor") + + def configureSensorMask(self, + sample_rate_divisor = 0x25, # Must be > 0 + packet_count = 0, + IMU_pitch = False, + IMU_roll = False, + IMU_yaw = False, + IMU_acc_x = False, + IMU_acc_y = False, + IMU_acc_z = False, + IMU_gyro_x = False, + IMU_gyro_y = False, + IMU_gyro_z = False): + + ''' + Send command to configure sensor mask using default values as found during bluetooth + sniffing of the Sphero Edu app. From experimentation, it seems that these are he functions of each: + + Sampling_rate_divisor. Slow data EG: Set to 0x32 to the divide data rate by 50. Setting below 25 (0x19) causes + bluetooth errors + + Packet_count: Select the number of packets to transmit before ending the stream. Set to zero to stream infinitely + + All IMU bool parameters: Toggle transmission of that value on or off (e.g. set IMU_acc_x = True to include the + X-axis accelerometer readings in the sensor stream) + ''' + + # Construct bitfields based on function parameters: + IMU_bitfield1 = (IMU_pitch<<2) + (IMU_roll<<1) + IMU_yaw + IMU_bitfield2 = ((IMU_acc_y<<7) + (IMU_acc_z<<6) + (IMU_acc_x<<5) + \ + (IMU_gyro_y<<4) + (IMU_gyro_x<<2) + (IMU_gyro_z<<2)) + + if self.verbosity > 2: + print("[SEND {}] Configuring sensor mask".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['sensorMask'], + payload=[0x00, # Unknown param - altering it seems to slow data rate. Possibly averages multiple readings? + sample_rate_divisor, + packet_count, # Packet count: select the number of packets to stop streaming after (zero = infinite) + 0b00, # Unknown param: seems to be another accelerometer bitfield? Z-acc, Y-acc + IMU_bitfield1, + IMU_bitfield2, + 0b00]) # reserved, Position?, Position?, velocity?, velocity?, Y-gyro, timer, reserved + + self.getAcknowledgement("Mask") + + ''' + Since the sensor values arrive as unlabelled lists in the order that they appear in the bitfields above, we need + to create a list of sensors that have been configured.Once we have this list, then in the default_delegate class, + we can get sensor values as attributes of the sphero_mini class. + e.g. print(sphero.IMU_yaw) # displays the current yaw angle + ''' + + # Initialize dictionary with sensor names as keys and their bool values (set by the user) as values: + availableSensors = {"IMU_pitch" : IMU_pitch, + "IMU_roll" : IMU_roll, + "IMU_yaw" : IMU_yaw, + "IMU_acc_y" : IMU_acc_y, + "IMU_acc_z" : IMU_acc_z, + "IMU_acc_x" : IMU_acc_x, + "IMU_gyro_y" : IMU_gyro_y, + "IMU_gyro_x" : IMU_gyro_x, + "IMU_gyro_z" : IMU_gyro_z} + + # Create list of of only sensors that have been "activated" (set as true in the method arguments): + self.configured_sensors = [name for name in availableSensors if availableSensors[name] == True] + + def sensor1(self): # Use default values + ''' + Unknown function. Observed in bluetooth sniffing. + ''' + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['sensor1'], + payload=[0x01]) + + self.getAcknowledgement("Sensor1") + + def sensor2(self): # Use default values + ''' + Unknown function. Observed in bluetooth sniffing. + ''' + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['sensor2'], + payload=[0x00]) + + self.getAcknowledgement("Sensor2") + +# ======================================================================= + +class MyDelegate(btle.DefaultDelegate): + + ''' + This class handles notifications (both responses and asynchronous notifications). + + Usage of this class is described in the Bluepy documentation + + ''' + + def __init__(self, sphero_class, user_delegate): + self.sphero_class = sphero_class # for saving sensor values as attributes of sphero class instance + self.user_delegate = user_delegate # to directly notify users of callbacks + btle.DefaultDelegate.__init__(self) + self.clear_notification() + self.notificationPacket = [] + + def clear_notification(self): + self.notification_ack = "DEFAULT ACK" + self.notification_seq = -1 + + def bits_to_num(self, bits): + ''' + This helper function decodes bytes from sensor packets into single precision floats. Encoding follows the + the IEEE-754 standard. + ''' + num = int(bits, 2).to_bytes(len(bits) // 8, byteorder='little') + num = struct.unpack('f', num)[0] + return num + + def handleNotification(self, cHandle, data): + ''' + This method acts as an interrupt service routine. When a notification comes in, this + method is invoked, with the variable 'cHandle' being the handle of the characteristic that + sent the notification, and 'data' being the payload (sent one byte at a time, so the packet + needs to be reconstructed) + + The method keeps appending bytes to the payload packet byte list until end-of-packet byte is + encountered. Note that this is an issue, because 0xD8 could be sent as part of the payload of, + say, the battery voltage notification. In future, a more sophisticated method will be required. + ''' + # Allow the user to intercept and process data first.. + if self.user_delegate != None: + if self.user_delegate.handleNotification(cHandle, data): + return + + #print("Received notification with packet ", str(data)) + + for data_byte in data: # parse each byte separately (sometimes they arrive simultaneously) + + self.notificationPacket.append(data_byte) # Add new byte to packet list + + # If end of packet (need to find a better way to segment the packets): + if data_byte == sendPacketConstants['EndOfPacket']: + # Once full the packet has arrived, parse it: + # Packet structure is similar to the outgoing send packets (see docstring in sphero_mini._send()) + + # Attempt to unpack. Might fail if packet is too badly corrupted + try: + start, flags_bits, devid, commcode, seq, *notification_payload, chsum, end = self.notificationPacket + except ValueError: + print("Warning: notification packet unparseable ", self.notificationPacket) + self.notificationPacket = [] # Discard this packet + return # exit + + # Compute and append checksum and add EOP byte: + # From Sphero docs: "The [checksum is the] modulo 256 sum of all the bytes + # from the device ID through the end of the data payload, + # bit inverted (1's complement)" + # For the sphero mini, the flag bits must be included too: + checksum_bytes = [flags_bits, devid, commcode, seq] + notification_payload + checksum = 0 # init + for num in checksum_bytes: + checksum = (checksum + num) & 0xFF # bitwise "and to get modulo 256 sum of appropriate bytes + checksum = 0xff - checksum # bitwise 'not' to invert checksum bits + if checksum != chsum: # check computed checksum against that recieved in the packet + print("Warning: notification packet checksum failed - ", str(self.notificationPacket)) + self.notificationPacket = [] # Discard this packet + return # exit + + # Check if response packet: + if flags_bits & flags['isResponse']: # it is a response + + # Use device ID and command code to determine which command is being acknowledged: + if devid == deviceID['powerInfo'] and commcode == powerCommandIDs['wake']: + self.notification_ack = "Wake acknowledged" # Acknowledgement after wake command + + elif devid == deviceID['driving'] and commcode == drivingCommands['driveWithHeading']: + self.notification_ack = "Roll command acknowledged" + + elif devid == deviceID['driving'] and commcode == drivingCommands['stabilization']: + self.notification_ack = "Stabilization command acknowledged" + + elif devid == deviceID['userIO'] and commcode == userIOCommandIDs['allLEDs']: + self.notification_ack = "LED/backlight color command acknowledged" + + elif devid == deviceID['driving'] and commcode == drivingCommands["resetHeading"]: + self.notification_ack = "Heading reset command acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["configureCollision"]: + self.notification_ack = "Collision detection configuration acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["configureSensorStream"]: + self.notification_ack = "Sensor stream configuration acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["sensorMask"]: + self.notification_ack = "Mask configuration acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["sensor1"]: + self.notification_ack = "Sensor1 acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["sensor2"]: + self.notification_ack = "Sensor2 acknowledged" + + elif devid == deviceID['powerInfo'] and commcode == powerCommandIDs['batteryVoltage']: + V_batt = notification_payload[2] + notification_payload[1]*256 + notification_payload[0]*65536 + V_batt /= 100 # Notification gives V_batt in 10mV increments. Divide by 100 to get to volts. + self.notification_ack = "Battery voltage:" + str(V_batt) + "v" + self.sphero_class.v_batt = V_batt + + elif devid == deviceID['systemInfo'] and commcode == SystemInfoCommands['mainApplicationVersion']: + version = '.'.join(str(x) for x in notification_payload) + self.notification_ack = "Firmware version: " + version + self.sphero_class.firmware_version = notification_payload + + else: + self.notification_ack = "Unknown acknowledgement" #print(self.notificationPacket) + print(self.notificationPacket, "===================> Unknown ack packet") + + self.notification_seq = seq + + else: # Not a response packet - therefore, asynchronous notification (e.g. collision detection, etc): + + # Collision detection: + if devid == deviceID['sensor'] and commcode == sensorCommands['collisionDetectedAsync']: + # The first four bytes are data that is still un-parsed. the remaining unsaved bytes are always zeros + _, _, _, _, _, _, axis, _, Y_mag, _, X_mag, *_ = notification_payload + if axis == 1: + dir = "Left/right" + else: + dir = 'Forward/back' + print("Collision detected:") + print("\tAxis: ", dir) + print("\tX_mag: ", X_mag) + print("\tY_mag: ", Y_mag) + + if self.sphero_class.collision_detection_callback is not None: + self.notificationPacket = [] # need to clear packet, in case new notification comes in during callback + self.sphero_class.collision_detection_callback() + + # Sensor response: + elif devid == deviceID['sensor'] and commcode == sensorCommands['sensorResponse']: + # Convert to binary, pad bytes with leading zeros: + val = '' + for byte in notification_payload: + val += format(int(bin(byte)[2:], 2), '#010b')[2:] + + # Break into 32-bit chunks + nums = [] + while(len(val) > 0): + num, val = val[:32], val[32:] # Slice off first 16 bits + nums.append(num) + + # convert from raw bits to float: + nums = [self.bits_to_num(num) for num in nums] + + # Set sensor values as class attributes: + for name, value in zip(self.sphero_class.configured_sensors, nums): + print("Setting sensor at .", name, str(value)) + setattr(self.sphero_class, name, value) + + # Unrecognized packet structure: + else: + self.notification_ack = "Unknown asynchronous notification" #print(self.notificationPacket) + print(str(self.notificationPacket) + " ===================> Unknown async packet") + + self.notificationPacket = [] # Start new payload after this byte \ No newline at end of file diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/wavefront.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/wavefront.py new file mode 100644 index 0000000000000000000000000000000000000000..1da387793ce36c131ca7ecc83355afdd5b70da7d --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/wavefront.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python +import time +import sys + +def newSearch(mapOfWorld, goal, start): + heap = [] + newheap = [] + x, y = goal + lastwave = 3 + # Start out by marking nodes around G with a 3 + moves = [(x + 1, y), (x - 1, y), (x, y - 1), (x, y + 1)] + + for move in moves: + if(mapOfWorld.positions[move] == ' '): + mapOfWorld.positions[move] = 3 + heap.append(move) + for currentwave in range(4, 10000): + lastwave = lastwave + 1 + while(heap != []): + position = heap.pop() + (x, y) = position + moves = [(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)] + #x, y = position + for move in moves: + if(mapOfWorld.positions[move] != 'W'): + if(mapOfWorld.positions[move] == ' ' and mapOfWorld.positions[position] == currentwave - 1): + mapOfWorld.positions[move] = currentwave + newheap.append(move) + if(move == start): + return mapOfWorld, lastwave + + #time.sleep(0.25) + #mapOfWorld.display() + #print heap + if(newheap == []): + print("Goal is unreachable") + return 1 + heap = newheap + newheap = [] + +def printf(format, *args): + sys.stdout.write(format % args) + +class Map(object): + + def __init__(self, xdim, ydim, positions): + self.xdim = xdim + self.ydim = ydim + self.positions = positions + def display(self): + printf(" ") + for i in range(self.ydim): + printf("%3s", str(i)) + print + for x in range(self.xdim): + printf("%2s", str(x)) + for y in range(self.ydim): + printf("%3s", str(self.positions[(x, y)])) + print + # Navigate though the number-populated maze + def nav(self, start, current): + self.pos = start + finished = False + + while(finished == False): # Run this code until we're at the goal + x, y = self.pos + self.positions[self.pos] = 'R' # Set the start on the map (this USUALLY keeps start the same) + # SOUTH NORTH WEST EAST + # v v v v + moves = [(x + 1, y), (x - 1, y), (x, y - 1), (x, y + 1)] # Establish our directions + moveDirections = ["South", "North", "West", "East"] # Create a corresponding list of the cardinal directions + """ We don't want least to be 0, because then nothing would be less than it. + However, in order to make our code more robust, we set it to one of the values, + so that we're comparing least to an actual value instead of an arbitrary number (like 10). + """ + # Do the actual comparing, and give us the least index so we know which move was the least + for w in range(len(moves)): + move = moves[w] + + # If the position has the current wave - 1 in it, move there. + if(self.positions[move] == current - 1): + self.least = self.positions[move] + leastIndex = w + # Or, if the position is the goal, stop the loop + elif(self.positions[move] == 'G'): + finished = True + leastIndex = w + # Decrement the current number so we can look for the next number + current = current - 1 + self.positions[self.pos] = ' ' + print( "Moved " + moveDirections[leastIndex]) + self.pos = moves[leastIndex] # This will be converted to "move robot in x direction" + + #time.sleep(0.25) + #self.display() + # Change the goal position (or wherever we stop) to an "!" to show that we've arrived. + self.positions[self.pos] = '!' + self.display() +# Find the goal, given the map +def findGoal(mapOfWorld): + positions = mapOfWorld.positions + for x in range(mapOfWorld.xdim): + for y in range(mapOfWorld.ydim): + if(mapOfWorld.positions[(x, y)] == 'G'): + return (x, y) +# Find the start, given the map +def findStart(mapOfWorld): + positions = mapOfWorld.positions + for x in range(mapOfWorld.xdim): + for y in range(mapOfWorld.ydim): + if(mapOfWorld.positions[(x, y)] == 'R'): + + return (x, y) + +def convertMap(mapOfWorld): + positions = {} + xdim = len(mapOfWorld) + ydim = len(mapOfWorld[1]) + for y in range(ydim): + for x in range(xdim): + positions[(x, y)] = mapOfWorld[x][y] + + return Map(xdim, ydim, positions) + +# Map erstellen +# Standard imports +import cv2 +import numpy as np; + +# Img Objekt +cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + +success, img = cap.read() + +gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) +mask = cv2.inRange(gray, np.array([90]), np.array([255])) + +mapOfWorld = [[0]*gray.shape[1]]*gray.shape[0] + +mask_list= np.ndarray.tolist(mask) + +for i in range(0,mask.shape[0]): + mapOfWorld[i] = ['W' if j > 200 else ' ' for j in mask_list[i]] + +mapOfWorld[200][200] = 'R' #Start Pos +mapOfWorld[300][300] = 'G' #Ziel Pos + +print(mapOfWorld[200][200]) +print(mapOfWorld[300][300]) + +mapOfLand = convertMap(mapOfWorld) +mapOfLand.display() +mapOfLand, lastwave = newSearch(mapOfLand, findGoal(mapOfLand), findStart(mapOfLand)) +mapOfLand.nav(findStart(mapOfLand), lastwave) diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/wavefront_coverage_path_planner.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/wavefront_coverage_path_planner.py new file mode 100644 index 0000000000000000000000000000000000000000..85861402d61f643d8a0639c708a494cca6383170 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/wavefront_coverage_path_planner.py @@ -0,0 +1,218 @@ +""" +Distance/Path Transform Wavefront Coverage Path Planner + +author: Todd Tang +paper: Planning paths of complete coverage of an unstructured environment + by a mobile robot - Zelinsky et.al. +link: http://pinkwink.kr/attachment/cfile3.uf@1354654A4E8945BD13FE77.pdf +""" + +import os +import sys + +import matplotlib.pyplot as plt +import numpy as np +from scipy import ndimage + +do_animation = True + + +def transform( + grid_map, src, distance_type='chessboard', + transform_type='path', alpha=0.01 +): + """transform + + calculating transform of transform_type from src + in given distance_type + + :param grid_map: 2d binary map + :param src: distance transform source + :param distance_type: type of distance used + :param transform_type: type of transform used + :param alpha: weight of Obstacle Transform used when using path_transform + """ + + n_rows, n_cols = grid_map.shape + + if n_rows == 0 or n_cols == 0: + sys.exit('Empty grid_map.') + + inc_order = [[0, 1], [1, 1], [1, 0], [1, -1], + [0, -1], [-1, -1], [-1, 0], [-1, 1]] + if distance_type == 'chessboard': + cost = [1, 1, 1, 1, 1, 1, 1, 1] + elif distance_type == 'eculidean': + cost = [1, np.sqrt(2), 1, np.sqrt(2), 1, np.sqrt(2), 1, np.sqrt(2)] + else: + sys.exit('Unsupported distance type.') + + transform_matrix = float('inf') * np.ones_like(grid_map, dtype=float) + transform_matrix[src[0], src[1]] = 0 + if transform_type == 'distance': + eT = np.zeros_like(grid_map) + elif transform_type == 'path': + eT = ndimage.distance_transform_cdt(1 - grid_map, distance_type) + else: + sys.exit('Unsupported transform type.') + + # set obstacle transform_matrix value to infinity + for i in range(n_rows): + for j in range(n_cols): + if grid_map[i][j] == 1.0: + transform_matrix[i][j] = float('inf') + is_visited = np.zeros_like(transform_matrix, dtype=bool) + is_visited[src[0], src[1]] = True + traversal_queue = [src] + calculated = [(src[0] - 1) * n_cols + src[1]] + + def is_valid_neighbor(g_i, g_j): + return 0 <= g_i < n_rows and 0 <= g_j < n_cols \ + and not grid_map[g_i][g_j] + + while traversal_queue: + i, j = traversal_queue.pop(0) + for k, inc in enumerate(inc_order): + ni = i + inc[0] + nj = j + inc[1] + if is_valid_neighbor(ni, nj): + is_visited[i][j] = True + + # update transform_matrix + transform_matrix[i][j] = min( + transform_matrix[i][j], + transform_matrix[ni][nj] + cost[k] + alpha * eT[ni][nj]) + + if not is_visited[ni][nj] \ + and ((ni - 1) * n_cols + nj) not in calculated: + traversal_queue.append((ni, nj)) + calculated.append((ni - 1) * n_cols + nj) + + return transform_matrix + + +def get_search_order_increment(start, goal): + if start[0] >= goal[0] and start[1] >= goal[1]: + order = [[1, 0], [0, 1], [-1, 0], [0, -1], + [1, 1], [1, -1], [-1, 1], [-1, -1]] + elif start[0] <= goal[0] and start[1] >= goal[1]: + order = [[-1, 0], [0, 1], [1, 0], [0, -1], + [-1, 1], [-1, -1], [1, 1], [1, -1]] + elif start[0] >= goal[0] and start[1] <= goal[1]: + order = [[1, 0], [0, -1], [-1, 0], [0, 1], + [1, -1], [-1, -1], [1, 1], [-1, 1]] + elif start[0] <= goal[0] and start[1] <= goal[1]: + order = [[-1, 0], [0, -1], [0, 1], [1, 0], + [-1, -1], [-1, 1], [1, -1], [1, 1]] + else: + sys.exit('get_search_order_increment: cannot determine \ + start=>goal increment order') + return order + + +def wavefront(transform_matrix, start, goal): + """wavefront + + performing wavefront coverage path planning + + :param transform_matrix: the transform matrix + :param start: start point of planning + :param goal: goal point of planning + """ + + path = [] + n_rows, n_cols = transform_matrix.shape + + def is_valid_neighbor(g_i, g_j): + is_i_valid_bounded = 0 <= g_i < n_rows + is_j_valid_bounded = 0 <= g_j < n_cols + if is_i_valid_bounded and is_j_valid_bounded: + return not is_visited[g_i][g_j] and \ + transform_matrix[g_i][g_j] != float('inf') + return False + + inc_order = get_search_order_increment(start, goal) + + current_node = start + is_visited = np.zeros_like(transform_matrix, dtype=bool) + + while current_node != goal: + i, j = current_node + path.append((i, j)) + is_visited[i][j] = True + + max_T = float('-inf') + i_max = (-1, -1) + i_last = 0 + for i_last in range(len(path)): + current_node = path[-1 - i_last] # get latest node in path + for ci, cj in inc_order: + ni, nj = current_node[0] + ci, current_node[1] + cj + if is_valid_neighbor(ni, nj) and \ + transform_matrix[ni][nj] > max_T: + i_max = (ni, nj) + max_T = transform_matrix[ni][nj] + + if i_max != (-1, -1): + break + + if i_max == (-1, -1): + break + else: + current_node = i_max + if i_last != 0: + print('backtracing to', current_node) + path.append(goal) + + return path + + +def visualize_path(grid_map, start, goal, path): # pragma: no cover + oy, ox = start + gy, gx = goal + px, py = np.transpose(np.flipud(np.fliplr(path))) + + if not do_animation: + plt.imshow(grid_map, cmap='Greys') + plt.plot(ox, oy, "-xy") + plt.plot(px, py, "-r") + plt.plot(gx, gy, "-pg") + plt.show() + else: + for ipx, ipy in zip(px, py): + plt.cla() + # for stopping simulation with the esc key. + plt.gcf().canvas.mpl_connect( + 'key_release_event', + lambda event: [exit(0) if event.key == 'escape' else None]) + plt.imshow(grid_map, cmap='Greys') + plt.plot(ox, oy, "-xb") + plt.plot(px, py, "-r") + plt.plot(gx, gy, "-pg") + plt.plot(ipx, ipy, "or") + plt.axis("equal") + plt.grid(True) + plt.pause(0.1) + + +def main(): + dir_path = os.path.dirname(os.path.realpath(__file__)) + img = plt.imread(os.path.join(dir_path, 'map', 'test_2.png')) + img = 1 - img # revert pixel values + + start = (43, 0) + goal = (0, 0) + + # distance transform wavefront + DT = transform(img, goal, transform_type='distance') + DT_path = wavefront(DT, start, goal) + visualize_path(img, start, goal, DT_path) + + # path transform wavefront + PT = transform(img, goal, transform_type='path', alpha=0.01) + PT_path = wavefront(PT, start, goal) + visualize_path(img, start, goal, PT_path) + + +if __name__ == "__main__": + main() diff --git a/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/wavefrontgpt2.py b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/wavefrontgpt2.py new file mode 100644 index 0000000000000000000000000000000000000000..ef76c9f457ef6872c3b242d9b6815864c9ed7a5c --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/wavefrontgpt2.py @@ -0,0 +1,76 @@ +from queue import Queue + +def wavefront_planner(map_array, start_pos, goal_pos): + rows = len(map_array) + cols = len(map_array[0]) + + # Überprüfe, ob Start- und Zielposition innerhalb der Karte liegen + if (start_pos[0] < 0 or start_pos[0] >= rows or start_pos[1] < 0 or start_pos[1] >= cols or + goal_pos[0] < 0 or goal_pos[0] >= rows or goal_pos[1] < 0 or goal_pos[1] >= cols): + raise ValueError("Start or goal position is out of bounds.") + + # Erzeuge eine Kopie der Karte für den Wavefront-Algorithmus + wavefront_map = [[-1] * cols for _ in range(rows)] + + # Definiere die Bewegungsrichtungen (4-Wege-Bewegung: oben, unten, links, rechts) + directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] + + # Erzeuge eine Warteschlange für die Wellenfrontausbreitung + queue = Queue() + queue.put(goal_pos) + + # Führe die Wellenfrontausbreitung durch + wavefront_map[goal_pos[0]][goal_pos[1]] = 0 + while not queue.empty(): + current_pos = queue.get() + + # Überprüfe die Nachbarzellen + for direction in directions: + new_pos = (current_pos[0] + direction[0], current_pos[1] + direction[1]) + + # Überprüfe, ob die Nachbarzelle gültig und noch nicht besucht ist + if (0 <= new_pos[0] < rows and 0 <= new_pos[1] < cols and + wavefront_map[new_pos[0]][new_pos[1]] == -1 and map_array[new_pos[0]][new_pos[1]] == 0): + wavefront_map[new_pos[0]][new_pos[1]] = wavefront_map[current_pos[0]][current_pos[1]] + 1 + queue.put(new_pos) + + # Überprüfe, ob der Startpunkt erreichbar ist + if wavefront_map[start_pos[0]][start_pos[1]] == -1: + raise ValueError("Start position is unreachable.") + + # Verfolge den Pfad basierend auf der Wellenfront + path = [start_pos] + current_pos = start_pos + while current_pos != goal_pos: + next_pos = None + min_distance = float('inf') + + for direction in directions: + neighbor_pos = (current_pos[0] + direction[0], current_pos[1] + direction[1]) + + if (0 <= neighbor_pos[0] < rows and 0 <= neighbor_pos[1] < cols and + wavefront_map[neighbor_pos[0]][neighbor_pos[1]] < min_distance): + next_pos = neighbor_pos + min_distance = wavefront_map[neighbor_pos[0]][neighbor_pos[1]] + + if next_pos is None: + raise ValueError("No path found.") + + path.append(next_pos) + current_pos = next_pos + + return path + +# Beispielverwendung +map_array = [ + [0, 0, 0, 0], + [0, 1, 1, 0], + [0, 0, 0, 0], + [0, 1, 1, 0], +] + +start_pos = (0, 0) +goal_pos = (1, 3) + +path = wavefront_planner(map_array, start_pos, goal_pos) +print("Path:", path) diff --git a/ros2_ws/build/sphero_mini_controller/colcon_build.rc b/ros2_ws/build/sphero_mini_controller/colcon_build.rc new file mode 100644 index 0000000000000000000000000000000000000000..573541ac9702dd3969c9bc859d2b91ec1f7e6e56 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/colcon_build.rc @@ -0,0 +1 @@ +0 diff --git a/ros2_ws/build/sphero_mini_controller/colcon_command_prefix_setup_py.sh b/ros2_ws/build/sphero_mini_controller/colcon_command_prefix_setup_py.sh new file mode 100644 index 0000000000000000000000000000000000000000..f9867d51322a8ef47d4951080db6e3cfd048835e --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/colcon_command_prefix_setup_py.sh @@ -0,0 +1 @@ +# generated from colcon_core/shell/template/command_prefix.sh.em diff --git a/ros2_ws/build/sphero_mini_controller/colcon_command_prefix_setup_py.sh.env b/ros2_ws/build/sphero_mini_controller/colcon_command_prefix_setup_py.sh.env new file mode 100644 index 0000000000000000000000000000000000000000..0e1cc2ad36a65414149f48cf06801963696c85bf --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/colcon_command_prefix_setup_py.sh.env @@ -0,0 +1,61 @@ +AMENT_PREFIX_PATH=/opt/ros/humble +COLCON=1 +COLORTERM=truecolor +DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/999/bus +DESKTOP_SESSION=ubuntu +DISPLAY=:0 +GDMSESSION=ubuntu +GNOME_DESKTOP_SESSION_ID=this-is-deprecated +GNOME_SHELL_SESSION_MODE=ubuntu +GNOME_TERMINAL_SCREEN=/org/gnome/Terminal/screen/211b2ac6_0a43_44d5_b3bf_a0e22ff3787d +GNOME_TERMINAL_SERVICE=:1.128 +GPG_AGENT_INFO=/run/user/999/gnupg/S.gpg-agent:0:1 +GTK_MODULES=gail:atk-bridge +HOME=/home/ubuntu +IM_CONFIG_PHASE=1 +LANG=de_DE.UTF-8 +LANGUAGE=de_DE:en +LC_ADDRESS=de_DE.UTF-8 +LC_IDENTIFICATION=de_DE.UTF-8 +LC_MEASUREMENT=de_DE.UTF-8 +LC_MONETARY=de_DE.UTF-8 +LC_NAME=de_DE.UTF-8 +LC_NUMERIC=de_DE.UTF-8 +LC_PAPER=de_DE.UTF-8 +LC_TELEPHONE=de_DE.UTF-8 +LC_TIME=de_DE.UTF-8 +LD_LIBRARY_PATH=/opt/ros/humble/opt/rviz_ogre_vendor/lib:/opt/ros/humble/lib/x86_64-linux-gnu:/opt/ros/humble/lib +LOGNAME=ubuntu +OLDPWD=/home/ubuntu +PAPERSIZE=a4 +PATH=/opt/ros/humble/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin +PWD=/home/ubuntu/ros2_ws/build/sphero_mini_controller +PYTHONPATH=/opt/ros/humble/lib/python3.10/site-packages:/opt/ros/humble/local/lib/python3.10/dist-packages +QT_ACCESSIBILITY=1 +QT_IM_MODULE=ibus +ROS_DISTRO=humble +ROS_LOCALHOST_ONLY=0 +ROS_PYTHON_VERSION=3 +ROS_VERSION=2 +SESSION_MANAGER=local/ubuntu:@/tmp/.ICE-unix/2661,unix/ubuntu:/tmp/.ICE-unix/2661 +SHELL=/bin/bash +SHLVL=1 +SSH_AGENT_LAUNCHER=gnome-keyring +SSH_AUTH_SOCK=/run/user/999/keyring/ssh +SYSTEMD_EXEC_PID=2684 +TERM=xterm-256color +USER=ubuntu +USERNAME=ubuntu +VTE_VERSION=6800 +WINDOWPATH=2 +XAUTHORITY=/run/user/999/gdm/Xauthority +XDG_CONFIG_DIRS=/etc/xdg/xdg-ubuntu:/etc/xdg +XDG_CURRENT_DESKTOP=ubuntu:GNOME +XDG_DATA_DIRS=/usr/share/ubuntu:/usr/share/gnome:/usr/local/share/:/usr/share/:/var/lib/snapd/desktop +XDG_MENU_PREFIX=gnome- +XDG_RUNTIME_DIR=/run/user/999 +XDG_SESSION_CLASS=user +XDG_SESSION_DESKTOP=ubuntu +XDG_SESSION_TYPE=x11 +XMODIFIERS=@im=ibus +_=/usr/bin/colcon diff --git a/ros2_ws/build/sphero_mini_controller/install.log b/ros2_ws/build/sphero_mini_controller/install.log new file mode 100644 index 0000000000000000000000000000000000000000..0881f1a7a57e5ec89b61b89766677395d6078cc4 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/install.log @@ -0,0 +1,22 @@ +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node.py +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__init__.py +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/WavefrontPlanner.py +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/treiber.py +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/_constants.py +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/blobErkennung.py +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node.cpython-310.pyc +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/__init__.cpython-310.pyc +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/WavefrontPlanner.cpython-310.pyc +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/treiber.cpython-310.pyc +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/_constants.cpython-310.pyc +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/blobErkennung.cpython-310.pyc +/home/ubuntu/ros2_ws/install/sphero_mini_controller/share/ament_index/resource_index/packages/sphero_mini_controller +/home/ubuntu/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.xml +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/entry_points.txt +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/top_level.txt +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/zip-safe +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/PKG-INFO +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/SOURCES.txt +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/requires.txt +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/dependency_links.txt +/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/sphero_mini_controller/sphero_mini diff --git a/ros2_ws/build/sphero_mini_controller/prefix_override/__pycache__/sitecustomize.cpython-310.pyc b/ros2_ws/build/sphero_mini_controller/prefix_override/__pycache__/sitecustomize.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2bd0054d0a2debc06129b0ef052f83ce7d0b4c8f Binary files /dev/null and b/ros2_ws/build/sphero_mini_controller/prefix_override/__pycache__/sitecustomize.cpython-310.pyc differ diff --git a/ros2_ws/build/sphero_mini_controller/prefix_override/sitecustomize.py b/ros2_ws/build/sphero_mini_controller/prefix_override/sitecustomize.py new file mode 100644 index 0000000000000000000000000000000000000000..231b97a501846f86cbc275a9fe99486605ce8148 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/prefix_override/sitecustomize.py @@ -0,0 +1,3 @@ +import sys +sys.real_prefix = sys.prefix +sys.prefix = sys.exec_prefix = '/home/ubuntu/ros2_ws/install/sphero_mini_controller' diff --git a/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/PKG-INFO b/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/PKG-INFO new file mode 100644 index 0000000000000000000000000000000000000000..1772b2b4b87ed715b1c9529109e2a64ffa956ef2 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/PKG-INFO @@ -0,0 +1,12 @@ +Metadata-Version: 2.1 +Name: sphero-mini-controller +Version: 0.0.0 +Summary: TODO: Package description +Home-page: UNKNOWN +Maintainer: ubuntu +Maintainer-email: ubuntu@todo.todo +License: TODO: License declaration +Platform: UNKNOWN + +UNKNOWN + diff --git a/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/SOURCES.txt b/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/SOURCES.txt new file mode 100644 index 0000000000000000000000000000000000000000..450bb514f818ddd322e445deee994a8cbb2136e8 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/SOURCES.txt @@ -0,0 +1,20 @@ +package.xml +setup.cfg +setup.py +../../build/sphero_mini_controller/sphero_mini_controller.egg-info/PKG-INFO +../../build/sphero_mini_controller/sphero_mini_controller.egg-info/SOURCES.txt +../../build/sphero_mini_controller/sphero_mini_controller.egg-info/dependency_links.txt +../../build/sphero_mini_controller/sphero_mini_controller.egg-info/entry_points.txt +../../build/sphero_mini_controller/sphero_mini_controller.egg-info/requires.txt +../../build/sphero_mini_controller/sphero_mini_controller.egg-info/top_level.txt +../../build/sphero_mini_controller/sphero_mini_controller.egg-info/zip-safe +resource/sphero_mini_controller +sphero_mini_controller/WavefrontPlanner.py +sphero_mini_controller/__init__.py +sphero_mini_controller/_constants.py +sphero_mini_controller/blobErkennung.py +sphero_mini_controller/my_first_node.py +sphero_mini_controller/treiber.py +test/test_copyright.py +test/test_flake8.py +test/test_pep257.py \ No newline at end of file diff --git a/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/dependency_links.txt b/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/dependency_links.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/entry_points.txt b/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/entry_points.txt new file mode 100644 index 0000000000000000000000000000000000000000..6a40ae61982b9df335a55fd38c98ba80a816cb93 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +sphero_mini = sphero_mini_controller.my_first_node:main + diff --git a/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/requires.txt b/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/requires.txt new file mode 100644 index 0000000000000000000000000000000000000000..49fe098d9e6bccd89482b34510da60ab28556880 --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/requires.txt @@ -0,0 +1 @@ +setuptools diff --git a/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/top_level.txt b/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..60845dab3412fa47e2fb49f683a3db10ce25c01e --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/top_level.txt @@ -0,0 +1 @@ +sphero_mini_controller diff --git a/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/zip-safe b/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/zip-safe new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/ros2_ws/build/sphero_mini_controller/sphero_mini_controller.egg-info/zip-safe @@ -0,0 +1 @@ + diff --git a/ros2_ws/install/.colcon_install_layout b/ros2_ws/install/.colcon_install_layout new file mode 100644 index 0000000000000000000000000000000000000000..3aad5336af1f22b8088508218dceeda3d7bc8cc2 --- /dev/null +++ b/ros2_ws/install/.colcon_install_layout @@ -0,0 +1 @@ +isolated diff --git a/ros2_ws/install/COLCON_IGNORE b/ros2_ws/install/COLCON_IGNORE new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ros2_ws/install/_local_setup_util_ps1.py b/ros2_ws/install/_local_setup_util_ps1.py new file mode 100644 index 0000000000000000000000000000000000000000..98348eebcfb4cc97ed554c9864b4a1811af7fb49 --- /dev/null +++ b/ros2_ws/install/_local_setup_util_ps1.py @@ -0,0 +1,404 @@ +# Copyright 2016-2019 Dirk Thomas +# Licensed under the Apache License, Version 2.0 + +import argparse +from collections import OrderedDict +import os +from pathlib import Path +import sys + + +FORMAT_STR_COMMENT_LINE = '# {comment}' +FORMAT_STR_SET_ENV_VAR = 'Set-Item -Path "Env:{name}" -Value "{value}"' +FORMAT_STR_USE_ENV_VAR = '$env:{name}' +FORMAT_STR_INVOKE_SCRIPT = '_colcon_prefix_powershell_source_script "{script_path}"' +FORMAT_STR_REMOVE_LEADING_SEPARATOR = '' +FORMAT_STR_REMOVE_TRAILING_SEPARATOR = '' + +DSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate' +DSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate' +DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists' +DSV_TYPE_SET = 'set' +DSV_TYPE_SET_IF_UNSET = 'set-if-unset' +DSV_TYPE_SOURCE = 'source' + + +def main(argv=sys.argv[1:]): # noqa: D103 + parser = argparse.ArgumentParser( + description='Output shell commands for the packages in topological ' + 'order') + parser.add_argument( + 'primary_extension', + help='The file extension of the primary shell') + parser.add_argument( + 'additional_extension', nargs='?', + help='The additional file extension to be considered') + parser.add_argument( + '--merged-install', action='store_true', + help='All install prefixes are merged into a single location') + args = parser.parse_args(argv) + + packages = get_packages(Path(__file__).parent, args.merged_install) + + ordered_packages = order_packages(packages) + for pkg_name in ordered_packages: + if _include_comments(): + print( + FORMAT_STR_COMMENT_LINE.format_map( + {'comment': 'Package: ' + pkg_name})) + prefix = os.path.abspath(os.path.dirname(__file__)) + if not args.merged_install: + prefix = os.path.join(prefix, pkg_name) + for line in get_commands( + pkg_name, prefix, args.primary_extension, + args.additional_extension + ): + print(line) + + for line in _remove_ending_separators(): + print(line) + + +def get_packages(prefix_path, merged_install): + """ + Find packages based on colcon-specific files created during installation. + + :param Path prefix_path: The install prefix path of all packages + :param bool merged_install: The flag if the packages are all installed + directly in the prefix or if each package is installed in a subdirectory + named after the package + :returns: A mapping from the package name to the set of runtime + dependencies + :rtype: dict + """ + packages = {} + # since importing colcon_core isn't feasible here the following constant + # must match colcon_core.location.get_relative_package_index_path() + subdirectory = 'share/colcon-core/packages' + if merged_install: + # return if workspace is empty + if not (prefix_path / subdirectory).is_dir(): + return packages + # find all files in the subdirectory + for p in (prefix_path / subdirectory).iterdir(): + if not p.is_file(): + continue + if p.name.startswith('.'): + continue + add_package_runtime_dependencies(p, packages) + else: + # for each subdirectory look for the package specific file + for p in prefix_path.iterdir(): + if not p.is_dir(): + continue + if p.name.startswith('.'): + continue + p = p / subdirectory / p.name + if p.is_file(): + add_package_runtime_dependencies(p, packages) + + # remove unknown dependencies + pkg_names = set(packages.keys()) + for k in packages.keys(): + packages[k] = {d for d in packages[k] if d in pkg_names} + + return packages + + +def add_package_runtime_dependencies(path, packages): + """ + Check the path and if it exists extract the packages runtime dependencies. + + :param Path path: The resource file containing the runtime dependencies + :param dict packages: A mapping from package names to the sets of runtime + dependencies to add to + """ + content = path.read_text() + dependencies = set(content.split(os.pathsep) if content else []) + packages[path.name] = dependencies + + +def order_packages(packages): + """ + Order packages topologically. + + :param dict packages: A mapping from package name to the set of runtime + dependencies + :returns: The package names + :rtype: list + """ + # select packages with no dependencies in alphabetical order + to_be_ordered = list(packages.keys()) + ordered = [] + while to_be_ordered: + pkg_names_without_deps = [ + name for name in to_be_ordered if not packages[name]] + if not pkg_names_without_deps: + reduce_cycle_set(packages) + raise RuntimeError( + 'Circular dependency between: ' + ', '.join(sorted(packages))) + pkg_names_without_deps.sort() + pkg_name = pkg_names_without_deps[0] + to_be_ordered.remove(pkg_name) + ordered.append(pkg_name) + # remove item from dependency lists + for k in list(packages.keys()): + if pkg_name in packages[k]: + packages[k].remove(pkg_name) + return ordered + + +def reduce_cycle_set(packages): + """ + Reduce the set of packages to the ones part of the circular dependency. + + :param dict packages: A mapping from package name to the set of runtime + dependencies which is modified in place + """ + last_depended = None + while len(packages) > 0: + # get all remaining dependencies + depended = set() + for pkg_name, dependencies in packages.items(): + depended = depended.union(dependencies) + # remove all packages which are not dependent on + for name in list(packages.keys()): + if name not in depended: + del packages[name] + if last_depended: + # if remaining packages haven't changed return them + if last_depended == depended: + return packages.keys() + # otherwise reduce again + last_depended = depended + + +def _include_comments(): + # skipping comment lines when COLCON_TRACE is not set speeds up the + # processing especially on Windows + return bool(os.environ.get('COLCON_TRACE')) + + +def get_commands(pkg_name, prefix, primary_extension, additional_extension): + commands = [] + package_dsv_path = os.path.join(prefix, 'share', pkg_name, 'package.dsv') + if os.path.exists(package_dsv_path): + commands += process_dsv_file( + package_dsv_path, prefix, primary_extension, additional_extension) + return commands + + +def process_dsv_file( + dsv_path, prefix, primary_extension=None, additional_extension=None +): + commands = [] + if _include_comments(): + commands.append(FORMAT_STR_COMMENT_LINE.format_map({'comment': dsv_path})) + with open(dsv_path, 'r') as h: + content = h.read() + lines = content.splitlines() + + basenames = OrderedDict() + for i, line in enumerate(lines): + # skip over empty or whitespace-only lines + if not line.strip(): + continue + try: + type_, remainder = line.split(';', 1) + except ValueError: + raise RuntimeError( + "Line %d in '%s' doesn't contain a semicolon separating the " + 'type from the arguments' % (i + 1, dsv_path)) + if type_ != DSV_TYPE_SOURCE: + # handle non-source lines + try: + commands += handle_dsv_types_except_source( + type_, remainder, prefix) + except RuntimeError as e: + raise RuntimeError( + "Line %d in '%s' %s" % (i + 1, dsv_path, e)) from e + else: + # group remaining source lines by basename + path_without_ext, ext = os.path.splitext(remainder) + if path_without_ext not in basenames: + basenames[path_without_ext] = set() + assert ext.startswith('.') + ext = ext[1:] + if ext in (primary_extension, additional_extension): + basenames[path_without_ext].add(ext) + + # add the dsv extension to each basename if the file exists + for basename, extensions in basenames.items(): + if not os.path.isabs(basename): + basename = os.path.join(prefix, basename) + if os.path.exists(basename + '.dsv'): + extensions.add('dsv') + + for basename, extensions in basenames.items(): + if not os.path.isabs(basename): + basename = os.path.join(prefix, basename) + if 'dsv' in extensions: + # process dsv files recursively + commands += process_dsv_file( + basename + '.dsv', prefix, primary_extension=primary_extension, + additional_extension=additional_extension) + elif primary_extension in extensions and len(extensions) == 1: + # source primary-only files + commands += [ + FORMAT_STR_INVOKE_SCRIPT.format_map({ + 'prefix': prefix, + 'script_path': basename + '.' + primary_extension})] + elif additional_extension in extensions: + # source non-primary files + commands += [ + FORMAT_STR_INVOKE_SCRIPT.format_map({ + 'prefix': prefix, + 'script_path': basename + '.' + additional_extension})] + + return commands + + +def handle_dsv_types_except_source(type_, remainder, prefix): + commands = [] + if type_ in (DSV_TYPE_SET, DSV_TYPE_SET_IF_UNSET): + try: + env_name, value = remainder.split(';', 1) + except ValueError: + raise RuntimeError( + "doesn't contain a semicolon separating the environment name " + 'from the value') + try_prefixed_value = os.path.join(prefix, value) if value else prefix + if os.path.exists(try_prefixed_value): + value = try_prefixed_value + if type_ == DSV_TYPE_SET: + commands += _set(env_name, value) + elif type_ == DSV_TYPE_SET_IF_UNSET: + commands += _set_if_unset(env_name, value) + else: + assert False + elif type_ in ( + DSV_TYPE_APPEND_NON_DUPLICATE, + DSV_TYPE_PREPEND_NON_DUPLICATE, + DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS + ): + try: + env_name_and_values = remainder.split(';') + except ValueError: + raise RuntimeError( + "doesn't contain a semicolon separating the environment name " + 'from the values') + env_name = env_name_and_values[0] + values = env_name_and_values[1:] + for value in values: + if not value: + value = prefix + elif not os.path.isabs(value): + value = os.path.join(prefix, value) + if ( + type_ == DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS and + not os.path.exists(value) + ): + comment = f'skip extending {env_name} with not existing ' \ + f'path: {value}' + if _include_comments(): + commands.append( + FORMAT_STR_COMMENT_LINE.format_map({'comment': comment})) + elif type_ == DSV_TYPE_APPEND_NON_DUPLICATE: + commands += _append_unique_value(env_name, value) + else: + commands += _prepend_unique_value(env_name, value) + else: + raise RuntimeError( + 'contains an unknown environment hook type: ' + type_) + return commands + + +env_state = {} + + +def _append_unique_value(name, value): + global env_state + if name not in env_state: + if os.environ.get(name): + env_state[name] = set(os.environ[name].split(os.pathsep)) + else: + env_state[name] = set() + # append even if the variable has not been set yet, in case a shell script sets the + # same variable without the knowledge of this Python script. + # later _remove_ending_separators() will cleanup any unintentional leading separator + extend = FORMAT_STR_USE_ENV_VAR.format_map({'name': name}) + os.pathsep + line = FORMAT_STR_SET_ENV_VAR.format_map( + {'name': name, 'value': extend + value}) + if value not in env_state[name]: + env_state[name].add(value) + else: + if not _include_comments(): + return [] + line = FORMAT_STR_COMMENT_LINE.format_map({'comment': line}) + return [line] + + +def _prepend_unique_value(name, value): + global env_state + if name not in env_state: + if os.environ.get(name): + env_state[name] = set(os.environ[name].split(os.pathsep)) + else: + env_state[name] = set() + # prepend even if the variable has not been set yet, in case a shell script sets the + # same variable without the knowledge of this Python script. + # later _remove_ending_separators() will cleanup any unintentional trailing separator + extend = os.pathsep + FORMAT_STR_USE_ENV_VAR.format_map({'name': name}) + line = FORMAT_STR_SET_ENV_VAR.format_map( + {'name': name, 'value': value + extend}) + if value not in env_state[name]: + env_state[name].add(value) + else: + if not _include_comments(): + return [] + line = FORMAT_STR_COMMENT_LINE.format_map({'comment': line}) + return [line] + + +# generate commands for removing prepended underscores +def _remove_ending_separators(): + # do nothing if the shell extension does not implement the logic + if FORMAT_STR_REMOVE_TRAILING_SEPARATOR is None: + return [] + + global env_state + commands = [] + for name in env_state: + # skip variables that already had values before this script started prepending + if name in os.environ: + continue + commands += [ + FORMAT_STR_REMOVE_LEADING_SEPARATOR.format_map({'name': name}), + FORMAT_STR_REMOVE_TRAILING_SEPARATOR.format_map({'name': name})] + return commands + + +def _set(name, value): + global env_state + env_state[name] = value + line = FORMAT_STR_SET_ENV_VAR.format_map( + {'name': name, 'value': value}) + return [line] + + +def _set_if_unset(name, value): + global env_state + line = FORMAT_STR_SET_ENV_VAR.format_map( + {'name': name, 'value': value}) + if env_state.get(name, os.environ.get(name)): + line = FORMAT_STR_COMMENT_LINE.format_map({'comment': line}) + return [line] + + +if __name__ == '__main__': # pragma: no cover + try: + rc = main() + except RuntimeError as e: + print(str(e), file=sys.stderr) + rc = 1 + sys.exit(rc) diff --git a/ros2_ws/install/_local_setup_util_sh.py b/ros2_ws/install/_local_setup_util_sh.py new file mode 100644 index 0000000000000000000000000000000000000000..35c017b2558a0b0a148d67778c1ae302f023361d --- /dev/null +++ b/ros2_ws/install/_local_setup_util_sh.py @@ -0,0 +1,404 @@ +# Copyright 2016-2019 Dirk Thomas +# Licensed under the Apache License, Version 2.0 + +import argparse +from collections import OrderedDict +import os +from pathlib import Path +import sys + + +FORMAT_STR_COMMENT_LINE = '# {comment}' +FORMAT_STR_SET_ENV_VAR = 'export {name}="{value}"' +FORMAT_STR_USE_ENV_VAR = '${name}' +FORMAT_STR_INVOKE_SCRIPT = 'COLCON_CURRENT_PREFIX="{prefix}" _colcon_prefix_sh_source_script "{script_path}"' +FORMAT_STR_REMOVE_LEADING_SEPARATOR = 'if [ "$(echo -n ${name} | head -c 1)" = ":" ]; then export {name}=${{{name}#?}} ; fi' +FORMAT_STR_REMOVE_TRAILING_SEPARATOR = 'if [ "$(echo -n ${name} | tail -c 1)" = ":" ]; then export {name}=${{{name}%?}} ; fi' + +DSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate' +DSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate' +DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists' +DSV_TYPE_SET = 'set' +DSV_TYPE_SET_IF_UNSET = 'set-if-unset' +DSV_TYPE_SOURCE = 'source' + + +def main(argv=sys.argv[1:]): # noqa: D103 + parser = argparse.ArgumentParser( + description='Output shell commands for the packages in topological ' + 'order') + parser.add_argument( + 'primary_extension', + help='The file extension of the primary shell') + parser.add_argument( + 'additional_extension', nargs='?', + help='The additional file extension to be considered') + parser.add_argument( + '--merged-install', action='store_true', + help='All install prefixes are merged into a single location') + args = parser.parse_args(argv) + + packages = get_packages(Path(__file__).parent, args.merged_install) + + ordered_packages = order_packages(packages) + for pkg_name in ordered_packages: + if _include_comments(): + print( + FORMAT_STR_COMMENT_LINE.format_map( + {'comment': 'Package: ' + pkg_name})) + prefix = os.path.abspath(os.path.dirname(__file__)) + if not args.merged_install: + prefix = os.path.join(prefix, pkg_name) + for line in get_commands( + pkg_name, prefix, args.primary_extension, + args.additional_extension + ): + print(line) + + for line in _remove_ending_separators(): + print(line) + + +def get_packages(prefix_path, merged_install): + """ + Find packages based on colcon-specific files created during installation. + + :param Path prefix_path: The install prefix path of all packages + :param bool merged_install: The flag if the packages are all installed + directly in the prefix or if each package is installed in a subdirectory + named after the package + :returns: A mapping from the package name to the set of runtime + dependencies + :rtype: dict + """ + packages = {} + # since importing colcon_core isn't feasible here the following constant + # must match colcon_core.location.get_relative_package_index_path() + subdirectory = 'share/colcon-core/packages' + if merged_install: + # return if workspace is empty + if not (prefix_path / subdirectory).is_dir(): + return packages + # find all files in the subdirectory + for p in (prefix_path / subdirectory).iterdir(): + if not p.is_file(): + continue + if p.name.startswith('.'): + continue + add_package_runtime_dependencies(p, packages) + else: + # for each subdirectory look for the package specific file + for p in prefix_path.iterdir(): + if not p.is_dir(): + continue + if p.name.startswith('.'): + continue + p = p / subdirectory / p.name + if p.is_file(): + add_package_runtime_dependencies(p, packages) + + # remove unknown dependencies + pkg_names = set(packages.keys()) + for k in packages.keys(): + packages[k] = {d for d in packages[k] if d in pkg_names} + + return packages + + +def add_package_runtime_dependencies(path, packages): + """ + Check the path and if it exists extract the packages runtime dependencies. + + :param Path path: The resource file containing the runtime dependencies + :param dict packages: A mapping from package names to the sets of runtime + dependencies to add to + """ + content = path.read_text() + dependencies = set(content.split(os.pathsep) if content else []) + packages[path.name] = dependencies + + +def order_packages(packages): + """ + Order packages topologically. + + :param dict packages: A mapping from package name to the set of runtime + dependencies + :returns: The package names + :rtype: list + """ + # select packages with no dependencies in alphabetical order + to_be_ordered = list(packages.keys()) + ordered = [] + while to_be_ordered: + pkg_names_without_deps = [ + name for name in to_be_ordered if not packages[name]] + if not pkg_names_without_deps: + reduce_cycle_set(packages) + raise RuntimeError( + 'Circular dependency between: ' + ', '.join(sorted(packages))) + pkg_names_without_deps.sort() + pkg_name = pkg_names_without_deps[0] + to_be_ordered.remove(pkg_name) + ordered.append(pkg_name) + # remove item from dependency lists + for k in list(packages.keys()): + if pkg_name in packages[k]: + packages[k].remove(pkg_name) + return ordered + + +def reduce_cycle_set(packages): + """ + Reduce the set of packages to the ones part of the circular dependency. + + :param dict packages: A mapping from package name to the set of runtime + dependencies which is modified in place + """ + last_depended = None + while len(packages) > 0: + # get all remaining dependencies + depended = set() + for pkg_name, dependencies in packages.items(): + depended = depended.union(dependencies) + # remove all packages which are not dependent on + for name in list(packages.keys()): + if name not in depended: + del packages[name] + if last_depended: + # if remaining packages haven't changed return them + if last_depended == depended: + return packages.keys() + # otherwise reduce again + last_depended = depended + + +def _include_comments(): + # skipping comment lines when COLCON_TRACE is not set speeds up the + # processing especially on Windows + return bool(os.environ.get('COLCON_TRACE')) + + +def get_commands(pkg_name, prefix, primary_extension, additional_extension): + commands = [] + package_dsv_path = os.path.join(prefix, 'share', pkg_name, 'package.dsv') + if os.path.exists(package_dsv_path): + commands += process_dsv_file( + package_dsv_path, prefix, primary_extension, additional_extension) + return commands + + +def process_dsv_file( + dsv_path, prefix, primary_extension=None, additional_extension=None +): + commands = [] + if _include_comments(): + commands.append(FORMAT_STR_COMMENT_LINE.format_map({'comment': dsv_path})) + with open(dsv_path, 'r') as h: + content = h.read() + lines = content.splitlines() + + basenames = OrderedDict() + for i, line in enumerate(lines): + # skip over empty or whitespace-only lines + if not line.strip(): + continue + try: + type_, remainder = line.split(';', 1) + except ValueError: + raise RuntimeError( + "Line %d in '%s' doesn't contain a semicolon separating the " + 'type from the arguments' % (i + 1, dsv_path)) + if type_ != DSV_TYPE_SOURCE: + # handle non-source lines + try: + commands += handle_dsv_types_except_source( + type_, remainder, prefix) + except RuntimeError as e: + raise RuntimeError( + "Line %d in '%s' %s" % (i + 1, dsv_path, e)) from e + else: + # group remaining source lines by basename + path_without_ext, ext = os.path.splitext(remainder) + if path_without_ext not in basenames: + basenames[path_without_ext] = set() + assert ext.startswith('.') + ext = ext[1:] + if ext in (primary_extension, additional_extension): + basenames[path_without_ext].add(ext) + + # add the dsv extension to each basename if the file exists + for basename, extensions in basenames.items(): + if not os.path.isabs(basename): + basename = os.path.join(prefix, basename) + if os.path.exists(basename + '.dsv'): + extensions.add('dsv') + + for basename, extensions in basenames.items(): + if not os.path.isabs(basename): + basename = os.path.join(prefix, basename) + if 'dsv' in extensions: + # process dsv files recursively + commands += process_dsv_file( + basename + '.dsv', prefix, primary_extension=primary_extension, + additional_extension=additional_extension) + elif primary_extension in extensions and len(extensions) == 1: + # source primary-only files + commands += [ + FORMAT_STR_INVOKE_SCRIPT.format_map({ + 'prefix': prefix, + 'script_path': basename + '.' + primary_extension})] + elif additional_extension in extensions: + # source non-primary files + commands += [ + FORMAT_STR_INVOKE_SCRIPT.format_map({ + 'prefix': prefix, + 'script_path': basename + '.' + additional_extension})] + + return commands + + +def handle_dsv_types_except_source(type_, remainder, prefix): + commands = [] + if type_ in (DSV_TYPE_SET, DSV_TYPE_SET_IF_UNSET): + try: + env_name, value = remainder.split(';', 1) + except ValueError: + raise RuntimeError( + "doesn't contain a semicolon separating the environment name " + 'from the value') + try_prefixed_value = os.path.join(prefix, value) if value else prefix + if os.path.exists(try_prefixed_value): + value = try_prefixed_value + if type_ == DSV_TYPE_SET: + commands += _set(env_name, value) + elif type_ == DSV_TYPE_SET_IF_UNSET: + commands += _set_if_unset(env_name, value) + else: + assert False + elif type_ in ( + DSV_TYPE_APPEND_NON_DUPLICATE, + DSV_TYPE_PREPEND_NON_DUPLICATE, + DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS + ): + try: + env_name_and_values = remainder.split(';') + except ValueError: + raise RuntimeError( + "doesn't contain a semicolon separating the environment name " + 'from the values') + env_name = env_name_and_values[0] + values = env_name_and_values[1:] + for value in values: + if not value: + value = prefix + elif not os.path.isabs(value): + value = os.path.join(prefix, value) + if ( + type_ == DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS and + not os.path.exists(value) + ): + comment = f'skip extending {env_name} with not existing ' \ + f'path: {value}' + if _include_comments(): + commands.append( + FORMAT_STR_COMMENT_LINE.format_map({'comment': comment})) + elif type_ == DSV_TYPE_APPEND_NON_DUPLICATE: + commands += _append_unique_value(env_name, value) + else: + commands += _prepend_unique_value(env_name, value) + else: + raise RuntimeError( + 'contains an unknown environment hook type: ' + type_) + return commands + + +env_state = {} + + +def _append_unique_value(name, value): + global env_state + if name not in env_state: + if os.environ.get(name): + env_state[name] = set(os.environ[name].split(os.pathsep)) + else: + env_state[name] = set() + # append even if the variable has not been set yet, in case a shell script sets the + # same variable without the knowledge of this Python script. + # later _remove_ending_separators() will cleanup any unintentional leading separator + extend = FORMAT_STR_USE_ENV_VAR.format_map({'name': name}) + os.pathsep + line = FORMAT_STR_SET_ENV_VAR.format_map( + {'name': name, 'value': extend + value}) + if value not in env_state[name]: + env_state[name].add(value) + else: + if not _include_comments(): + return [] + line = FORMAT_STR_COMMENT_LINE.format_map({'comment': line}) + return [line] + + +def _prepend_unique_value(name, value): + global env_state + if name not in env_state: + if os.environ.get(name): + env_state[name] = set(os.environ[name].split(os.pathsep)) + else: + env_state[name] = set() + # prepend even if the variable has not been set yet, in case a shell script sets the + # same variable without the knowledge of this Python script. + # later _remove_ending_separators() will cleanup any unintentional trailing separator + extend = os.pathsep + FORMAT_STR_USE_ENV_VAR.format_map({'name': name}) + line = FORMAT_STR_SET_ENV_VAR.format_map( + {'name': name, 'value': value + extend}) + if value not in env_state[name]: + env_state[name].add(value) + else: + if not _include_comments(): + return [] + line = FORMAT_STR_COMMENT_LINE.format_map({'comment': line}) + return [line] + + +# generate commands for removing prepended underscores +def _remove_ending_separators(): + # do nothing if the shell extension does not implement the logic + if FORMAT_STR_REMOVE_TRAILING_SEPARATOR is None: + return [] + + global env_state + commands = [] + for name in env_state: + # skip variables that already had values before this script started prepending + if name in os.environ: + continue + commands += [ + FORMAT_STR_REMOVE_LEADING_SEPARATOR.format_map({'name': name}), + FORMAT_STR_REMOVE_TRAILING_SEPARATOR.format_map({'name': name})] + return commands + + +def _set(name, value): + global env_state + env_state[name] = value + line = FORMAT_STR_SET_ENV_VAR.format_map( + {'name': name, 'value': value}) + return [line] + + +def _set_if_unset(name, value): + global env_state + line = FORMAT_STR_SET_ENV_VAR.format_map( + {'name': name, 'value': value}) + if env_state.get(name, os.environ.get(name)): + line = FORMAT_STR_COMMENT_LINE.format_map({'comment': line}) + return [line] + + +if __name__ == '__main__': # pragma: no cover + try: + rc = main() + except RuntimeError as e: + print(str(e), file=sys.stderr) + rc = 1 + sys.exit(rc) diff --git a/ros2_ws/install/local_setup.bash b/ros2_ws/install/local_setup.bash new file mode 100644 index 0000000000000000000000000000000000000000..efd5f8c9e24546b7d9b90d2e7928ea126de164e3 --- /dev/null +++ b/ros2_ws/install/local_setup.bash @@ -0,0 +1,107 @@ +# generated from colcon_bash/shell/template/prefix.bash.em + +# This script extends the environment with all packages contained in this +# prefix path. + +# a bash script is able to determine its own path if necessary +if [ -z "$COLCON_CURRENT_PREFIX" ]; then + _colcon_prefix_bash_COLCON_CURRENT_PREFIX="$(builtin cd "`dirname "${BASH_SOURCE[0]}"`" > /dev/null && pwd)" +else + _colcon_prefix_bash_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX" +fi + +# function to prepend a value to a variable +# which uses colons as separators +# duplicates as well as trailing separators are avoided +# first argument: the name of the result variable +# second argument: the value to be prepended +_colcon_prefix_bash_prepend_unique_value() { + # arguments + _listname="$1" + _value="$2" + + # get values from variable + eval _values=\"\$$_listname\" + # backup the field separator + _colcon_prefix_bash_prepend_unique_value_IFS="$IFS" + IFS=":" + # start with the new value + _all_values="$_value" + # iterate over existing values in the variable + for _item in $_values; do + # ignore empty strings + if [ -z "$_item" ]; then + continue + fi + # ignore duplicates of _value + if [ "$_item" = "$_value" ]; then + continue + fi + # keep non-duplicate values + _all_values="$_all_values:$_item" + done + unset _item + # restore the field separator + IFS="$_colcon_prefix_bash_prepend_unique_value_IFS" + unset _colcon_prefix_bash_prepend_unique_value_IFS + # export the updated variable + eval export $_listname=\"$_all_values\" + unset _all_values + unset _values + + unset _value + unset _listname +} + +# add this prefix to the COLCON_PREFIX_PATH +_colcon_prefix_bash_prepend_unique_value COLCON_PREFIX_PATH "$_colcon_prefix_bash_COLCON_CURRENT_PREFIX" +unset _colcon_prefix_bash_prepend_unique_value + +# check environment variable for custom Python executable +if [ -n "$COLCON_PYTHON_EXECUTABLE" ]; then + if [ ! -f "$COLCON_PYTHON_EXECUTABLE" ]; then + echo "error: COLCON_PYTHON_EXECUTABLE '$COLCON_PYTHON_EXECUTABLE' doesn't exist" + return 1 + fi + _colcon_python_executable="$COLCON_PYTHON_EXECUTABLE" +else + # try the Python executable known at configure time + _colcon_python_executable="/usr/bin/python3" + # if it doesn't exist try a fall back + if [ ! -f "$_colcon_python_executable" ]; then + if ! /usr/bin/env python3 --version > /dev/null 2> /dev/null; then + echo "error: unable to find python3 executable" + return 1 + fi + _colcon_python_executable=`/usr/bin/env python3 -c "import sys; print(sys.executable)"` + fi +fi + +# function to source another script with conditional trace output +# first argument: the path of the script +_colcon_prefix_sh_source_script() { + if [ -f "$1" ]; then + if [ -n "$COLCON_TRACE" ]; then + echo ". \"$1\"" + fi + . "$1" + else + echo "not found: \"$1\"" 1>&2 + fi +} + +# get all commands in topological order +_colcon_ordered_commands="$($_colcon_python_executable "$_colcon_prefix_bash_COLCON_CURRENT_PREFIX/_local_setup_util_sh.py" sh bash)" +unset _colcon_python_executable +if [ -n "$COLCON_TRACE" ]; then + echo "Execute generated script:" + echo "<<<" + echo "${_colcon_ordered_commands}" + echo ">>>" +fi +eval "${_colcon_ordered_commands}" +unset _colcon_ordered_commands + +unset _colcon_prefix_sh_source_script + +unset _colcon_prefix_bash_COLCON_CURRENT_PREFIX diff --git a/ros2_ws/install/local_setup.ps1 b/ros2_ws/install/local_setup.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..6f68c8dede9ed4ecb63a4eb6ac2a7450bd18ec3b --- /dev/null +++ b/ros2_ws/install/local_setup.ps1 @@ -0,0 +1,55 @@ +# generated from colcon_powershell/shell/template/prefix.ps1.em + +# This script extends the environment with all packages contained in this +# prefix path. + +# check environment variable for custom Python executable +if ($env:COLCON_PYTHON_EXECUTABLE) { + if (!(Test-Path "$env:COLCON_PYTHON_EXECUTABLE" -PathType Leaf)) { + echo "error: COLCON_PYTHON_EXECUTABLE '$env:COLCON_PYTHON_EXECUTABLE' doesn't exist" + exit 1 + } + $_colcon_python_executable="$env:COLCON_PYTHON_EXECUTABLE" +} else { + # use the Python executable known at configure time + $_colcon_python_executable="/usr/bin/python3" + # if it doesn't exist try a fall back + if (!(Test-Path "$_colcon_python_executable" -PathType Leaf)) { + if (!(Get-Command "python3" -ErrorAction SilentlyContinue)) { + echo "error: unable to find python3 executable" + exit 1 + } + $_colcon_python_executable="python3" + } +} + +# function to source another script with conditional trace output +# first argument: the path of the script +function _colcon_prefix_powershell_source_script { + param ( + $_colcon_prefix_powershell_source_script_param + ) + # source script with conditional trace output + if (Test-Path $_colcon_prefix_powershell_source_script_param) { + if ($env:COLCON_TRACE) { + echo ". '$_colcon_prefix_powershell_source_script_param'" + } + . "$_colcon_prefix_powershell_source_script_param" + } else { + Write-Error "not found: '$_colcon_prefix_powershell_source_script_param'" + } +} + +# get all commands in topological order +$_colcon_ordered_commands = & "$_colcon_python_executable" "$(Split-Path $PSCommandPath -Parent)/_local_setup_util_ps1.py" ps1 + +# execute all commands in topological order +if ($env:COLCON_TRACE) { + echo "Execute generated script:" + echo "<<<" + $_colcon_ordered_commands.Split([Environment]::NewLine, [StringSplitOptions]::RemoveEmptyEntries) | Write-Output + echo ">>>" +} +if ($_colcon_ordered_commands) { + $_colcon_ordered_commands.Split([Environment]::NewLine, [StringSplitOptions]::RemoveEmptyEntries) | Invoke-Expression +} diff --git a/ros2_ws/install/local_setup.sh b/ros2_ws/install/local_setup.sh new file mode 100644 index 0000000000000000000000000000000000000000..ef8aee8333db86abe0e07e0b1e44ac839636a33f --- /dev/null +++ b/ros2_ws/install/local_setup.sh @@ -0,0 +1,137 @@ +# generated from colcon_core/shell/template/prefix.sh.em + +# This script extends the environment with all packages contained in this +# prefix path. + +# since a plain shell script can't determine its own path when being sourced +# either use the provided COLCON_CURRENT_PREFIX +# or fall back to the build time prefix (if it exists) +_colcon_prefix_sh_COLCON_CURRENT_PREFIX="/home/ubuntu/ros2_ws/install" +if [ -z "$COLCON_CURRENT_PREFIX" ]; then + if [ ! -d "$_colcon_prefix_sh_COLCON_CURRENT_PREFIX" ]; then + echo "The build time path \"$_colcon_prefix_sh_COLCON_CURRENT_PREFIX\" doesn't exist. Either source a script for a different shell or set the environment variable \"COLCON_CURRENT_PREFIX\" explicitly." 1>&2 + unset _colcon_prefix_sh_COLCON_CURRENT_PREFIX + return 1 + fi +else + _colcon_prefix_sh_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX" +fi + +# function to prepend a value to a variable +# which uses colons as separators +# duplicates as well as trailing separators are avoided +# first argument: the name of the result variable +# second argument: the value to be prepended +_colcon_prefix_sh_prepend_unique_value() { + # arguments + _listname="$1" + _value="$2" + + # get values from variable + eval _values=\"\$$_listname\" + # backup the field separator + _colcon_prefix_sh_prepend_unique_value_IFS="$IFS" + IFS=":" + # start with the new value + _all_values="$_value" + _contained_value="" + # iterate over existing values in the variable + for _item in $_values; do + # ignore empty strings + if [ -z "$_item" ]; then + continue + fi + # ignore duplicates of _value + if [ "$_item" = "$_value" ]; then + _contained_value=1 + continue + fi + # keep non-duplicate values + _all_values="$_all_values:$_item" + done + unset _item + if [ -z "$_contained_value" ]; then + if [ -n "$COLCON_TRACE" ]; then + if [ "$_all_values" = "$_value" ]; then + echo "export $_listname=$_value" + else + echo "export $_listname=$_value:\$$_listname" + fi + fi + fi + unset _contained_value + # restore the field separator + IFS="$_colcon_prefix_sh_prepend_unique_value_IFS" + unset _colcon_prefix_sh_prepend_unique_value_IFS + # export the updated variable + eval export $_listname=\"$_all_values\" + unset _all_values + unset _values + + unset _value + unset _listname +} + +# add this prefix to the COLCON_PREFIX_PATH +_colcon_prefix_sh_prepend_unique_value COLCON_PREFIX_PATH "$_colcon_prefix_sh_COLCON_CURRENT_PREFIX" +unset _colcon_prefix_sh_prepend_unique_value + +# check environment variable for custom Python executable +if [ -n "$COLCON_PYTHON_EXECUTABLE" ]; then + if [ ! -f "$COLCON_PYTHON_EXECUTABLE" ]; then + echo "error: COLCON_PYTHON_EXECUTABLE '$COLCON_PYTHON_EXECUTABLE' doesn't exist" + return 1 + fi + _colcon_python_executable="$COLCON_PYTHON_EXECUTABLE" +else + # try the Python executable known at configure time + _colcon_python_executable="/usr/bin/python3" + # if it doesn't exist try a fall back + if [ ! -f "$_colcon_python_executable" ]; then + if ! /usr/bin/env python3 --version > /dev/null 2> /dev/null; then + echo "error: unable to find python3 executable" + return 1 + fi + _colcon_python_executable=`/usr/bin/env python3 -c "import sys; print(sys.executable)"` + fi +fi + +# function to source another script with conditional trace output +# first argument: the path of the script +_colcon_prefix_sh_source_script() { + if [ -f "$1" ]; then + if [ -n "$COLCON_TRACE" ]; then + echo "# . \"$1\"" + fi + . "$1" + else + echo "not found: \"$1\"" 1>&2 + fi +} + +# get all commands in topological order +_colcon_ordered_commands="$($_colcon_python_executable "$_colcon_prefix_sh_COLCON_CURRENT_PREFIX/_local_setup_util_sh.py" sh)" +unset _colcon_python_executable +if [ -n "$COLCON_TRACE" ]; then + echo "_colcon_prefix_sh_source_script() { + if [ -f \"\$1\" ]; then + if [ -n \"\$COLCON_TRACE\" ]; then + echo \"# . \\\"\$1\\\"\" + fi + . \"\$1\" + else + echo \"not found: \\\"\$1\\\"\" 1>&2 + fi + }" + echo "# Execute generated script:" + echo "# <<<" + echo "${_colcon_ordered_commands}" + echo "# >>>" + echo "unset _colcon_prefix_sh_source_script" +fi +eval "${_colcon_ordered_commands}" +unset _colcon_ordered_commands + +unset _colcon_prefix_sh_source_script + +unset _colcon_prefix_sh_COLCON_CURRENT_PREFIX diff --git a/ros2_ws/install/local_setup.zsh b/ros2_ws/install/local_setup.zsh new file mode 100644 index 0000000000000000000000000000000000000000..f7a8d904f2019736b6114cec0f6250da480c415c --- /dev/null +++ b/ros2_ws/install/local_setup.zsh @@ -0,0 +1,120 @@ +# generated from colcon_zsh/shell/template/prefix.zsh.em + +# This script extends the environment with all packages contained in this +# prefix path. + +# a zsh script is able to determine its own path if necessary +if [ -z "$COLCON_CURRENT_PREFIX" ]; then + _colcon_prefix_zsh_COLCON_CURRENT_PREFIX="$(builtin cd -q "`dirname "${(%):-%N}"`" > /dev/null && pwd)" +else + _colcon_prefix_zsh_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX" +fi + +# function to convert array-like strings into arrays +# to workaround SH_WORD_SPLIT not being set +_colcon_prefix_zsh_convert_to_array() { + local _listname=$1 + local _dollar="$" + local _split="{=" + local _to_array="(\"$_dollar$_split$_listname}\")" + eval $_listname=$_to_array +} + +# function to prepend a value to a variable +# which uses colons as separators +# duplicates as well as trailing separators are avoided +# first argument: the name of the result variable +# second argument: the value to be prepended +_colcon_prefix_zsh_prepend_unique_value() { + # arguments + _listname="$1" + _value="$2" + + # get values from variable + eval _values=\"\$$_listname\" + # backup the field separator + _colcon_prefix_zsh_prepend_unique_value_IFS="$IFS" + IFS=":" + # start with the new value + _all_values="$_value" + # workaround SH_WORD_SPLIT not being set + _colcon_prefix_zsh_convert_to_array _values + # iterate over existing values in the variable + for _item in $_values; do + # ignore empty strings + if [ -z "$_item" ]; then + continue + fi + # ignore duplicates of _value + if [ "$_item" = "$_value" ]; then + continue + fi + # keep non-duplicate values + _all_values="$_all_values:$_item" + done + unset _item + # restore the field separator + IFS="$_colcon_prefix_zsh_prepend_unique_value_IFS" + unset _colcon_prefix_zsh_prepend_unique_value_IFS + # export the updated variable + eval export $_listname=\"$_all_values\" + unset _all_values + unset _values + + unset _value + unset _listname +} + +# add this prefix to the COLCON_PREFIX_PATH +_colcon_prefix_zsh_prepend_unique_value COLCON_PREFIX_PATH "$_colcon_prefix_zsh_COLCON_CURRENT_PREFIX" +unset _colcon_prefix_zsh_prepend_unique_value +unset _colcon_prefix_zsh_convert_to_array + +# check environment variable for custom Python executable +if [ -n "$COLCON_PYTHON_EXECUTABLE" ]; then + if [ ! -f "$COLCON_PYTHON_EXECUTABLE" ]; then + echo "error: COLCON_PYTHON_EXECUTABLE '$COLCON_PYTHON_EXECUTABLE' doesn't exist" + return 1 + fi + _colcon_python_executable="$COLCON_PYTHON_EXECUTABLE" +else + # try the Python executable known at configure time + _colcon_python_executable="/usr/bin/python3" + # if it doesn't exist try a fall back + if [ ! -f "$_colcon_python_executable" ]; then + if ! /usr/bin/env python3 --version > /dev/null 2> /dev/null; then + echo "error: unable to find python3 executable" + return 1 + fi + _colcon_python_executable=`/usr/bin/env python3 -c "import sys; print(sys.executable)"` + fi +fi + +# function to source another script with conditional trace output +# first argument: the path of the script +_colcon_prefix_sh_source_script() { + if [ -f "$1" ]; then + if [ -n "$COLCON_TRACE" ]; then + echo ". \"$1\"" + fi + . "$1" + else + echo "not found: \"$1\"" 1>&2 + fi +} + +# get all commands in topological order +_colcon_ordered_commands="$($_colcon_python_executable "$_colcon_prefix_zsh_COLCON_CURRENT_PREFIX/_local_setup_util_sh.py" sh zsh)" +unset _colcon_python_executable +if [ -n "$COLCON_TRACE" ]; then + echo "Execute generated script:" + echo "<<<" + echo "${_colcon_ordered_commands}" + echo ">>>" +fi +eval "${_colcon_ordered_commands}" +unset _colcon_ordered_commands + +unset _colcon_prefix_sh_source_script + +unset _colcon_prefix_zsh_COLCON_CURRENT_PREFIX diff --git a/ros2_ws/install/setup.bash b/ros2_ws/install/setup.bash new file mode 100644 index 0000000000000000000000000000000000000000..4c55244159be959e730ef507f8e45f57c6ee7bb0 --- /dev/null +++ b/ros2_ws/install/setup.bash @@ -0,0 +1,31 @@ +# generated from colcon_bash/shell/template/prefix_chain.bash.em + +# This script extends the environment with the environment of other prefix +# paths which were sourced when this file was generated as well as all packages +# contained in this prefix path. + +# function to source another script with conditional trace output +# first argument: the path of the script +_colcon_prefix_chain_bash_source_script() { + if [ -f "$1" ]; then + if [ -n "$COLCON_TRACE" ]; then + echo ". \"$1\"" + fi + . "$1" + else + echo "not found: \"$1\"" 1>&2 + fi +} + +# source chained prefixes +# setting COLCON_CURRENT_PREFIX avoids determining the prefix in the sourced script +COLCON_CURRENT_PREFIX="/opt/ros/humble" +_colcon_prefix_chain_bash_source_script "$COLCON_CURRENT_PREFIX/local_setup.bash" + +# source this prefix +# setting COLCON_CURRENT_PREFIX avoids determining the prefix in the sourced script +COLCON_CURRENT_PREFIX="$(builtin cd "`dirname "${BASH_SOURCE[0]}"`" > /dev/null && pwd)" +_colcon_prefix_chain_bash_source_script "$COLCON_CURRENT_PREFIX/local_setup.bash" + +unset COLCON_CURRENT_PREFIX +unset _colcon_prefix_chain_bash_source_script diff --git a/ros2_ws/install/setup.ps1 b/ros2_ws/install/setup.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..558e9b9e627400bd65c3fc2e0751bb19ec5efa69 --- /dev/null +++ b/ros2_ws/install/setup.ps1 @@ -0,0 +1,29 @@ +# generated from colcon_powershell/shell/template/prefix_chain.ps1.em + +# This script extends the environment with the environment of other prefix +# paths which were sourced when this file was generated as well as all packages +# contained in this prefix path. + +# function to source another script with conditional trace output +# first argument: the path of the script +function _colcon_prefix_chain_powershell_source_script { + param ( + $_colcon_prefix_chain_powershell_source_script_param + ) + # source script with conditional trace output + if (Test-Path $_colcon_prefix_chain_powershell_source_script_param) { + if ($env:COLCON_TRACE) { + echo ". '$_colcon_prefix_chain_powershell_source_script_param'" + } + . "$_colcon_prefix_chain_powershell_source_script_param" + } else { + Write-Error "not found: '$_colcon_prefix_chain_powershell_source_script_param'" + } +} + +# source chained prefixes +_colcon_prefix_chain_powershell_source_script "/opt/ros/humble\local_setup.ps1" + +# source this prefix +$env:COLCON_CURRENT_PREFIX=(Split-Path $PSCommandPath -Parent) +_colcon_prefix_chain_powershell_source_script "$env:COLCON_CURRENT_PREFIX\local_setup.ps1" diff --git a/ros2_ws/install/setup.sh b/ros2_ws/install/setup.sh new file mode 100644 index 0000000000000000000000000000000000000000..5b2b7fe9c8f7b012cb21a74f58a8b069e712053c --- /dev/null +++ b/ros2_ws/install/setup.sh @@ -0,0 +1,45 @@ +# generated from colcon_core/shell/template/prefix_chain.sh.em + +# This script extends the environment with the environment of other prefix +# paths which were sourced when this file was generated as well as all packages +# contained in this prefix path. + +# since a plain shell script can't determine its own path when being sourced +# either use the provided COLCON_CURRENT_PREFIX +# or fall back to the build time prefix (if it exists) +_colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX=/home/ubuntu/ros2_ws/install +if [ ! -z "$COLCON_CURRENT_PREFIX" ]; then + _colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX" +elif [ ! -d "$_colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX" ]; then + echo "The build time path \"$_colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX\" doesn't exist. Either source a script for a different shell or set the environment variable \"COLCON_CURRENT_PREFIX\" explicitly." 1>&2 + unset _colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX + return 1 +fi + +# function to source another script with conditional trace output +# first argument: the path of the script +_colcon_prefix_chain_sh_source_script() { + if [ -f "$1" ]; then + if [ -n "$COLCON_TRACE" ]; then + echo "# . \"$1\"" + fi + . "$1" + else + echo "not found: \"$1\"" 1>&2 + fi +} + +# source chained prefixes +# setting COLCON_CURRENT_PREFIX avoids relying on the build time prefix of the sourced script +COLCON_CURRENT_PREFIX="/opt/ros/humble" +_colcon_prefix_chain_sh_source_script "$COLCON_CURRENT_PREFIX/local_setup.sh" + + +# source this prefix +# setting COLCON_CURRENT_PREFIX avoids relying on the build time prefix of the sourced script +COLCON_CURRENT_PREFIX="$_colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX" +_colcon_prefix_chain_sh_source_script "$COLCON_CURRENT_PREFIX/local_setup.sh" + +unset _colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX +unset _colcon_prefix_chain_sh_source_script +unset COLCON_CURRENT_PREFIX diff --git a/ros2_ws/install/setup.zsh b/ros2_ws/install/setup.zsh new file mode 100644 index 0000000000000000000000000000000000000000..990d17190a973865cdb60bcea8875b768db28150 --- /dev/null +++ b/ros2_ws/install/setup.zsh @@ -0,0 +1,31 @@ +# generated from colcon_zsh/shell/template/prefix_chain.zsh.em + +# This script extends the environment with the environment of other prefix +# paths which were sourced when this file was generated as well as all packages +# contained in this prefix path. + +# function to source another script with conditional trace output +# first argument: the path of the script +_colcon_prefix_chain_zsh_source_script() { + if [ -f "$1" ]; then + if [ -n "$COLCON_TRACE" ]; then + echo ". \"$1\"" + fi + . "$1" + else + echo "not found: \"$1\"" 1>&2 + fi +} + +# source chained prefixes +# setting COLCON_CURRENT_PREFIX avoids determining the prefix in the sourced script +COLCON_CURRENT_PREFIX="/opt/ros/humble" +_colcon_prefix_chain_zsh_source_script "$COLCON_CURRENT_PREFIX/local_setup.zsh" + +# source this prefix +# setting COLCON_CURRENT_PREFIX avoids determining the prefix in the sourced script +COLCON_CURRENT_PREFIX="$(builtin cd -q "`dirname "${(%):-%N}"`" > /dev/null && pwd)" +_colcon_prefix_chain_zsh_source_script "$COLCON_CURRENT_PREFIX/local_setup.zsh" + +unset COLCON_CURRENT_PREFIX +unset _colcon_prefix_chain_zsh_source_script diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/PKG-INFO b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/PKG-INFO new file mode 100644 index 0000000000000000000000000000000000000000..1772b2b4b87ed715b1c9529109e2a64ffa956ef2 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/PKG-INFO @@ -0,0 +1,12 @@ +Metadata-Version: 2.1 +Name: sphero-mini-controller +Version: 0.0.0 +Summary: TODO: Package description +Home-page: UNKNOWN +Maintainer: ubuntu +Maintainer-email: ubuntu@todo.todo +License: TODO: License declaration +Platform: UNKNOWN + +UNKNOWN + diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/SOURCES.txt b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/SOURCES.txt new file mode 100644 index 0000000000000000000000000000000000000000..450bb514f818ddd322e445deee994a8cbb2136e8 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/SOURCES.txt @@ -0,0 +1,20 @@ +package.xml +setup.cfg +setup.py +../../build/sphero_mini_controller/sphero_mini_controller.egg-info/PKG-INFO +../../build/sphero_mini_controller/sphero_mini_controller.egg-info/SOURCES.txt +../../build/sphero_mini_controller/sphero_mini_controller.egg-info/dependency_links.txt +../../build/sphero_mini_controller/sphero_mini_controller.egg-info/entry_points.txt +../../build/sphero_mini_controller/sphero_mini_controller.egg-info/requires.txt +../../build/sphero_mini_controller/sphero_mini_controller.egg-info/top_level.txt +../../build/sphero_mini_controller/sphero_mini_controller.egg-info/zip-safe +resource/sphero_mini_controller +sphero_mini_controller/WavefrontPlanner.py +sphero_mini_controller/__init__.py +sphero_mini_controller/_constants.py +sphero_mini_controller/blobErkennung.py +sphero_mini_controller/my_first_node.py +sphero_mini_controller/treiber.py +test/test_copyright.py +test/test_flake8.py +test/test_pep257.py \ No newline at end of file diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/dependency_links.txt b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/dependency_links.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/entry_points.txt b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/entry_points.txt new file mode 100644 index 0000000000000000000000000000000000000000..6a40ae61982b9df335a55fd38c98ba80a816cb93 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +sphero_mini = sphero_mini_controller.my_first_node:main + diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/requires.txt b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/requires.txt new file mode 100644 index 0000000000000000000000000000000000000000..49fe098d9e6bccd89482b34510da60ab28556880 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/requires.txt @@ -0,0 +1 @@ +setuptools diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/top_level.txt b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..60845dab3412fa47e2fb49f683a3db10ce25c01e --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/top_level.txt @@ -0,0 +1 @@ +sphero_mini_controller diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/zip-safe b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/zip-safe new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info/zip-safe @@ -0,0 +1 @@ + diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/GameWavefront.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/GameWavefront.py new file mode 100644 index 0000000000000000000000000000000000000000..aae934fa4259ad8bf8e9b6554fc34074aadac6ed --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/GameWavefront.py @@ -0,0 +1,375 @@ +# Wavefront Planning +# Sajad Saeedi, Andrew Davison 2017 +# Implementation is based on the following reference +# +# H. Choset, K. M. Lynch, S. Hutchinson, G. Kantor, W. Burgard, L. E. Kavraki and S. Thrun, +# Principles of Robot Motion: Theory, Algorithms, and Implementations, +# MIT Press, Boston, 2005. +# http://www.cs.cmu.edu/afs/cs/Web/People/motionplanning/ +# +# From Chapter 4.5 - Wave-Front Planner +# This planner determines a path via gradient descent on the grid starting from the start. +# Essentially, the planner determines the path one pixel at a time. +# The wave-front planner essentially forms a potential function on the grid which has one local minimum and thus is resolution complete. +# The planner also determines the shortest path, but at the cost of coming dangerously close to obstacles. +# The major drawback of this method is that the planner has to search the entire space for a path + +import pygame, os, math, time, random +import numpy as np +try: + import pygame + from pygame import surfarray + from pygame.locals import * +except ImportError: + raise ImportError('Error Importing Pygame/surfarray') + +pygame.init() + +SCALE = 1 + +# set the width and height of the screen +WIDTH = 1500/SCALE +HEIGHT = 1000/SCALE + +# The region we will fill with obstacles +PLAYFIELDCORNERS = (-3.0, -3.0, 3.0, 3.0) + +# Barrier locations +barriers = [] +# barrier contents are (bx, by, visibilitymask) +for i in range(100): + (bx, by) = (random.uniform(PLAYFIELDCORNERS[0], PLAYFIELDCORNERS[2]), random.uniform(PLAYFIELDCORNERS[1], PLAYFIELDCORNERS[3])) + barrier = [bx, by, 0] + barriers.append(barrier) + +BARRIERRADIUS = 0.1 + + +ROBOTRADIUS = 0.15 +W = 2 * ROBOTRADIUS +SAFEDIST = 0.2 +BARRIERINFILATE = ROBOTRADIUS + +MAXVELOCITY = 0.5 #ms^(-1) max speed of each wheel +MAXACCELERATION = 0.5 #ms^(-2) max rate we can change speed of each wheel + +target = (PLAYFIELDCORNERS[2] + 1.0, 0) + +k = 160/SCALE # pixels per metre for graphics +u0 = WIDTH / 2 +v0 = HEIGHT / 2 + +x = PLAYFIELDCORNERS[0] - 0.5 +y = 0.0 +theta = 0.0 + +vL = 0.00 +vR = 0.00 + +size = [int(WIDTH), int(HEIGHT)] +screen = pygame.display.set_mode(size) +black = (20,20,40) + +# This makes the normal mouse pointer invisible in graphics window +pygame.mouse.set_visible(0) + +# time delta +dt = 0.1 + + +def surfdemo_show(array_img, name): + "displays a surface, waits for user to continue" + screen = pygame.display.set_mode(array_img.shape[:2], 0, 32) + surfarray.blit_array(screen, array_img) + pygame.display.flip() + pygame.display.set_caption(name) + while 1: + e = pygame.event.wait() + if e.type == MOUSEBUTTONDOWN: break + if e.type == KEYDOWN: break + + +def predictPosition(vL, vR, x, y, theta, dt): + + # Position update in time dt + + # Special cases + # Straight line motion + if (vL == vR): + xnew = x + vL * dt * math.cos(theta) + ynew = y + vL * dt * math.sin(theta) + thetanew = theta + # Pure rotation motion + elif (vL == -vR): + xnew = x + ynew = y + thetanew = theta + ((vR - vL) * dt / W) + else: + # Rotation and arc angle of general circular motion + R = W / 2.0 * (vR + vL) / (vR - vL) + deltatheta = (vR - vL) * dt / W + xnew = x + R * (math.sin(deltatheta + theta) - math.sin(theta)) + ynew = y - R * (math.cos(deltatheta + theta) - math.cos(theta)) + thetanew = theta + deltatheta + + return (xnew, ynew, thetanew) + + +def drawBarriers(barriers): + for barrier in barriers: + if(barrier[2] == 0): + pygame.draw.circle(screen, (0,20,80), (int(u0 + k * barrier[0]), int(v0 - k * barrier[1])), int(k * BARRIERRADIUS), 0) + else: + pygame.draw.circle(screen, (0,120,255), (int(u0 + k * barrier[0]), int(v0 - k * barrier[1])), int(k * BARRIERRADIUS), 0) + return + +def dialtebarrieres(imgin, barriers): + imgout = imgin + for barrier in barriers: + if(barrier[2] == 0): + center_u = int(u0 + k * barrier[0]) + center_v = int(v0 - k * barrier[1]) + RAD = BARRIERRADIUS+BARRIERINFILATE + radius = int(k * (RAD)) + radius2 = int(k * (BARRIERRADIUS)) + points = [(x,y) for x in range(center_u-radius, center_u+radius) for y in range(center_v-radius, center_v+radius)] + for pt in points: + vu = center_u - pt[0] + vv = center_v - pt[1] + distance = math.sqrt(vv*vv + vu*vu) + if (distance < radius and distance > radius2 and pt[0] < WIDTH-1 and pt[1] < HEIGHT-1): + imgout[pt[0],pt[1],0] = 0 + imgout[pt[0],pt[1],1] = 40 + imgout[pt[0],pt[1],2] = 80 + return imgout + +def MakeWaveFront(imgarray, start_uv, target_uv): + print ("building wavefront, please wait ... ") + heap = [] + newheap = [] + + u = target_uv[0] + v = target_uv[1] + + imgarray[:,:,0] = 0 # use channel 0 for planning + imgarray[u, v, 0] = 2 + + lastwave = 3 # start by setting nodes around target to 3 + moves = [(u + 1, v), (u - 1, v), (u, v - 1), (u, v + 1)] + for move in moves: + imgarray[move[0], move[1], 0] = 3 + heap.append(move) + + path = False + max_search = int(np.sqrt(WIDTH*WIDTH + HEIGHT*HEIGHT)) + for currentwave in range(4, max_search): + lastwave = lastwave + 1 + while(heap != []): + position = heap.pop() + (u, v) = position + moves = [(u + 1, v), (u - 1, v), (u, v + 1), (u, v - 1)] + for move in moves: + if(imgarray[move[0], move[1], 2] != 80): + if(imgarray[move[0], move[1], 0] == 0 and imgarray[position[0], position[1], 0] == currentwave - 1 and move[0] < WIDTH-1 and move[1] < HEIGHT-1 and move[0]>2 and move[1]>2): + imgarray[move[0], move[1], 0] = currentwave + newheap.append(move) + if(move == start_uv): + path = True + break + if(path == True): + break + if(path == True): + break + heap = newheap + newheap = [] + return imgarray, currentwave + + +def FindPath(imgarray, start_uv, currentwave): + trajectory = [] + nextpt = start_uv + path = [] + for backwave in range(currentwave-1,2,-1): + path.append(nextpt) + (u,v) = nextpt + values = [] + val = [] + moves = [(u + 1, v), (u - 1, v), (u, v + 1), (u, v - 1), (u + 1, v + 1), (u - 1, v - 1), (u + 1, v - 1), (u - 1, v - 1)] + for move in moves: + val.append(imgarray[move[0], move[1], 0]) + if(imgarray[move[0], move[1], 0] == backwave): + values.append(imgarray[move[0], move[1], 0]) + minimum = min(values) + indices = [i for i, v in enumerate(val) if v == minimum] + nextid = random.choice(indices) + nextpt = moves[nextid] + return path + +def GetControl(x,y,theta, waypoint): + wpu = waypoint[0] + wpv = waypoint[1] + + vt = 0.2 + u = u0 + k * x + v = v0 - k * y + vector = (wpu - u, -wpv + v) + vectorangle = math.atan2(vector[1], vector[0]) + psi = vectorangle - theta + + if(abs(psi) > (2*3.14/180)): + if(psi > 0): + vR = vt/2 + vL = -vt/2 + else: + vR = -vt/2 + vL = vt/2 + else: + wlx = x - (W/2.0) * math.sin(theta) + wly = y + (W/2.0) * math.cos(theta) + ulx = u0 + k * wlx + vlx = v0 - k * wly + dl = math.sqrt((ulx-wpu)*(ulx-wpu) + (vlx-wpv)*(vlx-wpv)) + + wrx = x + (W/2.0) * math.sin(theta) + wry = y - (W/2.0) * math.cos(theta) + urx = u0 + k * wrx + vrx = v0 - k * wry + dr = math.sqrt((urx-wpu)*(urx-wpu) + (vrx-wpv)*(vrx-wpv)) + + vR = 1*(2*vt)/(1+(dl/dr)) + vL = 1*(2*vt - vR) + + return vL, vR + +def AnimateRobot(x,y,theta): + # Draw robot + u = u0 + k * x + v = v0 - k * y + pygame.draw.circle(screen, (255,255,255), (int(u), int(v)), int(k * ROBOTRADIUS), 3) + # Draw wheels + # left wheel centre + wlx = x - (W/2.0) * math.sin(theta) + wly = y + (W/2.0) * math.cos(theta) + ulx = u0 + k * wlx + vlx = v0 - k * wly + WHEELBLOB = 0.04 + pygame.draw.circle(screen, (0,0,255), (int(ulx), int(vlx)), int(k * WHEELBLOB)) + # right wheel centre + wrx = x + (W/2.0) * math.sin(theta) + wry = y - (W/2.0) * math.cos(theta) + urx = u0 + k * wrx + vrx = v0 - k * wry + pygame.draw.circle(screen, (0,0,255), (int(urx), int(vrx)), int(k * WHEELBLOB)) + + time.sleep(dt / 5) + pygame.display.flip() + #time.sleep(0.2) + +def AnimateNavigation(barriers, waypint, path): + screen.fill(black) + drawBarriers(barriers) + + for pt in path: + screen.set_at(pt, (255,255,255)) + + pygame.draw.circle(screen, (255,100,0), target_uv, int(k * ROBOTRADIUS), 0) + pygame.draw.circle(screen, (255,100,0), (int(u0 + k * target[0]), int(v0 - k * target[1])), int(k * ROBOTRADIUS), 0) + pygame.draw.circle(screen, (255,0,255), (int(waypint[0]), int(waypint[1])), int(5)) + +def AnimatePath(imgarray, path, start_uv): + for pt in path: + imgarray[pt[0], pt[1], 0] = 0 + imgarray[pt[0], pt[1], 1] = 255 + imgarray[pt[0], pt[1], 2] = 255 + + #imgarray[:,:,0] /= imgarray[:,:,0].max()/255.0 + scalefactor = imgarray[:,:,0].max()/255.0 + imgarray[:,:,0] = imgarray[:,:,0]/scalefactor + imgarray[:,:,0].astype(int) + imgarray[start_uv[0], start_uv[1], 0] = 0 + imgarray[start_uv[0], start_uv[1], 1] = 255 + imgarray[start_uv[0], start_uv[1], 2] = 255 + + surfdemo_show(imgarray, 'Wavefront Path Planning') + + +def GetWaypoint(x,y,theta, path, waypointIndex, waypointSeperation, target): + reset = False + waypoint = path[waypointIndex] + u = u0 + k * x + v = v0 - k * y + distance_to_wp = math.sqrt((waypoint[0]-u)**2+(waypoint[1]-v)**2) # todo compare distance in metrics + if(distance_to_wp < 3): + waypointIndex = waypointSeperation + waypointIndex + if(waypointIndex > len(path)): + waypointIndex = len(path) - 1 + + return waypoint, reset, waypointIndex + +def IsAtTarget(x,y,target): + disttotarget = math.sqrt((x - target[0])**2 + (y - target[1])**2) + if (disttotarget < 0.04): + return True + else: + return False + + +while(1): + start_uv = (int(u0 + k * x), int(v0 - k * y)) + target_uv = (int(u0 + k * target[0]), int(v0 - k * target[1])) + print ("start is: ", start_uv) + print ("goal is: ", target_uv) + + # prepare map of the world for plannign + screen.fill(black) + drawBarriers(barriers) + pygame.draw.circle(screen, (255,100,0), target_uv, int(k * ROBOTRADIUS), 0) + pygame.draw.circle(screen, (255,255,0), start_uv, int(k * ROBOTRADIUS), 0) + imgscreen8 = pygame.surfarray.array3d(screen) + imgscreen16 = np.array(imgscreen8, dtype=np.uint16) + drawBarriers(barriers) + imgarray = dialtebarrieres(imgscreen16, barriers) + pygame.display.flip() + pygame.display.set_caption('Wavefront Path Planning') + + # build wavefront, given the map, start point, and target point + imgarray, currentwave = MakeWaveFront(imgarray, start_uv, target_uv) + print ("press a key to start navaigation ... ") + + # find the path using the wavefront + path = FindPath(imgarray, start_uv, currentwave) + + # normlaize and show the path on the map + AnimatePath(imgarray, path, start_uv) + + # set start point + x = PLAYFIELDCORNERS[0] - 0.5 + y = 0 + theta = 0 + waypointSeperation = 40; + waypointIndex = waypointSeperation; + + # loop to navigate the path from start to target + while(1): + # get a waypoint to follow the path + (waypoint, reset, waypointIndex)= GetWaypoint(x,y,theta, path, waypointIndex, waypointSeperation, target) + + # reset the simulation, if robot is at target + if (IsAtTarget(x,y,target)): + target = (target[0], random.uniform(PLAYFIELDCORNERS[1], PLAYFIELDCORNERS[3])) + x = PLAYFIELDCORNERS[0] - 0.5 + y = 0.0 + theta = 0.0 + break + + + # calculate control signals to get to the next waypoint + (vL, vR) = GetControl(x,y,theta, waypoint) + + # Actually now move and update position based on chosen vL and vR + (x, y, theta) = predictPosition(vL, vR, x, y, theta, dt) + + # animate + AnimateNavigation(barriers, waypoint, path) + AnimateRobot(x,y,theta) + \ No newline at end of file diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/WavefrontChatGpt.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/WavefrontChatGpt.py new file mode 100644 index 0000000000000000000000000000000000000000..a358dfa0282ce93f94817eca2941d5fa2e991539 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/WavefrontChatGpt.py @@ -0,0 +1,53 @@ +import numpy as np +import cv2 +import matplotlib.pyplot as plt +import time + +class WaveFrontPlanner: + __wall = 999 + __goal = 1 + __nothing = '000' + __path = 'PATH' + + def __init__(self, mask_list): + if not isinstance(mask_list, np.ndarray): + raise ValueError("Invalid input type. 'mask_list' must be a numpy array.") + self.__mapOfWorld = np.array([['999' if j > 200 else '000' for j in row] for row in mask_list]) + + def minSurroundingNodeValue(self, x, y): + values = [self.__mapOfWorld[x + 1][y], self.__mapOfWorld[x - 1][y], + self.__mapOfWorld[x][y + 1], self.__mapOfWorld[x][y - 1]] + min_value = min(value for value in values if value != self.__nothing) + min_index = values.index(min_value) + 1 + return min_value, min_index + + def waveFrontAlgorithm(self): + goal_coordinates = np.where(self.__mapOfWorld == self.__goal) + goal_x, goal_y = goal_coordinates[0][0], goal_coordinates[1][0] + queue = [(goal_x, goal_y)] + + while queue: + x, y = queue.pop(0) + + if self.__mapOfWorld[x][y] == self.__nothing: + min_value, min_index = self.minSurroundingNodeValue(x, y) + self.__mapOfWorld[x][y] = min_value + 1 + queue.append((x + 1, y)) # Move down + queue.append((x - 1, y)) # Move up + queue.append((x, y + 1)) # Move right + queue.append((x, y - 1)) # Move left + + self.__mapOfWorld[goal_x][goal_y] = self.__goal + + def visualizeMap(self): + plt.imshow(cv2.flip(self.__mapOfWorld, 0), cmap='hot', interpolation='nearest') + plt.colorbar() + plt.show() + +# Beispielverwendung: +mask_list = np.zeros((200, 200)) +mask_list[50:150, 50:150] = 1 + +planner = WaveFrontPlanner(mask_list) +planner.waveFrontAlgorithm() +planner.visualizeMap() diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/WavefrontPlanner.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/WavefrontPlanner.py new file mode 100644 index 0000000000000000000000000000000000000000..13cebc92bac4ee4b8096a05c1e3007b8f8741a69 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/WavefrontPlanner.py @@ -0,0 +1,397 @@ +############################################################################ +# WAVEFRONT ALGORITHM +# Adapted to Python Code By Darin Velarde +# Fri Jan 29 13:56:53 PST 2010 +# from C code from John Palmisano +# (www.societyofrobots.com) +############################################################################ +import cv2 +import numpy as np +import matplotlib.pyplot as plt +import time + +class waveFrontPlanner: + ############################################################################ + # WAVEFRONT ALGORITHM + # Adapted to Python Code By Darin Velarde + # Fri Jan 29 13:56:53 PST 2010 + # from C code from John Palmisano + # (www.societyofrobots.com) + ############################################################################ + + def __init__(self, mapOfWorld, slow=False): + self.__slow = slow + self.__mapOfWorld = mapOfWorld + if str(type(mapOfWorld)).find("numpy") != -1: + #If its a numpy array + self.__height, self.__width = self.__mapOfWorld.shape + else: + self.__height, self.__width = len(self.__mapOfWorld), len(self.__mapOfWorld[0]) + + self.__nothing = 000 + self.__wall = 999 + self.__goal = 1 + self.__path = "PATH" + + self.__finalPath = [] + + #Robot value + self.__robot = 254 + #Robot default Location + self.__robot_x = 0 + self.__robot_y = 0 + + #default goal location + self.__goal_x = 8 + self.__goal_y = 9 + + #temp variables + self.__temp_A = 0 + self.__temp_B = 0 + self.__counter = 0 + self.__steps = 0 #determine how processor intensive the algorithm was + + #when searching for a node with a lower value + self.__minimum_node = 250 + self.__min_node_location = 250 + self.__new_state = 1 + self.__old_state = 1 + self.__reset_min = 250 #above this number is a special (wall or robot) + ########################################################################### + + def setRobotPosition(self, x, y): + """ + Sets the robot's current position + + """ + + self.__robot_x = x + self.__robot_y = y + ########################################################################### + + def setGoalPosition(self, x, y): + """ + Sets the goal position. + + """ + + self.__goal_x = x + self.__goal_y = y + ########################################################################### + + def robotPosition(self): + return (self.__robot_x, self.__robot_y) + ########################################################################### + + def goalPosition(self): + return (self.__goal_x, self.__goal_y) + ########################################################################### + + def run(self, prnt=False): + """ + The entry point for the robot algorithm to use wavefront propagation. + + """ + + path = [] + while self.__mapOfWorld[self.__robot_x][self.__robot_y] != self.__goal: + if self.__steps > 10000: + print ("Cannot find a path.") + return + #find new location to go to + self.__new_state = self.propagateWavefront() + #update location of robot + if self.__new_state == 1: + self.__robot_x -= 1 + if prnt: + print("Move to x=%d y=%d\n\n" % (self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + if self.__new_state == 2: + self.__robot_y += 1 + if prnt: + print("Move to x=%d y=%d\n\n" %(self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + if self.__new_state == 3: + self.__robot_x += 1 + if prnt: + print("Move to x=%d y=%d\n\n" %(self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + if self.__new_state == 4: + self.__robot_y -= 1 + if prnt: + print("Move to x=%d y=%d\n\n" %(self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + self.__old_state = self.__new_state + msg = "Found the goal in %i steps:\n" % self.__steps + msg += "mapOfWorld size= %i %i\n\n" % (self.__height, self.__width) + if prnt: + print(msg) + self.printMap() + return path + ########################################################################### + + def propagateWavefront(self, prnt=False): + self.unpropagate() + #Old robot location was deleted, store new robot location in mapOfWorld + self.__mapOfWorld[self.__robot_x][self.__robot_y] = self.__robot + self.__path = self.__robot + #start location to begin scan at goal location + self.__mapOfWorld[self.__goal_x][self.__goal_y] = self.__goal + counter = 0 + while counter < 200: #allows for recycling until robot is found + x = 0 + y = 0 + #time.sleep(0.00001) + #while the mapOfWorld hasnt been fully scanned + while x < self.__height and y < self.__width: + #if this location is a wall or the goal, just ignore it + if self.__mapOfWorld[x][y] != self.__wall and \ + self.__mapOfWorld[x][y] != self.__goal: + #a full trail to the robot has been located, finished! + minLoc = self.minSurroundingNodeValue(x, y) + if minLoc < self.__reset_min and \ + self.__mapOfWorld[x][y] == self.__robot: + if prnt: + print("Finished Wavefront:\n") + self.printMap() + # Tell the robot to move after this return. + return self.__min_node_location + #record a value in to this node + elif self.__minimum_node != self.__reset_min: + #if this isnt here, 'nothing' will go in the location + self.__mapOfWorld[x][y] = self.__minimum_node + 1 + #go to next node and/or row + y += 1 + if y == self.__width and x != self.__height: + x += 1 + y = 0 + #print self.__robot_x, self.__robot_y + if prnt: + print("Sweep #: %i\n" % (counter + 1)) + self.printMap() + self.__steps += 1 + counter += 1 + return 0 + ########################################################################### + + def unpropagate(self): + """ + clears old path to determine new path + stay within boundary + + """ + + for x in range(0, self.__height): + for y in range(0, self.__width): + if self.__mapOfWorld[x][y] != self.__wall and \ + self.__mapOfWorld[x][y] != self.__goal and \ + self.__mapOfWorld[x][y] != self.__path: + #if this location is a wall or goal, just ignore it + self.__mapOfWorld[x][y] = self.__nothing #clear that space + ########################################################################### + + def minSurroundingNodeValue(self, x, y): + """ + this method looks at a node and returns the lowest value around that + node. + + """ + + #reset minimum + self.__minimum_node = self.__reset_min + #down + if x < self.__height -1: + if self.__mapOfWorld[x + 1][y] < self.__minimum_node and \ + self.__mapOfWorld[x + 1][y] != self.__nothing: + #find the lowest number node, and exclude empty nodes (0's) + self.__minimum_node = self.__mapOfWorld[x + 1][y] + self.__min_node_location = 3 + #up + if x > 0: + if self.__mapOfWorld[x-1][y] < self.__minimum_node and \ + self.__mapOfWorld[x-1][y] != self.__nothing: + self.__minimum_node = self.__mapOfWorld[x-1][y] + self.__min_node_location = 1 + #right + if y < self.__width -1: + if self.__mapOfWorld[x][y + 1] < self.__minimum_node and \ + self.__mapOfWorld[x][y + 1] != self.__nothing: + self.__minimum_node = self.__mapOfWorld[x][y + 1] + self.__min_node_location = 2 + #left + if y > 0: + if self.__mapOfWorld[x][y - 1] < self.__minimum_node and \ + self.__mapOfWorld[x][y - 1] != self.__nothing: + self.__minimum_node = self.__mapOfWorld[x][y-1] + self.__min_node_location = 4 + return self.__minimum_node + ########################################################################### + + def printMap(self): + """ + Prints out the map of this instance of the class. + + """ + + msg = '' + for temp_B in range(0, self.__height): + for temp_A in range(0, self.__width): + if self.__mapOfWorld[temp_B][temp_A] == self.__wall: + msg += "%04s" % "[#]" + elif self.__mapOfWorld[temp_B][temp_A] == self.__robot: + msg += "%04s" % "-" + elif self.__mapOfWorld[temp_B][temp_A] == self.__goal: + msg += "%04s" % "G" + else: + msg += "%04s" % str(self.__mapOfWorld[temp_B][temp_A]) + msg += "\n\n" + msg += "\n\n" + print(msg) + # + if self.__slow == True: + time.sleep(0.05) +############################################################################ + +class mapCreate: + ############################################################################ + + def create_map(self, scale,img): + + # Map erstellen + + # Img Objekt + #cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + #success, img = cap.read() + + #Karte von Bild + #succ, img = cv2.imread(2) + print('Original Dimensions : ',img.shape) + + scale_percent = 100-scale # percent of original size + width = int(img.shape[1] * scale_percent / 100) + height = int(img.shape[0] * scale_percent / 100) + dim = (width, height) + + # resize image + resized = cv2.resize(img, dim, interpolation = cv2.INTER_AREA) + print('Resized Dimensions : ',resized.shape) + + gray = cv2.cvtColor(resized, cv2.COLOR_RGB2GRAY) + mask = cv2.inRange(gray, np.array([90]), np.array([255])) + + mapOfWorld = [[0]*gray.shape[1]]*gray.shape[0] + mask_list= np.ndarray.tolist(mask) + + #Markiere alle leeren Zellen mit 000 und alle Zellen mit Hinderniss mit 999 + for i in range(0,mask.shape[0]): + mapOfWorld[i] = ['999' if j > 200 else '000' for j in mask_list[i]] + + mapOfWorld = np.array(mapOfWorld, dtype = int) + mapOfWorldSized = mapOfWorld.copy() + + e = 3 + #Hindernisse Größer machen + for i in range(0,mapOfWorld.shape[0]): + for j in range(0,mapOfWorld.shape[1]): + for r in range(0,e): + try: + if (mapOfWorldSized[i][j] == 999): + mapOfWorld[i][j+e] = 999 + mapOfWorld[i+e][j] = 999 + mapOfWorld[i-e][j] = 999 + mapOfWorld[i][j-e] = 999 + mapOfWorld[i+e][j+e] = 999 + mapOfWorld[i+e][j-e] = 999 + mapOfWorld[i-e][j+e] = 999 + mapOfWorld[i-e][j-e] = 999 + except Exception: + continue + + return mapOfWorld + ############################################################################ + + def scale_koord(self, sx, sy, gx, gy,scale): + scale_percent = 100-scale # percent of original size + + sx = int(sx*scale_percent/100) + sy = int(sy*scale_percent/100) + gx = int(gx*scale_percent/100) + gy = int(gy*scale_percent/100) + + return sx,sy,gx,gy + ############################################################################ + + def rescale_koord(self, path,scale): + scale_percent = 100-scale # percent of original size100 + + path = np.array(path) + + for i in range(len(path)): + path[i] = path[i]*100/scale_percent + + return path + ############################################################################ + + def calc_trans(self, x, y, height): + yTrans = height - y + xTrans = x + + return xTrans, yTrans +############################################################################### + +if __name__ == "__main__": + """ + X is vertical, Y is horizontal + + """ + + Map = mapCreate() #Map Objekt erzeugen + + #Verkleinerungsfaktor in % + scale = 85 + + img = cv2.imread("D:/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/karte.jpg", cv2.COLOR_RGB2GRAY) + #cap = cv2.VideoCapture(2) + mapWorld = Map.create_map(scale, img) + height, width = img.shape[:2] + + #Angaben in Weltkoordinaten + sy = 300 #X-Koordinate im Weltkoordinaten System + sx = 200 #Y-Koordinate im Weltkoordinaten System + + gy = 700 #X-Koordinate im Weltkordinaten System + gx = 240 #Y-Koordinate im Weltkoordinaten System + + sx,sy,gx,gy = Map.scale_koord(sx,sy,gx,gy,scale) #Kordinaten Tauschen X,Y + + start = time.time() + planner = waveFrontPlanner(mapWorld) + planner.setGoalPosition(gx,gy) + planner.setRobotPosition(sx,sy) + path = planner.run(False) + end = time.time() + print("Took %f seconds to run wavefront simulation" % (end - start)) + + path = Map.rescale_koord(path, scale) + #print(path) +#%% Plot + #Programm Koordinaten + imgTrans = cv2.transpose(img) # X und Y-Achse im Bild tauschen + imgPlot, ax1 = plt.subplots() + + ax1.set_title('Programm Koordinaten') + ax1.imshow(imgTrans) + ax1.set_xlabel('[px]') + ax1.set_ylabel('[px]') + ax1.scatter(path[:,0], path[:,1], color='r') + + #Bild Koordinaten + imgPlot2, ax2 = plt.subplots() + ax2.set_title('Bild Koordinaten') + ax2.set_xlabel('[px]') + ax2.set_ylabel('[px]') + ax2.imshow(img) + ax2.scatter(path[:,1], path[:,0], color='r') + +#%% Sphero Pfad + pathWeltX, pathWeltY = Map.calc_trans(path[:,1], path[:,0], height) \ No newline at end of file diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__init__.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/GameWavefront.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/GameWavefront.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6ece39f995948bca62bb749c1b4a8dab486ff1e6 Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/GameWavefront.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/WavefrontChatGpt.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/WavefrontChatGpt.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fa1580cd086f8ded4733863dfce1d64fad32f881 Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/WavefrontChatGpt.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/WavefrontPlanner.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/WavefrontPlanner.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..69ecb8c18f678b45bcd6677f7b077115e6a96516 Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/WavefrontPlanner.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/__init__.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ec9298825be9e809c9848ee863fea5c84f958e7f Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/__init__.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/_constants.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/_constants.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7445d8092b0df51dc16b0e6b2f448a1c3754da6b Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/_constants.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/a_star.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/a_star.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..08bd874229860af63cd5c26676a69f05c506eb88 Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/a_star.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/blobErkennung.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/blobErkennung.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a67c7471a3ce7a50008d13c5d01bf261017add2 Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/blobErkennung.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/core.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/core.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f0d8bf82de3f38133e759720cfe87e1591b9d40 Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/core.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/dynamicPathPlanning.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/dynamicPathPlanning.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..783f7aad7b95b157db735ab8cb5f819656e8d116 Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/dynamicPathPlanning.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/map.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/map.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..620dd53b562bd7e966028c25d4cf45bea0a723f2 Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/map.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/maperstellen.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/maperstellen.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d7213af60b65c5c312c56bbf5e7092116999e2e1 Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/maperstellen.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node copy.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node copy.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ff02a86b8c9265a9299da6733a1150a03c43a61e Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node copy.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..248bb73e7affc7258d67ea37f5b04afb3cfde55b Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node_alt.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node_alt.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..06a366f1ce4b99ca1ddaf00017abd0a8c6868050 Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node_alt.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node_komisch.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node_komisch.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c1e46cdc304f86d99a14af9101ff3d91dc2a4b89 Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node_komisch.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node_refwinkel.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node_refwinkel.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e43bbb000175664ea4762646b70442df7a02e31f Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node_refwinkel.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node_testfahrt.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node_testfahrt.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..76054ad895508d64e1faf9ec5c48338bd50f956e Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/my_first_node_testfahrt.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/pfadplanung.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/pfadplanung.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c01561f4863ce4dd3f66016afa225f0416bf5b5 Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/pfadplanung.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/potential_field_planning (1).cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/potential_field_planning (1).cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7f6164b8357f6cfb2c910cd87d914c0b16dcb959 Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/potential_field_planning (1).cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/potential_field_planning.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/potential_field_planning.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..89654d357ee99d891520683d490f3272debfb4a9 Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/potential_field_planning.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/simple_moves.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/simple_moves.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..650d674336f70da01d14cf5aebb304a70229227c Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/simple_moves.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/sphero.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/sphero.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c18e5dffcef2b5c7adb3e92614aaaa2715e23296 Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/sphero.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/sphero_main.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/sphero_main.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d95a51ee66ceb429dcd3290ef44c9288bc375fdb Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/sphero_main.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/test.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/test.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1652f1531f8f1fa2134712bba5ab5118856c0730 Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/test.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/treiber.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/treiber.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..371445741325a38df010cc651d7b73d397ff1efc Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/treiber.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/wavefront.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/wavefront.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..751df02c9c93c0c2ace4b207f86ae0998a1571c7 Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/wavefront.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/wavefront_coverage_path_planner.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/wavefront_coverage_path_planner.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb64c53ebc91edff78d10e432020a92c12d0dd20 Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/wavefront_coverage_path_planner.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/wavefrontgpt2.cpython-310.pyc b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/wavefrontgpt2.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..693e46d875943c27484df42e4d6294ef7faa6e5f Binary files /dev/null and b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/__pycache__/wavefrontgpt2.cpython-310.pyc differ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/_constants.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/_constants.py new file mode 100644 index 0000000000000000000000000000000000000000..fa8a0e875eed9f484945c1eeaf2fda3fd98503a4 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/_constants.py @@ -0,0 +1,72 @@ +''' +Known Peripheral UUIDs, obtained by querying using the Bluepy module: +===================================================================== +Anti DOS Characteristic <00020005-574f-4f20-5370-6865726f2121> +Battery Level Characteristic <Battery Level> +Peripheral Preferred Connection Parameters Characteristic <Peripheral Preferred Connection Parameters> +API V2 Characteristic <00010002-574f-4f20-5370-6865726f2121> +DFU Control Characteristic <00020002-574f-4f20-5370-6865726f2121> +Name Characteristic <Device Name> +Appearance Characteristic <Appearance> +DFU Info Characteristic <00020004-574f-4f20-5370-6865726f2121> +Service Changed Characteristic <Service Changed> +Unknown1 Characteristic <00020003-574f-4f20-5370-6865726f2121> +Unknown2 Characteristic <00010003-574f-4f20-5370-6865726f2121> + +The rest of the values saved in the dictionaries below, were borrowed from +@igbopie's javacript library, which is available at https://github.com/igbopie/spherov2.js + +''' + +deviceID = {"apiProcessor": 0x10, # 16 + "systemInfo": 0x11, # 17 + "powerInfo": 0x13, # 19 + "driving": 0x16, # 22 + "animatronics": 0x17, # 23 + "sensor": 0x18, # 24 + "something": 0x19, # 25 + "userIO": 0x1a, # 26 + "somethingAPI": 0x1f} # 31 + +SystemInfoCommands = {"mainApplicationVersion": 0x00, # 00 + "bootloaderVersion": 0x01, # 01 + "something": 0x06, # 06 + "something2": 0x13, # 19 + "something6": 0x12, # 18 + "something7": 0x28} # 40 + +sendPacketConstants = {"StartOfPacket": 0x8d, # 141 + "EndOfPacket": 0xd8} # 216 + +userIOCommandIDs = {"allLEDs": 0x0e} # 14 + +flags= {"isResponse": 0x01, # 0x01 + "requestsResponse": 0x02, # 0x02 + "requestsOnlyErrorResponse": 0x04, # 0x04 + "resetsInactivityTimeout": 0x08} # 0x08 + +powerCommandIDs={"deepSleep": 0x00, # 0 + "sleep": 0x01, # 01 + "batteryVoltage": 0x03, # 03 + "wake": 0x0D, # 13 + "something": 0x05, # 05 + "something2": 0x10, # 16 + "something3": 0x04, # 04 + "something4": 0x1E} # 30 + +drivingCommands={"rawMotor": 0x01, # 1 + "resetHeading": 0x06, # 6 + "driveAsSphero": 0x04, # 4 + "driveAsRc": 0x02, # 2 + "driveWithHeading": 0x07, # 7 + "stabilization": 0x0C} # 12 + +sensorCommands={'sensorMask': 0x00, # 00 + 'sensorResponse': 0x02, # 02 + 'configureCollision': 0x11, # 17 + 'collisionDetectedAsync': 0x12, # 18 + 'resetLocator': 0x13, # 19 + 'enableCollisionAsync': 0x14, # 20 + 'sensor1': 0x0F, # 15 + 'sensor2': 0x17, # 23 + 'configureSensorStream': 0x0C} # 12 diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/a_star.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/a_star.py new file mode 100644 index 0000000000000000000000000000000000000000..6d203504327dd356614b08d9d8f169f63aa2ad55 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/a_star.py @@ -0,0 +1,282 @@ +""" + +A* grid planning + +author: Atsushi Sakai(@Atsushi_twi) + Nikos Kanargias (nkana@tee.gr) + +See Wikipedia article (https://en.wikipedia.org/wiki/A*_search_algorithm) + +""" + +import math + +import matplotlib.pyplot as plt + +show_animation = True + + +class AStarPlanner: + + def __init__(self, ox, oy, resolution, rr): + """ + Initialize grid map for a star planning + + ox: x position list of Obstacles [m] + oy: y position list of Obstacles [m] + resolution: grid resolution [m] + rr: robot radius[m] + """ + + self.resolution = resolution + self.rr = rr + self.min_x, self.min_y = 0, 0 + self.max_x, self.max_y = 0, 0 + self.obstacle_map = None + self.x_width, self.y_width = 0, 0 + self.motion = self.get_motion_model() + self.calc_obstacle_map(ox, oy) + + class Node: + def __init__(self, x, y, cost, parent_index): + self.x = x # index of grid + self.y = y # index of grid + self.cost = cost + self.parent_index = parent_index + + def __str__(self): + return str(self.x) + "," + str(self.y) + "," + str( + self.cost) + "," + str(self.parent_index) + + def planning(self, sx, sy, gx, gy): + """ + A star path search + + input: + s_x: start x position [m] + s_y: start y position [m] + gx: goal x position [m] + gy: goal y position [m] + + output: + rx: x position list of the final path + ry: y position list of the final path + """ + + start_node = self.Node(self.calc_xy_index(sx, self.min_x), + self.calc_xy_index(sy, self.min_y), 0.0, -1) + goal_node = self.Node(self.calc_xy_index(gx, self.min_x), + self.calc_xy_index(gy, self.min_y), 0.0, -1) + + open_set, closed_set = dict(), dict() + open_set[self.calc_grid_index(start_node)] = start_node + + while True: + if len(open_set) == 0: + print("Open set is empty..") + break + + c_id = min( + open_set, + key=lambda o: open_set[o].cost + self.calc_heuristic(goal_node, + open_set[ + o])) + current = open_set[c_id] + + # show graph + if show_animation: # pragma: no cover + plt.plot(self.calc_grid_position(current.x, self.min_x), + self.calc_grid_position(current.y, self.min_y), "xc") + # for stopping simulation with the esc key. + plt.gcf().canvas.mpl_connect('key_release_event', + lambda event: [exit( + 0) if event.key == 'escape' else None]) + if len(closed_set.keys()) % 10 == 0: + plt.pause(0.001) + + if current.x == goal_node.x and current.y == goal_node.y: + print("Find goal") + goal_node.parent_index = current.parent_index + goal_node.cost = current.cost + break + + # Remove the item from the open set + del open_set[c_id] + + # Add it to the closed set + closed_set[c_id] = current + + # expand_grid search grid based on motion model + for i, _ in enumerate(self.motion): + node = self.Node(current.x + self.motion[i][0], + current.y + self.motion[i][1], + current.cost + self.motion[i][2], c_id) + n_id = self.calc_grid_index(node) + + # If the node is not safe, do nothing + if not self.verify_node(node): + continue + + if n_id in closed_set: + continue + + if n_id not in open_set: + open_set[n_id] = node # discovered a new node + else: + if open_set[n_id].cost > node.cost: + # This path is the best until now. record it + open_set[n_id] = node + + rx, ry = self.calc_final_path(goal_node, closed_set) + + return rx, ry + + def calc_final_path(self, goal_node, closed_set): + # generate final course + rx, ry = [self.calc_grid_position(goal_node.x, self.min_x)], [ + self.calc_grid_position(goal_node.y, self.min_y)] + parent_index = goal_node.parent_index + while parent_index != -1: + n = closed_set[parent_index] + rx.append(self.calc_grid_position(n.x, self.min_x)) + ry.append(self.calc_grid_position(n.y, self.min_y)) + parent_index = n.parent_index + + return rx, ry + + @staticmethod + def calc_heuristic(n1, n2): + w = 1.0 # weight of heuristic + d = w * math.hypot(n1.x - n2.x, n1.y - n2.y) + return d + + def calc_grid_position(self, index, min_position): + """ + calc grid position + + :param index: + :param min_position: + :return: + """ + pos = index * self.resolution + min_position + return pos + + def calc_xy_index(self, position, min_pos): + return round((position - min_pos) / self.resolution) + + def calc_grid_index(self, node): + return (node.y - self.min_y) * self.x_width + (node.x - self.min_x) + + def verify_node(self, node): + px = self.calc_grid_position(node.x, self.min_x) + py = self.calc_grid_position(node.y, self.min_y) + + if px < self.min_x: + return False + elif py < self.min_y: + return False + elif px >= self.max_x: + return False + elif py >= self.max_y: + return False + + # collision check + if self.obstacle_map[node.x][node.y]: + return False + + return True + + def calc_obstacle_map(self, ox, oy): + + self.min_x = round(min(ox)) + self.min_y = round(min(oy)) + self.max_x = round(max(ox)) + self.max_y = round(max(oy)) + print("min_x:", self.min_x) + print("min_y:", self.min_y) + print("max_x:", self.max_x) + print("max_y:", self.max_y) + + self.x_width = round((self.max_x - self.min_x) / self.resolution) + self.y_width = round((self.max_y - self.min_y) / self.resolution) + print("x_width:", self.x_width) + print("y_width:", self.y_width) + + # obstacle map generation + self.obstacle_map = [[False for _ in range(self.y_width)] + for _ in range(self.x_width)] + for ix in range(self.x_width): + x = self.calc_grid_position(ix, self.min_x) + for iy in range(self.y_width): + y = self.calc_grid_position(iy, self.min_y) + for iox, ioy in zip(ox, oy): + d = math.hypot(iox - x, ioy - y) + if d <= self.rr: + self.obstacle_map[ix][iy] = True + break + + @staticmethod + def get_motion_model(): + # dx, dy, cost + motion = [[1, 0, 1], + [0, 1, 1], + [-1, 0, 1], + [0, -1, 1], + [-1, -1, math.sqrt(2)], + [-1, 1, math.sqrt(2)], + [1, -1, math.sqrt(2)], + [1, 1, math.sqrt(2)]] + + return motion + + +def main(): + print(__file__ + " start!!") + + # start and goal position + sx = 10.0 # [m] + sy = 10.0 # [m] + gx = 50.0 # [m] + gy = 50.0 # [m] + grid_size = 2.0 # [m] + robot_radius = 1.0 # [m] + + # set obstacle positions + ox, oy = [], [] + for i in range(-10, 60): + ox.append(i) + oy.append(-10.0) + for i in range(-10, 60): + ox.append(60.0) + oy.append(i) + for i in range(-10, 61): + ox.append(i) + oy.append(60.0) + for i in range(-10, 61): + ox.append(-10.0) + oy.append(i) + for i in range(-10, 40): + ox.append(20.0) + oy.append(i) + for i in range(0, 40): + ox.append(40.0) + oy.append(60.0 - i) + + if show_animation: # pragma: no cover + plt.plot(ox, oy, ".k") + plt.plot(sx, sy, "og") + plt.plot(gx, gy, "xb") + plt.grid(True) + plt.axis("equal") + + a_star = AStarPlanner(ox, oy, grid_size, robot_radius) + rx, ry = a_star.planning(sx, sy, gx, gy) + + if show_animation: # pragma: no cover + plt.plot(rx, ry, "-r") + plt.pause(0.001) + plt.show() + + +if __name__ == '__main__': + main() diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/blobErkennung.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/blobErkennung.py new file mode 100644 index 0000000000000000000000000000000000000000..20036f56c3bb1f237bb1ec84a06c18afa70b5308 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/blobErkennung.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + +# Standard imports +import cv2 +import numpy as np; + +# Img Objekt +#cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") +cap = cv2.VideoCapture(4) + +# Set up the detector with default parameters. +detector = cv2.SimpleBlobDetector() + +# Setup SimpleBlobDetector parameters. +params = cv2.SimpleBlobDetector_Params() + +# Change thresholds +#params.minThreshold = 5 +#params.maxThreshold = 500 + +#FIlter by Color +params.filterByColor = True +params.blobColor = 255 + +# Filter by Area. +params.filterByArea = True +params.minArea = 20 +params.maxArea = 200 + +# Filter by Circularity +#params.filterByCircularity = True +#params.minCircularity = 0.5 +#params.maxCircularity = 1 + +# Filter by Convexity +#params.filterByConvexity = True +#params.minConvexity = 0.7 +#params.maxConvexity = 1 + +# Filter by Inertia +params.filterByInertia = True +params.minInertiaRatio = 0.5 + +# Create a detector with the parameters +detector = cv2.SimpleBlobDetector_create(params) + +success, img = cap.read() +height, width = img.shape[:2] + +def calc_trans(x,y): + yTrans = height - y + xTrans = x + + return xTrans, yTrans + +while True: + + success, img = cap.read() + gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY ) + + # Detect blobs. + keypoints = detector.detect(gray) + + #print(keypoints) + for keyPoint in keypoints: + x = keyPoint.pt[0] + y = keyPoint.pt[1] + #s = keyPoint.sizekeyPoints + + # Draw detected blobs as red circles. + # cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures + # the size of the circle corresponds to the size of blob + + im_with_keypoints = cv2.drawKeypoints(gray, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) + + # Show blobs + cv2.imshow("Keypoints", im_with_keypoints) + + if cv2.waitKey(10) & 0xFF == ord('q'): + break \ No newline at end of file diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/core.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/core.py new file mode 100644 index 0000000000000000000000000000000000000000..e8fecdcfd3cef71e846e88909c52fd5eb52800da --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/core.py @@ -0,0 +1,638 @@ +from bluepy.btle import Peripheral +from bluepy import btle +from _constants import * +import struct +import time +import sys + +class SpheroMini(): + def __init__(self, MACAddr, verbosity = 4, user_delegate = None): + ''' + initialize class instance and then build collect BLE sevices and characteristics. + Also sends text string to Anti-DOS characteristic to prevent returning to sleep, + and initializes notifications (which is what the sphero uses to send data back to + the client). + ''' + self.verbosity = verbosity # 0 = Silent, + # 1 = Connection/disconnection only + # 2 = Init messages + # 3 = Recieved commands + # 4 = Acknowledgements + self.sequence = 1 + self.v_batt = None # will be updated with battery voltage when sphero.getBatteryVoltage() is called + self.firmware_version = [] # will be updated with firware version when sphero.returnMainApplicationVersion() is called + + if self.verbosity > 0: + print("[INFO] Connecting to %s", MACAddr) + self.p = Peripheral(MACAddr, "random") #connect + + if self.verbosity > 1: + print("[INIT] Initializing") + + # Subscribe to notifications + self.sphero_delegate = MyDelegate(self, user_delegate) # Pass a reference to this instance when initializing + self.p.setDelegate(self.sphero_delegate) + + if self.verbosity > 1: + print("[INIT] Read all characteristics and descriptors") + # Get characteristics and descriptors + self.API_V2_characteristic = self.p.getCharacteristics(uuid="00010002-574f-4f20-5370-6865726f2121")[0] + self.AntiDOS_characteristic = self.p.getCharacteristics(uuid="00020005-574f-4f20-5370-6865726f2121")[0] + self.DFU_characteristic = self.p.getCharacteristics(uuid="00020002-574f-4f20-5370-6865726f2121")[0] + self.DFU2_characteristic = self.p.getCharacteristics(uuid="00020004-574f-4f20-5370-6865726f2121")[0] + self.API_descriptor = self.API_V2_characteristic.getDescriptors(forUUID=0x2902)[0] + self.DFU_descriptor = self.DFU_characteristic.getDescriptors(forUUID=0x2902)[0] + + # The rest of this sequence was observed during bluetooth sniffing: + # Unlock code: prevent the sphero mini from going to sleep again after 10 seconds + if self.verbosity > 1: + print("[INIT] Writing AntiDOS characteristic unlock code") + self.AntiDOS_characteristic.write("usetheforce...band".encode(), withResponse=True) + + # Enable DFU notifications: + if self.verbosity > 1: + print("[INIT] Configuring DFU descriptor") + self.DFU_descriptor.write(struct.pack('<bb', 0x01, 0x00), withResponse = True) + + # No idea what this is for. Possibly a device ID of sorts? Read request returns '00 00 09 00 0c 00 02 02': + if self.verbosity > 1: + print("[INIT] Reading DFU2 characteristic") + _ = self.DFU2_characteristic.read() + + # Enable API notifications: + if self.verbosity > 1: + print("[INIT] Configuring API dectriptor") + self.API_descriptor.write(struct.pack('<bb', 0x01, 0x00), withResponse = True) + + self.wake() + + # Finished initializing: + if self.verbosity > 1: + print("[INIT] Initialization complete\n") + + def disconnect(self): + if self.verbosity > 0: + print("[INFO] Disconnecting") + + self.p.disconnect() + + def wake(self): + ''' + Bring device out of sleep mode (can only be done if device was in sleep, not deep sleep). + If in deep sleep, the device should be connected to USB power to wake. + ''' + if self.verbosity > 2: + print("[SEND {}] Waking".format(self.sequence)) + + self._send(characteristic=self.API_V2_characteristic, + devID=deviceID['powerInfo'], + commID=powerCommandIDs["wake"], + payload=[]) # empty payload + + self.getAcknowledgement("Wake") + + def sleep(self, deepSleep=False): + ''' + Put device to sleep or deep sleep (deep sleep needs USB power connected to wake up) + ''' + if deepSleep: + sleepCommID=powerCommandIDs["deepSleep"] + if self.verbosity > 0: + print("[INFO] Going into deep sleep. Connect USB power to wake.") + else: + sleepCommID=powerCommandIDs["sleep"] + self._send(characteristic=self.API_V2_characteristic, + devID=deviceID['powerInfo'], + commID=sleepCommID, + payload=[]) #empty payload + + def setLEDColor(self, red = None, green = None, blue = None): + ''' + Set device LED color based on RGB vales (each can range between 0 and 0xFF) + ''' + if self.verbosity > 2: + print("[SEND {}] Setting main LED colour to [{}, {}, {}]".format(self.sequence, red, green, blue)) + + self._send(characteristic = self.API_V2_characteristic, + devID = deviceID['userIO'], # 0x1a + commID = userIOCommandIDs["allLEDs"], # 0x0e + payload = [0x00, 0x0e, red, green, blue]) + + self.getAcknowledgement("LED/backlight") + + def setBackLEDIntensity(self, brightness=None): + ''' + Set device LED backlight intensity based on 0-255 values + + NOTE: this is not the same as aiming - it only turns on the LED + ''' + if self.verbosity > 2: + print("[SEND {}] Setting backlight intensity to {}".format(self.sequence, brightness)) + + self._send(characteristic = self.API_V2_characteristic, + devID = deviceID['userIO'], + commID = userIOCommandIDs["allLEDs"], + payload = [0x00, 0x01, brightness]) + + self.getAcknowledgement("LED/backlight") + + def roll(self, speed=None, heading=None): + ''' + Start to move the Sphero at a given direction and speed. + heading: integer from 0 - 360 (degrees) + speed: Integer from 0 - 255 + + Note: the zero heading should be set at startup with the resetHeading method. Otherwise, it may + seem that the sphero doesn't honor the heading argument + ''' + if self.verbosity > 2: + print("[SEND {}] Rolling with speed {} and heading {}".format(self.sequence, speed, heading)) + + if abs(speed) > 255: + print("WARNING: roll speed parameter outside of allowed range (-255 to +255)") + + if speed < 0: + speed = -1*speed+256 # speed values > 256 in the send packet make the spero go in reverse + + speedH = (speed & 0xFF00) >> 8 + speedL = speed & 0xFF + headingH = (heading & 0xFF00) >> 8 + headingL = heading & 0xFF + self._send(characteristic = self.API_V2_characteristic, + devID = deviceID['driving'], + commID = drivingCommands["driveWithHeading"], + payload = [speedL, headingH, headingL, speedH]) + + self.getAcknowledgement("Roll") + + def resetHeading(self): + ''' + Reset the heading zero angle to the current heading (useful during aiming) + Note: in order to manually rotate the sphero, you need to call stabilization(False). + Once the heading has been set, call stabilization(True). + ''' + if self.verbosity > 2: + print("[SEND {}] Resetting heading".format(self.sequence)) + + self._send(characteristic = self.API_V2_characteristic, + devID = deviceID['driving'], + commID = drivingCommands["resetHeading"], + payload = []) #empty payload + + self.getAcknowledgement("Heading") + + def returnMainApplicationVersion(self): + ''' + Sends command to return application data in a notification + ''' + if self.verbosity > 2: + print("[SEND {}] Requesting firmware version".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID = deviceID['systemInfo'], + commID = SystemInfoCommands['mainApplicationVersion'], + payload = []) # empty + + self.getAcknowledgement("Firmware") + + def getBatteryVoltage(self): + ''' + Sends command to return battery voltage data in a notification. + Data printed to console screen by the handleNotifications() method in the MyDelegate class. + ''' + if self.verbosity > 2: + print("[SEND {}] Requesting battery voltage".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID=deviceID['powerInfo'], + commID=powerCommandIDs['batteryVoltage'], + payload=[]) # empty + + self.getAcknowledgement("Battery") + + def stabilization(self, stab = True): + ''' + Sends command to turn on/off the motor stabilization system (required when manually turning/aiming the sphero) + ''' + if stab == True: + if self.verbosity > 2: + print("[SEND {}] Enabling stabilization".format(self.sequence)) + val = 1 + else: + if self.verbosity > 2: + print("[SEND {}] Disabling stabilization".format(self.sequence)) + val = 0 + self._send(self.API_V2_characteristic, + devID=deviceID['driving'], + commID=drivingCommands['stabilization'], + payload=[val]) + + self.getAcknowledgement("Stabilization") + + def wait(self, delay): + ''' + This is a non-blocking delay command. It is similar to time.sleep(), except it allows asynchronous + notification handling to still be performed. + ''' + start = time.time() + while(1): + self.p.waitForNotifications(0.001) + if time.time() - start > delay: + break + + def _send(self, characteristic=None, devID=None, commID=None, payload=[]): + ''' + A generic "send" method, which will be used by other methods to send a command ID, payload and + appropriate checksum to a specified device ID. Mainly useful because payloads are optional, + and can be of varying length, to convert packets to binary, and calculate and send the + checksum. For internal use only. + + Packet structure has the following format (in order): + + - Start byte: always 0x8D + - Flags byte: indicate response required, etc + - Virtual device ID: see _constants.py + - Command ID: see _constants.py + - Sequence number: Seems to be arbitrary. I suspect it is used to match commands to response packets (in which the number is echoed). + - Payload: Could be varying number of bytes (incl. none), depending on the command + - Checksum: See below for calculation + - End byte: always 0xD8 + + ''' + sendBytes = [sendPacketConstants["StartOfPacket"], + sum([flags["resetsInactivityTimeout"], flags["requestsResponse"]]), + devID, + commID, + self.sequence] + payload # concatenate payload list + + self.sequence += 1 # Increment sequence number, ensures we can identify response packets are for this command + if self.sequence > 255: + self.sequence = 0 + + # Compute and append checksum and add EOP byte: + # From Sphero docs: "The [checksum is the] modulo 256 sum of all the bytes + # from the device ID through the end of the data payload, + # bit inverted (1's complement)" + # For the sphero mini, the flag bits must be included too: + checksum = 0 + for num in sendBytes[1:]: + checksum = (checksum + num) & 0xFF # bitwise "and to get modulo 256 sum of appropriate bytes + checksum = 0xff - checksum # bitwise 'not' to invert checksum bits + sendBytes += [checksum, sendPacketConstants["EndOfPacket"]] # concatenate + + # Convert numbers to bytes + output = b"".join([x.to_bytes(1, byteorder='big') for x in sendBytes]) + + #send to specified characteristic: + characteristic.write(output, withResponse = True) + + def getAcknowledgement(self, ack): + #wait up to 10 secs for correct acknowledgement to come in, including sequence number! + start = time.time() + while(1): + self.p.waitForNotifications(1) + if self.sphero_delegate.notification_seq == self.sequence-1: # use one less than sequence, because _send function increments it for next send. + if self.verbosity > 3: + print("[RESP {}] {}".format(self.sequence-1, self.sphero_delegate.notification_ack)) + self.sphero_delegate.clear_notification() + break + elif self.sphero_delegate.notification_seq >= 0: + print("Unexpected ACK. Expected: {}/{}, received: {}/{}".format( + ack, self.sequence, self.sphero_delegate.notification_ack.split()[0], + self.sphero_delegate.notification_seq) + ) + if time.time() > start + 10: + print("Timeout waiting for acknowledgement: {}/{}".format(ack, self.sequence)) + break + +# ======================================================================= +# The following functions are experimental: +# ======================================================================= + + def configureCollisionDetection(self, + xThreshold = 50, + yThreshold = 50, + xSpeed = 50, + ySpeed = 50, + deadTime = 50, # in 10 millisecond increments + method = 0x01, # Must be 0x01 + callback = None): + ''' + Appears to function the same as other Sphero models, however speed settings seem to have no effect. + NOTE: Setting to zero seems to cause bluetooth errors with the Sphero Mini/bluepy library - set to + 255 to make it effectively disabled. + + deadTime disables future collisions for a short period of time to avoid repeat triggering by the same + event. Set in 10ms increments. So if deadTime = 50, that means the delay will be 500ms, or half a second. + + From Sphero docs: + + xThreshold/yThreshold: An 8-bit settable threshold for the X (left/right) and Y (front/back) axes + of Sphero. + + xSpeed/ySpeed: An 8-bit settable speed value for the X and Y axes. This setting is ranged by the + speed, then added to xThreshold, yThreshold to generate the final threshold value. + ''' + + if self.verbosity > 2: + print("[SEND {}] Configuring collision detection".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['configureCollision'], + payload=[method, xThreshold, xSpeed, yThreshold, ySpeed, deadTime]) + + self.collision_detection_callback = callback + + self.getAcknowledgement("Collision") + + def configureSensorStream(self): # Use default values + ''' + Send command to configure sensor stream using default values as found during bluetooth + sniffing of the Sphero Edu app. + + Must be called after calling configureSensorMask() + ''' + bitfield1 = 0b00000000 # Unknown function - needs experimenting + bitfield2 = 0b00000000 # Unknown function - needs experimenting + bitfield3 = 0b00000000 # Unknown function - needs experimenting + bitfield4 = 0b00000000 # Unknown function - needs experimenting + + if self.verbosity > 2: + print("[SEND {}] Configuring sensor stream".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['configureSensorStream'], + payload=[bitfield1, bitfield1, bitfield1, bitfield1]) + + self.getAcknowledgement("Sensor") + + def configureSensorMask(self, + sample_rate_divisor = 0x25, # Must be > 0 + packet_count = 0, + IMU_pitch = False, + IMU_roll = False, + IMU_yaw = False, + IMU_acc_x = False, + IMU_acc_y = False, + IMU_acc_z = False, + IMU_gyro_x = False, + IMU_gyro_y = False, + IMU_gyro_z = False): + + ''' + Send command to configure sensor mask using default values as found during bluetooth + sniffing of the Sphero Edu app. From experimentation, it seems that these are he functions of each: + + Sampling_rate_divisor. Slow data EG: Set to 0x32 to the divide data rate by 50. Setting below 25 (0x19) causes + bluetooth errors + + Packet_count: Select the number of packets to transmit before ending the stream. Set to zero to stream infinitely + + All IMU bool parameters: Toggle transmission of that value on or off (e.g. set IMU_acc_x = True to include the + X-axis accelerometer readings in the sensor stream) + ''' + + # Construct bitfields based on function parameters: + IMU_bitfield1 = (IMU_pitch<<2) + (IMU_roll<<1) + IMU_yaw + IMU_bitfield2 = ((IMU_acc_y<<7) + (IMU_acc_z<<6) + (IMU_acc_x<<5) + \ + (IMU_gyro_y<<4) + (IMU_gyro_x<<2) + (IMU_gyro_z<<2)) + + if self.verbosity > 2: + print("[SEND {}] Configuring sensor mask".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['sensorMask'], + payload=[0x00, # Unknown param - altering it seems to slow data rate. Possibly averages multiple readings? + sample_rate_divisor, + packet_count, # Packet count: select the number of packets to stop streaming after (zero = infinite) + 0b00, # Unknown param: seems to be another accelerometer bitfield? Z-acc, Y-acc + IMU_bitfield1, + IMU_bitfield2, + 0b00]) # reserved, Position?, Position?, velocity?, velocity?, Y-gyro, timer, reserved + + self.getAcknowledgement("Mask") + + ''' + Since the sensor values arrive as unlabelled lists in the order that they appear in the bitfields above, we need + to create a list of sensors that have been configured.Once we have this list, then in the default_delegate class, + we can get sensor values as attributes of the sphero_mini class. + e.g. print(sphero.IMU_yaw) # displays the current yaw angle + ''' + + # Initialize dictionary with sensor names as keys and their bool values (set by the user) as values: + availableSensors = {"IMU_pitch" : IMU_pitch, + "IMU_roll" : IMU_roll, + "IMU_yaw" : IMU_yaw, + "IMU_acc_y" : IMU_acc_y, + "IMU_acc_z" : IMU_acc_z, + "IMU_acc_x" : IMU_acc_x, + "IMU_gyro_y" : IMU_gyro_y, + "IMU_gyro_x" : IMU_gyro_x, + "IMU_gyro_z" : IMU_gyro_z} + + # Create list of of only sensors that have been "activated" (set as true in the method arguments): + self.configured_sensors = [name for name in availableSensors if availableSensors[name] == True] + + def sensor1(self): # Use default values + ''' + Unknown function. Observed in bluetooth sniffing. + ''' + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['sensor1'], + payload=[0x01]) + + self.getAcknowledgement("Sensor1") + + def sensor2(self): # Use default values + ''' + Unknown function. Observed in bluetooth sniffing. + ''' + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['sensor2'], + payload=[0x00]) + + self.getAcknowledgement("Sensor2") + +# ======================================================================= + +class MyDelegate(btle.DefaultDelegate): + + ''' + This class handles notifications (both responses and asynchronous notifications). + + Usage of this class is described in the Bluepy documentation + + ''' + + def __init__(self, sphero_class, user_delegate): + self.sphero_class = sphero_class # for saving sensor values as attributes of sphero class instance + self.user_delegate = user_delegate # to directly notify users of callbacks + btle.DefaultDelegate.__init__(self) + self.clear_notification() + self.notificationPacket = [] + + def clear_notification(self): + self.notification_ack = "DEFAULT ACK" + self.notification_seq = -1 + + def bits_to_num(self, bits): + ''' + This helper function decodes bytes from sensor packets into single precision floats. Encoding follows the + the IEEE-754 standard. + ''' + num = int(bits, 2).to_bytes(len(bits) // 8, byteorder='little') + num = struct.unpack('f', num)[0] + return num + + def handleNotification(self, cHandle, data): + ''' + This method acts as an interrupt service routine. When a notification comes in, this + method is invoked, with the variable 'cHandle' being the handle of the characteristic that + sent the notification, and 'data' being the payload (sent one byte at a time, so the packet + needs to be reconstructed) + + The method keeps appending bytes to the payload packet byte list until end-of-packet byte is + encountered. Note that this is an issue, because 0xD8 could be sent as part of the payload of, + say, the battery voltage notification. In future, a more sophisticated method will be required. + ''' + # Allow the user to intercept and process data first.. + if self.user_delegate != None: + if self.user_delegate.handleNotification(cHandle, data): + return + + print("Received notification with packet %s", str(data)) + + for data_byte in data: # parse each byte separately (sometimes they arrive simultaneously) + + self.notificationPacket.append(data_byte) # Add new byte to packet list + + # If end of packet (need to find a better way to segment the packets): + if data_byte == sendPacketConstants['EndOfPacket']: + # Once full the packet has arrived, parse it: + # Packet structure is similar to the outgoing send packets (see docstring in sphero_mini._send()) + + # Attempt to unpack. Might fail if packet is too badly corrupted + try: + start, flags_bits, devid, commcode, seq, *notification_payload, chsum, end = self.notificationPacket + except ValueError: + print("Warning: notification packet unparseable %s", self.notificationPacket) + self.notificationPacket = [] # Discard this packet + return # exit + + # Compute and append checksum and add EOP byte: + # From Sphero docs: "The [checksum is the] modulo 256 sum of all the bytes + # from the device ID through the end of the data payload, + # bit inverted (1's complement)" + # For the sphero mini, the flag bits must be included too: + checksum_bytes = [flags_bits, devid, commcode, seq] + notification_payload + checksum = 0 # init + for num in checksum_bytes: + checksum = (checksum + num) & 0xFF # bitwise "and to get modulo 256 sum of appropriate bytes + checksum = 0xff - checksum # bitwise 'not' to invert checksum bits + if checksum != chsum: # check computed checksum against that recieved in the packet + print("Warning: notification packet checksum failed - %s", str(self.notificationPacket)) + self.notificationPacket = [] # Discard this packet + return # exit + + # Check if response packet: + if flags_bits & flags['isResponse']: # it is a response + + # Use device ID and command code to determine which command is being acknowledged: + if devid == deviceID['powerInfo'] and commcode == powerCommandIDs['wake']: + self.notification_ack = "Wake acknowledged" # Acknowledgement after wake command + + elif devid == deviceID['driving'] and commcode == drivingCommands['driveWithHeading']: + self.notification_ack = "Roll command acknowledged" + + elif devid == deviceID['driving'] and commcode == drivingCommands['stabilization']: + self.notification_ack = "Stabilization command acknowledged" + + elif devid == deviceID['userIO'] and commcode == userIOCommandIDs['allLEDs']: + self.notification_ack = "LED/backlight color command acknowledged" + + elif devid == deviceID['driving'] and commcode == drivingCommands["resetHeading"]: + self.notification_ack = "Heading reset command acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["configureCollision"]: + self.notification_ack = "Collision detection configuration acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["configureSensorStream"]: + self.notification_ack = "Sensor stream configuration acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["sensorMask"]: + self.notification_ack = "Mask configuration acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["sensor1"]: + self.notification_ack = "Sensor1 acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["sensor2"]: + self.notification_ack = "Sensor2 acknowledged" + + elif devid == deviceID['powerInfo'] and commcode == powerCommandIDs['batteryVoltage']: + V_batt = notification_payload[2] + notification_payload[1]*256 + notification_payload[0]*65536 + V_batt /= 100 # Notification gives V_batt in 10mV increments. Divide by 100 to get to volts. + self.notification_ack = "Battery voltage:" + str(V_batt) + "v" + self.sphero_class.v_batt = V_batt + + elif devid == deviceID['systemInfo'] and commcode == SystemInfoCommands['mainApplicationVersion']: + version = '.'.join(str(x) for x in notification_payload) + self.notification_ack = "Firmware version: " + version + self.sphero_class.firmware_version = notification_payload + + else: + self.notification_ack = "Unknown acknowledgement" #print(self.notificationPacket) + print(self.notificationPacket, "===================> Unknown ack packet") + + self.notification_seq = seq + + else: # Not a response packet - therefore, asynchronous notification (e.g. collision detection, etc): + + # Collision detection: + if devid == deviceID['sensor'] and commcode == sensorCommands['collisionDetectedAsync']: + # The first four bytes are data that is still un-parsed. the remaining unsaved bytes are always zeros + _, _, _, _, _, _, axis, _, Y_mag, _, X_mag, *_ = notification_payload + if axis == 1: + dir = "Left/right" + else: + dir = 'Forward/back' + print("Collision detected:") + print("\tAxis: %s", dir) + print("\tX_mag: %s", X_mag) + print("\tY_mag: %s", Y_mag) + + if self.sphero_class.collision_detection_callback is not None: + self.notificationPacket = [] # need to clear packet, in case new notification comes in during callback + self.sphero_class.collision_detection_callback() + + # Sensor response: + elif devid == deviceID['sensor'] and commcode == sensorCommands['sensorResponse']: + # Convert to binary, pad bytes with leading zeros: + val = '' + for byte in notification_payload: + val += format(int(bin(byte)[2:], 2), '#010b')[2:] + + # Break into 32-bit chunks + nums = [] + while(len(val) > 0): + num, val = val[:32], val[32:] # Slice off first 16 bits + nums.append(num) + + # convert from raw bits to float: + nums = [self.bits_to_num(num) for num in nums] + + # Set sensor values as class attributes: + for name, value in zip(self.sphero_class.configured_sensors, nums): + print("Setting sensor %s at %s.", name, str(value)) + setattr(self.sphero_class, name, value) + + # Unrecognized packet structure: + else: + self.notification_ack = "Unknown asynchronous notification" #print(self.notificationPacket) + print(str(self.notificationPacket) + " ===================> Unknown async packet") + + self.notificationPacket = [] # Start new payload after this byte \ No newline at end of file diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/dynamicPathPlanning.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/dynamicPathPlanning.py new file mode 100644 index 0000000000000000000000000000000000000000..6607c85a415e48eac5483d8ecc329954fbf76051 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/dynamicPathPlanning.py @@ -0,0 +1,504 @@ +import numpy as np +import matplotlib.pyplot as plt +import robotController as rc +import mapGeneration as mg +import random +from controller import Robot + +#======[Scenario Control functions]============================================ + +#This function causes the robot to wait some time steps before starting it's operation +#DO NOT CHANGE THIS FUNCTION OR IT'S CALL IN THE CODE +def waitRandomTimeSteps(turtle, stop=50): + initialPose = np.zeros(7) + initialPose[0:2] = findRobotPosition(turtle) + turtle.setPoseGoal(initialPose) + turtle.drive_goalPose() + nRand = random.randint(1, stop) + for i in range(nRand): + turtle.robot.step(turtle.TIME_STEP) + turtle.update() + +#=========[Example functions for finding objects in the map]=================== + +def findTargetPosition(turtlebot): + mapInd = turtlebot.findInMap_FinalTarget() + if(len(mapInd) != 0): + pos = turtlebot.getPositionFromMapIndex(mapInd[0]) + else: + pos = [] + return np.array(pos, dtype=float).tolist() + +def findRobotPosition(turtlebot): + mapInds = turtlebot.findInMap_Robot() + robotPos = np.zeros((len(mapInds), 2)) + for i in range(len(mapInds)): + pos = turtlebot.getPositionFromMapIndex(mapInds[i]) + robotPos[i, ...] = pos + XYInd = np.average(robotPos, axis=0) + return np.array(XYInd, dtype=float).tolist() + +def findObstaclePositions(turtlebot): + mapInds = turtlebot.findInMap_Obstacles() + obsPos = np.zeros((len(mapInds), 2)) + for i in range(len(mapInds)): + pos = turtlebot.getPositionFromMapIndex(mapInds[i]) + obsPos[i, ...] = pos + return np.array(obsPos, dtype=float).tolist() + +#=========[Framework for own implementations of path tracking and planning]==== + +def checkForPossibleCollisions(turtlebot, turtlePos, collisionCntr): + #Fill this with your collision detection between the planed path and + #moving obstacles + hitBox = 0 + + faktor = 0.1 + + turtlePos1 = [turtlePos[0] + faktor, turtlePos[1] + faktor] + turtlePos2 = [turtlePos[0] + faktor, turtlePos[1] - faktor] + turtlePos3 = [turtlePos[0] - faktor, turtlePos[1] + faktor] + turtlePos4 = [turtlePos[0] - faktor, turtlePos[1] - faktor] + + turtlePos5 = [turtlePos[0] + faktor, turtlePos[1] ] + turtlePos6 = [turtlePos[0] , turtlePos[1] + faktor] + turtlePos7 = [turtlePos[0] - faktor, turtlePos[1] ] + turtlePos8 = [turtlePos[0] , turtlePos[1] - faktor] + + turtlePosBool = bool(turtlebot.isMapAtPosition_Obstacle(turtlePos)) + turtlePos1Bool = bool(turtlebot.isMapAtPosition_Obstacle(turtlePos1)) + turtlePos2Bool = bool(turtlebot.isMapAtPosition_Obstacle(turtlePos2)) + turtlePos3Bool = bool(turtlebot.isMapAtPosition_Obstacle(turtlePos3)) + turtlePos4Bool = bool(turtlebot.isMapAtPosition_Obstacle(turtlePos4)) + turtlePos5Bool = bool(turtlebot.isMapAtPosition_Obstacle(turtlePos5)) + turtlePos6Bool = bool(turtlebot.isMapAtPosition_Obstacle(turtlePos6)) + turtlePos7Bool = bool(turtlebot.isMapAtPosition_Obstacle(turtlePos7)) + turtlePos8Bool = bool(turtlebot.isMapAtPosition_Obstacle(turtlePos8)) + + hitBox = turtlePosBool | turtlePos1Bool | turtlePos2Bool | turtlePos3Bool | turtlePos4Bool | turtlePos5Bool | turtlePos6Bool | turtlePos7Bool | turtlePos8Bool + + if(hitBox): + collisionCntr += 1 + return True + + if(~hitBox): + return False + +def staticPathTracking(turtlebot, path, indCntr): + #This delta value determines the precision of the position control during path tracking + #A high value leads towards a larger curve radius at corners in the path, + # which might result in a smoothing of the path and keeping the velocity high. + #A lower value sets a higher precision for the precision control and thus, + # the robot might slow down when reaching a path point, but it stays very + # close to the original path + delta = 0.05 + if(indCntr == (len(path)-1)): + delta = 0.01 + #Control the turtlebot to drive towards the target position + isNearGoal = turtlebot.drive_goalPose(delta=delta) + #If the robot is close to the goal select the next goal position + if (isNearGoal): + indCntr += 1 + i = min(indCntr, len(path)) + if i == len(path): + indCntr = -1 + else: + #print("Aktuelle Position Pfad: ", path[i]) + turtlebot.setPoseGoal(path[i]) + return indCntr + +def planInitialPath(turtle): + + initialObstacles = findObstaclePositions(turtle) + initialPose = findRobotPosition(turtle) + initialFinalTarget = findTargetPosition(turtle) + + plt.scatter(np.array(initialObstacles)[..., 0], np.array(initialObstacles)[..., 1]) + plt.scatter(np.array(initialPose)[..., 0], np.array(initialPose)[..., 1] ) + plt.scatter(np.array(initialFinalTarget)[..., 0], np.array(initialFinalTarget)[..., 1]) + plt.show() + plt.pause(0.001) + + from matplotlib import pyplot as ppl + from matplotlib import cm + import random, sys, math, os.path + from matplotlib.pyplot import imread + + #Implementation of RRT + + MAP_IMG = './karte.png' #Black and white image for a map + MIN_NUM_VERT = 20 # Minimum number of vertex in the graph + MAX_NUM_VERT = 1500 # Maximum number of vertex in the graph + STEP_DISTANCE = 20 # Maximum distance between two vertex + SEED = None # For random numbers + + def rapidlyExploringRandomTree(ax, img, start, goal, seed=None): + hundreds = 100 + seed = random.seed(seed) + #print("Zufallsseed: ", seed) + points = [] + graph = [] + points.append(start) + graph.append((start, [])) + print('Generating and conecting random points') + occupied = True + phaseTwo = False + + # Phase two values (points 5 step distances around the goal point) + minX = max(goal[0] - 5 * STEP_DISTANCE, 0) + maxX = min(goal[0] + 5 * STEP_DISTANCE, len(img[0]) - 1) + minY = max(goal[1] - 5 * STEP_DISTANCE, 0) + maxY = min(goal[1] + 5 * STEP_DISTANCE, len(img) - 1) + + i = 0 + while (goal not in points) and (len(points) < MAX_NUM_VERT): + if (i % 100) == 0: + print(i, 'points randomly generated') + + if (len(points) % hundreds) == 0: + print(len(points), 'vertex generated') + hundreds = hundreds + 100 + + while (occupied): + if phaseTwo and (random.random() > 0.8): + point = [random.randint(minX, maxX), random.randint(minY, maxY)] + else: + point = [random.randint(0, len(img[0]) - 1), random.randint(0, len(img) - 1)] + + if (img[point[1]][point[0]][0] * 255 == 255): + occupied = False + + occupied = True + + nearest = findNearestPoint(points, point) + newPoints = connectPoints(point, nearest, img) + addToGraph(ax, graph, newPoints, point) + newPoints.pop(0) # The first element is already in the points list + points.extend(newPoints) + ppl.draw() + i = i + 1 + + if len(points) >= MIN_NUM_VERT: + if not phaseTwo: + print('Phase Two') + phaseTwo = True + + if phaseTwo: + nearest = findNearestPoint(points, goal) + newPoints = connectPoints(goal, nearest, img) + addToGraph(ax, graph, newPoints, goal) + newPoints.pop(0) + points.extend(newPoints) + ppl.draw() + + if goal in points: + print('Goal found, total vertex in graph:', len(points), 'total random points generated:', i) + + path = searchPath(graph, start, [start]) + + for i in range(len(path) - 1): + ax.plot([path[i][0], path[i + 1][0]], [path[i][1], path[i + 1][1]], color='g', linestyle='-', + linewidth=2) + ppl.draw() + + print('Showing resulting map') + print('Final path:', path) + print('The final path is made from:', len(path), 'connected points') + else: + path = None + print('Reached maximum number of vertex and goal was not found') + print('Total vertex in graph:', len(points), 'total random points generated:', i) + print('Showing resulting map') + + ppl.show() + return path + + def searchPath(graph, point, path): + for i in graph: + if point == i[0]: + p = i + + if p[0] == graph[-1][0]: + return path + + for link in p[1]: + path.append(link) + finalPath = searchPath(graph, link, path) + + if finalPath != None: + return finalPath + else: + path.pop() + + def addToGraph(ax, graph, newPoints, point): + if len(newPoints) > 1: # If there is anything to add to the graph + for p in range(len(newPoints) - 1): + nearest = [nearest for nearest in graph if (nearest[0] == [newPoints[p][0], newPoints[p][1]])] + nearest[0][1].append(newPoints[p + 1]) + graph.append((newPoints[p + 1], [])) + + if not p == 0: + ax.plot(newPoints[p][0], newPoints[p][1], '+k') # First point is already painted + ax.plot([newPoints[p][0], newPoints[p + 1][0]], [newPoints[p][1], newPoints[p + 1][1]], color='k', + linestyle='-', linewidth=1) + + if point in newPoints: + ax.plot(point[0], point[1], '.g') # Last point is green + else: + ax.plot(newPoints[p + 1][0], newPoints[p + 1][1], '+k') # Last point is not green + + def connectPoints(a, b, img): + newPoints = [] + newPoints.append([b[0], b[1]]) + step = [(a[0] - b[0]) / float(STEP_DISTANCE), (a[1] - b[1]) / float(STEP_DISTANCE)] + + # Set small steps to check for walls + pointsNeeded = int(math.floor(max(math.fabs(step[0]), math.fabs(step[1])))) + + if math.fabs(step[0]) > math.fabs(step[1]): + if step[0] >= 0: + step = [1, step[1] / math.fabs(step[0])] + else: + step = [-1, step[1] / math.fabs(step[0])] + + else: + if step[1] >= 0: + step = [step[0] / math.fabs(step[1]), 1] + else: + step = [step[0] / math.fabs(step[1]), -1] + + blocked = False + for i in range(pointsNeeded + 1): # Creates points between graph and solitary point + for j in range(STEP_DISTANCE): # Check if there are walls between points + coordX = round(newPoints[i][0] + step[0] * j) + coordY = round(newPoints[i][1] + step[1] * j) + + if coordX == a[0] and coordY == a[1]: + break + if coordY >= len(img) or coordX >= len(img[0]): + break + if img[int(coordY)][int(coordX)][0] * 255 < 255: + blocked = True + if blocked: + break + + if blocked: + break + if not (coordX == a[0] and coordY == a[1]): + newPoints.append( + [newPoints[i][0] + (step[0] * STEP_DISTANCE), newPoints[i][1] + (step[1] * STEP_DISTANCE)]) + + if not blocked: + newPoints.append([a[0], a[1]]) + return newPoints + + def findNearestPoint(points, point): + best = (sys.maxsize, sys.maxsize, sys.maxsize) + for p in points: + if p == point: + continue + dist = math.sqrt((p[0] - point[0]) ** 2 + (p[1] - point[1]) ** 2) + if dist < best[2]: + best = (p[0], p[1], dist) + return (best[0], best[1]) + + karte = np.ones([80, 80, 3], dtype=int) + + # Koordinatentransformation + initialObstacles = [[int(x[0] * 10), int(x[1] * 10)] for x in initialObstacles] + initialPose = [initialPose[0] * 10, initialPose[1] * 10] + initialFinalTarget = [initialFinalTarget[0] * 10, initialFinalTarget[1] * 10] + + initialObstacles = [[int(x[0] + 40), int(x[1] - 40)] for x in initialObstacles] + initialPose = [initialPose[0] + 40, initialPose[1] - 40] + initialFinalTarget = [initialFinalTarget[0] + 40, initialFinalTarget[1] - 40] + + initialObstacles = [[int(x[0]), int(x[1] * (-1))] for x in initialObstacles] + initialPose = [int(initialPose[0]), int(initialPose[1] * -1)] + initialFinalTarget = [int(initialFinalTarget[0]), int(initialFinalTarget[1] * -1)] + + # Karte Hindernisse einzeichnen + rot = np.array(0, dtype=int) + gruen = np.array(0, dtype=int) + blau = np.array(0, dtype=int) + + for k in initialObstacles: + karte[k[1], k[0], 0] = rot; + karte[k[1], k[0], 1] = gruen; + karte[k[1], k[0], 2] = blau; + + karte[k[1] + 1, k[0] + 0, 0] = rot; + karte[k[1] + 1, k[0] + 0, 1] = gruen; + karte[k[1] + 1, k[0] + 0, 2] = blau; + + karte[k[1] - 0, k[0] + 1, 0] = rot; + karte[k[1] - 0, k[0] + 1, 1] = gruen; + karte[k[1] - 0, k[0] + 1, 2] = blau; + + karte[k[1] + 0, k[0] - 1, 0] = rot; + karte[k[1] + 0, k[0] - 1, 1] = gruen; + karte[k[1] + 0, k[0] - 1, 2] = blau; + + karte[k[1] - 1, k[0] - 0, 0] = rot; + karte[k[1] - 1, k[0] - 0, 1] = gruen; + karte[k[1] - 1, k[0] - 0, 2] = blau; + + #Plotten als PNG speichern + from PIL import Image + im = Image.fromarray((karte * 255).astype(np.uint8)) + im.save('karte.png') + + print('Loading map... with file \'', MAP_IMG, '\'') + img = imread(MAP_IMG) + fig = ppl.gcf() + fig.clf() + ax = fig.add_subplot(1, 1, 1) + ax.imshow(img, cmap=cm.Greys_r) + ax.axis('image') + ppl.draw() + print('Map is', len(img[0]), 'x', len(img)) + start = initialPose + goal = initialFinalTarget + + path = rapidlyExploringRandomTree(ax, img, start, goal, seed=SEED) + + # Ruecktransformation + path1 = [[x[0], x[1] * (-1)] for x in path] + path2 = [[(x[0] - 40), (x[1] + 40)] for x in path1] + path3 = [[(x[0] / 10), (x[1] / 10)] for x in path2] + + path3 = [[(x[0]), x[1], 0, 0, 0, 0, 0] for x in path3] + + print("Umgerechneter Pfad", path3) + + if len(sys.argv) > 2: + print('Only one argument is needed') + elif len(sys.argv) > 1: + if os.path.isfile(sys.argv[1]): + MAP_IMG = sys.argv[1] + else: + print(sys.argv[1], 'is not a file') + + pathIndCntr = 0 + + return path3, pathIndCntr + +#=============[Evaluation functions]=========================================== +collisionCntr = 0 +recalcCntr = 0 +def updateEval_RobotCollisions(detector): + global collisionCntr + collision = np.nan_to_num(detector.getValue(), nan=0) + collisionCntr += int(collision) + return bool(collision) + +#======================================[MAIN Execution]======================== +if __name__ == "__main__": + plotting = False + nPlot = 80 + festgefahren = 0 + #Only increase this value if the simulation is running slowly + TIME_STEP = 64 + + #%%=============[DO NOT CHANGE THIS PART OF THE SCRIPT]================== + loopCntr = 0 # + turtle = rc.turtlebotController(TIME_STEP, + poseGoal_tcs=np.array([-0.863, -2.0, 0.0, 0.0, 0.0, 1.0, 3.14159])) # + detector = turtle.robot.getDevice("collisionDetector") # + detector.enable(TIME_STEP) # + # + #The map will be initialized after the first timestep (Transmission) # + turtle.robot.step(turtle.TIME_STEP) # + turtle.update() # + + #The map will have the dynamic objects after the second timestep # + turtle.robot.step(turtle.TIME_STEP) # + turtle.update() # + ScenarioFinalTarget = findTargetPosition(turtle) # + # + if plotting: + mg.plotting_GM_plotly(turtle.mapGenerator) + #DO NOT CHANGE OR REMOVE THIS FUNCTION CALL AND POSITION # + waitRandomTimeSteps(turtle) # + #===============[From here you can change the script]==================== + #%% + #here you should insert a function, which plans a path; the functions has to return a path and related index + try: + path, pathIndCntr = planInitialPath(turtle) + except TypeError: + print("Erste Pfadplanung nicht erfolgreich, neustart...") + try: + path, pathIndCntr = planInitialPath(turtle) + except TypeError: + print("Zweite Pfadplanung nicht erfolgreich, neustart...") + try: + path, pathIndCntr = planInitialPath(turtle) + except TypeError: + print("Dritte Pfadplanung nicht erfolgreich, neustart...") + exit() + + turtle.setPoseGoal(path[pathIndCntr]) + startTime = turtle.robot.getTime() + + inhibitCntr = 0 + while turtle.robot.step(turtle.TIME_STEP) != -1: + #%%===[DO NOT CHANGE THIS PART OF THE LOOP]========================== + loopCntr += 1 # + turtle.update() + + collision = checkForPossibleCollisions(turtle, findRobotPosition(turtle), collisionCntr) + + if ( (plotting) & (loopCntr % nPlot == 0)): # + mg.plotting_GM_plotly(turtle.mapGenerator) # + #====[From here you can change the loop]============================= + #%% + #Follow the static path towards the index pathIndCntr in the path + inhibitCntr = max(0, inhibitCntr - 1) + + if(collision == False): + pathIndCntr = staticPathTracking(turtle, path, pathIndCntr) + + if(collision == True): + print("Knallt") + turtle.stop() + festgefahren += 1 + + if(festgefahren == 10): + #Berechnen neue Pos + vorherigePos = path[pathIndCntr-1] + neueRichtung = [vorherigePos[0] / 2, vorherigePos[1] / 2, 0, 0, 0, 0, 0] + path.insert(pathIndCntr, neueRichtung) + + #Fahre neue Pos an bis du da bist + #Wenn du da bist festgefahren 0 und hoffentlich keine Collsion mehr + recalcCntr += 1 + inhibitCntr = 15 + festgefahren = 0 + collision = False + + #This is the exit condition. Changing this is not recommended + if(pathIndCntr == -1): + break + #End while + #%% + print("Simulation ended") + print("Timesteps with collisions: ", collisionCntr) + print("Recalculations done: ", recalcCntr) + print("Simulationtime: ", turtle.robot.getTime() - startTime) + print("") + print("Recalculation") + print("Timesteps with collisions: ", collisionCntr) + print("Recalculations done: ", recalcCntr) + print("Simulationtime: ", turtle.robot.getTime() - startTime) + + lastStateObstacles = np.array(findObstaclePositions(turtle)) + lastStateRobotPose = np.array(findRobotPosition(turtle)) + + trackRobot = np.array(turtle.trackRobot) + plt.scatter(lastStateObstacles[..., 0], lastStateObstacles[..., 1]) + plt.scatter(lastStateRobotPose[..., 0], lastStateRobotPose[..., 1]) + + plt.plot(trackRobot[..., 0], trackRobot[..., 1]) + plt.scatter(turtle.poseGoal[0], turtle.poseGoal[1]) + plt.scatter(turtle.mapGenerator.finalTarget[0], turtle.mapGenerator.finalTarget[1]) + plt.show() diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/map.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/map.py new file mode 100644 index 0000000000000000000000000000000000000000..379645de42c7898a6d3e846c21e3c8bf996ac70a --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/map.py @@ -0,0 +1,24 @@ +#Map erstellen + +# Standard imports +import cv2 +import numpy as np; + +# Img Objekt +cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + +img = cap.read() +#height, width = img.shape[:2] + +success, img = cap.read() +gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) +mask = cv2.inRange(gray, np.array([90]), np.array([255])) + +mask[mask == 255] = 87 + +mask = np.array(mask, dtype=np.uint8).view('S2').squeeze() + +mask[mask == b'WW'] = 'W' + +print(mask) + diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/maperstellen.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/maperstellen.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node copy.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node copy.py new file mode 100644 index 0000000000000000000000000000000000000000..29f2473b20a9c782704e15851518da8ec90ee823 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node copy.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 + +import rclpy +from rclpy.node import Node +import json +from sensor_msgs.msg import Imu +import threading +from std_msgs.msg import String +from geometry_msgs.msg import Quaternion, Vector3 +from box import Box +import numpy as np +from sphero_mini_controller.treiber import SpheroMini +import datetime + + +class MyNode(Node): + + def __init__(self): + super().__init__("sphero_mini") + self.publisher_ = self.create_publisher(String,"Imu",5) + #self.get_logger().info("Hello from ROS2") + + def connect(self): + #conf_file_path = file("/sphero_conf.json") + #with open("sphero_conf.json", "r") as f: + # cfg = Box(json.load(f)) + + MAC_ADDRESS = "C6:69:72:CD:BC:6D" + + # Connect: + sphero = SpheroMini(MAC_ADDRESS, verbosity = 4) + # battery voltage + sphero.getBatteryVoltage() + print(f"Bettery voltage: {sphero.v_batt}v") + + # firmware version number + sphero.returnMainApplicationVersion() + print(f"Firmware version: {'.'.join(str(x) for x in sphero.firmware_version)}") + return sphero + + + def create_quaternion(self, roll, pitch, yaw): + q = Quaternion() + cy, sy = np.cos(yaw * 0.5), np.sin(yaw * 0.5) + cp, sp = np.cos(pitch * 0.5), np.sin(pitch * 0.5) + cr, sr = np.cos(roll * 0.5), np.sin(roll * 0.5) + + q.w = cr * cp * cy + sr * sp * sy + q.x = sr * cp * cy - cr * sp * sy + q.y = cr * sp * cy + sr * cp * sy + q.z = cr * cp * sy - sr * sp * cy + + return q + + + def create_angular_veolocity_vector3(self, groll, gpitch, gyaw): + v = Vector3() + v.x = groll + v.y = gpitch + v.z = float(gyaw) + + return v + + + def create_linear_acc_vector3(self, xacc, yacc, zacc): + v = Vector3() + v.x = xacc + v.y = yacc + v.z = zacc + + return v + + + def get_sensors_data(self, sphero): + return { + "roll": sphero.IMU_roll, + "pitch": sphero.IMU_pitch, + "yaw": sphero.IMU_yaw, + "groll": sphero.IMU_gyro_x, + "gpitch": sphero.IMU_gyro_y, + "xacc": sphero.IMU_acc_x, + "yacc": sphero.IMU_acc_y, + "zacc": sphero.IMU_acc_z + } + + + def publish_imu(self, sensors_values,node): + i = Imu() + + i.header.stamp = node.get_clock().now().to_msg() + + i.orientation = self.create_quaternion( + roll=sensors_values["roll"], + pitch=sensors_values["pitch"], + yaw=sensors_values["yaw"] + ) + i.angular_velocity = self.create_angular_veolocity_vector3( + groll=sensors_values["groll"], + gpitch=sensors_values["gpitch"], + gyaw=0 # We don't have the IMU_gyro_z + ) + i.linear_acceleration = self.create_linear_acc_vector3( + xacc=sensors_values["xacc"], + yacc=sensors_values["yacc"], + zacc=sensors_values["zacc"] + ) + + #print(i) + + #self.publisher_.publish(i) + + + +def main(args = None): + + try: + rclpy.init(args=args) #Kommunikation Starten + node = MyNode() #Node erstellen + sphero = node.connect() + except Exception as e: # rclpy.ROSInterruptException + print("Konnte nicht verbinden") + raise e + + thread = threading.Thread(target=rclpy.spin, args=(node, ), daemon=True) + thread.start() + rate = node.create_rate(2) + + try: + while rclpy.ok(): + + sphero.configureSensorMask( + IMU_yaw=True, + IMU_pitch=True, + IMU_roll=True, + IMU_acc_y=True, + IMU_acc_z=True, + IMU_acc_x=True, + IMU_gyro_x=True, + IMU_gyro_y=True, + #IMU_gyro_z=True + ) + sphero.configureSensorStream() + + sensors_values = node.get_sensors_data(sphero) + #rclpy.logdebug(sensors_values) + node.publish_imu(sensors_values, node) + sphero.setLEDColor(red = 0, green = 255, blue = 0) # Turn LEDs green + + # Aiming: + sphero.setLEDColor(red = 0, green = 0, blue = 0) # Turn main LED off + sphero.stabilization(False) # Turn off stabilization + sphero.setBackLEDIntensity(255) # turn back LED on + sphero.wait(3) # Non-blocking pau`se + sphero.resetHeading() # Reset heading + sphero.stabilization(True) # Turn on stabilization + sphero.setBackLEDIntensity(0) # Turn back LED off + + # Move around: + sphero.setLEDColor(red = 0, green = 0, blue = 255) # Turn main LED blue + sphero.roll(100, 0) # roll forwards (heading = 0) at speed = 50 + + sphero.wait(3) # Keep rolling for three seconds + + sphero.roll(0, 0) # stop + sphero.wait(1) # Allow time to stop + + sphero.setLEDColor(red = 0, green = 255, blue = 0) # Turn main LED green + sphero.roll(-100, 0) # Keep facing forwards but roll backwards at speed = 50 + sphero.wait(3) # Keep rolling for three seconds + + sphero.roll(0, 0) # stop + sphero.wait(1) # Allow time to stop + + rate.sleep() + + except KeyboardInterrupt: + pass + + rclpy.shutdown() + thread.join() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node.py new file mode 100644 index 0000000000000000000000000000000000000000..8906b23876454b93bf060523bda262e61363cdd2 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node.py @@ -0,0 +1,766 @@ +#!/usr/bin/env python3 +############################################################################ +#%% Imports +import rclpy +from rclpy.node import Node +from sensor_msgs.msg import Imu +import threading +from geometry_msgs.msg import Quaternion, Vector3 +import numpy as np +from sphero_mini_controller.treiber import SpheroMini +import cv2 +import time +import queue +import matplotlib.pyplot as plt +############################################################################ +#%% Blob Detector / Globale Variablen +detector = cv2.SimpleBlobDetector() + +# Setup SimpleBlobDetector parameters. +params = cv2.SimpleBlobDetector_Params() + +# Change thresholds +#params.minThreshold = 5 +#params.maxThreshold = 500 + +#FIlter by Color +params.filterByColor = True +params.blobColor = 255 + +# Filter by Area. +params.filterByArea = True +params.minArea = 10 +params.maxArea = 200 + +# Filter by Circularity +#params.filterByCircularity = True +#params.minCircularity = 0.5 +#params.maxCircularity = 1 + +# Filter by Convexity +#params.filterByConvexity = True +#params.minConvexity = 0.7 +#params.maxConvexity = 1 + +# Filter by Inertia +#params.filterByInertia = True +#params.minInertiaRatio = 0.01 + +# Create a detector with the parameters +detector = cv2.SimpleBlobDetector_create(params) +############################################################################ +#%% Class Video Capture +# Bufferless VideoCapture +# Quelle ? +class VideoCapture: + def __init__(self, name): + self.cap = cv2.VideoCapture(name) + self.q = queue.Queue() + t = threading.Thread(target=self._reader) + t.daemon = True + t.start() + # read frames as soon as they are available, keeping only most recent one + def _reader(self): + while True: + ret, frame = self.cap.read() + #cv2.imshow("Cap", frame) + + if not ret: + break + if not self.q.empty(): + try: + self.q.get_nowait() # discard previous (unprocessed) frame + except queue.Empty: + pass + self.q.put(frame) + + def read(self): + return self.q.get() +############################################################################ +#%% Class Sphero Node +class MyNode(Node): + def __init__(self): + super().__init__("sphero_mini") + + def connect(self): + #Automatisch MAC-Adresse laden + #conf_file_path = file("/sphero_conf.json") + #with open("sphero_conf.json", "r") as f: + # cfg = Box(json.load(f)) + + MAC_ADDRESS = "C6:69:72:CD:BC:6D" + + # Connect: + sphero = SpheroMini(MAC_ADDRESS, verbosity = 4) + # battery voltage + sphero.getBatteryVoltage() + print(f"Bettery voltage: {sphero.v_batt}v") + + # firmware version number + sphero.returnMainApplicationVersion() + print(f"Firmware version: {'.'.join(str(x) for x in sphero.firmware_version)}") + return sphero + + def create_quaternion(self, roll, pitch, yaw): + q = Quaternion() + cy, sy = np.cos(yaw * 0.5), np.sin(yaw * 0.5) + cp, sp = np.cos(pitch * 0.5), np.sin(pitch * 0.5) + cr, sr = np.cos(roll * 0.5), np.sin(roll * 0.5) + + q.w = cr * cp * cy + sr * sp * sy + q.x = sr * cp * cy - cr * sp * sy + q.y = cr * sp * cy + sr * cp * sy + q.z = cr * cp * sy - sr * sp * cy + + return q + + def create_angular_veolocity_vector3(self, groll, gpitch, gyaw): + v = Vector3() + v.x = groll + v.y = gpitch + v.z = float(gyaw) + + return v + + def create_linear_acc_vector3(self, xacc, yacc, zacc): + v = Vector3() + v.x = xacc + v.y = yacc + v.z = zacc + + return v + + def get_sensors_data(self, sphero): + return { + "roll": sphero.IMU_roll, + "pitch": sphero.IMU_pitch, + "yaw": sphero.IMU_yaw, + "groll": sphero.IMU_gyro_x, + "gpitch": sphero.IMU_gyro_y, + "xacc": sphero.IMU_acc_x, + "yacc": sphero.IMU_acc_y, + "zacc": sphero.IMU_acc_z + } + + def publish_imu(self, sensors_values,node): + i = Imu() + + i.header.stamp = node.get_clock().now().to_msg() + + i.orientation = self.create_quaternion( + roll=sensors_values["roll"], + pitch=sensors_values["pitch"], + yaw=sensors_values["yaw"] + ) + i.angular_velocity = self.create_angular_veolocity_vector3( + groll=sensors_values["groll"], + gpitch=sensors_values["gpitch"], + gyaw=0 # We don't have the IMU_gyro_z + ) + i.linear_acceleration = self.create_linear_acc_vector3( + xacc=sensors_values["xacc"], + yacc=sensors_values["yacc"], + zacc=sensors_values["zacc"] + ) + + def get_pos(self, cap, height): + #zeitanfang = time.time() + + success = False + + while(success == False): + try: + img = cap.read() + + gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY ) + keypoints = detector.detect(gray) + + for keyPoint in keypoints: + x = keyPoint.pt[0] + y = keyPoint.pt[1] + success = True + + xTrans, yTrans = self.calc_trans(x,y, height) + except Exception: + continue + + #print("Blob X: %f Blob Y: %f" %(x,y)) + #zeitende = time.time() + #print("Dauer Programmausführung:",(zeitende-zeitanfang)) + #print("Get_Pos: X,Y:", xTrans,yTrans) + + return xTrans,yTrans + + def calc_offset(self, sphero, cap, height): + sphero.roll(100,0) + sphero.wait(1) + sphero.roll(0,0) + sphero.wait(0.5) + + ref = self.get_pos(cap, height) + print("calc_offset: Ref Punkt x,y", ref) + + sphero.wait(1) + sphero.roll(100,180) + sphero.wait(1) + sphero.roll(0,0) + + startpunkt = self.get_pos(cap, height) + print("calc_offset: Startpunkt x,y", startpunkt) + + ref = np.array(ref) + startpunkt = np.array(startpunkt) + + start_ref = ref-startpunkt + + phi = np.arctan2(start_ref[1],start_ref[0]) + phi = np.degrees(phi) + phi = int(-phi) + + print("Phi ohne alles:", phi) + + phi = self.phi_in_range(phi) + + print("Calc_Offset: ", phi) + + return phi + + def calc_av(self,istPos, sollPos, cap, height): + startPos = istPos + + startPos = np.array(startPos) + sollPos = np.array(sollPos) + + pktSpheroKord = sollPos - startPos + + phi = np.arctan2(pktSpheroKord[1], pktSpheroKord[0]) + phi = np.degrees(phi) + + phiZiel = int(phi) + + phiZiel = self.phi_in_range(phiZiel) + + v = 100 + + #print("Calc_AV: a,v", phiZiel, v) + + return phiZiel, v + + def drive_to(self,sphero, targetPos, aOffset, cap, height): + dmin = 20 + pos = self.get_pos(cap, height) + pos = np.array(pos) + + diff = targetPos - pos + + d = np.linalg.norm(diff, ord = 2) + + ar = [] + ar.append(0) + + i = 1 + + while d > dmin: + a,v = self.calc_av(pos,targetPos, cap, height) + + aR = -aOffset - a #Fallunterscheidung? + ar.append((self.phi_in_range(aR))) + + #Fahrbefehl + if(ar[i] != ar[i-1]): + sphero.roll(v, ar[i]) + sphero.wait(0.05) + else: + sphero.wait(0.05) + + #Aktuelle Pos + pos = self.get_pos(cap, height) + pos = np.array(pos) + + #Abweichung + diff = targetPos - pos + diff = np.array(diff) + d = np.linalg.norm(diff, ord = 2) + + i = i + 1 + + sphero.roll(0,0) + print("Ziel Erreicht") + + return + + def calc_trans(self,x,y, height): + yTrans = height - y + xTrans = x + + return xTrans, yTrans + + def phi_in_range(self,phi): + while(phi < 0): + phi = phi + 360 + while(phi > 360): + phi = phi - 360 + return phi +############################################################################ +#%% Class Wavefrontplanner +class waveFrontPlanner: + ############################################################################ + # WAVEFRONT ALGORITHM + # Adapted to Python Code By Darin Velarde + # Fri Jan 29 13:56:53 PST 2010 + # from C code from John Palmisano + # (www.societyofrobots.com) + ############################################################################ + + def __init__(self, mapOfWorld, slow=False): + self.__slow = slow + self.__mapOfWorld = mapOfWorld + if str(type(mapOfWorld)).find("numpy") != -1: + #If its a numpy array + self.__height, self.__width = self.__mapOfWorld.shape + else: + self.__height, self.__width = len(self.__mapOfWorld), len(self.__mapOfWorld[0]) + + self.__nothing = 000 + self.__wall = 999 + self.__goal = 1 + self.__path = "PATH" + + self.__finalPath = [] + + #Robot value + self.__robot = 254 + #Robot default Location + self.__robot_x = 0 + self.__robot_y = 0 + + #default goal location + self.__goal_x = 8 + self.__goal_y = 9 + + #temp variables + self.__temp_A = 0 + self.__temp_B = 0 + self.__counter = 0 + self.__steps = 0 #determine how processor intensive the algorithm was + + #when searching for a node with a lower value + self.__minimum_node = 250 + self.__min_node_location = 250 + self.__new_state = 1 + self.__old_state = 1 + self.__reset_min = 250 #above this number is a special (wall or robot) + ########################################################################### + + def setRobotPosition(self, x, y): + """ + Sets the robot's current position + + """ + + self.__robot_x = x + self.__robot_y = y + ########################################################################### + + def setGoalPosition(self, x, y): + """ + Sets the goal position. + + """ + + self.__goal_x = x + self.__goal_y = y + ########################################################################### + + def robotPosition(self): + return (self.__robot_x, self.__robot_y) + ########################################################################### + + def goalPosition(self): + return (self.__goal_x, self.__goal_y) + ########################################################################### + + def run(self, prnt=False): + """ + The entry point for the robot algorithm to use wavefront propagation. + + """ + + path = [] + while self.__mapOfWorld[self.__robot_x][self.__robot_y] != self.__goal: + if self.__steps > 10000: + print ("Cannot find a path.") + return + #find new location to go to + self.__new_state = self.propagateWavefront() + #update location of robot + if self.__new_state == 1: + self.__robot_x -= 1 + if prnt: + print("Move to x=%d y=%d\n\n" % (self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + if self.__new_state == 2: + self.__robot_y += 1 + if prnt: + print("Move to x=%d y=%d\n\n" %(self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + if self.__new_state == 3: + self.__robot_x += 1 + if prnt: + print("Move to x=%d y=%d\n\n" %(self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + if self.__new_state == 4: + self.__robot_y -= 1 + if prnt: + print("Move to x=%d y=%d\n\n" %(self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + self.__old_state = self.__new_state + msg = "Found the goal in %i steps:\n" % self.__steps + msg += "mapOfWorld size= %i %i\n\n" % (self.__height, self.__width) + if prnt: + print(msg) + self.printMap() + return path + ########################################################################### + + def propagateWavefront(self, prnt=False): + self.unpropagate() + #Old robot location was deleted, store new robot location in mapOfWorld + self.__mapOfWorld[self.__robot_x][self.__robot_y] = self.__robot + self.__path = self.__robot + #start location to begin scan at goal location + self.__mapOfWorld[self.__goal_x][self.__goal_y] = self.__goal + counter = 0 + while counter < 200: #allows for recycling until robot is found + x = 0 + y = 0 + #time.sleep(0.00001) + #while the mapOfWorld hasnt been fully scanned + while x < self.__height and y < self.__width: + #if this location is a wall or the goal, just ignore it + if self.__mapOfWorld[x][y] != self.__wall and \ + self.__mapOfWorld[x][y] != self.__goal: + #a full trail to the robot has been located, finished! + minLoc = self.minSurroundingNodeValue(x, y) + if minLoc < self.__reset_min and \ + self.__mapOfWorld[x][y] == self.__robot: + if prnt: + print("Finished Wavefront:\n") + self.printMap() + # Tell the robot to move after this return. + return self.__min_node_location + #record a value in to this node + elif self.__minimum_node != self.__reset_min: + #if this isnt here, 'nothing' will go in the location + self.__mapOfWorld[x][y] = self.__minimum_node + 1 + #go to next node and/or row + y += 1 + if y == self.__width and x != self.__height: + x += 1 + y = 0 + #print self.__robot_x, self.__robot_y + if prnt: + print("Sweep #: %i\n" % (counter + 1)) + self.printMap() + self.__steps += 1 + counter += 1 + return 0 + ########################################################################### + + def unpropagate(self): + """ + clears old path to determine new path + stay within boundary + + """ + + for x in range(0, self.__height): + for y in range(0, self.__width): + if self.__mapOfWorld[x][y] != self.__wall and \ + self.__mapOfWorld[x][y] != self.__goal and \ + self.__mapOfWorld[x][y] != self.__path: + #if this location is a wall or goal, just ignore it + self.__mapOfWorld[x][y] = self.__nothing #clear that space + ########################################################################### + + def minSurroundingNodeValue(self, x, y): + """ + this method looks at a node and returns the lowest value around that + node. + + """ + + #reset minimum + self.__minimum_node = self.__reset_min + #down + if x < self.__height -1: + if self.__mapOfWorld[x + 1][y] < self.__minimum_node and \ + self.__mapOfWorld[x + 1][y] != self.__nothing: + #find the lowest number node, and exclude empty nodes (0's) + self.__minimum_node = self.__mapOfWorld[x + 1][y] + self.__min_node_location = 3 + #up + if x > 0: + if self.__mapOfWorld[x-1][y] < self.__minimum_node and \ + self.__mapOfWorld[x-1][y] != self.__nothing: + self.__minimum_node = self.__mapOfWorld[x-1][y] + self.__min_node_location = 1 + #right + if y < self.__width -1: + if self.__mapOfWorld[x][y + 1] < self.__minimum_node and \ + self.__mapOfWorld[x][y + 1] != self.__nothing: + self.__minimum_node = self.__mapOfWorld[x][y + 1] + self.__min_node_location = 2 + #left + if y > 0: + if self.__mapOfWorld[x][y - 1] < self.__minimum_node and \ + self.__mapOfWorld[x][y - 1] != self.__nothing: + self.__minimum_node = self.__mapOfWorld[x][y-1] + self.__min_node_location = 4 + return self.__minimum_node + ########################################################################### + + def printMap(self): + """ + Prints out the map of this instance of the class. + + """ + + msg = '' + for temp_B in range(0, self.__height): + for temp_A in range(0, self.__width): + if self.__mapOfWorld[temp_B][temp_A] == self.__wall: + msg += "%04s" % "[#]" + elif self.__mapOfWorld[temp_B][temp_A] == self.__robot: + msg += "%04s" % "-" + elif self.__mapOfWorld[temp_B][temp_A] == self.__goal: + msg += "%04s" % "G" + else: + msg += "%04s" % str(self.__mapOfWorld[temp_B][temp_A]) + msg += "\n\n" + msg += "\n\n" + print(msg) + # + if self.__slow == True: + time.sleep(0.05) + + def plotMap(self,img,path): + #Programm Koordinaten + print("Plotten") + + imgTrans = cv2.transpose(img) # X und Y-Achse im Bild tauschen + imgPlot, ax1 = plt.subplots() + + ax1.set_title('Programm Koordinaten') + ax1.imshow(imgTrans) + ax1.set_xlabel('[px]') + ax1.set_ylabel('[px]') + ax1.scatter(path[:,0], path[:,1], color='r') + + #Bild Koordinaten + imgPlot2, ax2 = plt.subplots() + ax2.set_title('Bild Koordinaten') + ax2.set_xlabel('[px]') + ax2.set_ylabel('[px]') + ax2.imshow(img) + ax2.scatter(path[:,1], path[:,0], color='r') + + plt.show() + + return + + def getPathWelt(self,path,height,Mapobj): + + pathWeltX, pathWeltY = Mapobj.calc_trans_welt(path[:,1], path[:,0], height) + pathEckenX = [] + pathEckenY = [] + + for i,px in enumerate(pathWeltX): + if (i < len(pathWeltX)-1) & (i > 0): + if px != pathWeltX[i+1]: + if pathWeltY[i]!=pathWeltY[i-1]: + pathEckenX.append(px) + pathEckenY.append(pathWeltY[i]) + if pathWeltY[i] != pathWeltY[i+1]: + if pathWeltX[i]!=pathWeltX[i-1]: + pathEckenX.append(px) + pathEckenY.append(pathWeltY[i]) + + pathEckenX.append(pathWeltX[-1]) + pathEckenY.append(pathWeltY[-1]) + + + uebergabePath = [] + for i in range(0,len(pathEckenX)): + uebergabePath.append((pathEckenX[i],pathEckenY[i])) + + print("Ecken: ", uebergabePath) + + return uebergabePath + +############################################################################ +#%% Class Map +class mapCreate: + ############################################################################ + + def create_map(self, scale, cap): + + # Map erstellen + + # Img Objekt + #cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + img = cap.read() + + #Karte von Bild + #img = cv2.imread("D:/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/map/map.jpg") + print('Original Dimensions : ',img.shape) + + scale_percent = 100-scale # percent of original size + width = int(img.shape[1] * scale_percent / 100) + height = int(img.shape[0] * scale_percent / 100) + dim = (width, height) + + # resize image + resized = cv2.resize(img, dim, interpolation = cv2.INTER_AREA) + print('Resized Dimensions : ',resized.shape) + + gray = cv2.cvtColor(resized, cv2.COLOR_RGB2GRAY) + mask = cv2.inRange(gray, np.array([50]), np.array([255])) + + plt.plot(mask) + + mapOfWorld = [[0]*gray.shape[1]]*gray.shape[0] + mask_list= np.ndarray.tolist(mask) + + #Markiere alle leeren Zellen mit 000 und alle Zellen mit Hinderniss mit 999 + for i in range(0,mask.shape[0]): + mapOfWorld[i] = ['999' if j > 200 else '000' for j in mask_list[i]] + + mapOfWorld = np.array(mapOfWorld, dtype = int) + mapOfWorldSized = mapOfWorld.copy() + + e = 1 + #Hindernisse Größer machen + for i in range(0,mapOfWorld.shape[0]): + for j in range(0,mapOfWorld.shape[1]): + for r in range(0,e): + try: + if (mapOfWorldSized[i][j] == 999): + mapOfWorld[i][j+e] = 999 + mapOfWorld[i+e][j] = 999 + mapOfWorld[i-e][j] = 999 + mapOfWorld[i][j-e] = 999 + mapOfWorld[i+e][j+e] = 999 + mapOfWorld[i+e][j-e] = 999 + mapOfWorld[i-e][j+e] = 999 + mapOfWorld[i-e][j-e] = 999 + except Exception: + continue + + return mapOfWorld,img + ############################################################################ + + def scale_koord(self, sx, sy, gx, gy,scale): + scale_percent = 100-scale # percent of original size + + sx = int(sx*scale_percent/100) + sy = int(sy*scale_percent/100) + gx = int(gx*scale_percent/100) + gy = int(gy*scale_percent/100) + + return sx,sy,gx,gy + ############################################################################ + + def rescale_koord(self, path,scale): + scale_percent = 100-scale # percent of original size100 + + path = np.array(path) + + for i in range(len(path)): + path[i] = path[i]*100/scale_percent + + return path + ############################################################################ + + def calc_trans_welt(self, x, y, height): + yTrans = height - y + xTrans = x + + return xTrans, yTrans +############################################################################### +#%% Main Funktion +def main(args = None): + #Verbindung herstellen, Node erstellen + try: + rclpy.init(args=args) #Kommunikation Starten + node = MyNode() #Node erstellen + sphero = node.connect() + thread = threading.Thread(target=rclpy.spin, args=(node, ), daemon=True) + thread.start() + #rate = node.create_rate(5) + + except Exception as e: # rclpy.ROSInterruptException + print("Konnte nicht verbinden") + raise e + + #cap = VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + cap = VideoCapture(4) + Map = mapCreate() + + img = cap.read() + height, width = img.shape[:2] + + scale = 90 + + sphero.setLEDColor(255,0,0) + sphero.wait(1) + sphero.setBackLEDIntensity(0) + sphero.stabilization(True) + + aOffset = node.calc_offset(sphero, cap, height) + + sphero.wait(1) + + #while(True): + + mapWorld,img = Map.create_map(scale, cap) + height, width = img.shape[:2] + + #Befehle für WaveFrontPlanner/Maperstellung + sy,sx = node.get_pos(cap,height) #Aktuelle Roboter Pos in Welt + sy, sx = node.calc_trans(sy,sx,height) + + gy = int(input("Bitte geben Sie die X-Zielkoordinate im Bild Koordinatensystem ein:")) #X-Koordinate im Weltkoordinaten System + gx = int(input("Bitte geben Sie die Y-Zielkoordinate im Bild Koordinatensystem ein:")) #Y-Koordinate im Weltkordinaten System + + sx,sy,gx,gy = Map.scale_koord(sx,sy,gx,gy,scale) #Kordinaten Tauschen X,Y + + start = time.time() + planner = waveFrontPlanner(mapWorld) + planner.setGoalPosition(gx,gy) + planner.setRobotPosition(sx,sy) + path = planner.run(False) + end = time.time() + print("Took %f seconds to run wavefront simulation" % (end - start)) + + path = Map.rescale_koord(path, scale) + + planner.plotMap(img,path) + + pathWelt = planner.getPathWelt(path,height,Map) + + for p in pathWelt: + node.drive_to(sphero,p,aOffset,cap, height) + sphero.wait(1) + + print("Geschafft") + + #if cv2.waitKey(10) & 0xFF == ord('q'): + # break + + rclpy.shutdown() +############################################################################ +#%% Main +if __name__ == '__main__': + main() +############################################################################ diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node_alt.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node_alt.py new file mode 100644 index 0000000000000000000000000000000000000000..3b4ea7911e77fbc716fc3c714888fe11f6c6d3ab --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node_alt.py @@ -0,0 +1,323 @@ +#!/usr/bin/env python3 + +import rclpy +from rclpy.node import Node +import json +from sensor_msgs.msg import Imu +import threading +from std_msgs.msg import String +from geometry_msgs.msg import Quaternion, Vector3 +from box import Box +import numpy as np +from sphero_mini_controller.treiber import SpheroMini +import datetime +import cv2 +import time +import queue, threading, time + +# Set up the detector with default parameters. +detector = cv2.SimpleBlobDetector() + +# Setup SimpleBlobDetector parameters. +params = cv2.SimpleBlobDetector_Params() + +# Change thresholds +#params.minThreshold = 5 +#params.maxThreshold = 500 + +#FIlter by Color +params.filterByColor = True +params.blobColor = 255 + +# Filter by Area. +params.filterByArea = True +params.minArea = 10 +params.maxArea = 200 + +# Filter by Circularity +#params.filterByCircularity = True +#params.minCircularity = 0.5 +#params.maxCircularity = 1 + +# Filter by Convexity +#params.filterByConvexity = True +#params.minConvexity = 0.7 +#params.maxConvexity = 1 + +# Filter by Inertia +#params.filterByInertia = True +#params.minInertiaRatio = 0.01 + +# Create a detector with the parameters +detector = cv2.SimpleBlobDetector_create(params) + +# bufferless VideoCapture +class VideoCapture: + def __init__(self, name): + self.cap = cv2.VideoCapture(name) + self.q = queue.Queue() + t = threading.Thread(target=self._reader) + t.daemon = True + t.start() + # read frames as soon as they are available, keeping only most recent one + def _reader(self): + while True: + ret, frame = self.cap.read() + if not ret: + break + if not self.q.empty(): + try: + self.q.get_nowait() # discard previous (unprocessed) frame + except queue.Empty: + pass + self.q.put(frame) + + def read(self): + return self.q.get() + +class MyNode(Node): + + def __init__(self): + super().__init__("sphero_mini") + + def connect(self): + #Automatisch MAC-Adresse laden + #conf_file_path = file("/sphero_conf.json") + #with open("sphero_conf.json", "r") as f: + # cfg = Box(json.load(f)) + + MAC_ADDRESS = "C6:69:72:CD:BC:6D" + + # Connect: + sphero = SpheroMini(MAC_ADDRESS, verbosity = 4) + # battery voltage + sphero.getBatteryVoltage() + print(f"Bettery voltage: {sphero.v_batt}v") + + # firmware version number + sphero.returnMainApplicationVersion() + print(f"Firmware version: {'.'.join(str(x) for x in sphero.firmware_version)}") + return sphero + + def create_quaternion(self, roll, pitch, yaw): + q = Quaternion() + cy, sy = np.cos(yaw * 0.5), np.sin(yaw * 0.5) + cp, sp = np.cos(pitch * 0.5), np.sin(pitch * 0.5) + cr, sr = np.cos(roll * 0.5), np.sin(roll * 0.5) + + q.w = cr * cp * cy + sr * sp * sy + q.x = sr * cp * cy - cr * sp * sy + q.y = cr * sp * cy + sr * cp * sy + q.z = cr * cp * sy - sr * sp * cy + + return q + + def create_angular_veolocity_vector3(self, groll, gpitch, gyaw): + v = Vector3() + v.x = groll + v.y = gpitch + v.z = float(gyaw) + + return v + + def create_linear_acc_vector3(self, xacc, yacc, zacc): + v = Vector3() + v.x = xacc + v.y = yacc + v.z = zacc + + return v + + def get_sensors_data(self, sphero): + return { + "roll": sphero.IMU_roll, + "pitch": sphero.IMU_pitch, + "yaw": sphero.IMU_yaw, + "groll": sphero.IMU_gyro_x, + "gpitch": sphero.IMU_gyro_y, + "xacc": sphero.IMU_acc_x, + "yacc": sphero.IMU_acc_y, + "zacc": sphero.IMU_acc_z + } + + def publish_imu(self, sensors_values,node): + i = Imu() + + i.header.stamp = node.get_clock().now().to_msg() + + i.orientation = self.create_quaternion( + roll=sensors_values["roll"], + pitch=sensors_values["pitch"], + yaw=sensors_values["yaw"] + ) + i.angular_velocity = self.create_angular_veolocity_vector3( + groll=sensors_values["groll"], + gpitch=sensors_values["gpitch"], + gyaw=0 # We don't have the IMU_gyro_z + ) + i.linear_acceleration = self.create_linear_acc_vector3( + xacc=sensors_values["xacc"], + yacc=sensors_values["yacc"], + zacc=sensors_values["zacc"] + ) + + def get_pos(self, cap, height): + zeitanfang = time.time() + + img = cap.read() + + gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY ) + keypoints = detector.detect(gray) + + for keyPoint in keypoints: + x = keyPoint.pt[0] + y = keyPoint.pt[1] + + #print("Blob X: %f Blob Y: %f" %(x,y)) + + zeitende = time.time() + #print("Dauer Programmausführung:",(zeitende-zeitanfang)) + + xTrans, yTrans = self.calc_trans(x,y, height) + + #print("Get_Pos: X,Y:", xTrans,yTrans) + + return xTrans,yTrans + + def calc_offset(self, sphero, cap, height): + sphero.roll(100,0) + sphero.wait(2) + sphero.roll(0,0) + sphero.wait(0.5) + + ref = self.get_pos(cap, height) + print("calc_offset: Ref Punkt x,y", ref) + + sphero.wait(1) + sphero.roll(100,180) + sphero.wait(2) + sphero.roll(0,0) + + startpunkt = self.get_pos(cap, height) + print("calc_offset: Startpunkt x,y", startpunkt) + + ref = np.array(ref) + startpunkt = np.array(startpunkt) + + start_ref = ref-startpunkt + + phi = np.arctan2(start_ref[1],start_ref[0]) + phi = np.degrees(phi) + phi = int(phi) + + print("Phi ohne alles:", phi) + + if(phi < 0): + phi = phi + 360 + + if(phi > 360): + phi = phi - 360 + + print("Calc_Offset: ", phi) + + return phi + + def calc_av(self, sollPos, cap, height): + + startPos = self.get_pos(cap, height) + + startPos = np.array(startPos) + sollPos = np.array(sollPos) + + pktSpheroKord = sollPos - startPos + + phi = np.arctan2(pktSpheroKord[1], pktSpheroKord[0]) + phi = np.degrees(phi) + + phiZiel = int(phi) + + if(phiZiel < 0): + phiZiel = phiZiel + 360 + + if(phi > 360): + phi = phi - 360 + + v = 100 + + print("Calc_AV: a,v", phiZiel, v) + + return phiZiel, v + + def drive_to(self,sphero, targetPos, aOffset, cap, height): + dmin = 50 + pos = self.get_pos(cap, height) + pos = np.array(pos) + + diff = targetPos - pos + + d = np.linalg.norm(diff, ord = 2) + + while d > dmin: + a,v = self.calc_av(targetPos, cap, height) + ar = aOffset + a #Fallunterscheidung? + + pos = self.get_pos(cap, height) + pos = np.array(pos) + diff = targetPos - pos + diff = np.array(diff) + d = np.linalg.norm(diff, ord = 2) + + sphero.roll(v, ar) + time.sleep(0.1) + + sphero.roll(0,ar) + + return + + def calc_trans(self,x,y, height): + yTrans = height - y + xTrans = x + + return xTrans, yTrans + +def main(args = None): + #Verbindung herstellen, Node erstellen + try: + rclpy.init(args=args) #Kommunikation Starten + node = MyNode() #Node erstellen + sphero = node.connect() + thread = threading.Thread(target=rclpy.spin, args=(node, ), daemon=True) + thread.start() + rate = node.create_rate(5) + + except Exception as e: # rclpy.ROSInterruptException + print("Konnte nicht verbinden") + raise e + + cap = VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + + img = cap.read() + height, width = img.shape[:2] + + sphero.setLEDColor(255,0,0) + sphero.wait(1) + sphero.setBackLEDIntensity(100) + sphero.stabilization(True) + + aOffset = node.calc_offset(sphero, cap, height) + + sphero.roll(100,0+aOffset) + sphero.wait(2) + sphero.roll(0,0) + + sphero.wait(5) + + targetPos = 117,430 + + node.drive_to(sphero,targetPos,aOffset,cap, height) + + rclpy.shutdown() + +if __name__ == '__main__': + main() + diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node_komisch.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node_komisch.py new file mode 100644 index 0000000000000000000000000000000000000000..7c5dc3e024792433ecfc8652b43a8c8b3281bc30 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node_komisch.py @@ -0,0 +1,305 @@ +#!/usr/bin/env python3 + +import rclpy +from rclpy.node import Node +import json +from sensor_msgs.msg import Imu +import threading +from std_msgs.msg import String +from geometry_msgs.msg import Quaternion, Vector3 +from box import Box +import numpy as np +from sphero_mini_controller.treiber import SpheroMini +import datetime +import cv2 +import time + + +# Img Objekt +cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + +# Set up the detector with default parameters. +detector = cv2.SimpleBlobDetector() + +# Setup SimpleBlobDetector parameters. +params = cv2.SimpleBlobDetector_Params() + +# Change thresholds +#params.minThreshold = 5 +#params.maxThreshold = 500 + +#FIlter by Color +params.filterByColor = True +params.blobColor = 255 + +# Filter by Area. +#params.filterByArea = True +#params.minArea = 100 +#params.maxArea = 1000 + +# Filter by Circularity +#params.filterByCircularity = True +#params.minCircularity = 0.5 +#params.maxCircularity = 1 + +# Filter by Convexity +#params.filterByConvexity = True +#params.minConvexity = 0.7 +#params.maxConvexity = 1 + +# Filter by Inertia +#params.filterByInertia = True +#params.minInertiaRatio = 0.01 + +# Create a detector with the parameters +detector = cv2.SimpleBlobDetector_create(params) + +class MyNode(Node): + #Konstrukter Node Objekt + def __init__(self): + super().__init__("sphero_mini") + #self.publisher_ = self.create_publisher(String,"Imu",5) + + def connect(self): + #Automatisch MAC-Adresse laden + #conf_file_path = file("/sphero_conf.json") + #with open("sphero_conf.json", "r") as f: + # cfg = Box(json.load(f)) + + MAC_ADDRESS = "C6:69:72:CD:BC:6D" + + # Connect: + sphero = SpheroMini(MAC_ADDRESS, verbosity = 4) + # battery voltage + sphero.getBatteryVoltage() + print(f"Bettery voltage: {sphero.v_batt}v") + + # firmware version number + sphero.returnMainApplicationVersion() + print(f"Firmware version: {'.'.join(str(x) for x in sphero.firmware_version)}") + return sphero + + def create_quaternion(self, roll, pitch, yaw): + q = Quaternion() + cy, sy = np.cos(yaw * 0.5), np.sin(yaw * 0.5) + cp, sp = np.cos(pitch * 0.5), np.sin(pitch * 0.5) + cr, sr = np.cos(roll * 0.5), np.sin(roll * 0.5) + + q.w = cr * cp * cy + sr * sp * sy + q.x = sr * cp * cy - cr * sp * sy + q.y = cr * sp * cy + sr * cp * sy + q.z = cr * cp * sy - sr * sp * cy + + return q + + def create_angular_veolocity_vector3(self, groll, gpitch, gyaw): + v = Vector3() + v.x = groll + v.y = gpitch + v.z = float(gyaw) + + return v + + def create_linear_acc_vector3(self, xacc, yacc, zacc): + v = Vector3() + v.x = xacc + v.y = yacc + v.z = zacc + + return v + + def get_sensors_data(self, sphero): + return { + "roll": sphero.IMU_roll, + "pitch": sphero.IMU_pitch, + "yaw": sphero.IMU_yaw, + "groll": sphero.IMU_gyro_x, + "gpitch": sphero.IMU_gyro_y, + "xacc": sphero.IMU_acc_x, + "yacc": sphero.IMU_acc_y, + "zacc": sphero.IMU_acc_z + } + + def publish_imu(self, sensors_values,node): + i = Imu() + + i.header.stamp = node.get_clock().now().to_msg() + + i.orientation = self.create_quaternion( + roll=sensors_values["roll"], + pitch=sensors_values["pitch"], + yaw=sensors_values["yaw"] + ) + i.angular_velocity = self.create_angular_veolocity_vector3( + groll=sensors_values["groll"], + gpitch=sensors_values["gpitch"], + gyaw=0 # We don't have the IMU_gyro_z + ) + i.linear_acceleration = self.create_linear_acc_vector3( + xacc=sensors_values["xacc"], + yacc=sensors_values["yacc"], + zacc=sensors_values["zacc"] + ) + + def positionauslesen(self): + zeitanfang = time.time() + + x = (-1) + y = (-1) + + while x < 0 and y < 0: + + success, img = cap.read() + gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY ) + # Detect blobs. + keypoints = detector.detect(gray) + + #print(keypoints) + for keyPoint in keypoints: + x = keyPoint.pt[0] + y = keyPoint.pt[1] + + #print("Blob X: %f Blob Y: %f" %(x,y)) + zeitende = time.time() + print("Dauer Programmausführung:",(zeitende-zeitanfang)) + return (x,y) + + def ref_winkel(self, sphero): + + startpunkt = self.positionauslesen() + + sphero.roll(100,0) + sphero.wait(2) + sphero.roll(0,0) + sphero.wait(0.5) + + ref = self.positionauslesen() + + sphero.wait(0.5) + sphero.roll(100,180) + sphero.wait(2) + sphero.roll(0,0) + + soll = (startpunkt[0]+100,startpunkt[1]) + + soll = np.array(soll) + ref = np.array(ref) + startpunkt = np.array(startpunkt) + + start_ref = ref-startpunkt + start_soll = soll-startpunkt + + y = start_ref[0] * start_soll[1] - start_ref[1] * start_soll[0] + x = start_ref[0] * start_soll[0] + start_ref[1] * start_soll[1] + + phi = np.arctan2(y,x) + phi = np.degrees(phi) + phi = int(phi) + + if(phi < 0 ): + phi = phi + 360 + + return phi + + def fahre_ziel(self, sphero, x,y, phiAbw): + + startPos = self.positionauslesen() + sollPos = (x,y) + + startPos = np.array(startPos) + sollPos = np.array(sollPos) + + pktSpheroKord = sollPos - startPos + + phi = np.arctan2(pktSpheroKord[1], pktSpheroKord[0]) + phi = np.degrees(phi) + + phi = int(phi) + + phiZiel = phiAbw - phi + + if(phiZiel < 0 ): + phiZiel = phiZiel + 360 + + print("Winkel Sphero System ", phi) + + #sollPos = (x,y) + #startPos = self.positionauslesen() + #refPos = (startPos[0]+100,startPos[1]) + + #sollPos = np.array(sollPos) + #refPos = np.array(refPos) + #startPos = np.array(startPos) + + #start_ref = refPos-startPos + #start_soll = sollPos-startPos + + #y = start_ref[0] * start_soll[1] - start_ref[1] * start_soll[0] + #x = start_ref[0] * start_soll[0] + start_ref[1] * start_soll[1] + + #phi = np.arctan2(y,x) + #phi = np.degrees(phi) + #phiZiel = phiAbw - (int(phi) * -1) + + print("Zielwinkel: ", phiZiel) + + while(not(np.allclose(startPos, sollPos, atol = 50))): + + sphero.roll(100, (phiZiel)) + + aktPos = self.positionauslesen() + aktPos = np.array(aktPos) + + time.sleep(0.1) + + print("Ziel erreicht") + + sphero.roll(0, (0)) + + return + + +def main(args = None): + #Verbindung herstellen, Node erstellen + try: + rclpy.init(args=args) #Kommunikation Starten + node = MyNode() #Node erstellen + sphero = node.connect() + thread = threading.Thread(target=rclpy.spin, args=(node, ), daemon=True) + thread.start() + rate = node.create_rate(5) + + except Exception as e: # rclpy.ROSInterruptException + print("Konnte nicht verbinden") + raise e + + sphero.setLEDColor(255,0,0) + sphero.wait(1) + sphero.setBackLEDIntensity(100) + sphero.stabilization(True) + + #Differenzwinkel auslesen + phiAbw = node.ref_winkel(sphero) + print("Abweichender Winkel Initialfahrt:", phiAbw) + + sphero.wait(5) + sphero.roll(100, phiAbw) + + sphero.wait(5) + node.fahre_ziel(sphero, 94,94, phiAbw) + + sphero.wait(5) + node.fahre_ziel(sphero, 134,462, phiAbw) + + sphero.wait(5) + node.fahre_ziel(sphero, 581,372, phiAbw) + + sphero.wait(5) + node.fahre_ziel(sphero, 331,308, phiAbw) + + rclpy.spin() + rclpy.shutdown() + +if __name__ == '__main__': + main() + diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node_refwinkel.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node_refwinkel.py new file mode 100644 index 0000000000000000000000000000000000000000..78066a4c2fa4da3a7a78b3faca6b5427ae979e42 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node_refwinkel.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 + +import rclpy +from rclpy.node import Node +import json +from sensor_msgs.msg import Imu +import threading +from std_msgs.msg import String +from geometry_msgs.msg import Quaternion, Vector3 +from box import Box +import numpy as np +from sphero_mini_controller.treiber import SpheroMini +import datetime + +class MyNode(Node): + #Konstrukter Node Objekt + def __init__(self): + super().__init__("sphero_mini") + #self.publisher_ = self.create_publisher(String,"Imu",5) + + def connect(self): + #Automatisch MAC-Adresse laden + #conf_file_path = file("/sphero_conf.json") + #with open("sphero_conf.json", "r") as f: + # cfg = Box(json.load(f)) + + MAC_ADDRESS = "C6:69:72:CD:BC:6D" + + # Connect: + sphero = SpheroMini(MAC_ADDRESS, verbosity = 4) + # battery voltage + sphero.getBatteryVoltage() + print(f"Bettery voltage: {sphero.v_batt}v") + + # firmware version number + sphero.returnMainApplicationVersion() + print(f"Firmware version: {'.'.join(str(x) for x in sphero.firmware_version)}") + return sphero + + def create_quaternion(self, roll, pitch, yaw): + q = Quaternion() + cy, sy = np.cos(yaw * 0.5), np.sin(yaw * 0.5) + cp, sp = np.cos(pitch * 0.5), np.sin(pitch * 0.5) + cr, sr = np.cos(roll * 0.5), np.sin(roll * 0.5) + + q.w = cr * cp * cy + sr * sp * sy + q.x = sr * cp * cy - cr * sp * sy + q.y = cr * sp * cy + sr * cp * sy + q.z = cr * cp * sy - sr * sp * cy + + return q + + def create_angular_veolocity_vector3(self, groll, gpitch, gyaw): + v = Vector3() + v.x = groll + v.y = gpitch + v.z = float(gyaw) + + return v + + def create_linear_acc_vector3(self, xacc, yacc, zacc): + v = Vector3() + v.x = xacc + v.y = yacc + v.z = zacc + + return v + + def get_sensors_data(self, sphero): + return { + "roll": sphero.IMU_roll, + "pitch": sphero.IMU_pitch, + "yaw": sphero.IMU_yaw, + "groll": sphero.IMU_gyro_x, + "gpitch": sphero.IMU_gyro_y, + "xacc": sphero.IMU_acc_x, + "yacc": sphero.IMU_acc_y, + "zacc": sphero.IMU_acc_z + } + + def publish_imu(self, sensors_values,node): + i = Imu() + + i.header.stamp = node.get_clock().now().to_msg() + + i.orientation = self.create_quaternion( + roll=sensors_values["roll"], + pitch=sensors_values["pitch"], + yaw=sensors_values["yaw"] + ) + i.angular_velocity = self.create_angular_veolocity_vector3( + groll=sensors_values["groll"], + gpitch=sensors_values["gpitch"], + gyaw=0 # We don't have the IMU_gyro_z + ) + i.linear_acceleration = self.create_linear_acc_vector3( + xacc=sensors_values["xacc"], + yacc=sensors_values["yacc"], + zacc=sensors_values["zacc"] + ) + + def positionauslesen(self): + # Standard imports + import cv2 + import numpy as np; + + # Img Objekt + cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + + # Set up the detector with default parameters. + detector = cv2.SimpleBlobDetector() + + # Setup SimpleBlobDetector parameters. + params = cv2.SimpleBlobDetector_Params() + + # Change thresholds + #params.minThreshold = 5 + #params.maxThreshold = 500 + + #FIlter by Color + params.filterByColor = True + params.blobColor = 255 + + # Filter by Area. + #params.filterByArea = True + #params.minArea = 100 + #params.maxArea = 1000 + + # Filter by Circularity + #params.filterByCircularity = True + #params.minCircularity = 0.5 + #params.maxCircularity = 1 + + # Filter by Convexity + #params.filterByConvexity = True + #params.minConvexity = 0.7 + #params.maxConvexity = 1 + + # Filter by Inertia + #params.filterByInertia = True + #params.minInertiaRatio = 0.01 + + # Create a detector with the parameters + detector = cv2.SimpleBlobDetector_create(params) + + success, img = cap.read() + gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY ) + + # Detect blobs. + keypoints = detector.detect(gray) + + #print(keypoints) + for keyPoint in keypoints: + x = keyPoint.pt[0] + y = keyPoint.pt[1] + #keypointss = keyPoint.sizekeyPoints + + #print("Blob X: %f Blob Y: %f" %(x,y)) + + # Draw detected blobs as red circles. + # cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures + # the size of the circle corresponds to the size of blob + + im_with_keypoints = cv2.drawKeypoints(gray, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) + + # Show blobs + cv2.imshow("Keypoints", im_with_keypoints) + + return (x,y) + + def ref_winkel(self, sphero, x,y): + + startpunkt = self.positionauslesen() + + sphero.roll(100,0) + sphero.wait(2) + sphero.roll(0,0) + sphero.wait(0.5) + + ref = self.positionauslesen() + + #print(ref) + + sphero.wait(0.5) + sphero.roll(100,180) + sphero.wait(2) + sphero.roll(0,0) + + soll = (x,y) + + soll = np.array(soll) + ref = np.array(ref) + startpunkt = np.array(startpunkt) + + start_ref = ref-startpunkt + start_soll = soll-startpunkt + phi = np.arccos(np.dot(start_ref,start_soll) / (np.linalg.norm(start_ref)*np.linalg.norm(start_soll))) + return phi + +def main(args = None): + #Verbindung herstellen, Node erstellen + try: + rclpy.init(args=args) #Kommunikation Starten + node = MyNode() #Node erstellen + sphero = node.connect() + thread = threading.Thread(target=rclpy.spin, args=(node, ), daemon=True) + thread.start() + rate = node.create_rate(5) + + except Exception as e: # rclpy.ROSInterruptException + print("Konnte nicht verbinden") + raise e + + #Simple Moves, Erste Bewegung erkennen + #try:oll = (0,0) + # while rclpy.ok(): + + #sphero.positionauslesen() + #soll = 0,0 + phi = node.ref_winkel(sphero, 0,0) + + phi = np.degrees(phi) + + phi = int(phi) + + print("Abweihender Winkel:", phi) + + sphero.wait(0.5) + sphero.roll(100, (0 + phi)) + sphero.wait(2) + sphero.roll(0,0) + +# except KeyboardInterrupt: + # pass + + rclpy.shutdown() + # thread.join() + +if __name__ == '__main__': + main() + + #!/usr/bin/env python3 + diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node_testfahrt.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node_testfahrt.py new file mode 100644 index 0000000000000000000000000000000000000000..29f2473b20a9c782704e15851518da8ec90ee823 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node_testfahrt.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 + +import rclpy +from rclpy.node import Node +import json +from sensor_msgs.msg import Imu +import threading +from std_msgs.msg import String +from geometry_msgs.msg import Quaternion, Vector3 +from box import Box +import numpy as np +from sphero_mini_controller.treiber import SpheroMini +import datetime + + +class MyNode(Node): + + def __init__(self): + super().__init__("sphero_mini") + self.publisher_ = self.create_publisher(String,"Imu",5) + #self.get_logger().info("Hello from ROS2") + + def connect(self): + #conf_file_path = file("/sphero_conf.json") + #with open("sphero_conf.json", "r") as f: + # cfg = Box(json.load(f)) + + MAC_ADDRESS = "C6:69:72:CD:BC:6D" + + # Connect: + sphero = SpheroMini(MAC_ADDRESS, verbosity = 4) + # battery voltage + sphero.getBatteryVoltage() + print(f"Bettery voltage: {sphero.v_batt}v") + + # firmware version number + sphero.returnMainApplicationVersion() + print(f"Firmware version: {'.'.join(str(x) for x in sphero.firmware_version)}") + return sphero + + + def create_quaternion(self, roll, pitch, yaw): + q = Quaternion() + cy, sy = np.cos(yaw * 0.5), np.sin(yaw * 0.5) + cp, sp = np.cos(pitch * 0.5), np.sin(pitch * 0.5) + cr, sr = np.cos(roll * 0.5), np.sin(roll * 0.5) + + q.w = cr * cp * cy + sr * sp * sy + q.x = sr * cp * cy - cr * sp * sy + q.y = cr * sp * cy + sr * cp * sy + q.z = cr * cp * sy - sr * sp * cy + + return q + + + def create_angular_veolocity_vector3(self, groll, gpitch, gyaw): + v = Vector3() + v.x = groll + v.y = gpitch + v.z = float(gyaw) + + return v + + + def create_linear_acc_vector3(self, xacc, yacc, zacc): + v = Vector3() + v.x = xacc + v.y = yacc + v.z = zacc + + return v + + + def get_sensors_data(self, sphero): + return { + "roll": sphero.IMU_roll, + "pitch": sphero.IMU_pitch, + "yaw": sphero.IMU_yaw, + "groll": sphero.IMU_gyro_x, + "gpitch": sphero.IMU_gyro_y, + "xacc": sphero.IMU_acc_x, + "yacc": sphero.IMU_acc_y, + "zacc": sphero.IMU_acc_z + } + + + def publish_imu(self, sensors_values,node): + i = Imu() + + i.header.stamp = node.get_clock().now().to_msg() + + i.orientation = self.create_quaternion( + roll=sensors_values["roll"], + pitch=sensors_values["pitch"], + yaw=sensors_values["yaw"] + ) + i.angular_velocity = self.create_angular_veolocity_vector3( + groll=sensors_values["groll"], + gpitch=sensors_values["gpitch"], + gyaw=0 # We don't have the IMU_gyro_z + ) + i.linear_acceleration = self.create_linear_acc_vector3( + xacc=sensors_values["xacc"], + yacc=sensors_values["yacc"], + zacc=sensors_values["zacc"] + ) + + #print(i) + + #self.publisher_.publish(i) + + + +def main(args = None): + + try: + rclpy.init(args=args) #Kommunikation Starten + node = MyNode() #Node erstellen + sphero = node.connect() + except Exception as e: # rclpy.ROSInterruptException + print("Konnte nicht verbinden") + raise e + + thread = threading.Thread(target=rclpy.spin, args=(node, ), daemon=True) + thread.start() + rate = node.create_rate(2) + + try: + while rclpy.ok(): + + sphero.configureSensorMask( + IMU_yaw=True, + IMU_pitch=True, + IMU_roll=True, + IMU_acc_y=True, + IMU_acc_z=True, + IMU_acc_x=True, + IMU_gyro_x=True, + IMU_gyro_y=True, + #IMU_gyro_z=True + ) + sphero.configureSensorStream() + + sensors_values = node.get_sensors_data(sphero) + #rclpy.logdebug(sensors_values) + node.publish_imu(sensors_values, node) + sphero.setLEDColor(red = 0, green = 255, blue = 0) # Turn LEDs green + + # Aiming: + sphero.setLEDColor(red = 0, green = 0, blue = 0) # Turn main LED off + sphero.stabilization(False) # Turn off stabilization + sphero.setBackLEDIntensity(255) # turn back LED on + sphero.wait(3) # Non-blocking pau`se + sphero.resetHeading() # Reset heading + sphero.stabilization(True) # Turn on stabilization + sphero.setBackLEDIntensity(0) # Turn back LED off + + # Move around: + sphero.setLEDColor(red = 0, green = 0, blue = 255) # Turn main LED blue + sphero.roll(100, 0) # roll forwards (heading = 0) at speed = 50 + + sphero.wait(3) # Keep rolling for three seconds + + sphero.roll(0, 0) # stop + sphero.wait(1) # Allow time to stop + + sphero.setLEDColor(red = 0, green = 255, blue = 0) # Turn main LED green + sphero.roll(-100, 0) # Keep facing forwards but roll backwards at speed = 50 + sphero.wait(3) # Keep rolling for three seconds + + sphero.roll(0, 0) # stop + sphero.wait(1) # Allow time to stop + + rate.sleep() + + except KeyboardInterrupt: + pass + + rclpy.shutdown() + thread.join() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/pfadplanung.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/pfadplanung.py new file mode 100644 index 0000000000000000000000000000000000000000..d07a2a00e56b7626e47d9ae9ba004c28b8fa3a94 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/pfadplanung.py @@ -0,0 +1,223 @@ + +def planInitialPath(start, goal): + + from matplotlib import pyplot as ppl + from matplotlib import cm + import random, sys, math, os.path + from matplotlib.pyplot import imread + + #Implementation of RRT + + MAP_IMG = './map.jpg' #Black and white image for a map + MIN_NUM_VERT = 20 # Minimum number of vertex in the graph + MAX_NUM_VERT = 1500 # Maximum number of vertex in the graph + STEP_DISTANCE = 10 # Maximum distance between two vertex + SEED = None # For random numbers + + def rapidlyExploringRandomTree(ax, img, start, goal, seed=None): + hundreds = 100 + seed = random.seed(seed) + #print("Zufallsseed: ", seed) + points = [] + graph = [] + points.append(start) + graph.append((start, [])) + print('Generating and conecting random points') + occupied = True + phaseTwo = False + + # Phase two values (points 5 step distances around the goal point) + minX = max(goal[0] - 5 * STEP_DISTANCE, 0) + maxX = min(goal[0] + 5 * STEP_DISTANCE, len(img[0]) - 1) + minY = max(goal[1] - 5 * STEP_DISTANCE, 0) + maxY = min(goal[1] + 5 * STEP_DISTANCE, len(img) - 1) + + i = 0 + while (goal not in points) and (len(points) < MAX_NUM_VERT): + if (i % 100) == 0: + print(i, 'points randomly generated') + + if (len(points) % hundreds) == 0: + print(len(points), 'vertex generated') + hundreds = hundreds + 100 + + while (occupied): + if phaseTwo and (random.random() > 0.8): + point = [random.randint(minX, maxX), random.randint(minY, maxY)] + else: + point = [random.randint(0, len(img[0]) - 1), random.randint(0, len(img) - 1)] + + if (img[point[1]][point[0]][0] * 255 == 255): + occupied = False + + occupied = True + + nearest = findNearestPoint(points, point) + newPoints = connectPoints(point, nearest, img) + addToGraph(ax, graph, newPoints, point) + newPoints.pop(0) # The first element is already in the points list + points.extend(newPoints) + ppl.draw() + i = i + 1 + + if len(points) >= MIN_NUM_VERT: + if not phaseTwo: + print('Phase Two') + phaseTwo = True + + if phaseTwo: + nearest = findNearestPoint(points, goal) + newPoints = connectPoints(goal, nearest, img) + addToGraph(ax, graph, newPoints, goal) + newPoints.pop(0) + points.extend(newPoints) + ppl.draw() + + if goal in points: + print('Goal found, total vertex in graph:', len(points), 'total random points generated:', i) + + path = searchPath(graph, start, [start]) + + for i in range(len(path) - 1): + ax.plot([path[i][0], path[i + 1][0]], [path[i][1], path[i + 1][1]], color='g', linestyle='-', + linewidth=2) + ppl.draw() + + print('Showing resulting map') + print('Final path:', path) + print('The final path is made from:', len(path), 'connected points') + else: + path = None + print('Reached maximum number of vertex and goal was not found') + print('Total vertex in graph:', len(points), 'total random points generated:', i) + print('Showing resulting map') + + ppl.show() + return path + + def searchPath(graph, point, path): + for i in graph: + if point == i[0]: + p = i + + if p[0] == graph[-1][0]: + return path + + for link in p[1]: + path.append(link) + finalPath = searchPath(graph, link, path) + + if finalPath != None: + return finalPath + else: + path.pop() + + def addToGraph(ax, graph, newPoints, point): + if len(newPoints) > 1: # If there is anything to add to the graph + for p in range(len(newPoints) - 1): + nearest = [nearest for nearest in graph if (nearest[0] == [newPoints[p][0], newPoints[p][1]])] + nearest[0][1].append(newPoints[p + 1]) + graph.append((newPoints[p + 1], [])) + + if not p == 0: + ax.plot(newPoints[p][0], newPoints[p][1], '+k') # First point is already painted + ax.plot([newPoints[p][0], newPoints[p + 1][0]], [newPoints[p][1], newPoints[p + 1][1]], color='k', + linestyle='-', linewidth=1) + + if point in newPoints: + ax.plot(point[0], point[1], '.g') # Last point is green + else: + ax.plot(newPoints[p + 1][0], newPoints[p + 1][1], '+k') # Last point is not green + + def connectPoints(a, b, img): + newPoints = [] + newPoints.append([b[0], b[1]]) + step = [(a[0] - b[0]) / float(STEP_DISTANCE), (a[1] - b[1]) / float(STEP_DISTANCE)] + + # Set small steps to check for walls + pointsNeeded = int(math.floor(max(math.fabs(step[0]), math.fabs(step[1])))) + + if math.fabs(step[0]) > math.fabs(step[1]): + if step[0] >= 0: + step = [1, step[1] / math.fabs(step[0])] + else: + step = [-1, step[1] / math.fabs(step[0])] + + else: + if step[1] >= 0: + step = [step[0] / math.fabs(step[1]), 1] + else: + step = [step[0] / math.fabs(step[1]), -1] + + blocked = False + for i in range(pointsNeeded + 1): # Creates points between graph and solitary point + for j in range(STEP_DISTANCE): # Check if there are walls between points + coordX = round(newPoints[i][0] + step[0] * j) + coordY = round(newPoints[i][1] + step[1] * j) + + if coordX == a[0] and coordY == a[1]: + break + if coordY >= len(img) or coordX >= len(img[0]): + break + if img[int(coordY)][int(coordX)][0] * 255 < 255: + blocked = True + if blocked: + break + + if blocked: + break + if not (coordX == a[0] and coordY == a[1]): + newPoints.append( + [newPoints[i][0] + (step[0] * STEP_DISTANCE), newPoints[i][1] + (step[1] * STEP_DISTANCE)]) + + if not blocked: + newPoints.append([a[0], a[1]]) + return newPoints + + def findNearestPoint(points, point): + best = (sys.maxsize, sys.maxsize, sys.maxsize) + for p in points: + if p == point: + continue + dist = math.sqrt((p[0] - point[0]) ** 2 + (p[1] - point[1]) ** 2) + if dist < best[2]: + best = (p[0], p[1], dist) + return (best[0], best[1]) + + # Standard imports + import cv2 + import numpy as np; + + # Img Objekt + cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + val, frame = cap.read() + inverted_image = np.invert(frame) + cv2.imwrite("map.jpg", inverted_image) + + print('Loading map... with file \'', MAP_IMG, '\'') + img = imread(MAP_IMG) + fig = ppl.gcf() + fig.clf() + ax = fig.add_subplot(1, 1, 1) + ax.imshow(img, cmap=cm.Greys_r) + ax.axis('image') + ppl.draw() + print('Map is', len(img[0]), 'x', len(img)) + + path = rapidlyExploringRandomTree(ax, img, start, goal, seed=SEED) + + if len(sys.argv) > 2: + print('Only one argument is needed') + elif len(sys.argv) > 1: + if os.path.isfile(sys.argv[1]): + MAP_IMG = sys.argv[1] + else: + print(sys.argv[1], 'is not a file') + + pathIndCntr = 0 + + return path, pathIndCntr + +start = (200,200) +ziel = (450, 450) +planInitialPath(start, ziel) \ No newline at end of file diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/potential_field_planning (1).py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/potential_field_planning (1).py new file mode 100644 index 0000000000000000000000000000000000000000..8f136b5ee3fbee7f9cd0d40fd368a4e10822030b --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/potential_field_planning (1).py @@ -0,0 +1,199 @@ +""" + +Potential Field based path planner + +author: Atsushi Sakai (@Atsushi_twi) + +Ref: +https://www.cs.cmu.edu/~motionplanning/lecture/Chap4-Potential-Field_howie.pdf + +""" + +from collections import deque +import numpy as np +import matplotlib.pyplot as plt + +# Parameters +KP = 5.0 # attractive potential gain +ETA = 100.0 # repulsive potential gain +AREA_WIDTH = 30.0 # potential area width [m] +# the number of previous positions used to check oscillations +OSCILLATIONS_DETECTION_LENGTH = 3 + +show_animation = True + + +def calc_potential_field(gx, gy, ox, oy, reso, rr, sx, sy): + minx = min(min(ox), sx, gx) - AREA_WIDTH / 2.0 + miny = min(min(oy), sy, gy) - AREA_WIDTH / 2.0 + maxx = max(max(ox), sx, gx) + AREA_WIDTH / 2.0 + maxy = max(max(oy), sy, gy) + AREA_WIDTH / 2.0 + xw = int(round((maxx - minx) / reso)) + yw = int(round((maxy - miny) / reso)) + + # calc each potential + pmap = [[0.0 for i in range(yw)] for i in range(xw)] + + for ix in range(xw): + x = ix * reso + minx + + for iy in range(yw): + y = iy * reso + miny + ug = calc_attractive_potential(x, y, gx, gy) + uo = calc_repulsive_potential(x, y, ox, oy, rr) + uf = ug + uo + pmap[ix][iy] = uf + + return pmap, minx, miny + + +def calc_attractive_potential(x, y, gx, gy): + return 0.5 * KP * np.hypot(x - gx, y - gy) + + +def calc_repulsive_potential(x, y, ox, oy, rr): + # search nearest obstacle + minid = -1 + dmin = float("inf") + for i, _ in enumerate(ox): + d = np.hypot(x - ox[i], y - oy[i]) + if dmin >= d: + dmin = d + minid = i + + # calc repulsive potential + dq = np.hypot(x - ox[minid], y - oy[minid]) + + if dq <= rr: + if dq <= 0.1: + dq = 0.1 + + return 0.5 * ETA * (1.0 / dq - 1.0 / rr) ** 2 + else: + return 0.0 + + +def get_motion_model(): + # dx, dy + motion = [[1, 0], + [0, 1], + [-1, 0], + [0, -1], + [-1, -1], + [-1, 1], + [1, -1], + [1, 1]] + + return motion + + +def oscillations_detection(previous_ids, ix, iy): + previous_ids.append((ix, iy)) + + if (len(previous_ids) > OSCILLATIONS_DETECTION_LENGTH): + previous_ids.popleft() + + # check if contains any duplicates by copying into a set + previous_ids_set = set() + for index in previous_ids: + if index in previous_ids_set: + return True + else: + previous_ids_set.add(index) + return False + + +def potential_field_planning(sx, sy, gx, gy, ox, oy, reso, rr): + + # calc potential field + pmap, minx, miny = calc_potential_field(gx, gy, ox, oy, reso, rr, sx, sy) + + # search path + d = np.hypot(sx - gx, sy - gy) + ix = round((sx - minx) / reso) + iy = round((sy - miny) / reso) + gix = round((gx - minx) / reso) + giy = round((gy - miny) / reso) + + if show_animation: + draw_heatmap(pmap) + # for stopping simulation with the esc key. + plt.gcf().canvas.mpl_connect('key_release_event', + lambda event: [exit(0) if event.key == 'escape' else None]) + plt.plot(ix, iy, "*k") + plt.plot(gix, giy, "*m") + + rx, ry = [sx], [sy] + motion = get_motion_model() + previous_ids = deque() + + while d >= reso: + minp = float("inf") + minix, miniy = -1, -1 + for i, _ in enumerate(motion): + inx = int(ix + motion[i][0]) + iny = int(iy + motion[i][1]) + if inx >= len(pmap) or iny >= len(pmap[0]) or inx < 0 or iny < 0: + p = float("inf") # outside area + print("outside potential!") + else: + p = pmap[inx][iny] + if minp > p: + minp = p + minix = inx + miniy = iny + ix = minix + iy = miniy + xp = ix * reso + minx + yp = iy * reso + miny + d = np.hypot(gx - xp, gy - yp) + rx.append(xp) + ry.append(yp) + + if (oscillations_detection(previous_ids, ix, iy)): + print("Oscillation detected at ({},{})!".format(ix, iy)) + break + + if show_animation: + plt.plot(ix, iy, ".r") + plt.pause(0.01) + + print("Goal!!") + + return rx, ry + + +def draw_heatmap(data): + data = np.array(data).T + plt.pcolor(data, vmax=100.0, cmap=plt.cm.Blues) + + +def main(): + print("potential_field_planning start") + + sx = 0.0 # start x position [m] + sy = 10.0 # start y positon [m] + gx = 30.0 # goal x position [m] + gy = 30.0 # goal y position [m] + grid_size = 0.5 # potential grid size [m] + robot_radius = 5.0 # robot radius [m] + + ox = [15.0, 5.0, 20.0, 25.0] # obstacle x position list [m] + oy = [25.0, 15.0, 26.0, 25.0] # obstacle y position list [m] + + if show_animation: + plt.grid(True) + plt.axis("equal") + + # path generation + _, _ = potential_field_planning( + sx, sy, gx, gy, ox, oy, grid_size, robot_radius) + + if show_animation: + plt.show() + + +if __name__ == '__main__': + print(__file__ + " start!!") + main() + print(__file__ + " Done!!") diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/potential_field_planning.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/potential_field_planning.py new file mode 100644 index 0000000000000000000000000000000000000000..c28279d83d3ea64eaee8592afabe70a8fe5372d1 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/potential_field_planning.py @@ -0,0 +1,248 @@ +""" + +Potential Field based path planner + +author: Atsushi Sakai (@Atsushi_twi) + +Ref: +https://www.cs.cmu.edu/~motionplanning/lecture/Chap4-Potential-Field_howie.pdf + +""" + +from collections import deque +import numpy as np +import matplotlib.pyplot as plt + +# Parameters +KP = 10.0 # attractive potential gain +ETA = 300.0 # repulsive potential gain +AREA_WIDTH = 704.0 # potential area width [m] +# the number of previous positions used to check oscillations +OSCILLATIONS_DETECTION_LENGTH = 3 + +show_animation = True + +px = [] +py = [] + +def calc_potential_field(gx, gy, ox, oy, reso, rr, sx, sy): + minx = min(min(ox), sx, gx) - AREA_WIDTH / 2.0 + miny = min(min(oy), sy, gy) - AREA_WIDTH / 2.0 + maxx = max(max(ox), sx, gx) + AREA_WIDTH / 2.0 + maxy = max(max(oy), sy, gy) + AREA_WIDTH / 2.0 + xw = int(round((maxx - minx) / reso)) + yw = int(round((maxy - miny) / reso)) + + # calc each potential + pmap = [[0.0 for i in range(yw)] for i in range(xw)] + + for ix in range(xw): + x = ix * reso + minx + + for iy in range(yw): + y = iy * reso + miny + ug = calc_attractive_potential(x, y, gx, gy) + uo = calc_repulsive_potential(x, y, ox, oy, rr) + uf = ug + uo + pmap[ix][iy] = uf + + return pmap, minx, miny + + +def calc_attractive_potential(x, y, gx, gy): + return 0.5 * KP * np.hypot(x - gx, y - gy) + + +def calc_repulsive_potential(x, y, ox, oy, rr): + # search nearest obstacle + minid = -1 + dmin = float("inf") + for i, _ in enumerate(ox): + d = np.hypot(x - ox[i], y - oy[i]) + if dmin >= d: + dmin = d + minid = i + + # calc repulsive potential + dq = np.hypot(x - ox[minid], y - oy[minid]) + + if dq <= rr: + if dq <= 0.1: + dq = 0.1 + + return 0.5 * ETA * (1.0 / dq - 1.0 / rr) ** 2 + else: + return 0.0 + + +def get_motion_model(): + # dx, dy + motion = [[1, 0], + [0, 1], + [-1, 0], + [0, -1], + [-1, -1], + [-1, 1], + [1, -1], + [1, 1]] + + return motion + + +def oscillations_detection(previous_ids, ix, iy): + previous_ids.append((ix, iy)) + + if (len(previous_ids) > OSCILLATIONS_DETECTION_LENGTH): + previous_ids.popleft() + + # check if contains any duplicates by copying into a set + previous_ids_set = set() + for index in previous_ids: + if index in previous_ids_set: + return True + else: + previous_ids_set.add(index) + return False + + +def potential_field_planning(sx, sy, gx, gy, ox, oy, reso, rr): + + # calc potential field + pmap, minx, miny = calc_potential_field(gx, gy, ox, oy, reso, rr, sx, sy) + + # search path + d = np.hypot(sx - gx, sy - gy) + ix = round((sx - minx) / reso) + iy = round((sy - miny) / reso) + gix = round((gx - minx) / reso) + giy = round((gy - miny) / reso) + + if show_animation: + draw_heatmap(pmap) + # for stopping simulation with the esc key. + plt.gcf().canvas.mpl_connect('key_release_event', + lambda event: [exit(0) if event.key == 'escape' else None]) + plt.plot(ix, iy, "*k") + plt.plot(gix, giy, "*m") + + rx, ry = [sx], [sy] + motion = get_motion_model() + previous_ids = deque() + + while d >= reso: + minp = float("inf") + minix, miniy = -1, -1 + for i, _ in enumerate(motion): + inx = int(ix + motion[i][0]) + iny = int(iy + motion[i][1]) + if inx >= len(pmap) or iny >= len(pmap[0]) or inx < 0 or iny < 0: + p = float("inf") # outside area + print("outside potential!") + else: + p = pmap[inx][iny] + if minp > p: + minp = p + minix = inx + miniy = iny + ix = minix + iy = miniy + xp = ix * reso + minx + yp = iy * reso + miny + d = np.hypot(gx - xp, gy - yp) + rx.append(xp) + ry.append(yp) + + if (oscillations_detection(previous_ids, ix, iy)): + print("Oscillation detected at ({},{})!".format(ix, iy)) + break + + if show_animation: + px.append(ix) + py.append(iy) + plt.plot(ix, iy, ".r") + plt.pause(0.01) + + print("Goal!!") + + return rx, ry + + +def draw_heatmap(data): + data = np.array(data).T + plt.pcolor(data, vmax=100.0, cmap=plt.cm.Blues) + + +def create_map(): + # Map erstellen + # Standard imports + import cv2 + import numpy as np; + + # Img Objekt + #cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + + #success, img = cap.read() + + gray = cv2.imread("/home/ubuntu/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/map/map.jpg",cv2.IMREAD_GRAYSCALE) + + #gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) + mask = cv2.inRange(gray, np.array([90]), np.array([255])) + + mapOfWorld = [[0]*gray.shape[1]]*gray.shape[0] + + mask_list= np.ndarray.tolist(mask) + + for i in range(0,mask.shape[0]): + mapOfWorld[i] = ['W' if j > 200 else ' ' for j in mask_list[i]] + + #mapOfWorld[200][200] = 'R' #Start Pos + #mapOfWorld[300][300] = 'G' #Ziel Pos + + return mapOfWorld + + +def create_obsticles(mapOfWorld): + ox = [] + oy = [] + + mapOfWorld = np.array(mapOfWorld) + + for i in range(0,mapOfWorld.shape[0]): + for j in range(0,mapOfWorld.shape[1]): + if mapOfWorld[i][j] == 'W': + ox.append(i) + oy.append(j) + + return ox,oy + +def main(): + print("potential_field_planning start") + + mapOfWorld = create_map() + + sx = 50 # start x position [m] + sy = 400 # start y positon [m] + gx = 600 # goal x position [m] + gy = 100 # goal y position [m] + grid_size = 10 # potential grid size [m] + robot_radius = 10 # robot radius [m] + + ox, oy = create_obsticles(mapOfWorld) + + if show_animation: + plt.grid(True) + plt.axis("equal") + + # path generation + _, _ = potential_field_planning(sx, sy, gx, gy, ox, oy, grid_size, robot_radius) + + if show_animation: + plt.show() + + print("X_Pos", px) + print("Y_Pos", py) + +if __name__ == '__main__': + print(__file__ + " start!!") + main() + print(__file__ + " Done!!") diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/simple_moves.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/simple_moves.py new file mode 100644 index 0000000000000000000000000000000000000000..b1e9f0e01674843c28ab6564ff572a695ff74d8b --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/simple_moves.py @@ -0,0 +1,34 @@ +""" +Simple moves +------------ + +This module contains functions to have the Sphero mini do some simple movements (e.g. circle). +""" + +import sys + +def forward(sphero): + # Aiming: + sphero.setLEDColor(red = 0, green = 0, blue = 0) # Turn main LED off + sphero.stabilization(False) # Turn off stabilization + sphero.setBackLEDIntensity(255) # turn back LED on + sphero.wait(3) # Non-blocking pau`se + sphero.resetHeading() # Reset heading + sphero.stabilization(True) # Turn on stabilization + sphero.setBackLEDIntensity(0) # Turn back LED off + + # Move around: + sphero.setLEDColor(red = 0, green = 0, blue = 255) # Turn main LED blue + sphero.roll(100, 0) # roll forwards (heading = 0) at speed = 50 + + sphero.wait(3) # Keep rolling for three seconds + + sphero.roll(0, 0) # stop + sphero.wait(1) # Allow time to stop + + sphero.setLEDColor(red = 0, green = 255, blue = 0) # Turn main LED green + sphero.roll(-100, 0) # Keep facing forwards but roll backwards at speed = 50 + sphero.wait(3) # Keep rolling for three seconds + + sphero.roll(0, 0) # stop + sphero.wait(1) # Allow time to stop diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/sphero.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/sphero.py new file mode 100644 index 0000000000000000000000000000000000000000..b5138d1b23a13a1117038f444cc8a0de0dbc0078 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/sphero.py @@ -0,0 +1,22 @@ +import numpy as np +def ref_winkel(sphero, soll): + + startpunkt = sphero.positionsauslesen + + sphero.roll(100,0) + sphero.wait(2) + sphero.roll(0,0) + sphero.wait(0.5) + + ref = sphero.positionsauslesen + + sphero.wait(0.5) + sphero.roll(100,0) + sphero.wait(2) + sphero.roll(0,0) + + start_ref = ref-startpunkt + start_soll = soll-startpunkt + phi = np.arccos(start_ref*start_soll / (np.linalg.norm(start_ref)*np.linalg.norm(start_soll))) + return phi + diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/sphero_main.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/sphero_main.py new file mode 100644 index 0000000000000000000000000000000000000000..37e337b8ed0ee0faf7ea8c2c1b698cc6e693f1f8 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/sphero_main.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 + +import json + +import rclpy +from rclpy.node import Node +from sensor_msgs.msg import Imu +from geometry_msgs.msg import Quaternion, Vector3 +from box import Box +import numpy as np +from core import SpheroMini +#from simple_moves import forward + +def connect(): + conf_file_path = rclpy.get_param("/conf_file_path") + with open(conf_file_path, 'r') as f: + cfg = Box(json.load(f)) + + # Connect: + sphero = SpheroMini(cfg.MAC_ADDRESS, verbosity = 4) + # battery voltage + sphero.getBatteryVoltage() + print(f"Bettery voltage: {sphero.v_batt}v") + + # firmware version number + sphero.returnMainApplicationVersion() + print(f"Firmware version: {'.'.join(str(x) for x in sphero.firmware_version)}") + return sphero + + +def disconnect(sphero): + sphero.sleep() + sphero.disconnect() + + +def create_quaternion(roll, pitch, yaw): + q = Quaternion() + cy, sy = np.cos(yaw * 0.5), np.sin(yaw * 0.5) + cp, sp = np.cos(pitch * 0.5), np.sin(pitch * 0.5) + cr, sr = np.cos(roll * 0.5), np.sin(roll * 0.5) + + q.w = cr * cp * cy + sr * sp * sy + q.x = sr * cp * cy - cr * sp * sy + q.y = cr * sp * cy + sr * cp * sy + q.z = cr * cp * sy - sr * sp * cy + + return q + + +def create_angular_veolocity_vector3(groll, gpitch, gyaw): + v = Vector3() + v.x = groll + v.y = gpitch + v.z = gyaw + + return v + + +def create_linear_acc_vector3(xacc, yacc, zacc): + v = Vector3() + v.x = xacc + v.y = yacc + v.z = zacc + + return v + + +def get_sensors_data(sphero): + return { + "roll": sphero.IMU_roll, + "pitch": sphero.IMU_pitch, + "yaw": sphero.IMU_yaw, + "groll": sphero.IMU_gyro_x, + "gpitch": sphero.IMU_gyro_y, + "xacc": sphero.IMU_acc_x, + "yacc": sphero.IMU_acc_y, + "zacc": sphero.IMU_acc_z + } + + +def publish_imu(pub, sensors_values): + i = Imu() + + i.header.stamp = rclpy.Time.now() + i.orientation = create_quaternion( + roll=sensors_values["roll"], + pitch=sensors_values["pitch"], + yaw=sensors_values["yaw"] + ) + i.angular_velocity = create_angular_veolocity_vector3( + groll=sensors_values["groll"], + gpitch=sensors_values["gpitch"], + gyaw=0 # We don't have the IMU_gyro_z + ) + i.linear_acceleration = create_linear_acc_vector3( + xacc=sensors_values["xacc"], + yacc=sensors_values["yacc"], + zacc=sensors_values["zacc"] + ) + pub.publish(i) + + +def main(sphero): + rclpy.init_node('sphero', anonymous=True, log_level=rclpy.DEBUG) + rate = rclpy.Rate(10) # 10 Hz + + pub = rclpy.Publisher("/imu", Imu, queue_size=5) + + sphero.configureSensorMask( + IMU_yaw=True, + IMU_pitch=True, + IMU_roll=True, + IMU_acc_y=True, + IMU_acc_z=True, + IMU_acc_x=True, + IMU_gyro_x=True, + IMU_gyro_y=True, + #IMU_gyro_z=True + ) + sphero.configureSensorStream() + + while not rclpy.is_shutdown(): + sensors_values = get_sensors_data(sphero) + #rclpy.logdebug(sensors_values) + publish_imu(pub, sensors_values) + sphero.setLEDColor(red = 0, green = 255, blue = 0) # Turn LEDs green + rate.sleep() + + +if __name__ == "__main__": + sphero = connect() + try: + main(sphero) + except Exception as e: # rclpy.ROSInterruptException + disconnect(sphero) + raise e + \ No newline at end of file diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/test.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/test.py new file mode 100644 index 0000000000000000000000000000000000000000..b5c4cbbe779a35653aad3fe176e1a0b9802319fd --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/test.py @@ -0,0 +1,23 @@ +#Map erstellen +# Standard imports +import cv2 +import numpy as np; + +# Img Objekt +cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + +success, img = cap.read() + +gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) +mask = cv2.inRange(gray, np.array([90]), np.array([255])) + +mapOfWorld = [[0]*gray.shape[1]]*gray.shape[0] + +mask_list= np.ndarray.tolist(mask) + +for i in range(0,mask.shape[0]): + mapOfWorld[i] = ['W' if j > 200 else ' ' for j in mask_list[i]] + +print(mapOfWorld) + + diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/treiber.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/treiber.py new file mode 100644 index 0000000000000000000000000000000000000000..ec3435051de5aa72fc393d224b0fe1a53927b8b0 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/treiber.py @@ -0,0 +1,638 @@ +from bluepy.btle import Peripheral +from bluepy import btle +from sphero_mini_controller._constants import * +import struct +import time +import sys + +class SpheroMini(): + def __init__(self, MACAddr, verbosity = 4, user_delegate = None): + ''' + initialize class instance and then build collect BLE sevices and characteristics. + Also sends text string to Anti-DOS characteristic to prevent returning to sleep, + and initializes notifications (which is what the sphero uses to send data back to + the client). + ''' + self.verbosity = verbosity # 0 = Silent, + # 1 = Connection/disconnection only + # 2 = Init messages + # 3 = Recieved commands + # 4 = Acknowledgements + self.sequence = 1 + self.v_batt = None # will be updated with battery voltage when sphero.getBatteryVoltage() is called + self.firmware_version = [] # will be updated with firware version when sphero.returnMainApplicationVersion() is called + + if self.verbosity > 0: + print("[INFO] Connecting to ", MACAddr) + self.p = Peripheral(MACAddr, "random") #connect + + if self.verbosity > 1: + print("[INIT] Initializing") + + # Subscribe to notifications + self.sphero_delegate = MyDelegate(self, user_delegate) # Pass a reference to this instance when initializing + self.p.setDelegate(self.sphero_delegate) + + if self.verbosity > 1: + print("[INIT] Read all characteristics and descriptors") + # Get characteristics and descriptors + self.API_V2_characteristic = self.p.getCharacteristics(uuid="00010002-574f-4f20-5370-6865726f2121")[0] + self.AntiDOS_characteristic = self.p.getCharacteristics(uuid="00020005-574f-4f20-5370-6865726f2121")[0] + self.DFU_characteristic = self.p.getCharacteristics(uuid="00020002-574f-4f20-5370-6865726f2121")[0] + self.DFU2_characteristic = self.p.getCharacteristics(uuid="00020004-574f-4f20-5370-6865726f2121")[0] + self.API_descriptor = self.API_V2_characteristic.getDescriptors(forUUID=0x2902)[0] + self.DFU_descriptor = self.DFU_characteristic.getDescriptors(forUUID=0x2902)[0] + + # The rest of this sequence was observed during bluetooth sniffing: + # Unlock code: prevent the sphero mini from going to sleep again after 10 seconds + if self.verbosity > 1: + print("[INIT] Writing AntiDOS characteristic unlock code") + self.AntiDOS_characteristic.write("usetheforce...band".encode(), withResponse=True) + + # Enable DFU notifications: + if self.verbosity > 1: + print("[INIT] Configuring DFU descriptor") + self.DFU_descriptor.write(struct.pack('<bb', 0x01, 0x00), withResponse = True) + + # No idea what this is for. Possibly a device ID of sorts? Read request returns '00 00 09 00 0c 00 02 02': + if self.verbosity > 1: + print("[INIT] Reading DFU2 characteristic") + _ = self.DFU2_characteristic.read() + + # Enable API notifications: + if self.verbosity > 1: + print("[INIT] Configuring API dectriptor") + self.API_descriptor.write(struct.pack('<bb', 0x01, 0x00), withResponse = True) + + self.wake() + + # Finished initializing: + if self.verbosity > 1: + print("[INIT] Initialization complete\n") + + def disconnect(self): + if self.verbosity > 0: + print("[INFO] Disconnecting") + + self.p.disconnect() + + def wake(self): + ''' + Bring device out of sleep mode (can only be done if device was in sleep, not deep sleep). + If in deep sleep, the device should be connected to USB power to wake. + ''' + if self.verbosity > 2: + print("[SEND {}] Waking".format(self.sequence)) + + self._send(characteristic=self.API_V2_characteristic, + devID=deviceID['powerInfo'], + commID=powerCommandIDs["wake"], + payload=[]) # empty payload + + self.getAcknowledgement("Wake") + + def sleep(self, deepSleep=False): + ''' + Put device to sleep or deep sleep (deep sleep needs USB power connected to wake up) + ''' + if deepSleep: + sleepCommID=powerCommandIDs["deepSleep"] + if self.verbosity > 0: + print("[INFO] Going into deep sleep. Connect USB power to wake.") + else: + sleepCommID=powerCommandIDs["sleep"] + self._send(characteristic=self.API_V2_characteristic, + devID=deviceID['powerInfo'], + commID=sleepCommID, + payload=[]) #empty payload + + def setLEDColor(self, red = None, green = None, blue = None): + ''' + Set device LED color based on RGB vales (each can range between 0 and 0xFF) + ''' + if self.verbosity > 2: + print("[SEND {}] Setting main LED colour to [{}, {}, {}]".format(self.sequence, red, green, blue)) + + self._send(characteristic = self.API_V2_characteristic, + devID = deviceID['userIO'], # 0x1a + commID = userIOCommandIDs["allLEDs"], # 0x0e + payload = [0x00, 0x0e, red, green, blue]) + + self.getAcknowledgement("LED/backlight") + + def setBackLEDIntensity(self, brightness=None): + ''' + Set device LED backlight intensity based on 0-255 values + + NOTE: this is not the same as aiming - it only turns on the LED + ''' + if self.verbosity > 2: + print("[SEND {}] Setting backlight intensity to {}".format(self.sequence, brightness)) + + self._send(characteristic = self.API_V2_characteristic, + devID = deviceID['userIO'], + commID = userIOCommandIDs["allLEDs"], + payload = [0x00, 0x01, brightness]) + + self.getAcknowledgement("LED/backlight") + + def roll(self, speed=None, heading=None): + ''' + Start to move the Sphero at a given direction and speed. + heading: integer from 0 - 360 (degrees) + speed: Integer from 0 - 255 + + Note: the zero heading should be set at startup with the resetHeading method. Otherwise, it may + seem that the sphero doesn't honor the heading argument + ''' + if self.verbosity > 2: + print("[SEND {}] Rolling with speed {} and heading {}".format(self.sequence, speed, heading)) + + if abs(speed) > 255: + print("WARNING: roll speed parameter outside of allowed range (-255 to +255)") + + if speed < 0: + speed = -1*speed+256 # speed values > 256 in the send packet make the spero go in reverse + + speedH = (speed & 0xFF00) >> 8 + speedL = speed & 0xFF + headingH = (heading & 0xFF00) >> 8 + headingL = heading & 0xFF + self._send(characteristic = self.API_V2_characteristic, + devID = deviceID['driving'], + commID = drivingCommands["driveWithHeading"], + payload = [speedL, headingH, headingL, speedH]) + + self.getAcknowledgement("Roll") + + def resetHeading(self): + ''' + Reset the heading zero angle to the current heading (useful during aiming) + Note: in order to manually rotate the sphero, you need to call stabilization(False). + Once the heading has been set, call stabilization(True). + ''' + if self.verbosity > 2: + print("[SEND {}] Resetting heading".format(self.sequence)) + + self._send(characteristic = self.API_V2_characteristic, + devID = deviceID['driving'], + commID = drivingCommands["resetHeading"], + payload = []) #empty payload + + self.getAcknowledgement("Heading") + + def returnMainApplicationVersion(self): + ''' + Sends command to return application data in a notification + ''' + if self.verbosity > 2: + print("[SEND {}] Requesting firmware version".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID = deviceID['systemInfo'], + commID = SystemInfoCommands['mainApplicationVersion'], + payload = []) # empty + + self.getAcknowledgement("Firmware") + + def getBatteryVoltage(self): + ''' + Sends command to return battery voltage data in a notification. + Data printed to console screen by the handleNotifications() method in the MyDelegate class. + ''' + if self.verbosity > 2: + print("[SEND {}] Requesting battery voltage".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID=deviceID['powerInfo'], + commID=powerCommandIDs['batteryVoltage'], + payload=[]) # empty + + self.getAcknowledgement("Battery") + + def stabilization(self, stab = True): + ''' + Sends command to turn on/off the motor stabilization system (required when manually turning/aiming the sphero) + ''' + if stab == True: + if self.verbosity > 2: + print("[SEND {}] Enabling stabilization".format(self.sequence)) + val = 1 + else: + if self.verbosity > 2: + print("[SEND {}] Disabling stabilization".format(self.sequence)) + val = 0 + self._send(self.API_V2_characteristic, + devID=deviceID['driving'], + commID=drivingCommands['stabilization'], + payload=[val]) + + self.getAcknowledgement("Stabilization") + + def wait(self, delay): + ''' + This is a non-blocking delay command. It is similar to time.sleep(), except it allows asynchronous + notification handling to still be performed. + ''' + start = time.time() + while(1): + self.p.waitForNotifications(0.001) + if time.time() - start > delay: + break + + def _send(self, characteristic=None, devID=None, commID=None, payload=[]): + ''' + A generic "send" method, which will be used by other methods to send a command ID, payload and + appropriate checksum to a specified device ID. Mainly useful because payloads are optional, + and can be of varying length, to convert packets to binary, and calculate and send the + checksum. For internal use only. + + Packet structure has the following format (in order): + + - Start byte: always 0x8D + - Flags byte: indicate response required, etc + - Virtual device ID: see _constants.py + - Command ID: see _constants.py + - Sequence number: Seems to be arbitrary. I suspect it is used to match commands to response packets (in which the number is echoed). + - Payload: Could be varying number of bytes (incl. none), depending on the command + - Checksum: See below for calculation + - End byte: always 0xD8 + + ''' + sendBytes = [sendPacketConstants["StartOfPacket"], + sum([flags["resetsInactivityTimeout"], flags["requestsResponse"]]), + devID, + commID, + self.sequence] + payload # concatenate payload list + + self.sequence += 1 # Increment sequence number, ensures we can identify response packets are for this command + if self.sequence > 255: + self.sequence = 0 + + # Compute and append checksum and add EOP byte: + # From Sphero docs: "The [checksum is the] modulo 256 sum of all the bytes + # from the device ID through the end of the data payload, + # bit inverted (1's complement)" + # For the sphero mini, the flag bits must be included too: + checksum = 0 + for num in sendBytes[1:]: + checksum = (checksum + num) & 0xFF # bitwise "and to get modulo 256 sum of appropriate bytes + checksum = 0xff - checksum # bitwise 'not' to invert checksum bits + sendBytes += [checksum, sendPacketConstants["EndOfPacket"]] # concatenate + + # Convert numbers to bytes + output = b"".join([x.to_bytes(1, byteorder='big') for x in sendBytes]) + + #send to specified characteristic: + characteristic.write(output, withResponse = True) + + def getAcknowledgement(self, ack): + #wait up to 10 secs for correct acknowledgement to come in, including sequence number! + start = time.time() + while(1): + self.p.waitForNotifications(1) + if self.sphero_delegate.notification_seq == self.sequence-1: # use one less than sequence, because _send function increments it for next send. + if self.verbosity > 3: + print("[RESP {}] {}".format(self.sequence-1, self.sphero_delegate.notification_ack)) + self.sphero_delegate.clear_notification() + break + elif self.sphero_delegate.notification_seq >= 0: + print("Unexpected ACK. Expected: {}/{}, received: {}/{}".format( + ack, self.sequence, self.sphero_delegate.notification_ack.split()[0], + self.sphero_delegate.notification_seq) + ) + if time.time() > start + 10: + print("Timeout waiting for acknowledgement: {}/{}".format(ack, self.sequence)) + break + +# ======================================================================= +# The following functions are experimental: +# ======================================================================= + + def configureCollisionDetection(self, + xThreshold = 50, + yThreshold = 50, + xSpeed = 50, + ySpeed = 50, + deadTime = 50, # in 10 millisecond increments + method = 0x01, # Must be 0x01 + callback = None): + ''' + Appears to function the same as other Sphero models, however speed settings seem to have no effect. + NOTE: Setting to zero seems to cause bluetooth errors with the Sphero Mini/bluepy library - set to + 255 to make it effectively disabled. + + deadTime disables future collisions for a short period of time to avoid repeat triggering by the same + event. Set in 10ms increments. So if deadTime = 50, that means the delay will be 500ms, or half a second. + + From Sphero docs: + + xThreshold/yThreshold: An 8-bit settable threshold for the X (left/right) and Y (front/back) axes + of Sphero. + + xSpeed/ySpeed: An 8-bit settable speed value for the X and Y axes. This setting is ranged by the + speed, then added to xThreshold, yThreshold to generate the final threshold value. + ''' + + if self.verbosity > 2: + print("[SEND {}] Configuring collision detection".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['configureCollision'], + payload=[method, xThreshold, xSpeed, yThreshold, ySpeed, deadTime]) + + self.collision_detection_callback = callback + + self.getAcknowledgement("Collision") + + def configureSensorStream(self): # Use default values + ''' + Send command to configure sensor stream using default values as found during bluetooth + sniffing of the Sphero Edu app. + + Must be called after calling configureSensorMask() + ''' + bitfield1 = 0b00000000 # Unknown function - needs experimenting + bitfield2 = 0b00000000 # Unknown function - needs experimenting + bitfield3 = 0b00000000 # Unknown function - needs experimenting + bitfield4 = 0b00000000 # Unknown function - needs experimenting + + if self.verbosity > 2: + print("[SEND {}] Configuring sensor stream".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['configureSensorStream'], + payload=[bitfield1, bitfield1, bitfield1, bitfield1]) + + self.getAcknowledgement("Sensor") + + def configureSensorMask(self, + sample_rate_divisor = 0x25, # Must be > 0 + packet_count = 0, + IMU_pitch = False, + IMU_roll = False, + IMU_yaw = False, + IMU_acc_x = False, + IMU_acc_y = False, + IMU_acc_z = False, + IMU_gyro_x = False, + IMU_gyro_y = False, + IMU_gyro_z = False): + + ''' + Send command to configure sensor mask using default values as found during bluetooth + sniffing of the Sphero Edu app. From experimentation, it seems that these are he functions of each: + + Sampling_rate_divisor. Slow data EG: Set to 0x32 to the divide data rate by 50. Setting below 25 (0x19) causes + bluetooth errors + + Packet_count: Select the number of packets to transmit before ending the stream. Set to zero to stream infinitely + + All IMU bool parameters: Toggle transmission of that value on or off (e.g. set IMU_acc_x = True to include the + X-axis accelerometer readings in the sensor stream) + ''' + + # Construct bitfields based on function parameters: + IMU_bitfield1 = (IMU_pitch<<2) + (IMU_roll<<1) + IMU_yaw + IMU_bitfield2 = ((IMU_acc_y<<7) + (IMU_acc_z<<6) + (IMU_acc_x<<5) + \ + (IMU_gyro_y<<4) + (IMU_gyro_x<<2) + (IMU_gyro_z<<2)) + + if self.verbosity > 2: + print("[SEND {}] Configuring sensor mask".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['sensorMask'], + payload=[0x00, # Unknown param - altering it seems to slow data rate. Possibly averages multiple readings? + sample_rate_divisor, + packet_count, # Packet count: select the number of packets to stop streaming after (zero = infinite) + 0b00, # Unknown param: seems to be another accelerometer bitfield? Z-acc, Y-acc + IMU_bitfield1, + IMU_bitfield2, + 0b00]) # reserved, Position?, Position?, velocity?, velocity?, Y-gyro, timer, reserved + + self.getAcknowledgement("Mask") + + ''' + Since the sensor values arrive as unlabelled lists in the order that they appear in the bitfields above, we need + to create a list of sensors that have been configured.Once we have this list, then in the default_delegate class, + we can get sensor values as attributes of the sphero_mini class. + e.g. print(sphero.IMU_yaw) # displays the current yaw angle + ''' + + # Initialize dictionary with sensor names as keys and their bool values (set by the user) as values: + availableSensors = {"IMU_pitch" : IMU_pitch, + "IMU_roll" : IMU_roll, + "IMU_yaw" : IMU_yaw, + "IMU_acc_y" : IMU_acc_y, + "IMU_acc_z" : IMU_acc_z, + "IMU_acc_x" : IMU_acc_x, + "IMU_gyro_y" : IMU_gyro_y, + "IMU_gyro_x" : IMU_gyro_x, + "IMU_gyro_z" : IMU_gyro_z} + + # Create list of of only sensors that have been "activated" (set as true in the method arguments): + self.configured_sensors = [name for name in availableSensors if availableSensors[name] == True] + + def sensor1(self): # Use default values + ''' + Unknown function. Observed in bluetooth sniffing. + ''' + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['sensor1'], + payload=[0x01]) + + self.getAcknowledgement("Sensor1") + + def sensor2(self): # Use default values + ''' + Unknown function. Observed in bluetooth sniffing. + ''' + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['sensor2'], + payload=[0x00]) + + self.getAcknowledgement("Sensor2") + +# ======================================================================= + +class MyDelegate(btle.DefaultDelegate): + + ''' + This class handles notifications (both responses and asynchronous notifications). + + Usage of this class is described in the Bluepy documentation + + ''' + + def __init__(self, sphero_class, user_delegate): + self.sphero_class = sphero_class # for saving sensor values as attributes of sphero class instance + self.user_delegate = user_delegate # to directly notify users of callbacks + btle.DefaultDelegate.__init__(self) + self.clear_notification() + self.notificationPacket = [] + + def clear_notification(self): + self.notification_ack = "DEFAULT ACK" + self.notification_seq = -1 + + def bits_to_num(self, bits): + ''' + This helper function decodes bytes from sensor packets into single precision floats. Encoding follows the + the IEEE-754 standard. + ''' + num = int(bits, 2).to_bytes(len(bits) // 8, byteorder='little') + num = struct.unpack('f', num)[0] + return num + + def handleNotification(self, cHandle, data): + ''' + This method acts as an interrupt service routine. When a notification comes in, this + method is invoked, with the variable 'cHandle' being the handle of the characteristic that + sent the notification, and 'data' being the payload (sent one byte at a time, so the packet + needs to be reconstructed) + + The method keeps appending bytes to the payload packet byte list until end-of-packet byte is + encountered. Note that this is an issue, because 0xD8 could be sent as part of the payload of, + say, the battery voltage notification. In future, a more sophisticated method will be required. + ''' + # Allow the user to intercept and process data first.. + if self.user_delegate != None: + if self.user_delegate.handleNotification(cHandle, data): + return + + #print("Received notification with packet ", str(data)) + + for data_byte in data: # parse each byte separately (sometimes they arrive simultaneously) + + self.notificationPacket.append(data_byte) # Add new byte to packet list + + # If end of packet (need to find a better way to segment the packets): + if data_byte == sendPacketConstants['EndOfPacket']: + # Once full the packet has arrived, parse it: + # Packet structure is similar to the outgoing send packets (see docstring in sphero_mini._send()) + + # Attempt to unpack. Might fail if packet is too badly corrupted + try: + start, flags_bits, devid, commcode, seq, *notification_payload, chsum, end = self.notificationPacket + except ValueError: + print("Warning: notification packet unparseable ", self.notificationPacket) + self.notificationPacket = [] # Discard this packet + return # exit + + # Compute and append checksum and add EOP byte: + # From Sphero docs: "The [checksum is the] modulo 256 sum of all the bytes + # from the device ID through the end of the data payload, + # bit inverted (1's complement)" + # For the sphero mini, the flag bits must be included too: + checksum_bytes = [flags_bits, devid, commcode, seq] + notification_payload + checksum = 0 # init + for num in checksum_bytes: + checksum = (checksum + num) & 0xFF # bitwise "and to get modulo 256 sum of appropriate bytes + checksum = 0xff - checksum # bitwise 'not' to invert checksum bits + if checksum != chsum: # check computed checksum against that recieved in the packet + print("Warning: notification packet checksum failed - ", str(self.notificationPacket)) + self.notificationPacket = [] # Discard this packet + return # exit + + # Check if response packet: + if flags_bits & flags['isResponse']: # it is a response + + # Use device ID and command code to determine which command is being acknowledged: + if devid == deviceID['powerInfo'] and commcode == powerCommandIDs['wake']: + self.notification_ack = "Wake acknowledged" # Acknowledgement after wake command + + elif devid == deviceID['driving'] and commcode == drivingCommands['driveWithHeading']: + self.notification_ack = "Roll command acknowledged" + + elif devid == deviceID['driving'] and commcode == drivingCommands['stabilization']: + self.notification_ack = "Stabilization command acknowledged" + + elif devid == deviceID['userIO'] and commcode == userIOCommandIDs['allLEDs']: + self.notification_ack = "LED/backlight color command acknowledged" + + elif devid == deviceID['driving'] and commcode == drivingCommands["resetHeading"]: + self.notification_ack = "Heading reset command acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["configureCollision"]: + self.notification_ack = "Collision detection configuration acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["configureSensorStream"]: + self.notification_ack = "Sensor stream configuration acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["sensorMask"]: + self.notification_ack = "Mask configuration acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["sensor1"]: + self.notification_ack = "Sensor1 acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["sensor2"]: + self.notification_ack = "Sensor2 acknowledged" + + elif devid == deviceID['powerInfo'] and commcode == powerCommandIDs['batteryVoltage']: + V_batt = notification_payload[2] + notification_payload[1]*256 + notification_payload[0]*65536 + V_batt /= 100 # Notification gives V_batt in 10mV increments. Divide by 100 to get to volts. + self.notification_ack = "Battery voltage:" + str(V_batt) + "v" + self.sphero_class.v_batt = V_batt + + elif devid == deviceID['systemInfo'] and commcode == SystemInfoCommands['mainApplicationVersion']: + version = '.'.join(str(x) for x in notification_payload) + self.notification_ack = "Firmware version: " + version + self.sphero_class.firmware_version = notification_payload + + else: + self.notification_ack = "Unknown acknowledgement" #print(self.notificationPacket) + print(self.notificationPacket, "===================> Unknown ack packet") + + self.notification_seq = seq + + else: # Not a response packet - therefore, asynchronous notification (e.g. collision detection, etc): + + # Collision detection: + if devid == deviceID['sensor'] and commcode == sensorCommands['collisionDetectedAsync']: + # The first four bytes are data that is still un-parsed. the remaining unsaved bytes are always zeros + _, _, _, _, _, _, axis, _, Y_mag, _, X_mag, *_ = notification_payload + if axis == 1: + dir = "Left/right" + else: + dir = 'Forward/back' + print("Collision detected:") + print("\tAxis: ", dir) + print("\tX_mag: ", X_mag) + print("\tY_mag: ", Y_mag) + + if self.sphero_class.collision_detection_callback is not None: + self.notificationPacket = [] # need to clear packet, in case new notification comes in during callback + self.sphero_class.collision_detection_callback() + + # Sensor response: + elif devid == deviceID['sensor'] and commcode == sensorCommands['sensorResponse']: + # Convert to binary, pad bytes with leading zeros: + val = '' + for byte in notification_payload: + val += format(int(bin(byte)[2:], 2), '#010b')[2:] + + # Break into 32-bit chunks + nums = [] + while(len(val) > 0): + num, val = val[:32], val[32:] # Slice off first 16 bits + nums.append(num) + + # convert from raw bits to float: + nums = [self.bits_to_num(num) for num in nums] + + # Set sensor values as class attributes: + for name, value in zip(self.sphero_class.configured_sensors, nums): + print("Setting sensor at .", name, str(value)) + setattr(self.sphero_class, name, value) + + # Unrecognized packet structure: + else: + self.notification_ack = "Unknown asynchronous notification" #print(self.notificationPacket) + print(str(self.notificationPacket) + " ===================> Unknown async packet") + + self.notificationPacket = [] # Start new payload after this byte \ No newline at end of file diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/wavefront.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/wavefront.py new file mode 100644 index 0000000000000000000000000000000000000000..1da387793ce36c131ca7ecc83355afdd5b70da7d --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/wavefront.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python +import time +import sys + +def newSearch(mapOfWorld, goal, start): + heap = [] + newheap = [] + x, y = goal + lastwave = 3 + # Start out by marking nodes around G with a 3 + moves = [(x + 1, y), (x - 1, y), (x, y - 1), (x, y + 1)] + + for move in moves: + if(mapOfWorld.positions[move] == ' '): + mapOfWorld.positions[move] = 3 + heap.append(move) + for currentwave in range(4, 10000): + lastwave = lastwave + 1 + while(heap != []): + position = heap.pop() + (x, y) = position + moves = [(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)] + #x, y = position + for move in moves: + if(mapOfWorld.positions[move] != 'W'): + if(mapOfWorld.positions[move] == ' ' and mapOfWorld.positions[position] == currentwave - 1): + mapOfWorld.positions[move] = currentwave + newheap.append(move) + if(move == start): + return mapOfWorld, lastwave + + #time.sleep(0.25) + #mapOfWorld.display() + #print heap + if(newheap == []): + print("Goal is unreachable") + return 1 + heap = newheap + newheap = [] + +def printf(format, *args): + sys.stdout.write(format % args) + +class Map(object): + + def __init__(self, xdim, ydim, positions): + self.xdim = xdim + self.ydim = ydim + self.positions = positions + def display(self): + printf(" ") + for i in range(self.ydim): + printf("%3s", str(i)) + print + for x in range(self.xdim): + printf("%2s", str(x)) + for y in range(self.ydim): + printf("%3s", str(self.positions[(x, y)])) + print + # Navigate though the number-populated maze + def nav(self, start, current): + self.pos = start + finished = False + + while(finished == False): # Run this code until we're at the goal + x, y = self.pos + self.positions[self.pos] = 'R' # Set the start on the map (this USUALLY keeps start the same) + # SOUTH NORTH WEST EAST + # v v v v + moves = [(x + 1, y), (x - 1, y), (x, y - 1), (x, y + 1)] # Establish our directions + moveDirections = ["South", "North", "West", "East"] # Create a corresponding list of the cardinal directions + """ We don't want least to be 0, because then nothing would be less than it. + However, in order to make our code more robust, we set it to one of the values, + so that we're comparing least to an actual value instead of an arbitrary number (like 10). + """ + # Do the actual comparing, and give us the least index so we know which move was the least + for w in range(len(moves)): + move = moves[w] + + # If the position has the current wave - 1 in it, move there. + if(self.positions[move] == current - 1): + self.least = self.positions[move] + leastIndex = w + # Or, if the position is the goal, stop the loop + elif(self.positions[move] == 'G'): + finished = True + leastIndex = w + # Decrement the current number so we can look for the next number + current = current - 1 + self.positions[self.pos] = ' ' + print( "Moved " + moveDirections[leastIndex]) + self.pos = moves[leastIndex] # This will be converted to "move robot in x direction" + + #time.sleep(0.25) + #self.display() + # Change the goal position (or wherever we stop) to an "!" to show that we've arrived. + self.positions[self.pos] = '!' + self.display() +# Find the goal, given the map +def findGoal(mapOfWorld): + positions = mapOfWorld.positions + for x in range(mapOfWorld.xdim): + for y in range(mapOfWorld.ydim): + if(mapOfWorld.positions[(x, y)] == 'G'): + return (x, y) +# Find the start, given the map +def findStart(mapOfWorld): + positions = mapOfWorld.positions + for x in range(mapOfWorld.xdim): + for y in range(mapOfWorld.ydim): + if(mapOfWorld.positions[(x, y)] == 'R'): + + return (x, y) + +def convertMap(mapOfWorld): + positions = {} + xdim = len(mapOfWorld) + ydim = len(mapOfWorld[1]) + for y in range(ydim): + for x in range(xdim): + positions[(x, y)] = mapOfWorld[x][y] + + return Map(xdim, ydim, positions) + +# Map erstellen +# Standard imports +import cv2 +import numpy as np; + +# Img Objekt +cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + +success, img = cap.read() + +gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) +mask = cv2.inRange(gray, np.array([90]), np.array([255])) + +mapOfWorld = [[0]*gray.shape[1]]*gray.shape[0] + +mask_list= np.ndarray.tolist(mask) + +for i in range(0,mask.shape[0]): + mapOfWorld[i] = ['W' if j > 200 else ' ' for j in mask_list[i]] + +mapOfWorld[200][200] = 'R' #Start Pos +mapOfWorld[300][300] = 'G' #Ziel Pos + +print(mapOfWorld[200][200]) +print(mapOfWorld[300][300]) + +mapOfLand = convertMap(mapOfWorld) +mapOfLand.display() +mapOfLand, lastwave = newSearch(mapOfLand, findGoal(mapOfLand), findStart(mapOfLand)) +mapOfLand.nav(findStart(mapOfLand), lastwave) diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/wavefront_coverage_path_planner.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/wavefront_coverage_path_planner.py new file mode 100644 index 0000000000000000000000000000000000000000..85861402d61f643d8a0639c708a494cca6383170 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/wavefront_coverage_path_planner.py @@ -0,0 +1,218 @@ +""" +Distance/Path Transform Wavefront Coverage Path Planner + +author: Todd Tang +paper: Planning paths of complete coverage of an unstructured environment + by a mobile robot - Zelinsky et.al. +link: http://pinkwink.kr/attachment/cfile3.uf@1354654A4E8945BD13FE77.pdf +""" + +import os +import sys + +import matplotlib.pyplot as plt +import numpy as np +from scipy import ndimage + +do_animation = True + + +def transform( + grid_map, src, distance_type='chessboard', + transform_type='path', alpha=0.01 +): + """transform + + calculating transform of transform_type from src + in given distance_type + + :param grid_map: 2d binary map + :param src: distance transform source + :param distance_type: type of distance used + :param transform_type: type of transform used + :param alpha: weight of Obstacle Transform used when using path_transform + """ + + n_rows, n_cols = grid_map.shape + + if n_rows == 0 or n_cols == 0: + sys.exit('Empty grid_map.') + + inc_order = [[0, 1], [1, 1], [1, 0], [1, -1], + [0, -1], [-1, -1], [-1, 0], [-1, 1]] + if distance_type == 'chessboard': + cost = [1, 1, 1, 1, 1, 1, 1, 1] + elif distance_type == 'eculidean': + cost = [1, np.sqrt(2), 1, np.sqrt(2), 1, np.sqrt(2), 1, np.sqrt(2)] + else: + sys.exit('Unsupported distance type.') + + transform_matrix = float('inf') * np.ones_like(grid_map, dtype=float) + transform_matrix[src[0], src[1]] = 0 + if transform_type == 'distance': + eT = np.zeros_like(grid_map) + elif transform_type == 'path': + eT = ndimage.distance_transform_cdt(1 - grid_map, distance_type) + else: + sys.exit('Unsupported transform type.') + + # set obstacle transform_matrix value to infinity + for i in range(n_rows): + for j in range(n_cols): + if grid_map[i][j] == 1.0: + transform_matrix[i][j] = float('inf') + is_visited = np.zeros_like(transform_matrix, dtype=bool) + is_visited[src[0], src[1]] = True + traversal_queue = [src] + calculated = [(src[0] - 1) * n_cols + src[1]] + + def is_valid_neighbor(g_i, g_j): + return 0 <= g_i < n_rows and 0 <= g_j < n_cols \ + and not grid_map[g_i][g_j] + + while traversal_queue: + i, j = traversal_queue.pop(0) + for k, inc in enumerate(inc_order): + ni = i + inc[0] + nj = j + inc[1] + if is_valid_neighbor(ni, nj): + is_visited[i][j] = True + + # update transform_matrix + transform_matrix[i][j] = min( + transform_matrix[i][j], + transform_matrix[ni][nj] + cost[k] + alpha * eT[ni][nj]) + + if not is_visited[ni][nj] \ + and ((ni - 1) * n_cols + nj) not in calculated: + traversal_queue.append((ni, nj)) + calculated.append((ni - 1) * n_cols + nj) + + return transform_matrix + + +def get_search_order_increment(start, goal): + if start[0] >= goal[0] and start[1] >= goal[1]: + order = [[1, 0], [0, 1], [-1, 0], [0, -1], + [1, 1], [1, -1], [-1, 1], [-1, -1]] + elif start[0] <= goal[0] and start[1] >= goal[1]: + order = [[-1, 0], [0, 1], [1, 0], [0, -1], + [-1, 1], [-1, -1], [1, 1], [1, -1]] + elif start[0] >= goal[0] and start[1] <= goal[1]: + order = [[1, 0], [0, -1], [-1, 0], [0, 1], + [1, -1], [-1, -1], [1, 1], [-1, 1]] + elif start[0] <= goal[0] and start[1] <= goal[1]: + order = [[-1, 0], [0, -1], [0, 1], [1, 0], + [-1, -1], [-1, 1], [1, -1], [1, 1]] + else: + sys.exit('get_search_order_increment: cannot determine \ + start=>goal increment order') + return order + + +def wavefront(transform_matrix, start, goal): + """wavefront + + performing wavefront coverage path planning + + :param transform_matrix: the transform matrix + :param start: start point of planning + :param goal: goal point of planning + """ + + path = [] + n_rows, n_cols = transform_matrix.shape + + def is_valid_neighbor(g_i, g_j): + is_i_valid_bounded = 0 <= g_i < n_rows + is_j_valid_bounded = 0 <= g_j < n_cols + if is_i_valid_bounded and is_j_valid_bounded: + return not is_visited[g_i][g_j] and \ + transform_matrix[g_i][g_j] != float('inf') + return False + + inc_order = get_search_order_increment(start, goal) + + current_node = start + is_visited = np.zeros_like(transform_matrix, dtype=bool) + + while current_node != goal: + i, j = current_node + path.append((i, j)) + is_visited[i][j] = True + + max_T = float('-inf') + i_max = (-1, -1) + i_last = 0 + for i_last in range(len(path)): + current_node = path[-1 - i_last] # get latest node in path + for ci, cj in inc_order: + ni, nj = current_node[0] + ci, current_node[1] + cj + if is_valid_neighbor(ni, nj) and \ + transform_matrix[ni][nj] > max_T: + i_max = (ni, nj) + max_T = transform_matrix[ni][nj] + + if i_max != (-1, -1): + break + + if i_max == (-1, -1): + break + else: + current_node = i_max + if i_last != 0: + print('backtracing to', current_node) + path.append(goal) + + return path + + +def visualize_path(grid_map, start, goal, path): # pragma: no cover + oy, ox = start + gy, gx = goal + px, py = np.transpose(np.flipud(np.fliplr(path))) + + if not do_animation: + plt.imshow(grid_map, cmap='Greys') + plt.plot(ox, oy, "-xy") + plt.plot(px, py, "-r") + plt.plot(gx, gy, "-pg") + plt.show() + else: + for ipx, ipy in zip(px, py): + plt.cla() + # for stopping simulation with the esc key. + plt.gcf().canvas.mpl_connect( + 'key_release_event', + lambda event: [exit(0) if event.key == 'escape' else None]) + plt.imshow(grid_map, cmap='Greys') + plt.plot(ox, oy, "-xb") + plt.plot(px, py, "-r") + plt.plot(gx, gy, "-pg") + plt.plot(ipx, ipy, "or") + plt.axis("equal") + plt.grid(True) + plt.pause(0.1) + + +def main(): + dir_path = os.path.dirname(os.path.realpath(__file__)) + img = plt.imread(os.path.join(dir_path, 'map', 'test_2.png')) + img = 1 - img # revert pixel values + + start = (43, 0) + goal = (0, 0) + + # distance transform wavefront + DT = transform(img, goal, transform_type='distance') + DT_path = wavefront(DT, start, goal) + visualize_path(img, start, goal, DT_path) + + # path transform wavefront + PT = transform(img, goal, transform_type='path', alpha=0.01) + PT_path = wavefront(PT, start, goal) + visualize_path(img, start, goal, PT_path) + + +if __name__ == "__main__": + main() diff --git a/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/wavefrontgpt2.py b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/wavefrontgpt2.py new file mode 100644 index 0000000000000000000000000000000000000000..ef76c9f457ef6872c3b242d9b6815864c9ed7a5c --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/wavefrontgpt2.py @@ -0,0 +1,76 @@ +from queue import Queue + +def wavefront_planner(map_array, start_pos, goal_pos): + rows = len(map_array) + cols = len(map_array[0]) + + # Überprüfe, ob Start- und Zielposition innerhalb der Karte liegen + if (start_pos[0] < 0 or start_pos[0] >= rows or start_pos[1] < 0 or start_pos[1] >= cols or + goal_pos[0] < 0 or goal_pos[0] >= rows or goal_pos[1] < 0 or goal_pos[1] >= cols): + raise ValueError("Start or goal position is out of bounds.") + + # Erzeuge eine Kopie der Karte für den Wavefront-Algorithmus + wavefront_map = [[-1] * cols for _ in range(rows)] + + # Definiere die Bewegungsrichtungen (4-Wege-Bewegung: oben, unten, links, rechts) + directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] + + # Erzeuge eine Warteschlange für die Wellenfrontausbreitung + queue = Queue() + queue.put(goal_pos) + + # Führe die Wellenfrontausbreitung durch + wavefront_map[goal_pos[0]][goal_pos[1]] = 0 + while not queue.empty(): + current_pos = queue.get() + + # Überprüfe die Nachbarzellen + for direction in directions: + new_pos = (current_pos[0] + direction[0], current_pos[1] + direction[1]) + + # Überprüfe, ob die Nachbarzelle gültig und noch nicht besucht ist + if (0 <= new_pos[0] < rows and 0 <= new_pos[1] < cols and + wavefront_map[new_pos[0]][new_pos[1]] == -1 and map_array[new_pos[0]][new_pos[1]] == 0): + wavefront_map[new_pos[0]][new_pos[1]] = wavefront_map[current_pos[0]][current_pos[1]] + 1 + queue.put(new_pos) + + # Überprüfe, ob der Startpunkt erreichbar ist + if wavefront_map[start_pos[0]][start_pos[1]] == -1: + raise ValueError("Start position is unreachable.") + + # Verfolge den Pfad basierend auf der Wellenfront + path = [start_pos] + current_pos = start_pos + while current_pos != goal_pos: + next_pos = None + min_distance = float('inf') + + for direction in directions: + neighbor_pos = (current_pos[0] + direction[0], current_pos[1] + direction[1]) + + if (0 <= neighbor_pos[0] < rows and 0 <= neighbor_pos[1] < cols and + wavefront_map[neighbor_pos[0]][neighbor_pos[1]] < min_distance): + next_pos = neighbor_pos + min_distance = wavefront_map[neighbor_pos[0]][neighbor_pos[1]] + + if next_pos is None: + raise ValueError("No path found.") + + path.append(next_pos) + current_pos = next_pos + + return path + +# Beispielverwendung +map_array = [ + [0, 0, 0, 0], + [0, 1, 1, 0], + [0, 0, 0, 0], + [0, 1, 1, 0], +] + +start_pos = (0, 0) +goal_pos = (1, 3) + +path = wavefront_planner(map_array, start_pos, goal_pos) +print("Path:", path) diff --git a/ros2_ws/install/sphero_mini_controller/lib/sphero_mini_controller/sphero_mini b/ros2_ws/install/sphero_mini_controller/lib/sphero_mini_controller/sphero_mini new file mode 100644 index 0000000000000000000000000000000000000000..01ae998b4f9a5df3b55c3a10b2f5bbbc5fc171e8 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/sphero_mini_controller/sphero_mini @@ -0,0 +1,33 @@ +#!/usr/bin/python3 +# EASY-INSTALL-ENTRY-SCRIPT: 'sphero-mini-controller==0.0.0','console_scripts','sphero_mini' +import re +import sys + +# for compatibility with easy_install; see #2198 +__requires__ = 'sphero-mini-controller==0.0.0' + +try: + from importlib.metadata import distribution +except ImportError: + try: + from importlib_metadata import distribution + except ImportError: + from pkg_resources import load_entry_point + + +def importlib_load_entry_point(spec, group, name): + dist_name, _, _ = spec.partition('==') + matches = ( + entry_point + for entry_point in distribution(dist_name).entry_points + if entry_point.group == group and entry_point.name == name + ) + return next(matches).load() + + +globals().setdefault('load_entry_point', importlib_load_entry_point) + + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(load_entry_point('sphero-mini-controller==0.0.0', 'console_scripts', 'sphero_mini')()) diff --git a/ros2_ws/install/sphero_mini_controller/lib/sphero_mini_controller/test_node b/ros2_ws/install/sphero_mini_controller/lib/sphero_mini_controller/test_node new file mode 100644 index 0000000000000000000000000000000000000000..42b5555ce7a5ab5c9ccab6e382562d82c4e11a22 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/lib/sphero_mini_controller/test_node @@ -0,0 +1,33 @@ +#!/usr/bin/python3 +# EASY-INSTALL-ENTRY-SCRIPT: 'sphero-mini-controller==0.0.0','console_scripts','test_node' +import re +import sys + +# for compatibility with easy_install; see #2198 +__requires__ = 'sphero-mini-controller==0.0.0' + +try: + from importlib.metadata import distribution +except ImportError: + try: + from importlib_metadata import distribution + except ImportError: + from pkg_resources import load_entry_point + + +def importlib_load_entry_point(spec, group, name): + dist_name, _, _ = spec.partition('==') + matches = ( + entry_point + for entry_point in distribution(dist_name).entry_points + if entry_point.group == group and entry_point.name == name + ) + return next(matches).load() + + +globals().setdefault('load_entry_point', importlib_load_entry_point) + + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(load_entry_point('sphero-mini-controller==0.0.0', 'console_scripts', 'test_node')()) diff --git a/ros2_ws/install/sphero_mini_controller/share/ament_index/resource_index/packages/sphero_mini_controller b/ros2_ws/install/sphero_mini_controller/share/ament_index/resource_index/packages/sphero_mini_controller new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ros2_ws/install/sphero_mini_controller/share/colcon-core/packages/sphero_mini_controller b/ros2_ws/install/sphero_mini_controller/share/colcon-core/packages/sphero_mini_controller new file mode 100644 index 0000000000000000000000000000000000000000..4fb1cbf964f229c49b5b6b9bf5ce6c251451f9e1 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/share/colcon-core/packages/sphero_mini_controller @@ -0,0 +1 @@ +_constants:bluepy:bluepy.btle:box:cv2:geometry_msgs.msg:matplotlib.pyplot:numpy:queue:rclpy:sensor_msgs.msg:struct:sys:threading:time:treiber \ No newline at end of file diff --git a/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/ament_prefix_path.dsv b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/ament_prefix_path.dsv new file mode 100644 index 0000000000000000000000000000000000000000..79d4c95b55cb72a17c9be498c3758478e2c7bb8d --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/ament_prefix_path.dsv @@ -0,0 +1 @@ +prepend-non-duplicate;AMENT_PREFIX_PATH; diff --git a/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/ament_prefix_path.ps1 b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/ament_prefix_path.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..26b99975794bb42ea3d6a17150e313cbfc45fc24 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/ament_prefix_path.ps1 @@ -0,0 +1,3 @@ +# generated from colcon_powershell/shell/template/hook_prepend_value.ps1.em + +colcon_prepend_unique_value AMENT_PREFIX_PATH "$env:COLCON_CURRENT_PREFIX" diff --git a/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/ament_prefix_path.sh b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/ament_prefix_path.sh new file mode 100644 index 0000000000000000000000000000000000000000..f3041f688a623ea5c66e65c917bc503e5fae6dc9 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/ament_prefix_path.sh @@ -0,0 +1,3 @@ +# generated from colcon_core/shell/template/hook_prepend_value.sh.em + +_colcon_prepend_unique_value AMENT_PREFIX_PATH "$COLCON_CURRENT_PREFIX" diff --git a/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/pythonpath.dsv b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/pythonpath.dsv new file mode 100644 index 0000000000000000000000000000000000000000..257067d44dde1ca226afa26a3def27b5e9ee7cbb --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/pythonpath.dsv @@ -0,0 +1 @@ +prepend-non-duplicate;PYTHONPATH;lib/python3.10/site-packages diff --git a/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/pythonpath.ps1 b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/pythonpath.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..caffe83fd5f7176279ca966b9f2d43b5e275c9b2 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/pythonpath.ps1 @@ -0,0 +1,3 @@ +# generated from colcon_powershell/shell/template/hook_prepend_value.ps1.em + +colcon_prepend_unique_value PYTHONPATH "$env:COLCON_CURRENT_PREFIX\lib/python3.10/site-packages" diff --git a/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/pythonpath.sh b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/pythonpath.sh new file mode 100644 index 0000000000000000000000000000000000000000..660c34836dfb678ad2d087f61a86a435b6715d2b --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/pythonpath.sh @@ -0,0 +1,3 @@ +# generated from colcon_core/shell/template/hook_prepend_value.sh.em + +_colcon_prepend_unique_value PYTHONPATH "$COLCON_CURRENT_PREFIX/lib/python3.10/site-packages" diff --git a/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.bash b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.bash new file mode 100644 index 0000000000000000000000000000000000000000..ba62e9b8979eb16b0f9c9dc4aedaca7582d2d4a3 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.bash @@ -0,0 +1,31 @@ +# generated from colcon_bash/shell/template/package.bash.em + +# This script extends the environment for this package. + +# a bash script is able to determine its own path if necessary +if [ -z "$COLCON_CURRENT_PREFIX" ]; then + # the prefix is two levels up from the package specific share directory + _colcon_package_bash_COLCON_CURRENT_PREFIX="$(builtin cd "`dirname "${BASH_SOURCE[0]}"`/../.." > /dev/null && pwd)" +else + _colcon_package_bash_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX" +fi + +# function to source another script with conditional trace output +# first argument: the path of the script +# additional arguments: arguments to the script +_colcon_package_bash_source_script() { + if [ -f "$1" ]; then + if [ -n "$COLCON_TRACE" ]; then + echo ". \"$1\"" + fi + . "$@" + else + echo "not found: \"$1\"" 1>&2 + fi +} + +# source sh script of this package +_colcon_package_bash_source_script "$_colcon_package_bash_COLCON_CURRENT_PREFIX/share/sphero_mini_controller/package.sh" + +unset _colcon_package_bash_source_script +unset _colcon_package_bash_COLCON_CURRENT_PREFIX diff --git a/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.dsv b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.dsv new file mode 100644 index 0000000000000000000000000000000000000000..2e5c1c2d2b0a1c60797a05178ac6529a7239d39d --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.dsv @@ -0,0 +1,6 @@ +source;share/sphero_mini_controller/hook/pythonpath.ps1 +source;share/sphero_mini_controller/hook/pythonpath.dsv +source;share/sphero_mini_controller/hook/pythonpath.sh +source;share/sphero_mini_controller/hook/ament_prefix_path.ps1 +source;share/sphero_mini_controller/hook/ament_prefix_path.dsv +source;share/sphero_mini_controller/hook/ament_prefix_path.sh diff --git a/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.ps1 b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..66a433ec81a81ddcc339ec1919cdbad24df67c36 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.ps1 @@ -0,0 +1,116 @@ +# generated from colcon_powershell/shell/template/package.ps1.em + +# function to append a value to a variable +# which uses colons as separators +# duplicates as well as leading separators are avoided +# first argument: the name of the result variable +# second argument: the value to be prepended +function colcon_append_unique_value { + param ( + $_listname, + $_value + ) + + # get values from variable + if (Test-Path Env:$_listname) { + $_values=(Get-Item env:$_listname).Value + } else { + $_values="" + } + $_duplicate="" + # start with no values + $_all_values="" + # iterate over existing values in the variable + if ($_values) { + $_values.Split(";") | ForEach { + # not an empty string + if ($_) { + # not a duplicate of _value + if ($_ -eq $_value) { + $_duplicate="1" + } + if ($_all_values) { + $_all_values="${_all_values};$_" + } else { + $_all_values="$_" + } + } + } + } + # append only non-duplicates + if (!$_duplicate) { + # avoid leading separator + if ($_all_values) { + $_all_values="${_all_values};${_value}" + } else { + $_all_values="${_value}" + } + } + + # export the updated variable + Set-Item env:\$_listname -Value "$_all_values" +} + +# function to prepend a value to a variable +# which uses colons as separators +# duplicates as well as trailing separators are avoided +# first argument: the name of the result variable +# second argument: the value to be prepended +function colcon_prepend_unique_value { + param ( + $_listname, + $_value + ) + + # get values from variable + if (Test-Path Env:$_listname) { + $_values=(Get-Item env:$_listname).Value + } else { + $_values="" + } + # start with the new value + $_all_values="$_value" + # iterate over existing values in the variable + if ($_values) { + $_values.Split(";") | ForEach { + # not an empty string + if ($_) { + # not a duplicate of _value + if ($_ -ne $_value) { + # keep non-duplicate values + $_all_values="${_all_values};$_" + } + } + } + } + # export the updated variable + Set-Item env:\$_listname -Value "$_all_values" +} + +# function to source another script with conditional trace output +# first argument: the path of the script +# additional arguments: arguments to the script +function colcon_package_source_powershell_script { + param ( + $_colcon_package_source_powershell_script + ) + # source script with conditional trace output + if (Test-Path $_colcon_package_source_powershell_script) { + if ($env:COLCON_TRACE) { + echo ". '$_colcon_package_source_powershell_script'" + } + . "$_colcon_package_source_powershell_script" + } else { + Write-Error "not found: '$_colcon_package_source_powershell_script'" + } +} + + +# a powershell script is able to determine its own path +# the prefix is two levels up from the package specific share directory +$env:COLCON_CURRENT_PREFIX=(Get-Item $PSCommandPath).Directory.Parent.Parent.FullName + +colcon_package_source_powershell_script "$env:COLCON_CURRENT_PREFIX\share/sphero_mini_controller/hook/pythonpath.ps1" +colcon_package_source_powershell_script "$env:COLCON_CURRENT_PREFIX\share/sphero_mini_controller/hook/ament_prefix_path.ps1" + +Remove-Item Env:\COLCON_CURRENT_PREFIX diff --git a/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.sh b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.sh new file mode 100644 index 0000000000000000000000000000000000000000..ccf55cf346e95c4a74b8a9920895946946143fa7 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.sh @@ -0,0 +1,87 @@ +# generated from colcon_core/shell/template/package.sh.em + +# This script extends the environment for this package. + +# function to prepend a value to a variable +# which uses colons as separators +# duplicates as well as trailing separators are avoided +# first argument: the name of the result variable +# second argument: the value to be prepended +_colcon_prepend_unique_value() { + # arguments + _listname="$1" + _value="$2" + + # get values from variable + eval _values=\"\$$_listname\" + # backup the field separator + _colcon_prepend_unique_value_IFS=$IFS + IFS=":" + # start with the new value + _all_values="$_value" + # workaround SH_WORD_SPLIT not being set in zsh + if [ "$(command -v colcon_zsh_convert_to_array)" ]; then + colcon_zsh_convert_to_array _values + fi + # iterate over existing values in the variable + for _item in $_values; do + # ignore empty strings + if [ -z "$_item" ]; then + continue + fi + # ignore duplicates of _value + if [ "$_item" = "$_value" ]; then + continue + fi + # keep non-duplicate values + _all_values="$_all_values:$_item" + done + unset _item + # restore the field separator + IFS=$_colcon_prepend_unique_value_IFS + unset _colcon_prepend_unique_value_IFS + # export the updated variable + eval export $_listname=\"$_all_values\" + unset _all_values + unset _values + + unset _value + unset _listname +} + +# since a plain shell script can't determine its own path when being sourced +# either use the provided COLCON_CURRENT_PREFIX +# or fall back to the build time prefix (if it exists) +_colcon_package_sh_COLCON_CURRENT_PREFIX="/home/ubuntu/ros2_ws/install/sphero_mini_controller" +if [ -z "$COLCON_CURRENT_PREFIX" ]; then + if [ ! -d "$_colcon_package_sh_COLCON_CURRENT_PREFIX" ]; then + echo "The build time path \"$_colcon_package_sh_COLCON_CURRENT_PREFIX\" doesn't exist. Either source a script for a different shell or set the environment variable \"COLCON_CURRENT_PREFIX\" explicitly." 1>&2 + unset _colcon_package_sh_COLCON_CURRENT_PREFIX + return 1 + fi + COLCON_CURRENT_PREFIX="$_colcon_package_sh_COLCON_CURRENT_PREFIX" +fi +unset _colcon_package_sh_COLCON_CURRENT_PREFIX + +# function to source another script with conditional trace output +# first argument: the path of the script +# additional arguments: arguments to the script +_colcon_package_sh_source_script() { + if [ -f "$1" ]; then + if [ -n "$COLCON_TRACE" ]; then + echo "# . \"$1\"" + fi + . "$@" + else + echo "not found: \"$1\"" 1>&2 + fi +} + +# source sh hooks +_colcon_package_sh_source_script "$COLCON_CURRENT_PREFIX/share/sphero_mini_controller/hook/pythonpath.sh" +_colcon_package_sh_source_script "$COLCON_CURRENT_PREFIX/share/sphero_mini_controller/hook/ament_prefix_path.sh" + +unset _colcon_package_sh_source_script +unset COLCON_CURRENT_PREFIX + +# do not unset _colcon_prepend_unique_value since it might be used by non-primary shell hooks diff --git a/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.xml b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.xml new file mode 100644 index 0000000000000000000000000000000000000000..abe0e77d18dda0c557f4e3de050a4bd1f9d4ffc4 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.xml @@ -0,0 +1,35 @@ +<?xml version="1.0"?> +<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?> +<package format="3"> + <name>sphero_mini_controller</name> + <version>0.0.0</version> + <description>TODO: Package description</description> + <maintainer email="ubuntu@todo.todo">ubuntu</maintainer> + <license>TODO: License declaration</license> + + <depend>rclpy</depend> + <depend>sensor_msgs.msg</depend> + <depend>geometry_msgs.msg</depend> + <depend>box</depend> + <depend>numpy</depend> + <depend>treiber</depend> + <depend>bluepy.btle</depend> + <depend>bluepy</depend> + <depend>_constants</depend> + <depend>struct</depend> + <depend>time</depend> + <depend>sys</depend> + <depend>threading</depend> + <depend>cv2</depend> + <depend>queue</depend> + <depend>matplotlib.pyplot</depend> + + <test_depend>ament_copyright</test_depend> + <test_depend>ament_flake8</test_depend> + <test_depend>ament_pep257</test_depend> + <test_depend>python3-pytest</test_depend> + + <export> + <build_type>ament_python</build_type> + </export> +</package> diff --git a/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.zsh b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.zsh new file mode 100644 index 0000000000000000000000000000000000000000..4229ad78a6154edcea070a12484f10a3844976a2 --- /dev/null +++ b/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.zsh @@ -0,0 +1,42 @@ +# generated from colcon_zsh/shell/template/package.zsh.em + +# This script extends the environment for this package. + +# a zsh script is able to determine its own path if necessary +if [ -z "$COLCON_CURRENT_PREFIX" ]; then + # the prefix is two levels up from the package specific share directory + _colcon_package_zsh_COLCON_CURRENT_PREFIX="$(builtin cd -q "`dirname "${(%):-%N}"`/../.." > /dev/null && pwd)" +else + _colcon_package_zsh_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX" +fi + +# function to source another script with conditional trace output +# first argument: the path of the script +# additional arguments: arguments to the script +_colcon_package_zsh_source_script() { + if [ -f "$1" ]; then + if [ -n "$COLCON_TRACE" ]; then + echo ". \"$1\"" + fi + . "$@" + else + echo "not found: \"$1\"" 1>&2 + fi +} + +# function to convert array-like strings into arrays +# to workaround SH_WORD_SPLIT not being set +colcon_zsh_convert_to_array() { + local _listname=$1 + local _dollar="$" + local _split="{=" + local _to_array="(\"$_dollar$_split$_listname}\")" + eval $_listname=$_to_array +} + +# source sh script of this package +_colcon_package_zsh_source_script "$_colcon_package_zsh_COLCON_CURRENT_PREFIX/share/sphero_mini_controller/package.sh" +unset convert_zsh_to_array + +unset _colcon_package_zsh_source_script +unset _colcon_package_zsh_COLCON_CURRENT_PREFIX diff --git a/ros2_ws/log/COLCON_IGNORE b/ros2_ws/log/COLCON_IGNORE new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ros2_ws/log/build_2023-07-05_16-11-36/events.log b/ros2_ws/log/build_2023-07-05_16-11-36/events.log new file mode 100644 index 0000000000000000000000000000000000000000..b51af15d2c6aeb5902f174f4d5b3b2642ab55014 --- /dev/null +++ b/ros2_ws/log/build_2023-07-05_16-11-36/events.log @@ -0,0 +1,38 @@ +[0.000000] (-) TimerEvent: {} +[0.000094] (sphero_mini_controller) JobQueued: {'identifier': 'sphero_mini_controller', 'dependencies': OrderedDict()} +[0.000145] (sphero_mini_controller) JobStarted: {'identifier': 'sphero_mini_controller'} +[0.098612] (-) TimerEvent: {} +[0.198900] (-) TimerEvent: {} +[0.299181] (-) TimerEvent: {} +[0.399461] (-) TimerEvent: {} +[0.499733] (-) TimerEvent: {} +[0.600065] (-) TimerEvent: {} +[0.613340] (sphero_mini_controller) Command: {'cmd': ['/usr/bin/python3', 'setup.py', 'egg_info', '--egg-base', '../../build/sphero_mini_controller', 'build', '--build-base', '/home/ubuntu/ros2_ws/build/sphero_mini_controller/build', 'install', '--record', '/home/ubuntu/ros2_ws/build/sphero_mini_controller/install.log', '--single-version-externally-managed'], 'cwd': '/home/ubuntu/ros2_ws/src/sphero_mini_controller', 'env': {'LANGUAGE': 'de_DE:en', 'USER': 'ubuntu', 'LC_TIME': 'de_DE.UTF-8', 'XDG_SESSION_TYPE': 'x11', 'SHLVL': '1', 'LD_LIBRARY_PATH': '/opt/ros/humble/opt/rviz_ogre_vendor/lib:/opt/ros/humble/lib/x86_64-linux-gnu:/opt/ros/humble/lib', 'HOME': '/home/ubuntu', 'OLDPWD': '/home/ubuntu', 'DESKTOP_SESSION': 'ubuntu', 'ROS_PYTHON_VERSION': '3', 'GNOME_SHELL_SESSION_MODE': 'ubuntu', 'GTK_MODULES': 'gail:atk-bridge', 'LC_MONETARY': 'de_DE.UTF-8', 'SYSTEMD_EXEC_PID': '2511', 'DBUS_SESSION_BUS_ADDRESS': 'unix:path=/run/user/999/bus', 'COLORTERM': 'truecolor', 'IM_CONFIG_PHASE': '1', 'ROS_DISTRO': 'humble', 'LOGNAME': 'ubuntu', '_': '/usr/bin/colcon', 'ROS_VERSION': '2', 'XDG_SESSION_CLASS': 'user', 'USERNAME': 'ubuntu', 'TERM': 'xterm-256color', 'GNOME_DESKTOP_SESSION_ID': 'this-is-deprecated', 'ROS_LOCALHOST_ONLY': '0', 'WINDOWPATH': '2', 'PATH': '/opt/ros/humble/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin', 'SESSION_MANAGER': 'local/ubuntu:@/tmp/.ICE-unix/2486,unix/ubuntu:/tmp/.ICE-unix/2486', 'PAPERSIZE': 'a4', 'XDG_MENU_PREFIX': 'gnome-', 'LC_ADDRESS': 'de_DE.UTF-8', 'GNOME_TERMINAL_SCREEN': '/org/gnome/Terminal/screen/e134bf1a_3465_43a9_88f4_21d27316a303', 'XDG_RUNTIME_DIR': '/run/user/999', 'DISPLAY': ':0', 'LANG': 'de_DE.UTF-8', 'XDG_CURRENT_DESKTOP': 'ubuntu:GNOME', 'LC_TELEPHONE': 'de_DE.UTF-8', 'XMODIFIERS': '@im=ibus', 'XDG_SESSION_DESKTOP': 'ubuntu', 'XAUTHORITY': '/run/user/999/gdm/Xauthority', 'GNOME_TERMINAL_SERVICE': ':1.129', 'SSH_AGENT_LAUNCHER': 'gnome-keyring', 'SSH_AUTH_SOCK': '/run/user/999/keyring/ssh', 'AMENT_PREFIX_PATH': '/opt/ros/humble', 'SHELL': '/bin/bash', 'LC_NAME': 'de_DE.UTF-8', 'QT_ACCESSIBILITY': '1', 'GDMSESSION': 'ubuntu', 'WEBOTS_HOME': '/usr/local/webots', 'LC_MEASUREMENT': 'de_DE.UTF-8', 'GPG_AGENT_INFO': '/run/user/999/gnupg/S.gpg-agent:0:1', 'LC_IDENTIFICATION': 'de_DE.UTF-8', 'QT_IM_MODULE': 'ibus', 'PWD': '/home/ubuntu/ros2_ws/build/sphero_mini_controller', 'XDG_CONFIG_DIRS': '/etc/xdg/xdg-ubuntu:/etc/xdg', 'XDG_DATA_DIRS': '/usr/share/ubuntu:/usr/share/gnome:/usr/local/share/:/usr/share/:/var/lib/snapd/desktop', 'PYTHONPATH': '/home/ubuntu/ros2_ws/build/sphero_mini_controller/prefix_override:/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages:/opt/ros/humble/lib/python3.10/site-packages:/opt/ros/humble/local/lib/python3.10/dist-packages', 'LC_NUMERIC': 'de_DE.UTF-8', 'LC_PAPER': 'de_DE.UTF-8', 'COLCON': '1', 'VTE_VERSION': '6800'}, 'shell': False} +[0.700191] (-) TimerEvent: {} +[0.800481] (-) TimerEvent: {} +[0.837582] (sphero_mini_controller) StdoutLine: {'line': b'running egg_info\n'} +[0.838134] (sphero_mini_controller) StdoutLine: {'line': b'writing ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/PKG-INFO\n'} +[0.838269] (sphero_mini_controller) StdoutLine: {'line': b'writing dependency_links to ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/dependency_links.txt\n'} +[0.838506] (sphero_mini_controller) StdoutLine: {'line': b'writing entry points to ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/entry_points.txt\n'} +[0.838674] (sphero_mini_controller) StdoutLine: {'line': b'writing requirements to ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/requires.txt\n'} +[0.838808] (sphero_mini_controller) StdoutLine: {'line': b'writing top-level names to ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/top_level.txt\n'} +[0.840404] (sphero_mini_controller) StdoutLine: {'line': b"reading manifest file '../../build/sphero_mini_controller/sphero_mini_controller.egg-info/SOURCES.txt'\n"} +[0.841553] (sphero_mini_controller) StdoutLine: {'line': b"writing manifest file '../../build/sphero_mini_controller/sphero_mini_controller.egg-info/SOURCES.txt'\n"} +[0.841722] (sphero_mini_controller) StdoutLine: {'line': b'running build\n'} +[0.841821] (sphero_mini_controller) StdoutLine: {'line': b'running build_py\n'} +[0.842619] (sphero_mini_controller) StdoutLine: {'line': b'copying sphero_mini_controller/my_first_node.py -> /home/ubuntu/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller\n'} +[0.843943] (sphero_mini_controller) StdoutLine: {'line': b'running install\n'} +[0.844204] (sphero_mini_controller) StdoutLine: {'line': b'running install_lib\n'} +[0.844828] (sphero_mini_controller) StdoutLine: {'line': b'copying /home/ubuntu/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node.py -> /home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller\n'} +[0.846179] (sphero_mini_controller) StdoutLine: {'line': b'byte-compiling /home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node.py to my_first_node.cpython-310.pyc\n'} +[0.850045] (sphero_mini_controller) StdoutLine: {'line': b'running install_data\n'} +[0.851406] (sphero_mini_controller) StdoutLine: {'line': b'running install_egg_info\n'} +[0.852604] (sphero_mini_controller) StdoutLine: {'line': b"removing '/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info' (and everything under it)\n"} +[0.852894] (sphero_mini_controller) StdoutLine: {'line': b'Copying ../../build/sphero_mini_controller/sphero_mini_controller.egg-info to /home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info\n'} +[0.853897] (sphero_mini_controller) StdoutLine: {'line': b'running install_scripts\n'} +[0.869686] (sphero_mini_controller) StdoutLine: {'line': b'Installing sphero_mini script to /home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/sphero_mini_controller\n'} +[0.870533] (sphero_mini_controller) StdoutLine: {'line': b"writing list of installed files to '/home/ubuntu/ros2_ws/build/sphero_mini_controller/install.log'\n"} +[0.894265] (sphero_mini_controller) CommandEnded: {'returncode': 0} +[0.900811] (-) TimerEvent: {} +[0.911226] (sphero_mini_controller) JobEnded: {'identifier': 'sphero_mini_controller', 'rc': 0} +[0.911779] (-) EventReactorShutdown: {} diff --git a/ros2_ws/log/build_2023-07-05_16-11-36/logger_all.log b/ros2_ws/log/build_2023-07-05_16-11-36/logger_all.log new file mode 100644 index 0000000000000000000000000000000000000000..79ba79dcfe52c9e6f9790fbc93f4d081a7b27893 --- /dev/null +++ b/ros2_ws/log/build_2023-07-05_16-11-36/logger_all.log @@ -0,0 +1,126 @@ +[0.285s] DEBUG:colcon:Command line arguments: ['/usr/bin/colcon', 'build'] +[0.285s] DEBUG:colcon:Parsed command line arguments: Namespace(log_base=None, log_level=None, verb_name='build', build_base='build', install_base='install', merge_install=False, symlink_install=False, test_result_base=None, continue_on_error=False, executor='parallel', parallel_workers=8, event_handlers=None, ignore_user_meta=False, metas=['./colcon.meta'], base_paths=['.'], packages_ignore=None, packages_ignore_regex=None, paths=None, packages_up_to=None, packages_up_to_regex=None, packages_above=None, packages_above_and_dependencies=None, packages_above_depth=None, packages_select_by_dep=None, packages_skip_by_dep=None, packages_skip_up_to=None, packages_select_build_failed=False, packages_skip_build_finished=False, packages_select_test_failures=False, packages_skip_test_passed=False, packages_select=None, packages_skip=None, packages_select_regex=None, packages_skip_regex=None, packages_start=None, packages_end=None, allow_overriding=[], cmake_args=None, cmake_target=None, cmake_target_skip_unavailable=False, cmake_clean_cache=False, cmake_clean_first=False, cmake_force_configure=False, ament_cmake_args=None, catkin_cmake_args=None, catkin_skip_building_tests=False, verb_parser=<colcon_defaults.argument_parser.defaults.DefaultArgumentsDecorator object at 0x7f34ebc57490>, verb_extension=<colcon_core.verb.build.BuildVerb object at 0x7f34ebc56f20>, main=<bound method BuildVerb.main of <colcon_core.verb.build.BuildVerb object at 0x7f34ebc56f20>>) +[0.322s] Level 1:colcon.colcon_core.package_discovery:discover_packages(colcon_meta) check parameters +[0.322s] Level 1:colcon.colcon_core.package_discovery:discover_packages(recursive) check parameters +[0.322s] Level 1:colcon.colcon_core.package_discovery:discover_packages(ignore) check parameters +[0.322s] Level 1:colcon.colcon_core.package_discovery:discover_packages(path) check parameters +[0.322s] Level 1:colcon.colcon_core.package_discovery:discover_packages(colcon_meta) discover +[0.322s] Level 1:colcon.colcon_core.package_discovery:discover_packages(recursive) discover +[0.322s] INFO:colcon.colcon_core.package_discovery:Crawling recursively for packages in '/home/ubuntu/ros2_ws' +[0.322s] Level 1:colcon.colcon_core.package_identification:_identify(.) by extensions ['ignore', 'ignore_ament_install'] +[0.322s] Level 1:colcon.colcon_core.package_identification:_identify(.) by extension 'ignore' +[0.322s] Level 1:colcon.colcon_core.package_identification:_identify(.) by extension 'ignore_ament_install' +[0.322s] Level 1:colcon.colcon_core.package_identification:_identify(.) by extensions ['colcon_pkg'] +[0.322s] Level 1:colcon.colcon_core.package_identification:_identify(.) by extension 'colcon_pkg' +[0.322s] Level 1:colcon.colcon_core.package_identification:_identify(.) by extensions ['colcon_meta'] +[0.322s] Level 1:colcon.colcon_core.package_identification:_identify(.) by extension 'colcon_meta' +[0.323s] Level 1:colcon.colcon_core.package_identification:_identify(.) by extensions ['ros'] +[0.323s] Level 1:colcon.colcon_core.package_identification:_identify(.) by extension 'ros' +[0.332s] Level 1:colcon.colcon_core.package_identification:_identify(.) by extensions ['cmake', 'python'] +[0.332s] Level 1:colcon.colcon_core.package_identification:_identify(.) by extension 'cmake' +[0.332s] Level 1:colcon.colcon_core.package_identification:_identify(.) by extension 'python' +[0.332s] Level 1:colcon.colcon_core.package_identification:_identify(.) by extensions ['python_setup_py'] +[0.332s] Level 1:colcon.colcon_core.package_identification:_identify(.) by extension 'python_setup_py' +[0.332s] Level 1:colcon.colcon_core.package_identification:_identify(build) by extensions ['ignore', 'ignore_ament_install'] +[0.332s] Level 1:colcon.colcon_core.package_identification:_identify(build) by extension 'ignore' +[0.332s] Level 1:colcon.colcon_core.package_identification:_identify(build) ignored +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(install) by extensions ['ignore', 'ignore_ament_install'] +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(install) by extension 'ignore' +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(install) ignored +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(log) by extensions ['ignore', 'ignore_ament_install'] +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(log) by extension 'ignore' +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(log) ignored +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(src) by extensions ['ignore', 'ignore_ament_install'] +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(src) by extension 'ignore' +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(src) by extension 'ignore_ament_install' +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(src) by extensions ['colcon_pkg'] +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(src) by extension 'colcon_pkg' +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(src) by extensions ['colcon_meta'] +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(src) by extension 'colcon_meta' +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(src) by extensions ['ros'] +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(src) by extension 'ros' +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(src) by extensions ['cmake', 'python'] +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(src) by extension 'cmake' +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(src) by extension 'python' +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(src) by extensions ['python_setup_py'] +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(src) by extension 'python_setup_py' +[0.333s] Level 1:colcon.colcon_core.package_identification:_identify(src/sphero_mini_controller) by extensions ['ignore', 'ignore_ament_install'] +[0.334s] Level 1:colcon.colcon_core.package_identification:_identify(src/sphero_mini_controller) by extension 'ignore' +[0.334s] Level 1:colcon.colcon_core.package_identification:_identify(src/sphero_mini_controller) by extension 'ignore_ament_install' +[0.334s] Level 1:colcon.colcon_core.package_identification:_identify(src/sphero_mini_controller) by extensions ['colcon_pkg'] +[0.334s] Level 1:colcon.colcon_core.package_identification:_identify(src/sphero_mini_controller) by extension 'colcon_pkg' +[0.334s] Level 1:colcon.colcon_core.package_identification:_identify(src/sphero_mini_controller) by extensions ['colcon_meta'] +[0.334s] Level 1:colcon.colcon_core.package_identification:_identify(src/sphero_mini_controller) by extension 'colcon_meta' +[0.334s] Level 1:colcon.colcon_core.package_identification:_identify(src/sphero_mini_controller) by extensions ['ros'] +[0.334s] Level 1:colcon.colcon_core.package_identification:_identify(src/sphero_mini_controller) by extension 'ros' +[0.338s] DEBUG:colcon.colcon_core.package_identification:Package 'src/sphero_mini_controller' with type 'ros.ament_python' and name 'sphero_mini_controller' +[0.338s] Level 1:colcon.colcon_core.package_discovery:discover_packages(recursive) using defaults +[0.338s] Level 1:colcon.colcon_core.package_discovery:discover_packages(ignore) discover +[0.338s] Level 1:colcon.colcon_core.package_discovery:discover_packages(ignore) using defaults +[0.338s] Level 1:colcon.colcon_core.package_discovery:discover_packages(path) discover +[0.338s] Level 1:colcon.colcon_core.package_discovery:discover_packages(path) using defaults +[0.359s] Level 1:colcon.colcon_core.package_discovery:discover_packages(prefix_path) check parameters +[0.359s] Level 1:colcon.colcon_core.package_discovery:discover_packages(prefix_path) discover +[0.364s] DEBUG:colcon.colcon_installed_package_information.package_discovery:Found 302 installed packages in /opt/ros/humble +[0.365s] Level 1:colcon.colcon_core.package_discovery:discover_packages(prefix_path) using defaults +[0.418s] Level 5:colcon.colcon_core.verb:set package 'sphero_mini_controller' build argument 'cmake_args' from command line to 'None' +[0.418s] Level 5:colcon.colcon_core.verb:set package 'sphero_mini_controller' build argument 'cmake_target' from command line to 'None' +[0.418s] Level 5:colcon.colcon_core.verb:set package 'sphero_mini_controller' build argument 'cmake_target_skip_unavailable' from command line to 'False' +[0.418s] Level 5:colcon.colcon_core.verb:set package 'sphero_mini_controller' build argument 'cmake_clean_cache' from command line to 'False' +[0.418s] Level 5:colcon.colcon_core.verb:set package 'sphero_mini_controller' build argument 'cmake_clean_first' from command line to 'False' +[0.418s] Level 5:colcon.colcon_core.verb:set package 'sphero_mini_controller' build argument 'cmake_force_configure' from command line to 'False' +[0.418s] Level 5:colcon.colcon_core.verb:set package 'sphero_mini_controller' build argument 'ament_cmake_args' from command line to 'None' +[0.418s] Level 5:colcon.colcon_core.verb:set package 'sphero_mini_controller' build argument 'catkin_cmake_args' from command line to 'None' +[0.418s] Level 5:colcon.colcon_core.verb:set package 'sphero_mini_controller' build argument 'catkin_skip_building_tests' from command line to 'False' +[0.418s] DEBUG:colcon.colcon_core.verb:Building package 'sphero_mini_controller' with the following arguments: {'ament_cmake_args': None, 'build_base': '/home/ubuntu/ros2_ws/build/sphero_mini_controller', 'catkin_cmake_args': None, 'catkin_skip_building_tests': False, 'cmake_args': None, 'cmake_clean_cache': False, 'cmake_clean_first': False, 'cmake_force_configure': False, 'cmake_target': None, 'cmake_target_skip_unavailable': False, 'install_base': '/home/ubuntu/ros2_ws/install/sphero_mini_controller', 'merge_install': False, 'path': '/home/ubuntu/ros2_ws/src/sphero_mini_controller', 'symlink_install': False, 'test_result_base': None} +[0.419s] INFO:colcon.colcon_core.executor:Executing jobs using 'parallel' executor +[0.421s] DEBUG:colcon.colcon_parallel_executor.executor.parallel:run_until_complete +[0.421s] INFO:colcon.colcon_ros.task.ament_python.build:Building ROS package in '/home/ubuntu/ros2_ws/src/sphero_mini_controller' with build type 'ament_python' +[0.421s] Level 1:colcon.colcon_core.shell:create_environment_hook('sphero_mini_controller', 'ament_prefix_path') +[0.427s] INFO:colcon.colcon_core.plugin_system:Skipping extension 'colcon_core.shell.bat': Not used on non-Windows systems +[0.427s] INFO:colcon.colcon_core.shell:Creating environment hook '/home/ubuntu/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/ament_prefix_path.ps1' +[0.428s] INFO:colcon.colcon_core.shell:Creating environment descriptor '/home/ubuntu/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/ament_prefix_path.dsv' +[0.429s] INFO:colcon.colcon_core.shell:Creating environment hook '/home/ubuntu/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/ament_prefix_path.sh' +[0.431s] INFO:colcon.colcon_core.shell:Skip shell extension 'powershell' for command environment: Not usable outside of PowerShell +[0.431s] DEBUG:colcon.colcon_core.shell:Skip shell extension 'dsv' for command environment +[0.671s] INFO:colcon.colcon_core.task.python.build:Building Python package in '/home/ubuntu/ros2_ws/src/sphero_mini_controller' +[0.672s] INFO:colcon.colcon_core.shell:Skip shell extension 'powershell' for command environment: Not usable outside of PowerShell +[0.672s] DEBUG:colcon.colcon_core.shell:Skip shell extension 'dsv' for command environment +[1.036s] DEBUG:colcon.colcon_core.event_handler.log_command:Invoking command in '/home/ubuntu/ros2_ws/src/sphero_mini_controller': PYTHONPATH=/home/ubuntu/ros2_ws/build/sphero_mini_controller/prefix_override:/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages:${PYTHONPATH} /usr/bin/python3 setup.py egg_info --egg-base ../../build/sphero_mini_controller build --build-base /home/ubuntu/ros2_ws/build/sphero_mini_controller/build install --record /home/ubuntu/ros2_ws/build/sphero_mini_controller/install.log --single-version-externally-managed +[1.317s] DEBUG:colcon.colcon_core.event_handler.log_command:Invoked command in '/home/ubuntu/ros2_ws/src/sphero_mini_controller' returned '0': PYTHONPATH=/home/ubuntu/ros2_ws/build/sphero_mini_controller/prefix_override:/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages:${PYTHONPATH} /usr/bin/python3 setup.py egg_info --egg-base ../../build/sphero_mini_controller build --build-base /home/ubuntu/ros2_ws/build/sphero_mini_controller/build install --record /home/ubuntu/ros2_ws/build/sphero_mini_controller/install.log --single-version-externally-managed +[1.323s] Level 1:colcon.colcon_core.environment:checking '/home/ubuntu/ros2_ws/install/sphero_mini_controller' for CMake module files +[1.324s] Level 1:colcon.colcon_core.environment:checking '/home/ubuntu/ros2_ws/install/sphero_mini_controller' for CMake config files +[1.325s] Level 1:colcon.colcon_core.environment:checking '/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib' +[1.325s] Level 1:colcon.colcon_core.environment:checking '/home/ubuntu/ros2_ws/install/sphero_mini_controller/bin' +[1.325s] Level 1:colcon.colcon_core.environment:checking '/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/pkgconfig/sphero_mini_controller.pc' +[1.325s] Level 1:colcon.colcon_core.environment:checking '/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages' +[1.325s] Level 1:colcon.colcon_core.shell:create_environment_hook('sphero_mini_controller', 'pythonpath') +[1.327s] INFO:colcon.colcon_core.shell:Creating environment hook '/home/ubuntu/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/pythonpath.ps1' +[1.327s] INFO:colcon.colcon_core.shell:Creating environment descriptor '/home/ubuntu/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/pythonpath.dsv' +[1.327s] INFO:colcon.colcon_core.shell:Creating environment hook '/home/ubuntu/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/hook/pythonpath.sh' +[1.328s] Level 1:colcon.colcon_core.environment:checking '/home/ubuntu/ros2_ws/install/sphero_mini_controller/bin' +[1.328s] Level 1:colcon.colcon_core.environment:create_environment_scripts_only(sphero_mini_controller) +[1.329s] INFO:colcon.colcon_core.shell:Creating package script '/home/ubuntu/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.ps1' +[1.330s] INFO:colcon.colcon_core.shell:Creating package descriptor '/home/ubuntu/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.dsv' +[1.331s] INFO:colcon.colcon_core.shell:Creating package script '/home/ubuntu/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.sh' +[1.331s] INFO:colcon.colcon_core.shell:Creating package script '/home/ubuntu/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.bash' +[1.332s] INFO:colcon.colcon_core.shell:Creating package script '/home/ubuntu/ros2_ws/install/sphero_mini_controller/share/sphero_mini_controller/package.zsh' +[1.333s] Level 1:colcon.colcon_core.environment:create_file_with_runtime_dependencies(/home/ubuntu/ros2_ws/install/sphero_mini_controller/share/colcon-core/packages/sphero_mini_controller) +[1.333s] DEBUG:colcon.colcon_parallel_executor.executor.parallel:closing loop +[1.334s] DEBUG:colcon.colcon_parallel_executor.executor.parallel:loop closed +[1.334s] DEBUG:colcon.colcon_parallel_executor.executor.parallel:run_until_complete finished with '0' +[1.334s] DEBUG:colcon.colcon_core.event_reactor:joining thread +[1.341s] INFO:colcon.colcon_core.plugin_system:Skipping extension 'colcon_notification.desktop_notification.terminal_notifier': Not used on non-Darwin systems +[1.341s] INFO:colcon.colcon_core.plugin_system:Skipping extension 'colcon_notification.desktop_notification.win32': Not used on non-Windows systems +[1.341s] INFO:colcon.colcon_notification.desktop_notification:Sending desktop notification using 'notify2' +[1.356s] DEBUG:colcon.colcon_core.event_reactor:joined thread +[1.358s] INFO:colcon.colcon_core.shell:Creating prefix script '/home/ubuntu/ros2_ws/install/local_setup.ps1' +[1.359s] INFO:colcon.colcon_core.shell:Creating prefix util module '/home/ubuntu/ros2_ws/install/_local_setup_util_ps1.py' +[1.361s] INFO:colcon.colcon_core.shell:Creating prefix chain script '/home/ubuntu/ros2_ws/install/setup.ps1' +[1.362s] INFO:colcon.colcon_core.shell:Creating prefix script '/home/ubuntu/ros2_ws/install/local_setup.sh' +[1.363s] INFO:colcon.colcon_core.shell:Creating prefix util module '/home/ubuntu/ros2_ws/install/_local_setup_util_sh.py' +[1.363s] INFO:colcon.colcon_core.shell:Creating prefix chain script '/home/ubuntu/ros2_ws/install/setup.sh' +[1.365s] INFO:colcon.colcon_core.shell:Creating prefix script '/home/ubuntu/ros2_ws/install/local_setup.bash' +[1.366s] INFO:colcon.colcon_core.shell:Creating prefix chain script '/home/ubuntu/ros2_ws/install/setup.bash' +[1.367s] INFO:colcon.colcon_core.shell:Creating prefix script '/home/ubuntu/ros2_ws/install/local_setup.zsh' +[1.368s] INFO:colcon.colcon_core.shell:Creating prefix chain script '/home/ubuntu/ros2_ws/install/setup.zsh' diff --git a/ros2_ws/log/build_2023-07-05_16-11-36/sphero_mini_controller/command.log b/ros2_ws/log/build_2023-07-05_16-11-36/sphero_mini_controller/command.log new file mode 100644 index 0000000000000000000000000000000000000000..4f3d645a95ba6297270da17046f956b89cf8380d --- /dev/null +++ b/ros2_ws/log/build_2023-07-05_16-11-36/sphero_mini_controller/command.log @@ -0,0 +1,2 @@ +Invoking command in '/home/ubuntu/ros2_ws/src/sphero_mini_controller': PYTHONPATH=/home/ubuntu/ros2_ws/build/sphero_mini_controller/prefix_override:/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages:${PYTHONPATH} /usr/bin/python3 setup.py egg_info --egg-base ../../build/sphero_mini_controller build --build-base /home/ubuntu/ros2_ws/build/sphero_mini_controller/build install --record /home/ubuntu/ros2_ws/build/sphero_mini_controller/install.log --single-version-externally-managed +Invoked command in '/home/ubuntu/ros2_ws/src/sphero_mini_controller' returned '0': PYTHONPATH=/home/ubuntu/ros2_ws/build/sphero_mini_controller/prefix_override:/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages:${PYTHONPATH} /usr/bin/python3 setup.py egg_info --egg-base ../../build/sphero_mini_controller build --build-base /home/ubuntu/ros2_ws/build/sphero_mini_controller/build install --record /home/ubuntu/ros2_ws/build/sphero_mini_controller/install.log --single-version-externally-managed diff --git a/ros2_ws/log/build_2023-07-05_16-11-36/sphero_mini_controller/stderr.log b/ros2_ws/log/build_2023-07-05_16-11-36/sphero_mini_controller/stderr.log new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ros2_ws/log/build_2023-07-05_16-11-36/sphero_mini_controller/stdout.log b/ros2_ws/log/build_2023-07-05_16-11-36/sphero_mini_controller/stdout.log new file mode 100644 index 0000000000000000000000000000000000000000..e0f7d75bc0319cdf3b92b2221e732950b19b046e --- /dev/null +++ b/ros2_ws/log/build_2023-07-05_16-11-36/sphero_mini_controller/stdout.log @@ -0,0 +1,22 @@ +running egg_info +writing ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/PKG-INFO +writing dependency_links to ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/dependency_links.txt +writing entry points to ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/entry_points.txt +writing requirements to ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/requires.txt +writing top-level names to ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/top_level.txt +reading manifest file '../../build/sphero_mini_controller/sphero_mini_controller.egg-info/SOURCES.txt' +writing manifest file '../../build/sphero_mini_controller/sphero_mini_controller.egg-info/SOURCES.txt' +running build +running build_py +copying sphero_mini_controller/my_first_node.py -> /home/ubuntu/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller +running install +running install_lib +copying /home/ubuntu/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node.py -> /home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller +byte-compiling /home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node.py to my_first_node.cpython-310.pyc +running install_data +running install_egg_info +removing '/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info' (and everything under it) +Copying ../../build/sphero_mini_controller/sphero_mini_controller.egg-info to /home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info +running install_scripts +Installing sphero_mini script to /home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/sphero_mini_controller +writing list of installed files to '/home/ubuntu/ros2_ws/build/sphero_mini_controller/install.log' diff --git a/ros2_ws/log/build_2023-07-05_16-11-36/sphero_mini_controller/stdout_stderr.log b/ros2_ws/log/build_2023-07-05_16-11-36/sphero_mini_controller/stdout_stderr.log new file mode 100644 index 0000000000000000000000000000000000000000..e0f7d75bc0319cdf3b92b2221e732950b19b046e --- /dev/null +++ b/ros2_ws/log/build_2023-07-05_16-11-36/sphero_mini_controller/stdout_stderr.log @@ -0,0 +1,22 @@ +running egg_info +writing ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/PKG-INFO +writing dependency_links to ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/dependency_links.txt +writing entry points to ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/entry_points.txt +writing requirements to ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/requires.txt +writing top-level names to ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/top_level.txt +reading manifest file '../../build/sphero_mini_controller/sphero_mini_controller.egg-info/SOURCES.txt' +writing manifest file '../../build/sphero_mini_controller/sphero_mini_controller.egg-info/SOURCES.txt' +running build +running build_py +copying sphero_mini_controller/my_first_node.py -> /home/ubuntu/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller +running install +running install_lib +copying /home/ubuntu/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node.py -> /home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller +byte-compiling /home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node.py to my_first_node.cpython-310.pyc +running install_data +running install_egg_info +removing '/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info' (and everything under it) +Copying ../../build/sphero_mini_controller/sphero_mini_controller.egg-info to /home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info +running install_scripts +Installing sphero_mini script to /home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/sphero_mini_controller +writing list of installed files to '/home/ubuntu/ros2_ws/build/sphero_mini_controller/install.log' diff --git a/ros2_ws/log/build_2023-07-05_16-11-36/sphero_mini_controller/streams.log b/ros2_ws/log/build_2023-07-05_16-11-36/sphero_mini_controller/streams.log new file mode 100644 index 0000000000000000000000000000000000000000..c3cd89373169e0c66b063bac913fdb295878e014 --- /dev/null +++ b/ros2_ws/log/build_2023-07-05_16-11-36/sphero_mini_controller/streams.log @@ -0,0 +1,24 @@ +[0.614s] Invoking command in '/home/ubuntu/ros2_ws/src/sphero_mini_controller': PYTHONPATH=/home/ubuntu/ros2_ws/build/sphero_mini_controller/prefix_override:/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages:${PYTHONPATH} /usr/bin/python3 setup.py egg_info --egg-base ../../build/sphero_mini_controller build --build-base /home/ubuntu/ros2_ws/build/sphero_mini_controller/build install --record /home/ubuntu/ros2_ws/build/sphero_mini_controller/install.log --single-version-externally-managed +[0.838s] running egg_info +[0.838s] writing ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/PKG-INFO +[0.838s] writing dependency_links to ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/dependency_links.txt +[0.838s] writing entry points to ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/entry_points.txt +[0.839s] writing requirements to ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/requires.txt +[0.839s] writing top-level names to ../../build/sphero_mini_controller/sphero_mini_controller.egg-info/top_level.txt +[0.840s] reading manifest file '../../build/sphero_mini_controller/sphero_mini_controller.egg-info/SOURCES.txt' +[0.841s] writing manifest file '../../build/sphero_mini_controller/sphero_mini_controller.egg-info/SOURCES.txt' +[0.842s] running build +[0.842s] running build_py +[0.843s] copying sphero_mini_controller/my_first_node.py -> /home/ubuntu/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller +[0.844s] running install +[0.844s] running install_lib +[0.845s] copying /home/ubuntu/ros2_ws/build/sphero_mini_controller/build/lib/sphero_mini_controller/my_first_node.py -> /home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller +[0.846s] byte-compiling /home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller/my_first_node.py to my_first_node.cpython-310.pyc +[0.850s] running install_data +[0.851s] running install_egg_info +[0.853s] removing '/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info' (and everything under it) +[0.853s] Copying ../../build/sphero_mini_controller/sphero_mini_controller.egg-info to /home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages/sphero_mini_controller-0.0.0-py3.10.egg-info +[0.854s] running install_scripts +[0.870s] Installing sphero_mini script to /home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/sphero_mini_controller +[0.870s] writing list of installed files to '/home/ubuntu/ros2_ws/build/sphero_mini_controller/install.log' +[0.894s] Invoked command in '/home/ubuntu/ros2_ws/src/sphero_mini_controller' returned '0': PYTHONPATH=/home/ubuntu/ros2_ws/build/sphero_mini_controller/prefix_override:/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages:${PYTHONPATH} /usr/bin/python3 setup.py egg_info --egg-base ../../build/sphero_mini_controller build --build-base /home/ubuntu/ros2_ws/build/sphero_mini_controller/build install --record /home/ubuntu/ros2_ws/build/sphero_mini_controller/install.log --single-version-externally-managed diff --git a/ros2_ws/log/latest b/ros2_ws/log/latest new file mode 100644 index 0000000000000000000000000000000000000000..3244609549432b76ee2b151871ad47cc1e1f0f2d Binary files /dev/null and b/ros2_ws/log/latest differ diff --git a/ros2_ws/log/latest_build b/ros2_ws/log/latest_build new file mode 100644 index 0000000000000000000000000000000000000000..ca67e1aa835f2f7f207f34c5b460777363f7af47 Binary files /dev/null and b/ros2_ws/log/latest_build differ diff --git a/ros2_ws/src/sphero_mini_controller/.vscode/c_cpp_properties.json b/ros2_ws/src/sphero_mini_controller/.vscode/c_cpp_properties.json new file mode 100644 index 0000000000000000000000000000000000000000..af5a8df28dd58b3113f960a5986bbe0612c4a286 --- /dev/null +++ b/ros2_ws/src/sphero_mini_controller/.vscode/c_cpp_properties.json @@ -0,0 +1,20 @@ +{ + "configurations": [ + { + "browse": { + "databaseFilename": "${default}", + "limitSymbolsToIncludedHeaders": false + }, + "includePath": [ + "/opt/ros/humble/include/**", + "/usr/include/**" + ], + "name": "ROS", + "intelliSenseMode": "gcc-x64", + "compilerPath": "/usr/bin/gcc", + "cStandard": "gnu11", + "cppStandard": "c++14" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/ros2_ws/src/sphero_mini_controller/.vscode/settings.json b/ros2_ws/src/sphero_mini_controller/.vscode/settings.json new file mode 100644 index 0000000000000000000000000000000000000000..2acdf0c3dc18f14096514c0a8cba87088761a3cf --- /dev/null +++ b/ros2_ws/src/sphero_mini_controller/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "python.autoComplete.extraPaths": [ + "/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages", + "/opt/ros/humble/lib/python3.10/site-packages", + "/opt/ros/humble/local/lib/python3.10/dist-packages" + ], + "python.analysis.extraPaths": [ + "/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages", + "/opt/ros/humble/lib/python3.10/site-packages", + "/opt/ros/humble/local/lib/python3.10/dist-packages" + ], + "ros.distro": "humble" +} \ No newline at end of file diff --git a/ros2_ws/src/sphero_mini_controller/package.xml b/ros2_ws/src/sphero_mini_controller/package.xml new file mode 100644 index 0000000000000000000000000000000000000000..abe0e77d18dda0c557f4e3de050a4bd1f9d4ffc4 --- /dev/null +++ b/ros2_ws/src/sphero_mini_controller/package.xml @@ -0,0 +1,35 @@ +<?xml version="1.0"?> +<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?> +<package format="3"> + <name>sphero_mini_controller</name> + <version>0.0.0</version> + <description>TODO: Package description</description> + <maintainer email="ubuntu@todo.todo">ubuntu</maintainer> + <license>TODO: License declaration</license> + + <depend>rclpy</depend> + <depend>sensor_msgs.msg</depend> + <depend>geometry_msgs.msg</depend> + <depend>box</depend> + <depend>numpy</depend> + <depend>treiber</depend> + <depend>bluepy.btle</depend> + <depend>bluepy</depend> + <depend>_constants</depend> + <depend>struct</depend> + <depend>time</depend> + <depend>sys</depend> + <depend>threading</depend> + <depend>cv2</depend> + <depend>queue</depend> + <depend>matplotlib.pyplot</depend> + + <test_depend>ament_copyright</test_depend> + <test_depend>ament_flake8</test_depend> + <test_depend>ament_pep257</test_depend> + <test_depend>python3-pytest</test_depend> + + <export> + <build_type>ament_python</build_type> + </export> +</package> diff --git a/ros2_ws/src/sphero_mini_controller/resource/sphero_conf.json b/ros2_ws/src/sphero_mini_controller/resource/sphero_conf.json new file mode 100644 index 0000000000000000000000000000000000000000..a490b4b798c696938452dac610e3cfac087c6279 --- /dev/null +++ b/ros2_ws/src/sphero_mini_controller/resource/sphero_conf.json @@ -0,0 +1,3 @@ +{ + "MAC_ADDRESS": "C6:69:72:CD:BC:6D" +} diff --git a/ros2_ws/src/sphero_mini_controller/resource/sphero_mini_controller b/ros2_ws/src/sphero_mini_controller/resource/sphero_mini_controller new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ros2_ws/src/sphero_mini_controller/setup.cfg b/ros2_ws/src/sphero_mini_controller/setup.cfg new file mode 100644 index 0000000000000000000000000000000000000000..54dced33ec1eaa5f3aa8fec3c45d4ccc9afdc2a0 --- /dev/null +++ b/ros2_ws/src/sphero_mini_controller/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script_dir=$base/lib/sphero_mini_controller +[install] +install_scripts=$base/lib/sphero_mini_controller diff --git a/ros2_ws/src/sphero_mini_controller/setup.py b/ros2_ws/src/sphero_mini_controller/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..60d245ad79ffb3a6cf68f974fd2806e38e9b957a --- /dev/null +++ b/ros2_ws/src/sphero_mini_controller/setup.py @@ -0,0 +1,27 @@ +from setuptools import setup + +package_name = 'sphero_mini_controller' + +setup( + name=package_name, + version='0.0.0', + packages=[package_name], + data_files=[ + ('share/ament_index/resource_index/packages', + ['resource/' + package_name]), + ('share/' + package_name, ['package.xml']), + ], + install_requires=['setuptools'], + zip_safe=True, + maintainer='ubuntu', + maintainer_email='ubuntu@todo.todo', + description='TODO: Package description', + license='TODO: License declaration', + tests_require=['pytest'], + entry_points={ + 'console_scripts': [ + "sphero_mini = sphero_mini_controller.sphero_mini:main" + + ], + }, +) diff --git a/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/.vscode/c_cpp_properties.json b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/.vscode/c_cpp_properties.json new file mode 100644 index 0000000000000000000000000000000000000000..af5a8df28dd58b3113f960a5986bbe0612c4a286 --- /dev/null +++ b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/.vscode/c_cpp_properties.json @@ -0,0 +1,20 @@ +{ + "configurations": [ + { + "browse": { + "databaseFilename": "${default}", + "limitSymbolsToIncludedHeaders": false + }, + "includePath": [ + "/opt/ros/humble/include/**", + "/usr/include/**" + ], + "name": "ROS", + "intelliSenseMode": "gcc-x64", + "compilerPath": "/usr/bin/gcc", + "cStandard": "gnu11", + "cppStandard": "c++14" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/.vscode/settings.json b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/.vscode/settings.json new file mode 100644 index 0000000000000000000000000000000000000000..c7c0d891180b53a0c36591808f3201d9656bd763 --- /dev/null +++ b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "python.autoComplete.extraPaths": [ + "/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages", + "/opt/ros/humble/lib/python3.10/site-packages", + "/opt/ros/humble/local/lib/python3.10/dist-packages" + ], + "python.analysis.extraPaths": [ + "/home/ubuntu/ros2_ws/install/sphero_mini_controller/lib/python3.10/site-packages", + "/opt/ros/humble/lib/python3.10/site-packages", + "/opt/ros/humble/local/lib/python3.10/dist-packages" + ] +} \ No newline at end of file diff --git a/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/WavefrontPlanner.py b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/WavefrontPlanner.py new file mode 100644 index 0000000000000000000000000000000000000000..9904dd94b1bd926376bc75e671e1687a80a9df4f --- /dev/null +++ b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/WavefrontPlanner.py @@ -0,0 +1,389 @@ +############################################################################ +# WAVEFRONT ALGORITHM +# Adapted to Python Code By Darin Velarde +# Fri Jan 29 13:56:53 PST 2010 +# from C code from John Palmisano +# (www.societyofrobots.com) +############################################################################ +import cv2 +import numpy as np +import matplotlib.pyplot as plt +import time + +class waveFrontPlanner: + ############################################################################ + # WAVEFRONT ALGORITHM + # Adapted to Python Code By Darin Velarde + # Fri Jan 29 13:56:53 PST 2010 + # from C code from John Palmisano + # (www.societyofrobots.com) + ############################################################################ + + def __init__(self, mapOfWorld, slow=False): + self.__slow = slow + self.__mapOfWorld = mapOfWorld + if str(type(mapOfWorld)).find("numpy") != -1: + #If its a numpy array + self.__height, self.__width = self.__mapOfWorld.shape + else: + self.__height, self.__width = len(self.__mapOfWorld), len(self.__mapOfWorld[0]) + + self.__nothing = 000 + self.__wall = 999 + self.__goal = 1 + self.__path = "PATH" + + self.__finalPath = [] + + #Robot value + self.__robot = 254 + #Robot default Location + self.__robot_x = 0 + self.__robot_y = 0 + + #default goal location + self.__goal_x = 8 + self.__goal_y = 9 + + #temp variables + self.__temp_A = 0 + self.__temp_B = 0 + self.__counter = 0 + self.__steps = 0 #determine how processor intensive the algorithm was + + #when searching for a node with a lower value + self.__minimum_node = 250 + self.__min_node_location = 250 + self.__new_state = 1 + self.__old_state = 1 + self.__reset_min = 250 #above this number is a special (wall or robot) + ########################################################################### + + def setRobotPosition(self, x, y): + """ + Sets the robot's current position + + """ + + self.__robot_x = x + self.__robot_y = y + ########################################################################### + + def setGoalPosition(self, x, y): + """ + Sets the goal position. + + """ + + self.__goal_x = x + self.__goal_y = y + ########################################################################### + + def robotPosition(self): + return (self.__robot_x, self.__robot_y) + ########################################################################### + + def goalPosition(self): + return (self.__goal_x, self.__goal_y) + ########################################################################### + + def run(self, prnt=False): + """ + The entry point for the robot algorithm to use wavefront propagation. + + """ + + path = [] + while self.__mapOfWorld[self.__robot_x][self.__robot_y] != self.__goal: + if self.__steps > 20000: + print ("Cannot find a path.") + return + #find new location to go to + self.__new_state = self.propagateWavefront() + #update location of robot + if self.__new_state == 1: + self.__robot_x -= 1 + if prnt: + print("Move to x=%d y=%d\n\n" % (self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + if self.__new_state == 2: + self.__robot_y += 1 + if prnt: + print("Move to x=%d y=%d\n\n" %(self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + if self.__new_state == 3: + self.__robot_x += 1 + if prnt: + print("Move to x=%d y=%d\n\n" %(self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + if self.__new_state == 4: + self.__robot_y -= 1 + if prnt: + print("Move to x=%d y=%d\n\n" %(self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + self.__old_state = self.__new_state + msg = "Found the goal in %i steps:\n" % self.__steps + msg += "mapOfWorld size= %i %i\n\n" % (self.__height, self.__width) + if prnt: + print(msg) + self.printMap() + return path + ########################################################################### + + def propagateWavefront(self, prnt=False): + self.unpropagate() + #Old robot location was deleted, store new robot location in mapOfWorld + self.__mapOfWorld[self.__robot_x][self.__robot_y] = self.__robot + self.__path = self.__robot + #start location to begin scan at goal location + self.__mapOfWorld[self.__goal_x][self.__goal_y] = self.__goal + counter = 0 + while counter < 200: #allows for recycling until robot is found + x = 0 + y = 0 + #time.sleep(0.00001) + #while the mapOfWorld hasnt been fully scanned + while x < self.__height and y < self.__width: + #if this location is a wall or the goal, just ignore it + if self.__mapOfWorld[x][y] != self.__wall and \ + self.__mapOfWorld[x][y] != self.__goal: + #a full trail to the robot has been located, finished! + minLoc = self.minSurroundingNodeValue(x, y) + if minLoc < self.__reset_min and \ + self.__mapOfWorld[x][y] == self.__robot: + if prnt: + print("Finished Wavefront:\n") + self.printMap() + # Tell the robot to move after this return. + return self.__min_node_location + #record a value in to this node + elif self.__minimum_node != self.__reset_min: + #if this isnt here, 'nothing' will go in the location + self.__mapOfWorld[x][y] = self.__minimum_node + 1 + #go to next node and/or row + y += 1 + if y == self.__width and x != self.__height: + x += 1 + y = 0 + #print self.__robot_x, self.__robot_y + if prnt: + print("Sweep #: %i\n" % (counter + 1)) + self.printMap() + self.__steps += 1 + counter += 1 + return 0 + ########################################################################### + + def unpropagate(self): + """ + clears old path to determine new path + stay within boundary + + """ + + for x in range(0, self.__height): + for y in range(0, self.__width): + if self.__mapOfWorld[x][y] != self.__wall and \ + self.__mapOfWorld[x][y] != self.__goal and \ + self.__mapOfWorld[x][y] != self.__path: + #if this location is a wall or goal, just ignore it + self.__mapOfWorld[x][y] = self.__nothing #clear that space + ########################################################################### + + def minSurroundingNodeValue(self, x, y): + """ + this method looks at a node and returns the lowest value around that + node. + + """ + + #reset minimum + self.__minimum_node = self.__reset_min + #down + if x < self.__height -1: + if self.__mapOfWorld[x + 1][y] < self.__minimum_node and \ + self.__mapOfWorld[x + 1][y] != self.__nothing: + #find the lowest number node, and exclude empty nodes (0's) + self.__minimum_node = self.__mapOfWorld[x + 1][y] + self.__min_node_location = 3 + #up + if x > 0: + if self.__mapOfWorld[x-1][y] < self.__minimum_node and \ + self.__mapOfWorld[x-1][y] != self.__nothing: + self.__minimum_node = self.__mapOfWorld[x-1][y] + self.__min_node_location = 1 + #right + if y < self.__width -1: + if self.__mapOfWorld[x][y + 1] < self.__minimum_node and \ + self.__mapOfWorld[x][y + 1] != self.__nothing: + self.__minimum_node = self.__mapOfWorld[x][y + 1] + self.__min_node_location = 2 + #left + if y > 0: + if self.__mapOfWorld[x][y - 1] < self.__minimum_node and \ + self.__mapOfWorld[x][y - 1] != self.__nothing: + self.__minimum_node = self.__mapOfWorld[x][y-1] + self.__min_node_location = 4 + return self.__minimum_node + ########################################################################### + + def printMap(self): + """ + Prints out the map of this instance of the class. + + """ + + msg = '' + for temp_B in range(0, self.__height): + for temp_A in range(0, self.__width): + if self.__mapOfWorld[temp_B][temp_A] == self.__wall: + msg += "%04s" % "[#]" + elif self.__mapOfWorld[temp_B][temp_A] == self.__robot: + msg += "%04s" % "-" + elif self.__mapOfWorld[temp_B][temp_A] == self.__goal: + msg += "%04s" % "G" + else: + msg += "%04s" % str(self.__mapOfWorld[temp_B][temp_A]) + msg += "\n\n" + msg += "\n\n" + print(msg) + # + if self.__slow == True: + time.sleep(0.05) +############################################################################ + +class mapCreate: + ############################################################################ + + def create_map(self, scale,img): + + print('Original Dimensions : ',img.shape) + + scale_percent = 100-scale # percent of original size + width = int(img.shape[1] * scale_percent / 100) + height = int(img.shape[0] * scale_percent / 100) + dim = (width, height) + + # resize image + resized = cv2.resize(img, dim, interpolation = cv2.INTER_AREA) + print('Resized Dimensions : ',resized.shape) + + gray = cv2.cvtColor(resized, cv2.COLOR_RGB2GRAY) + mask = cv2.inRange(gray, np.array([200]), np.array([255])) + + mapOfWorld = [[0]*gray.shape[1]]*gray.shape[0] + mask_list= np.ndarray.tolist(mask) + + #Markiere alle leeren Zellen mit 000 und alle Zellen mit Hinderniss mit 999 + for i in range(0,mask.shape[0]): + mapOfWorld[i] = ['999' if j > 200 else '000' for j in mask_list[i]] + + mapOfWorld = np.array(mapOfWorld, dtype = int) + mapOfWorldSized = mapOfWorld.copy() + + e = 3 + #Hindernisse Größer machen + for i in range(0,mapOfWorld.shape[0]): + for j in range(0,mapOfWorld.shape[1]): + for r in range(0,e): + try: + if (mapOfWorldSized[i][j] == 999): + mapOfWorld[i][j+e] = 999 + mapOfWorld[i+e][j] = 999 + mapOfWorld[i-e][j] = 999 + mapOfWorld[i][j-e] = 999 + mapOfWorld[i+e][j+e] = 999 + mapOfWorld[i+e][j-e] = 999 + mapOfWorld[i-e][j+e] = 999 + mapOfWorld[i-e][j-e] = 999 + except Exception: + continue + + return mapOfWorld + ############################################################################ + + def scale_koord(self, sx, sy, gx, gy,scale): + scale_percent = 100-scale # percent of original size + + sx = int(sx*scale_percent/100) + sy = int(sy*scale_percent/100) + gx = int(gx*scale_percent/100) + gy = int(gy*scale_percent/100) + + return sx,sy,gx,gy + ############################################################################ + + def rescale_koord(self, path,scale): + scale_percent = 100-scale # percent of original size100 + + path = np.array(path) + + for i in range(len(path)): + path[i] = path[i]*100/scale_percent + + return path + ############################################################################ + + def calc_trans(self, x, y, height): + yTrans = height - y + xTrans = x + + return xTrans, yTrans +############################################################################### + +if __name__ == "__main__": + """ + X is vertical, Y is horizontal + + """ + + Map = mapCreate() #Map Objekt erzeugen + + #Verkleinerungsfaktor in % + scale = 90 + + img = cv2.imread("D:/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/karte.jpg", cv2.COLOR_RGB2GRAY) + #cap = cv2.VideoCapture(2) + mapWorld = Map.create_map(scale, img) + height, width = img.shape[:2] + + #Angaben in Weltkoordinaten + sy = 300 #X-Koordinate im Weltkoordinaten System + sx = 200 #Y-Koordinate im Weltkoordinaten System + + gy = 700 #X-Koordinate im Weltkordinaten System + gx = 240 #Y-Koordinate im Weltkoordinaten System + + sx,sy,gx,gy = Map.scale_koord(sx,sy,gx,gy,scale) #Kordinaten Tauschen X,Y + + start = time.time() + planner = waveFrontPlanner(mapWorld) + planner.setGoalPosition(gx,gy) + planner.setRobotPosition(sx,sy) + path = planner.run(False) + end = time.time() + print("Took %f seconds to run wavefront simulation" % (end - start)) + + path = Map.rescale_koord(path, scale) + #print(path) +#%% Plot + #Programm Koordinaten + imgTrans = cv2.transpose(img) # X und Y-Achse im Bild tauschen + imgPlot, ax1 = plt.subplots() + + ax1.set_title('Programm Koordinaten') + ax1.imshow(imgTrans) + ax1.set_xlabel('[px]') + ax1.set_ylabel('[px]') + ax1.scatter(path[:,0], path[:,1], color='r') + + #Bild Koordinaten + imgPlot2, ax2 = plt.subplots() + ax2.set_title('Bild Koordinaten') + ax2.set_xlabel('[px]') + ax2.set_ylabel('[px]') + ax2.imshow(img) + ax2.scatter(path[:,1], path[:,0], color='r') + +#%% Sphero Pfad + pathWeltX, pathWeltY = Map.calc_trans(path[:,1], path[:,0], height) \ No newline at end of file diff --git a/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/__init__.py b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/__pycache__/_constants.cpython-310.pyc b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/__pycache__/_constants.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a7dc990c992e83d0131942566db7b7d4f80a057 Binary files /dev/null and b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/__pycache__/_constants.cpython-310.pyc differ diff --git a/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/__pycache__/core.cpython-310.pyc b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/__pycache__/core.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..268efa0a4ba0ab4916f275a675473967f0d05ef1 Binary files /dev/null and b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/__pycache__/core.cpython-310.pyc differ diff --git a/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/__pycache__/simple_moves.cpython-310.pyc b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/__pycache__/simple_moves.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1aac01a6d648c63bd3f5ffbf474fde28d479087f Binary files /dev/null and b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/__pycache__/simple_moves.cpython-310.pyc differ diff --git a/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/_constants.py b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/_constants.py new file mode 100644 index 0000000000000000000000000000000000000000..fa8a0e875eed9f484945c1eeaf2fda3fd98503a4 --- /dev/null +++ b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/_constants.py @@ -0,0 +1,72 @@ +''' +Known Peripheral UUIDs, obtained by querying using the Bluepy module: +===================================================================== +Anti DOS Characteristic <00020005-574f-4f20-5370-6865726f2121> +Battery Level Characteristic <Battery Level> +Peripheral Preferred Connection Parameters Characteristic <Peripheral Preferred Connection Parameters> +API V2 Characteristic <00010002-574f-4f20-5370-6865726f2121> +DFU Control Characteristic <00020002-574f-4f20-5370-6865726f2121> +Name Characteristic <Device Name> +Appearance Characteristic <Appearance> +DFU Info Characteristic <00020004-574f-4f20-5370-6865726f2121> +Service Changed Characteristic <Service Changed> +Unknown1 Characteristic <00020003-574f-4f20-5370-6865726f2121> +Unknown2 Characteristic <00010003-574f-4f20-5370-6865726f2121> + +The rest of the values saved in the dictionaries below, were borrowed from +@igbopie's javacript library, which is available at https://github.com/igbopie/spherov2.js + +''' + +deviceID = {"apiProcessor": 0x10, # 16 + "systemInfo": 0x11, # 17 + "powerInfo": 0x13, # 19 + "driving": 0x16, # 22 + "animatronics": 0x17, # 23 + "sensor": 0x18, # 24 + "something": 0x19, # 25 + "userIO": 0x1a, # 26 + "somethingAPI": 0x1f} # 31 + +SystemInfoCommands = {"mainApplicationVersion": 0x00, # 00 + "bootloaderVersion": 0x01, # 01 + "something": 0x06, # 06 + "something2": 0x13, # 19 + "something6": 0x12, # 18 + "something7": 0x28} # 40 + +sendPacketConstants = {"StartOfPacket": 0x8d, # 141 + "EndOfPacket": 0xd8} # 216 + +userIOCommandIDs = {"allLEDs": 0x0e} # 14 + +flags= {"isResponse": 0x01, # 0x01 + "requestsResponse": 0x02, # 0x02 + "requestsOnlyErrorResponse": 0x04, # 0x04 + "resetsInactivityTimeout": 0x08} # 0x08 + +powerCommandIDs={"deepSleep": 0x00, # 0 + "sleep": 0x01, # 01 + "batteryVoltage": 0x03, # 03 + "wake": 0x0D, # 13 + "something": 0x05, # 05 + "something2": 0x10, # 16 + "something3": 0x04, # 04 + "something4": 0x1E} # 30 + +drivingCommands={"rawMotor": 0x01, # 1 + "resetHeading": 0x06, # 6 + "driveAsSphero": 0x04, # 4 + "driveAsRc": 0x02, # 2 + "driveWithHeading": 0x07, # 7 + "stabilization": 0x0C} # 12 + +sensorCommands={'sensorMask': 0x00, # 00 + 'sensorResponse': 0x02, # 02 + 'configureCollision': 0x11, # 17 + 'collisionDetectedAsync': 0x12, # 18 + 'resetLocator': 0x13, # 19 + 'enableCollisionAsync': 0x14, # 20 + 'sensor1': 0x0F, # 15 + 'sensor2': 0x17, # 23 + 'configureSensorStream': 0x0C} # 12 diff --git a/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/blobErkennung.py b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/blobErkennung.py new file mode 100644 index 0000000000000000000000000000000000000000..4b0c0cd6c73880c65396252247ee3ad195d155cb --- /dev/null +++ b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/blobErkennung.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +# Standard imports +import cv2 +import numpy as np; + +# Img Objekt +#cap = cv2.VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") +cap = cv2.VideoCapture(4) + +# Set up the detector with default parameters. +detector = cv2.SimpleBlobDetector() + +# Setup SimpleBlobDetector parameters. +params = cv2.SimpleBlobDetector_Params() + +# Change thresholds +#params.minThreshold = 5 +#params.maxThreshold = 500 + +#FIlter by Color +params.filterByColor = True +params.blobColor = 255 + +# Filter by Area. +params.filterByArea = True +params.minArea = 20 +params.maxArea = 200 + +# Filter by Circularity +#params.filterByCircularity = True +#params.minCircularity = 0.5 +#params.maxCircularity = 1 + +# Filter by Convexity +#params.filterByConvexity = True +#params.minConvexity = 0.7 +#params.maxConvexity = 1 + +# Filter by Inertia +params.filterByInertia = True +params.minInertiaRatio = 0.5 + +# Create a detector with the parameters +detector = cv2.SimpleBlobDetector_create(params) + +while True: + + success, img = cap.read() + gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY ) + + # Detect blobs. + keypoints = detector.detect(gray) + + #print(keypoints) + for keyPoint in keypoints: + x = keyPoint.pt[0] + y = keyPoint.pt[1] + + im_with_keypoints = cv2.drawKeypoints(gray, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) + + # Show blobs + cv2.imshow("Keypoints", im_with_keypoints) + + if cv2.waitKey(10) & 0xFF == ord('q'): + break \ No newline at end of file diff --git a/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/coreROS2.py b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/coreROS2.py new file mode 100644 index 0000000000000000000000000000000000000000..ec3435051de5aa72fc393d224b0fe1a53927b8b0 --- /dev/null +++ b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/coreROS2.py @@ -0,0 +1,638 @@ +from bluepy.btle import Peripheral +from bluepy import btle +from sphero_mini_controller._constants import * +import struct +import time +import sys + +class SpheroMini(): + def __init__(self, MACAddr, verbosity = 4, user_delegate = None): + ''' + initialize class instance and then build collect BLE sevices and characteristics. + Also sends text string to Anti-DOS characteristic to prevent returning to sleep, + and initializes notifications (which is what the sphero uses to send data back to + the client). + ''' + self.verbosity = verbosity # 0 = Silent, + # 1 = Connection/disconnection only + # 2 = Init messages + # 3 = Recieved commands + # 4 = Acknowledgements + self.sequence = 1 + self.v_batt = None # will be updated with battery voltage when sphero.getBatteryVoltage() is called + self.firmware_version = [] # will be updated with firware version when sphero.returnMainApplicationVersion() is called + + if self.verbosity > 0: + print("[INFO] Connecting to ", MACAddr) + self.p = Peripheral(MACAddr, "random") #connect + + if self.verbosity > 1: + print("[INIT] Initializing") + + # Subscribe to notifications + self.sphero_delegate = MyDelegate(self, user_delegate) # Pass a reference to this instance when initializing + self.p.setDelegate(self.sphero_delegate) + + if self.verbosity > 1: + print("[INIT] Read all characteristics and descriptors") + # Get characteristics and descriptors + self.API_V2_characteristic = self.p.getCharacteristics(uuid="00010002-574f-4f20-5370-6865726f2121")[0] + self.AntiDOS_characteristic = self.p.getCharacteristics(uuid="00020005-574f-4f20-5370-6865726f2121")[0] + self.DFU_characteristic = self.p.getCharacteristics(uuid="00020002-574f-4f20-5370-6865726f2121")[0] + self.DFU2_characteristic = self.p.getCharacteristics(uuid="00020004-574f-4f20-5370-6865726f2121")[0] + self.API_descriptor = self.API_V2_characteristic.getDescriptors(forUUID=0x2902)[0] + self.DFU_descriptor = self.DFU_characteristic.getDescriptors(forUUID=0x2902)[0] + + # The rest of this sequence was observed during bluetooth sniffing: + # Unlock code: prevent the sphero mini from going to sleep again after 10 seconds + if self.verbosity > 1: + print("[INIT] Writing AntiDOS characteristic unlock code") + self.AntiDOS_characteristic.write("usetheforce...band".encode(), withResponse=True) + + # Enable DFU notifications: + if self.verbosity > 1: + print("[INIT] Configuring DFU descriptor") + self.DFU_descriptor.write(struct.pack('<bb', 0x01, 0x00), withResponse = True) + + # No idea what this is for. Possibly a device ID of sorts? Read request returns '00 00 09 00 0c 00 02 02': + if self.verbosity > 1: + print("[INIT] Reading DFU2 characteristic") + _ = self.DFU2_characteristic.read() + + # Enable API notifications: + if self.verbosity > 1: + print("[INIT] Configuring API dectriptor") + self.API_descriptor.write(struct.pack('<bb', 0x01, 0x00), withResponse = True) + + self.wake() + + # Finished initializing: + if self.verbosity > 1: + print("[INIT] Initialization complete\n") + + def disconnect(self): + if self.verbosity > 0: + print("[INFO] Disconnecting") + + self.p.disconnect() + + def wake(self): + ''' + Bring device out of sleep mode (can only be done if device was in sleep, not deep sleep). + If in deep sleep, the device should be connected to USB power to wake. + ''' + if self.verbosity > 2: + print("[SEND {}] Waking".format(self.sequence)) + + self._send(characteristic=self.API_V2_characteristic, + devID=deviceID['powerInfo'], + commID=powerCommandIDs["wake"], + payload=[]) # empty payload + + self.getAcknowledgement("Wake") + + def sleep(self, deepSleep=False): + ''' + Put device to sleep or deep sleep (deep sleep needs USB power connected to wake up) + ''' + if deepSleep: + sleepCommID=powerCommandIDs["deepSleep"] + if self.verbosity > 0: + print("[INFO] Going into deep sleep. Connect USB power to wake.") + else: + sleepCommID=powerCommandIDs["sleep"] + self._send(characteristic=self.API_V2_characteristic, + devID=deviceID['powerInfo'], + commID=sleepCommID, + payload=[]) #empty payload + + def setLEDColor(self, red = None, green = None, blue = None): + ''' + Set device LED color based on RGB vales (each can range between 0 and 0xFF) + ''' + if self.verbosity > 2: + print("[SEND {}] Setting main LED colour to [{}, {}, {}]".format(self.sequence, red, green, blue)) + + self._send(characteristic = self.API_V2_characteristic, + devID = deviceID['userIO'], # 0x1a + commID = userIOCommandIDs["allLEDs"], # 0x0e + payload = [0x00, 0x0e, red, green, blue]) + + self.getAcknowledgement("LED/backlight") + + def setBackLEDIntensity(self, brightness=None): + ''' + Set device LED backlight intensity based on 0-255 values + + NOTE: this is not the same as aiming - it only turns on the LED + ''' + if self.verbosity > 2: + print("[SEND {}] Setting backlight intensity to {}".format(self.sequence, brightness)) + + self._send(characteristic = self.API_V2_characteristic, + devID = deviceID['userIO'], + commID = userIOCommandIDs["allLEDs"], + payload = [0x00, 0x01, brightness]) + + self.getAcknowledgement("LED/backlight") + + def roll(self, speed=None, heading=None): + ''' + Start to move the Sphero at a given direction and speed. + heading: integer from 0 - 360 (degrees) + speed: Integer from 0 - 255 + + Note: the zero heading should be set at startup with the resetHeading method. Otherwise, it may + seem that the sphero doesn't honor the heading argument + ''' + if self.verbosity > 2: + print("[SEND {}] Rolling with speed {} and heading {}".format(self.sequence, speed, heading)) + + if abs(speed) > 255: + print("WARNING: roll speed parameter outside of allowed range (-255 to +255)") + + if speed < 0: + speed = -1*speed+256 # speed values > 256 in the send packet make the spero go in reverse + + speedH = (speed & 0xFF00) >> 8 + speedL = speed & 0xFF + headingH = (heading & 0xFF00) >> 8 + headingL = heading & 0xFF + self._send(characteristic = self.API_V2_characteristic, + devID = deviceID['driving'], + commID = drivingCommands["driveWithHeading"], + payload = [speedL, headingH, headingL, speedH]) + + self.getAcknowledgement("Roll") + + def resetHeading(self): + ''' + Reset the heading zero angle to the current heading (useful during aiming) + Note: in order to manually rotate the sphero, you need to call stabilization(False). + Once the heading has been set, call stabilization(True). + ''' + if self.verbosity > 2: + print("[SEND {}] Resetting heading".format(self.sequence)) + + self._send(characteristic = self.API_V2_characteristic, + devID = deviceID['driving'], + commID = drivingCommands["resetHeading"], + payload = []) #empty payload + + self.getAcknowledgement("Heading") + + def returnMainApplicationVersion(self): + ''' + Sends command to return application data in a notification + ''' + if self.verbosity > 2: + print("[SEND {}] Requesting firmware version".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID = deviceID['systemInfo'], + commID = SystemInfoCommands['mainApplicationVersion'], + payload = []) # empty + + self.getAcknowledgement("Firmware") + + def getBatteryVoltage(self): + ''' + Sends command to return battery voltage data in a notification. + Data printed to console screen by the handleNotifications() method in the MyDelegate class. + ''' + if self.verbosity > 2: + print("[SEND {}] Requesting battery voltage".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID=deviceID['powerInfo'], + commID=powerCommandIDs['batteryVoltage'], + payload=[]) # empty + + self.getAcknowledgement("Battery") + + def stabilization(self, stab = True): + ''' + Sends command to turn on/off the motor stabilization system (required when manually turning/aiming the sphero) + ''' + if stab == True: + if self.verbosity > 2: + print("[SEND {}] Enabling stabilization".format(self.sequence)) + val = 1 + else: + if self.verbosity > 2: + print("[SEND {}] Disabling stabilization".format(self.sequence)) + val = 0 + self._send(self.API_V2_characteristic, + devID=deviceID['driving'], + commID=drivingCommands['stabilization'], + payload=[val]) + + self.getAcknowledgement("Stabilization") + + def wait(self, delay): + ''' + This is a non-blocking delay command. It is similar to time.sleep(), except it allows asynchronous + notification handling to still be performed. + ''' + start = time.time() + while(1): + self.p.waitForNotifications(0.001) + if time.time() - start > delay: + break + + def _send(self, characteristic=None, devID=None, commID=None, payload=[]): + ''' + A generic "send" method, which will be used by other methods to send a command ID, payload and + appropriate checksum to a specified device ID. Mainly useful because payloads are optional, + and can be of varying length, to convert packets to binary, and calculate and send the + checksum. For internal use only. + + Packet structure has the following format (in order): + + - Start byte: always 0x8D + - Flags byte: indicate response required, etc + - Virtual device ID: see _constants.py + - Command ID: see _constants.py + - Sequence number: Seems to be arbitrary. I suspect it is used to match commands to response packets (in which the number is echoed). + - Payload: Could be varying number of bytes (incl. none), depending on the command + - Checksum: See below for calculation + - End byte: always 0xD8 + + ''' + sendBytes = [sendPacketConstants["StartOfPacket"], + sum([flags["resetsInactivityTimeout"], flags["requestsResponse"]]), + devID, + commID, + self.sequence] + payload # concatenate payload list + + self.sequence += 1 # Increment sequence number, ensures we can identify response packets are for this command + if self.sequence > 255: + self.sequence = 0 + + # Compute and append checksum and add EOP byte: + # From Sphero docs: "The [checksum is the] modulo 256 sum of all the bytes + # from the device ID through the end of the data payload, + # bit inverted (1's complement)" + # For the sphero mini, the flag bits must be included too: + checksum = 0 + for num in sendBytes[1:]: + checksum = (checksum + num) & 0xFF # bitwise "and to get modulo 256 sum of appropriate bytes + checksum = 0xff - checksum # bitwise 'not' to invert checksum bits + sendBytes += [checksum, sendPacketConstants["EndOfPacket"]] # concatenate + + # Convert numbers to bytes + output = b"".join([x.to_bytes(1, byteorder='big') for x in sendBytes]) + + #send to specified characteristic: + characteristic.write(output, withResponse = True) + + def getAcknowledgement(self, ack): + #wait up to 10 secs for correct acknowledgement to come in, including sequence number! + start = time.time() + while(1): + self.p.waitForNotifications(1) + if self.sphero_delegate.notification_seq == self.sequence-1: # use one less than sequence, because _send function increments it for next send. + if self.verbosity > 3: + print("[RESP {}] {}".format(self.sequence-1, self.sphero_delegate.notification_ack)) + self.sphero_delegate.clear_notification() + break + elif self.sphero_delegate.notification_seq >= 0: + print("Unexpected ACK. Expected: {}/{}, received: {}/{}".format( + ack, self.sequence, self.sphero_delegate.notification_ack.split()[0], + self.sphero_delegate.notification_seq) + ) + if time.time() > start + 10: + print("Timeout waiting for acknowledgement: {}/{}".format(ack, self.sequence)) + break + +# ======================================================================= +# The following functions are experimental: +# ======================================================================= + + def configureCollisionDetection(self, + xThreshold = 50, + yThreshold = 50, + xSpeed = 50, + ySpeed = 50, + deadTime = 50, # in 10 millisecond increments + method = 0x01, # Must be 0x01 + callback = None): + ''' + Appears to function the same as other Sphero models, however speed settings seem to have no effect. + NOTE: Setting to zero seems to cause bluetooth errors with the Sphero Mini/bluepy library - set to + 255 to make it effectively disabled. + + deadTime disables future collisions for a short period of time to avoid repeat triggering by the same + event. Set in 10ms increments. So if deadTime = 50, that means the delay will be 500ms, or half a second. + + From Sphero docs: + + xThreshold/yThreshold: An 8-bit settable threshold for the X (left/right) and Y (front/back) axes + of Sphero. + + xSpeed/ySpeed: An 8-bit settable speed value for the X and Y axes. This setting is ranged by the + speed, then added to xThreshold, yThreshold to generate the final threshold value. + ''' + + if self.verbosity > 2: + print("[SEND {}] Configuring collision detection".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['configureCollision'], + payload=[method, xThreshold, xSpeed, yThreshold, ySpeed, deadTime]) + + self.collision_detection_callback = callback + + self.getAcknowledgement("Collision") + + def configureSensorStream(self): # Use default values + ''' + Send command to configure sensor stream using default values as found during bluetooth + sniffing of the Sphero Edu app. + + Must be called after calling configureSensorMask() + ''' + bitfield1 = 0b00000000 # Unknown function - needs experimenting + bitfield2 = 0b00000000 # Unknown function - needs experimenting + bitfield3 = 0b00000000 # Unknown function - needs experimenting + bitfield4 = 0b00000000 # Unknown function - needs experimenting + + if self.verbosity > 2: + print("[SEND {}] Configuring sensor stream".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['configureSensorStream'], + payload=[bitfield1, bitfield1, bitfield1, bitfield1]) + + self.getAcknowledgement("Sensor") + + def configureSensorMask(self, + sample_rate_divisor = 0x25, # Must be > 0 + packet_count = 0, + IMU_pitch = False, + IMU_roll = False, + IMU_yaw = False, + IMU_acc_x = False, + IMU_acc_y = False, + IMU_acc_z = False, + IMU_gyro_x = False, + IMU_gyro_y = False, + IMU_gyro_z = False): + + ''' + Send command to configure sensor mask using default values as found during bluetooth + sniffing of the Sphero Edu app. From experimentation, it seems that these are he functions of each: + + Sampling_rate_divisor. Slow data EG: Set to 0x32 to the divide data rate by 50. Setting below 25 (0x19) causes + bluetooth errors + + Packet_count: Select the number of packets to transmit before ending the stream. Set to zero to stream infinitely + + All IMU bool parameters: Toggle transmission of that value on or off (e.g. set IMU_acc_x = True to include the + X-axis accelerometer readings in the sensor stream) + ''' + + # Construct bitfields based on function parameters: + IMU_bitfield1 = (IMU_pitch<<2) + (IMU_roll<<1) + IMU_yaw + IMU_bitfield2 = ((IMU_acc_y<<7) + (IMU_acc_z<<6) + (IMU_acc_x<<5) + \ + (IMU_gyro_y<<4) + (IMU_gyro_x<<2) + (IMU_gyro_z<<2)) + + if self.verbosity > 2: + print("[SEND {}] Configuring sensor mask".format(self.sequence)) + + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['sensorMask'], + payload=[0x00, # Unknown param - altering it seems to slow data rate. Possibly averages multiple readings? + sample_rate_divisor, + packet_count, # Packet count: select the number of packets to stop streaming after (zero = infinite) + 0b00, # Unknown param: seems to be another accelerometer bitfield? Z-acc, Y-acc + IMU_bitfield1, + IMU_bitfield2, + 0b00]) # reserved, Position?, Position?, velocity?, velocity?, Y-gyro, timer, reserved + + self.getAcknowledgement("Mask") + + ''' + Since the sensor values arrive as unlabelled lists in the order that they appear in the bitfields above, we need + to create a list of sensors that have been configured.Once we have this list, then in the default_delegate class, + we can get sensor values as attributes of the sphero_mini class. + e.g. print(sphero.IMU_yaw) # displays the current yaw angle + ''' + + # Initialize dictionary with sensor names as keys and their bool values (set by the user) as values: + availableSensors = {"IMU_pitch" : IMU_pitch, + "IMU_roll" : IMU_roll, + "IMU_yaw" : IMU_yaw, + "IMU_acc_y" : IMU_acc_y, + "IMU_acc_z" : IMU_acc_z, + "IMU_acc_x" : IMU_acc_x, + "IMU_gyro_y" : IMU_gyro_y, + "IMU_gyro_x" : IMU_gyro_x, + "IMU_gyro_z" : IMU_gyro_z} + + # Create list of of only sensors that have been "activated" (set as true in the method arguments): + self.configured_sensors = [name for name in availableSensors if availableSensors[name] == True] + + def sensor1(self): # Use default values + ''' + Unknown function. Observed in bluetooth sniffing. + ''' + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['sensor1'], + payload=[0x01]) + + self.getAcknowledgement("Sensor1") + + def sensor2(self): # Use default values + ''' + Unknown function. Observed in bluetooth sniffing. + ''' + self._send(self.API_V2_characteristic, + devID=deviceID['sensor'], + commID=sensorCommands['sensor2'], + payload=[0x00]) + + self.getAcknowledgement("Sensor2") + +# ======================================================================= + +class MyDelegate(btle.DefaultDelegate): + + ''' + This class handles notifications (both responses and asynchronous notifications). + + Usage of this class is described in the Bluepy documentation + + ''' + + def __init__(self, sphero_class, user_delegate): + self.sphero_class = sphero_class # for saving sensor values as attributes of sphero class instance + self.user_delegate = user_delegate # to directly notify users of callbacks + btle.DefaultDelegate.__init__(self) + self.clear_notification() + self.notificationPacket = [] + + def clear_notification(self): + self.notification_ack = "DEFAULT ACK" + self.notification_seq = -1 + + def bits_to_num(self, bits): + ''' + This helper function decodes bytes from sensor packets into single precision floats. Encoding follows the + the IEEE-754 standard. + ''' + num = int(bits, 2).to_bytes(len(bits) // 8, byteorder='little') + num = struct.unpack('f', num)[0] + return num + + def handleNotification(self, cHandle, data): + ''' + This method acts as an interrupt service routine. When a notification comes in, this + method is invoked, with the variable 'cHandle' being the handle of the characteristic that + sent the notification, and 'data' being the payload (sent one byte at a time, so the packet + needs to be reconstructed) + + The method keeps appending bytes to the payload packet byte list until end-of-packet byte is + encountered. Note that this is an issue, because 0xD8 could be sent as part of the payload of, + say, the battery voltage notification. In future, a more sophisticated method will be required. + ''' + # Allow the user to intercept and process data first.. + if self.user_delegate != None: + if self.user_delegate.handleNotification(cHandle, data): + return + + #print("Received notification with packet ", str(data)) + + for data_byte in data: # parse each byte separately (sometimes they arrive simultaneously) + + self.notificationPacket.append(data_byte) # Add new byte to packet list + + # If end of packet (need to find a better way to segment the packets): + if data_byte == sendPacketConstants['EndOfPacket']: + # Once full the packet has arrived, parse it: + # Packet structure is similar to the outgoing send packets (see docstring in sphero_mini._send()) + + # Attempt to unpack. Might fail if packet is too badly corrupted + try: + start, flags_bits, devid, commcode, seq, *notification_payload, chsum, end = self.notificationPacket + except ValueError: + print("Warning: notification packet unparseable ", self.notificationPacket) + self.notificationPacket = [] # Discard this packet + return # exit + + # Compute and append checksum and add EOP byte: + # From Sphero docs: "The [checksum is the] modulo 256 sum of all the bytes + # from the device ID through the end of the data payload, + # bit inverted (1's complement)" + # For the sphero mini, the flag bits must be included too: + checksum_bytes = [flags_bits, devid, commcode, seq] + notification_payload + checksum = 0 # init + for num in checksum_bytes: + checksum = (checksum + num) & 0xFF # bitwise "and to get modulo 256 sum of appropriate bytes + checksum = 0xff - checksum # bitwise 'not' to invert checksum bits + if checksum != chsum: # check computed checksum against that recieved in the packet + print("Warning: notification packet checksum failed - ", str(self.notificationPacket)) + self.notificationPacket = [] # Discard this packet + return # exit + + # Check if response packet: + if flags_bits & flags['isResponse']: # it is a response + + # Use device ID and command code to determine which command is being acknowledged: + if devid == deviceID['powerInfo'] and commcode == powerCommandIDs['wake']: + self.notification_ack = "Wake acknowledged" # Acknowledgement after wake command + + elif devid == deviceID['driving'] and commcode == drivingCommands['driveWithHeading']: + self.notification_ack = "Roll command acknowledged" + + elif devid == deviceID['driving'] and commcode == drivingCommands['stabilization']: + self.notification_ack = "Stabilization command acknowledged" + + elif devid == deviceID['userIO'] and commcode == userIOCommandIDs['allLEDs']: + self.notification_ack = "LED/backlight color command acknowledged" + + elif devid == deviceID['driving'] and commcode == drivingCommands["resetHeading"]: + self.notification_ack = "Heading reset command acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["configureCollision"]: + self.notification_ack = "Collision detection configuration acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["configureSensorStream"]: + self.notification_ack = "Sensor stream configuration acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["sensorMask"]: + self.notification_ack = "Mask configuration acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["sensor1"]: + self.notification_ack = "Sensor1 acknowledged" + + elif devid == deviceID['sensor'] and commcode == sensorCommands["sensor2"]: + self.notification_ack = "Sensor2 acknowledged" + + elif devid == deviceID['powerInfo'] and commcode == powerCommandIDs['batteryVoltage']: + V_batt = notification_payload[2] + notification_payload[1]*256 + notification_payload[0]*65536 + V_batt /= 100 # Notification gives V_batt in 10mV increments. Divide by 100 to get to volts. + self.notification_ack = "Battery voltage:" + str(V_batt) + "v" + self.sphero_class.v_batt = V_batt + + elif devid == deviceID['systemInfo'] and commcode == SystemInfoCommands['mainApplicationVersion']: + version = '.'.join(str(x) for x in notification_payload) + self.notification_ack = "Firmware version: " + version + self.sphero_class.firmware_version = notification_payload + + else: + self.notification_ack = "Unknown acknowledgement" #print(self.notificationPacket) + print(self.notificationPacket, "===================> Unknown ack packet") + + self.notification_seq = seq + + else: # Not a response packet - therefore, asynchronous notification (e.g. collision detection, etc): + + # Collision detection: + if devid == deviceID['sensor'] and commcode == sensorCommands['collisionDetectedAsync']: + # The first four bytes are data that is still un-parsed. the remaining unsaved bytes are always zeros + _, _, _, _, _, _, axis, _, Y_mag, _, X_mag, *_ = notification_payload + if axis == 1: + dir = "Left/right" + else: + dir = 'Forward/back' + print("Collision detected:") + print("\tAxis: ", dir) + print("\tX_mag: ", X_mag) + print("\tY_mag: ", Y_mag) + + if self.sphero_class.collision_detection_callback is not None: + self.notificationPacket = [] # need to clear packet, in case new notification comes in during callback + self.sphero_class.collision_detection_callback() + + # Sensor response: + elif devid == deviceID['sensor'] and commcode == sensorCommands['sensorResponse']: + # Convert to binary, pad bytes with leading zeros: + val = '' + for byte in notification_payload: + val += format(int(bin(byte)[2:], 2), '#010b')[2:] + + # Break into 32-bit chunks + nums = [] + while(len(val) > 0): + num, val = val[:32], val[32:] # Slice off first 16 bits + nums.append(num) + + # convert from raw bits to float: + nums = [self.bits_to_num(num) for num in nums] + + # Set sensor values as class attributes: + for name, value in zip(self.sphero_class.configured_sensors, nums): + print("Setting sensor at .", name, str(value)) + setattr(self.sphero_class, name, value) + + # Unrecognized packet structure: + else: + self.notification_ack = "Unknown asynchronous notification" #print(self.notificationPacket) + print(str(self.notificationPacket) + " ===================> Unknown async packet") + + self.notificationPacket = [] # Start new payload after this byte \ No newline at end of file diff --git a/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/karte.jpg b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/karte.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2dfab8ae944dcd069b9970cebe77965f549b21a0 Binary files /dev/null and b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/karte.jpg differ diff --git a/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/sphero_conf.json b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/sphero_conf.json new file mode 100644 index 0000000000000000000000000000000000000000..a490b4b798c696938452dac610e3cfac087c6279 --- /dev/null +++ b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/sphero_conf.json @@ -0,0 +1,3 @@ +{ + "MAC_ADDRESS": "C6:69:72:CD:BC:6D" +} diff --git a/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/sphero_mini.py b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/sphero_mini.py new file mode 100644 index 0000000000000000000000000000000000000000..2dd16aaa64b8df585becfba8c698b561e06fb62f --- /dev/null +++ b/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/sphero_mini.py @@ -0,0 +1,756 @@ +#!/usr/bin/env python3 +############################################################################ +#%% Imports +import rclpy +from rclpy.node import Node +from sensor_msgs.msg import Imu +import threading +from geometry_msgs.msg import Quaternion, Vector3 +import numpy as np +from sphero_mini_controller.coreROS2 import SpheroMini +import cv2 +import time +import queue +import matplotlib.pyplot as plt +############################################################################ +#%% Blob Detector / Globale Variablen +detector = cv2.SimpleBlobDetector() + +# Setup SimpleBlobDetector parameters. +params = cv2.SimpleBlobDetector_Params() + +# Change thresholds +#params.minThreshold = 5 +#params.maxThreshold = 500 + +#FIlter by Color +params.filterByColor = True +params.blobColor = 255 + +# Filter by Area. +params.filterByArea = True +params.minArea = 10 +params.maxArea = 200 + +# Filter by Circularity +#params.filterByCircularity = True +#params.minCircularity = 0.5 +#params.maxCircularity = 1 + +# Filter by Convexity +#params.filterByConvexity = True +#params.minConvexity = 0.7 +#params.maxConvexity = 1 + +# Filter by Inertia +#params.filterByInertia = True +#params.minInertiaRatio = 0.01 + +# Create a detector with the parameters +detector = cv2.SimpleBlobDetector_create(params) +############################################################################ +#%% Class Video Capture +# Bufferless VideoCapture +# Quelle ? +class VideoCapture: + def __init__(self, name): + self.cap = cv2.VideoCapture(name) + self.q = queue.Queue() + t = threading.Thread(target=self._reader) + t.daemon = True + t.start() + # read frames as soon as they are available, keeping only most recent one + def _reader(self): + while True: + ret, frame = self.cap.read() + #cv2.imshow("Cap", frame) + + if not ret: + break + if not self.q.empty(): + try: + self.q.get_nowait() # discard previous (unprocessed) frame + except queue.Empty: + pass + self.q.put(frame) + + def read(self): + return self.q.get() +############################################################################ +#%% Class Sphero Node +class MyNode(Node): + def __init__(self): + super().__init__("sphero_mini") + + def connect(self): + MAC_ADDRESS = "C6:69:72:CD:BC:6D" + + # Connect: + sphero = SpheroMini(MAC_ADDRESS, verbosity = 4) + # battery voltage + sphero.getBatteryVoltage() + print(f"Bettery voltage: {sphero.v_batt}v") + + # firmware version number + sphero.returnMainApplicationVersion() + print(f"Firmware version: {'.'.join(str(x) for x in sphero.firmware_version)}") + return sphero + + def create_quaternion(self, roll, pitch, yaw): + q = Quaternion() + cy, sy = np.cos(yaw * 0.5), np.sin(yaw * 0.5) + cp, sp = np.cos(pitch * 0.5), np.sin(pitch * 0.5) + cr, sr = np.cos(roll * 0.5), np.sin(roll * 0.5) + + q.w = cr * cp * cy + sr * sp * sy + q.x = sr * cp * cy - cr * sp * sy + q.y = cr * sp * cy + sr * cp * sy + q.z = cr * cp * sy - sr * sp * cy + + return q + + def create_angular_veolocity_vector3(self, groll, gpitch, gyaw): + v = Vector3() + v.x = groll + v.y = gpitch + v.z = float(gyaw) + + return v + + def create_linear_acc_vector3(self, xacc, yacc, zacc): + v = Vector3() + v.x = xacc + v.y = yacc + v.z = zacc + + return v + + def get_sensors_data(self, sphero): + return { + "roll": sphero.IMU_roll, + "pitch": sphero.IMU_pitch, + "yaw": sphero.IMU_yaw, + "groll": sphero.IMU_gyro_x, + "gpitch": sphero.IMU_gyro_y, + "xacc": sphero.IMU_acc_x, + "yacc": sphero.IMU_acc_y, + "zacc": sphero.IMU_acc_z + } + + def publish_imu(self, sensors_values,node): + i = Imu() + + i.header.stamp = node.get_clock().now().to_msg() + + i.orientation = self.create_quaternion( + roll=sensors_values["roll"], + pitch=sensors_values["pitch"], + yaw=sensors_values["yaw"] + ) + i.angular_velocity = self.create_angular_veolocity_vector3( + groll=sensors_values["groll"], + gpitch=sensors_values["gpitch"], + gyaw=0 # We don't have the IMU_gyro_z + ) + i.linear_acceleration = self.create_linear_acc_vector3( + xacc=sensors_values["xacc"], + yacc=sensors_values["yacc"], + zacc=sensors_values["zacc"] + ) + + def get_pos(self, cap, height): + #zeitanfang = time.time() + + success = False + + while(success == False): + try: + img = cap.read() + + gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY ) + keypoints = detector.detect(gray) + + for keyPoint in keypoints: + x = keyPoint.pt[0] + y = keyPoint.pt[1] + success = True + + xTrans, yTrans = self.calc_trans(x,y, height) + except Exception: + continue + + #zeitende = time.time() + #print("Dauer Programmausführung:",(zeitende-zeitanfang)) + #print("Get_Pos: X,Y:", xTrans,yTrans) + + return xTrans,yTrans + + def calc_offset(self, sphero, cap, height): + sphero.roll(100,0) + sphero.wait(1) + sphero.roll(0,0) + sphero.wait(0.5) + + ref = self.get_pos(cap, height) + print("calc_offset: Ref Punkt x,y", ref) + + sphero.wait(1) + sphero.roll(100,180) + sphero.wait(1) + sphero.roll(0,0) + + startpunkt = self.get_pos(cap, height) + print("calc_offset: Startpunkt x,y", startpunkt) + + ref = np.array(ref) + startpunkt = np.array(startpunkt) + + start_ref = ref-startpunkt + + phi = np.arctan2(start_ref[1],start_ref[0]) + phi = np.degrees(phi) + phi = int(-phi) + + print("Phi ohne alles:", phi) + + phi = self.phi_in_range(phi) + + print("Calc_Offset: ", phi) + + return phi + + def calc_av(self,startPos, sollPos, cap, height): + startPos = np.array(startPos) + sollPos = np.array(sollPos) + + pktSpheroKord = sollPos - startPos + + phi = np.arctan2(pktSpheroKord[1], pktSpheroKord[0]) + phi = np.degrees(phi) + + phiZiel = int(phi) + + phiZiel = self.phi_in_range(phiZiel) + + v = 100 + + #print("Calc_AV: a,v", phiZiel, v) + + return phiZiel, v + + def drive_to(self,sphero, targetPos, aOffset, cap, height): + dmin = 20 + pos = self.get_pos(cap, height) + pos = np.array(pos) + + diff = targetPos - pos + + d = np.linalg.norm(diff, ord = 2) + + ar = [] + ar.append(0) + + i = 1 + + while d > dmin: + a,v = self.calc_av(pos,targetPos, cap, height) + + aR = -aOffset - a + ar.append((self.phi_in_range(aR))) + + #Fahrbefehl + if(ar[i] != ar[i-1]): + sphero.roll(v, ar[i]) + sphero.wait(0.05) + else: + sphero.wait(0.05) + + #Aktuelle Pos + pos = self.get_pos(cap, height) + pos = np.array(pos) + + #Abweichung + diff = targetPos - pos + diff = np.array(diff) + d = np.linalg.norm(diff, ord = 2) + + i = i + 1 + + sphero.roll(0,0) + print("Ziel Erreicht") + + return + + def calc_trans(self,x,y, height): + yTrans = height - y + xTrans = x + + return xTrans, yTrans + + def phi_in_range(self,phi): + while(phi < 0): + phi = phi + 360 + while(phi > 360): + phi = phi - 360 + return phi +############################################################################ +#%% Class Wavefrontplanner +class waveFrontPlanner: + ############################################################################ + # WAVEFRONT ALGORITHM + # Adapted to Python Code By Darin Velarde + # Fri Jan 29 13:56:53 PST 2010 + # from C code from John Palmisano + # (www.societyofrobots.com) + ############################################################################ + + def __init__(self, mapOfWorld, slow=False): + self.__slow = slow + self.__mapOfWorld = mapOfWorld + if str(type(mapOfWorld)).find("numpy") != -1: + #If its a numpy array + self.__height, self.__width = self.__mapOfWorld.shape + else: + self.__height, self.__width = len(self.__mapOfWorld), len(self.__mapOfWorld[0]) + + self.__nothing = 000 + self.__wall = 999 + self.__goal = 1 + self.__path = "PATH" + + self.__finalPath = [] + + #Robot value + self.__robot = 254 + #Robot default Location + self.__robot_x = 0 + self.__robot_y = 0 + + #default goal location + self.__goal_x = 8 + self.__goal_y = 9 + + #temp variables + self.__temp_A = 0 + self.__temp_B = 0 + self.__counter = 0 + self.__steps = 0 #determine how processor intensive the algorithm was + + #when searching for a node with a lower value + self.__minimum_node = 250 + self.__min_node_location = 250 + self.__new_state = 1 + self.__old_state = 1 + self.__reset_min = 250 #above this number is a special (wall or robot) + ########################################################################### + + def setRobotPosition(self, x, y): + """ + Sets the robot's current position + + """ + + self.__robot_x = x + self.__robot_y = y + ########################################################################### + + def setGoalPosition(self, x, y): + """ + Sets the goal position. + + """ + + self.__goal_x = x + self.__goal_y = y + ########################################################################### + + def robotPosition(self): + return (self.__robot_x, self.__robot_y) + ########################################################################### + + def goalPosition(self): + return (self.__goal_x, self.__goal_y) + ########################################################################### + + def run(self, prnt=False): + """ + The entry point for the robot algorithm to use wavefront propagation. + + """ + + path = [] + while self.__mapOfWorld[self.__robot_x][self.__robot_y] != self.__goal: + if self.__steps > 20000: + print ("Cannot find a path.") + return + #find new location to go to + self.__new_state = self.propagateWavefront() + #update location of robot + if self.__new_state == 1: + self.__robot_x -= 1 + if prnt: + print("Move to x=%d y=%d\n\n" % (self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + if self.__new_state == 2: + self.__robot_y += 1 + if prnt: + print("Move to x=%d y=%d\n\n" %(self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + if self.__new_state == 3: + self.__robot_x += 1 + if prnt: + print("Move to x=%d y=%d\n\n" %(self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + if self.__new_state == 4: + self.__robot_y -= 1 + if prnt: + print("Move to x=%d y=%d\n\n" %(self.__robot_x, self.__robot_y)) + path.append((self.__robot_x, self.__robot_y)) + self.__old_state = self.__new_state + msg = "Found the goal in %i steps:\n" % self.__steps + msg += "mapOfWorld size= %i %i\n\n" % (self.__height, self.__width) + if prnt: + print(msg) + self.printMap() + return path + ########################################################################### + + def propagateWavefront(self, prnt=False): + self.unpropagate() + #Old robot location was deleted, store new robot location in mapOfWorld + self.__mapOfWorld[self.__robot_x][self.__robot_y] = self.__robot + self.__path = self.__robot + #start location to begin scan at goal location + self.__mapOfWorld[self.__goal_x][self.__goal_y] = self.__goal + counter = 0 + while counter < 200: #allows for recycling until robot is found + x = 0 + y = 0 + #time.sleep(0.00001) + #while the mapOfWorld hasnt been fully scanned + while x < self.__height and y < self.__width: + #if this location is a wall or the goal, just ignore it + if self.__mapOfWorld[x][y] != self.__wall and \ + self.__mapOfWorld[x][y] != self.__goal: + #a full trail to the robot has been located, finished! + minLoc = self.minSurroundingNodeValue(x, y) + if minLoc < self.__reset_min and \ + self.__mapOfWorld[x][y] == self.__robot: + if prnt: + print("Finished Wavefront:\n") + self.printMap() + # Tell the robot to move after this return. + return self.__min_node_location + #record a value in to this node + elif self.__minimum_node != self.__reset_min: + #if this isnt here, 'nothing' will go in the location + self.__mapOfWorld[x][y] = self.__minimum_node + 1 + #go to next node and/or row + y += 1 + if y == self.__width and x != self.__height: + x += 1 + y = 0 + #print self.__robot_x, self.__robot_y + if prnt: + print("Sweep #: %i\n" % (counter + 1)) + self.printMap() + self.__steps += 1 + counter += 1 + return 0 + ########################################################################### + + def unpropagate(self): + """ + clears old path to determine new path + stay within boundary + + """ + + for x in range(0, self.__height): + for y in range(0, self.__width): + if self.__mapOfWorld[x][y] != self.__wall and \ + self.__mapOfWorld[x][y] != self.__goal and \ + self.__mapOfWorld[x][y] != self.__path: + #if this location is a wall or goal, just ignore it + self.__mapOfWorld[x][y] = self.__nothing #clear that space + ########################################################################### + + def minSurroundingNodeValue(self, x, y): + """ + this method looks at a node and returns the lowest value around that + node. + + """ + + #reset minimum + self.__minimum_node = self.__reset_min + #down + if x < self.__height -1: + if self.__mapOfWorld[x + 1][y] < self.__minimum_node and \ + self.__mapOfWorld[x + 1][y] != self.__nothing: + #find the lowest number node, and exclude empty nodes (0's) + self.__minimum_node = self.__mapOfWorld[x + 1][y] + self.__min_node_location = 3 + #up + if x > 0: + if self.__mapOfWorld[x-1][y] < self.__minimum_node and \ + self.__mapOfWorld[x-1][y] != self.__nothing: + self.__minimum_node = self.__mapOfWorld[x-1][y] + self.__min_node_location = 1 + #right + if y < self.__width -1: + if self.__mapOfWorld[x][y + 1] < self.__minimum_node and \ + self.__mapOfWorld[x][y + 1] != self.__nothing: + self.__minimum_node = self.__mapOfWorld[x][y + 1] + self.__min_node_location = 2 + #left + if y > 0: + if self.__mapOfWorld[x][y - 1] < self.__minimum_node and \ + self.__mapOfWorld[x][y - 1] != self.__nothing: + self.__minimum_node = self.__mapOfWorld[x][y-1] + self.__min_node_location = 4 + return self.__minimum_node + ########################################################################### + + def printMap(self): + """ + Prints out the map of this instance of the class. + + """ + + msg = '' + for temp_B in range(0, self.__height): + for temp_A in range(0, self.__width): + if self.__mapOfWorld[temp_B][temp_A] == self.__wall: + msg += "%04s" % "[#]" + elif self.__mapOfWorld[temp_B][temp_A] == self.__robot: + msg += "%04s" % "-" + elif self.__mapOfWorld[temp_B][temp_A] == self.__goal: + msg += "%04s" % "G" + else: + msg += "%04s" % str(self.__mapOfWorld[temp_B][temp_A]) + msg += "\n\n" + msg += "\n\n" + print(msg) + # + if self.__slow == True: + time.sleep(0.05) + + def plotMap(self, img, path,mapWorldScaled,mapWorldOrg): + #Programm Koordinaten + imgTrans = cv2.transpose(img) # X und Y-Achse im Bild tauschen + imgPlot, ax0 = plt.subplots() + ax0.set_title('Programm Koordinaten') + ax0.imshow(imgTrans) + ax0.set_xlabel('[px]') + ax0.set_ylabel('[px]') + ax0.scatter(path[:,0], path[:,1], color='r') + #Karten Array Scaled + imgPlot1, ax1 = plt.subplots() + ax1.set_title('Karten-Array (Vergrößerte Hindernisse e=3)') + ax1.imshow(mapWorldScaled, cmap = 'Greys') + ax1.set_xlabel('[px]') + ax1.set_ylabel('[px]') + #Karten Array Normal + imgPlot2, ax2 = plt.subplots() + ax2.set_title('Karten-Array (Originale Hindernisse)') + ax2.imshow(mapWorldOrg, cmap = 'Greys') + ax2.set_xlabel('[px]') + ax2.set_ylabel('[px]') + #Bild Koordinaten + imgPlot3, ax3 = plt.subplots() + ax3.set_title('Bild Koordinaten') + ax3.set_xlabel('[px]') + ax3.set_ylabel('[px]') + ax3.imshow(img) + ax3.scatter(path[:,1], path[:,0], color='r') + return + + + def getPathWelt(self,path,height,Mapobj): + pathWeltX, pathWeltY = Mapobj.calc_trans_welt(path[:,1], path[:,0], height) + pathEckenX = [] + pathEckenY = [] + + for i,px in enumerate(pathWeltX): + if (i < len(pathWeltX)-1) & (i > 0): + if px != pathWeltX[i+1]: + if pathWeltY[i]!=pathWeltY[i-1]: + pathEckenX.append(px) + pathEckenY.append(pathWeltY[i]) + if pathWeltY[i] != pathWeltY[i+1]: + if pathWeltX[i]!=pathWeltX[i-1]: + pathEckenX.append(px) + pathEckenY.append(pathWeltY[i]) + + pathEckenX.append(pathWeltX[-1]) + pathEckenY.append(pathWeltY[-1]) + + + uebergabePath = [] + for i in range(0,len(pathEckenX)): + uebergabePath.append((pathEckenX[i],pathEckenY[i])) + + print("Ecken: ", uebergabePath) + + return uebergabePath + +############################################################################ +#%% Class Map +class mapCreate: + ############################################################################ + + def create_map(self, scale, cap): + img = cap.read() + + #Karte von Bild + #img = cv2.imread("D:/ros2_ws/src/sphero_mini_controller/sphero_mini_controller/map/map.jpg") + print('Original Dimensions : ',img.shape) + + scale_percent = 100-scale # percent of original size + width = int(img.shape[1] * scale_percent / 100) + height = int(img.shape[0] * scale_percent / 100) + dim = (width, height) + + # resize image + resized = cv2.resize(img, dim, interpolation = cv2.INTER_AREA) + print('Resized Dimensions : ',resized.shape) + + gray = cv2.cvtColor(resized, cv2.COLOR_RGB2GRAY) + mask = cv2.inRange(gray, np.array([200]), np.array([255])) #Parameter hängt von Umgebung ab + + plt.plot(mask) + + mapOfWorld = [[0]*gray.shape[1]]*gray.shape[0] + mask_list= np.ndarray.tolist(mask) + + #Markiere alle leeren Zellen mit 000 und alle Zellen mit Hinderniss mit 999 + for i in range(0,mask.shape[0]): + mapOfWorld[i] = ['999' if j > 200 else '000' for j in mask_list[i]] + + mapOfWorld = np.array(mapOfWorld, dtype = int) + mapOfWorldSized = mapOfWorld.copy() + + e = 3 + #Hindernisse Größer machen + for i in range(0,mapOfWorld.shape[0]): + for j in range(0,mapOfWorld.shape[1]): + for r in range(0,e): + try: + if (mapOfWorldSized[i][j] == 999): + mapOfWorld[i][j+e] = 999 + mapOfWorld[i+e][j] = 999 + mapOfWorld[i-e][j] = 999 + mapOfWorld[i][j-e] = 999 + mapOfWorld[i+e][j+e] = 999 + mapOfWorld[i+e][j-e] = 999 + mapOfWorld[i-e][j+e] = 999 + mapOfWorld[i-e][j-e] = 999 + except Exception: + continue + + return mapOfWorld, mapOfWorldSized + ############################################################################ + + def scale_koord(self, sx, sy, gx, gy,scale): + scale_percent = 100-scale # percent of original size + + sx = int(sx*scale_percent/100) + sy = int(sy*scale_percent/100) + gx = int(gx*scale_percent/100) + gy = int(gy*scale_percent/100) + + return sx,sy,gx,gy + ############################################################################ + + def rescale_koord(self, path,scale): + scale_percent = 100-scale # percent of original size100 + + path = np.array(path) + + for i in range(len(path)): + path[i] = path[i]*100/scale_percent + + return path + ############################################################################ + + def calc_trans_welt(self, x, y, height): + yTrans = height - y + xTrans = x + + return xTrans, yTrans +############################################################################### +#%% Main Funktion +def main(args = None): + #Verbindung herstellen, Node erstellen + try: + rclpy.init(args=args) #Kommunikation Starten + node = MyNode() #Node erstellen + sphero = node.connect() + thread = threading.Thread(target=rclpy.spin, args=(node, ), daemon=True) + thread.start() + + except Exception as e: # rclpy.ROSInterruptException + print("Konnte nicht verbinden") + raise e + + cap = VideoCapture("http://root:root@10.128.41.239:80/mjpg/video.mjpg") + #cap = VideoCapture(4) + Map = mapCreate() + + img = cap.read() + height, width = img.shape[:2] + + scale = 90 + + sphero.setLEDColor(255,0,0) + sphero.wait(1) + sphero.setBackLEDIntensity(0) + sphero.stabilization(True) + + aOffset = node.calc_offset(sphero, cap, height) + + sphero.wait(1) + + while(True): + mapWorldScaled, mapWorldOrg = Map.create_map(scale, cap) + + #Befehle für WaveFrontPlanner/Maperstellung + sy,sx = node.get_pos(cap,height) #Aktuelle Roboter Pos in Welt + sy, sx = node.calc_trans(sy,sx,height) + + gy = int(input("Bitte geben Sie die X-Zielkoordinate im Bild Koordinatensystem ein:")) #X-Koordinate im Weltkoordinaten System + gx = int(input("Bitte geben Sie die Y-Zielkoordinate im Bild Koordinatensystem ein:")) #Y-Koordinate im Weltkordinaten System + + sx,sy,gx,gy = Map.scale_koord(sx,sy,gx,gy,scale) #Kordinaten Tauschen X,Y + + start = time.time() + planner = waveFrontPlanner(mapWorldScaled) + planner.setGoalPosition(gx,gy) + planner.setRobotPosition(sx,sy) + path = planner.run(False) + end = time.time() + print("Took %f seconds to run wavefront simulation" % (end - start)) + + path = Map.rescale_koord(path, scale) + + planner.plotMap(img, path, mapWorldScaled, mapWorldOrg) + + pathWelt = planner.getPathWelt(path, height, Map) + + for p in pathWelt: + node.drive_to(sphero, p, aOffset, cap, height) + sphero.wait(1) + + print("Geschafft") + + if cv2.waitKey(10) & 0xFF == ord('q'): + break + + rclpy.shutdown() + +############################################################################ +#%% Main +if __name__ == '__main__': + main() +############################################################################ diff --git a/ros2_ws/src/sphero_mini_controller/test/test_copyright.py b/ros2_ws/src/sphero_mini_controller/test/test_copyright.py new file mode 100644 index 0000000000000000000000000000000000000000..97a39196e84db97954341162a6d2e7f771d938c0 --- /dev/null +++ b/ros2_ws/src/sphero_mini_controller/test/test_copyright.py @@ -0,0 +1,25 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_copyright.main import main +import pytest + + +# Remove the `skip` decorator once the source file(s) have a copyright header +@pytest.mark.skip(reason='No copyright header has been placed in the generated source file.') +@pytest.mark.copyright +@pytest.mark.linter +def test_copyright(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found errors' diff --git a/ros2_ws/src/sphero_mini_controller/test/test_flake8.py b/ros2_ws/src/sphero_mini_controller/test/test_flake8.py new file mode 100644 index 0000000000000000000000000000000000000000..27ee1078ff077cc3a0fec75b7d023101a68164d1 --- /dev/null +++ b/ros2_ws/src/sphero_mini_controller/test/test_flake8.py @@ -0,0 +1,25 @@ +# Copyright 2017 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_flake8.main import main_with_errors +import pytest + + +@pytest.mark.flake8 +@pytest.mark.linter +def test_flake8(): + rc, errors = main_with_errors(argv=[]) + assert rc == 0, \ + 'Found %d code style errors / warnings:\n' % len(errors) + \ + '\n'.join(errors) diff --git a/ros2_ws/src/sphero_mini_controller/test/test_pep257.py b/ros2_ws/src/sphero_mini_controller/test/test_pep257.py new file mode 100644 index 0000000000000000000000000000000000000000..b234a3840f4c5bd38f043638c8622b8f240e1185 --- /dev/null +++ b/ros2_ws/src/sphero_mini_controller/test/test_pep257.py @@ -0,0 +1,23 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_pep257.main import main +import pytest + + +@pytest.mark.linter +@pytest.mark.pep257 +def test_pep257(): + rc = main(argv=['.', 'test']) + assert rc == 0, 'Found code style errors / warnings'