Path: blob/master/loop_reshape_test_motion.py
104 views
# program for only testing the physical motion control algorithm of the loop reshape process1# a new SMA algorithm inspired by the behavior of shape memory alloy2# the starting and ending formations will be read from file3# the goal is to reshape the initial loop to the shape of target loop45# Two arguments specifying the loop formation files needs to be passed, first one is for6# initial loop formation, second for target formation. The loop formation files should be7# located under 'loop-data' folder. A third argument is optional, specifying the shift of8# which node it prefers in the target formation, 0 is default if not specified.9# ex1: 'python loop_reshape_test_motion.py 30-3 30-4'10# ex2: 'python loop_reshape_test_motion.py 30-9 30-12 15'1112# comments on first algorithm test:13# First algorithm focusing on the idea of influencing neighbors. The motion of a node is14# controller by external effects. One part of the effects is the pulling or pushing when15# the neighbor distance is larger or smaller than desired loop space, this is modelled as16# as a linear spring with relative large spring constant. Second part of the effects is the17# bending force driven by the desired interior angle of neighbor nodes.If a neighbor wants18# smaller interior angle, it exerts a bending force on host node inward the polygon, with19# direction perpendicular to the connecting line. And larger interior angle will produce20# bending force outward the polygon.21# The simulation results that the polygon is always bending part of nodes inward and curling22# up the whole loop.23# Presumption: the perpendicular bending force may produce unwanted motion that curls up24# the loop, make the bending force just along the central axis.2526# comments on second algorithm test:27# A revision based on first algorithm test has been made, that the bending force is only28# along the central axis. The second algorithm presumes all motion effects are initialed29# by the host node itself. It judges the two neighbor distances, and calculate the force30# from the linear springs. It finds the difference to the desired interior angle, and31# produce the force along the central axis. All motion effects are proportional to the32# driven factors.33# The simulation result is just what I have been expecting for a while. It could really34# reshape the loop in a very fast way.3536import pygame37from formation_functions import *38import numpy as np39import sys, os40import math4142if len(sys.argv) < 3:43# three arguments at least, first one is this program's filename44# the other two are for the loop formation files45print("incorrect number of input arguments")46sys.exit()47form_files = []48form_files.append(sys.argv[1])49form_files.append(sys.argv[2])50# make sure the files exist51loop_folder = "loop-data"52for i in range(2):53new_filepath = os.path.join(os.getcwd(), loop_folder, form_files[0])54if not os.path.isfile(new_filepath):55if i == 0: print("incorrect filename for initial formation")56else: print("incorrect filename for target formation")57sys.exit()58# try to get another argument for shift in desired node59targ_shift = 0 # default60try:61targ_shift = int(sys.argv[3])62except: pass6364pygame.init() # initialize the pygame65# parameters for display, window origin is at left up corner66screen_size = (600, 800) # width and height in pixels67# top half for initial formation, bottom half for target formation68background_color = (0,0,0) # black background69robot_color = (255,0,0) # red for robot and the line segments70robot_size = 5 # robot modeled as dot, number of pixels for radius71world_size = (100.0, 100.0 * screen_size[1]/screen_size[0])7273# set up the simulation window and surface object74icon = pygame.image.load("icon_geometry_art.jpg")75pygame.display.set_icon(icon)76screen = pygame.display.set_mode(screen_size)77pygame.display.set_caption("Loop Reshape Motion Test")7879# variables to configure the simulation80poly_n = 0 # will be decided when reading formation files81loop_space = 4.0 # side length of the equilateral polygon82# linear spring constant modeling the pulling and pushing effect of neighbor nodes83linear_const = 1.084# # angular spring constant modeling the bending effect of neighbor nodes85# angular_const = 0.286# bending spring constant modeling the bending initial by the host node itself87bend_const = 1.088disp_coef = 0.5 # coefficient from feedback vector to displacement8990# construct the polygons from formation data from file91nodes = [[], []] # node positions for the two formation, index is the robot's ID92nodes[0].append([0, 0]) # first node starts at origin93nodes[0].append([loop_space, 0]) # second node is loop space away on the right94nodes[1].append([0, 0])95nodes[1].append([loop_space, 0])96for i in range(2):97new_filepath = os.path.join(os.getcwd(), loop_folder, form_files[i])98f = open(new_filepath, 'r') # read only99# check consistence of polygon size100if i == 0: # this is the first polygon101poly_n = int(f.readline()) # initialize with first polygon size102else: # check consistence when reading second polygon103if int(f.readline()) != poly_n:104print("inconsistent polygon size from the formation files")105sys.exit()106# read all interior angles form the file107inter_ang = []108new_line = f.readline()109while len(new_line) != 0: # not the end of the file yet110inter_ang.append(float(new_line))111new_line = f.readline()112# check if this file has the nubmer of interior angles as it promised113if len(inter_ang) != poly_n-3:114print 'file "{}" has incorrect number of interior angles'.format(form_files[i])115sys.exit()116# construct positions of all nodes from these interior angles117ori_current = 0 # orientation of current line segment118for j in range(2, poly_n-1):119inter_ang_t = inter_ang[j-2] # interior angle of previous node120ori_current = reset_radian(ori_current + (math.pi - inter_ang_t))121nodes[i].append([nodes[i][j-1][0] + loop_space*math.cos(ori_current),122nodes[i][j-1][1] + loop_space*math.sin(ori_current)])123# calculate position of last node124vect_temp = [nodes[i][poly_n-2][0] - nodes[i][0][0],125nodes[i][poly_n-2][1] - nodes[i][0][1]] # from node 0 to n-2126dist_temp = math.sqrt(vect_temp[0]*vect_temp[0]+127vect_temp[1]*vect_temp[1])128midpoint = [(nodes[i][poly_n-2][0]+nodes[i][0][0])/2,129(nodes[i][poly_n-2][1]+nodes[i][0][1])/2]130perp_dist = math.sqrt(loop_space*loop_space - dist_temp*dist_temp/4)131perp_ori = math.atan2(vect_temp[1], vect_temp[0]) + math.pi/2132nodes[i].append([midpoint[0] + perp_dist*math.cos(perp_ori),133midpoint[1] + perp_dist*math.sin(perp_ori)])134135# shift the two polygons to the top and bottom halves136nodes = np.array(nodes) # convert to numpy array137for i in range(2):138# calculate the geometry center of current polygon139geometry_center = np.mean(nodes[i], axis=0)140nodes[i,:,0] = nodes[i,:,0] - geometry_center[0] + world_size[0]/2141if i == 0: # initial formation shift to top half142nodes[i,:,1] = nodes[i,:,1] - geometry_center[1] + 3*world_size[1]/4143else: # target formation shift to bottom half144nodes[i,:,1] = nodes[i,:,1] - geometry_center[1] + world_size[1]/4145146# calculate the interior angles of the target formation147inter_targ = [0 for i in range(poly_n)]148for i in range(poly_n):149vect_l = nodes[1][(i-1)%poly_n] - nodes[1][i] # from host to left150vect_r = nodes[1][(i+1)%poly_n] - nodes[1][i] # from host to right151# get the small angle between vect_l and vect_r152inter_targ[i] = math.acos((vect_l[0]*vect_r[0] + vect_l[1]*vect_r[1])/153(loop_space*loop_space))154if (vect_r[0]*vect_l[1] - vect_r[1]*vect_l[0]) < 0:155# cross product of vect_r to vect_l is smaller than 0156# the resulting interior angle should be in range of [0, 2*pi)157inter_targ[i] = 2*math.pi - inter_targ[i]158159# loop for the physical motion update160sim_exit = False161sim_pause = False162frame_period = 100 # updating period of the simulation and graphics, in millisecond163timer_last = pygame.time.get_ticks()164timer_now = timer_last165# the interior angle of initial formation will be updated dynamically166inter_curr = [0 for i in range(poly_n)]167# the variable for the neighboring robots on loop168dist_neigh = [0 for i in range(poly_n)]169while not sim_exit:170# exit the program by close window button, or Esc or Q on keyboard171for event in pygame.event.get():172if event.type == pygame.QUIT:173sim_exit = True # exit with the close window button174if event.type == pygame.KEYUP:175if event.key == pygame.K_SPACE:176sim_pause = not sim_pause # reverse the pause flag177if (event.key == pygame.K_ESCAPE) or (event.key == pygame.K_q):178sim_exit = True # exit with ESC key or Q key179# skip the rest if paused180if sim_pause: continue181182timer_now = pygame.time.get_ticks()183if (timer_now - timer_last) > frame_period:184timer_last = timer_now # reset timer185186# update the dynamic neighbor distances of the loop187for i in range(poly_n):188vect_r = nodes[0][(i+1)%poly_n] - nodes[0][i] # from host to right189dist_neigh[i] = math.sqrt(vect_r[0]*vect_r[0] + vect_r[1]*vect_r[1])190191# update the dynamic interior angles of the loop192for i in range(poly_n):193i_l = (i-1)%poly_n # index of left neighbor194i_r = (i+1)%poly_n # index of right neighbor195vect_l = nodes[0][i_l] - nodes[0][i] # from host to left196vect_r = nodes[0][i_r] - nodes[0][i] # from host to right197# get the small angle between vect_l and vect_r198inter_curr[i] = math.acos((vect_l[0]*vect_r[0] + vect_l[1]*vect_r[1])/199(dist_neigh[i_l] * dist_neigh[i]))200if (vect_r[0]*vect_l[1] - vect_r[1]*vect_l[0]) < 0:201# cross product of vect_r to vect_l is smaller than 0202# the resulting interior angle should be in range of [0, 2*pi)203inter_curr[i] = 2*math.pi - inter_curr[i]204205# # first algorithm test, failed206# # variable for feedback from all spring effects207# fb_vect = [np.zeros([1,2]) for i in range(poly_n)]208# for i in range(poly_n):209# # get feedback from pulling and pushing of the linear spring210# i_l = (i-1)%poly_n # index of left neighbor211# i_r = (i+1)%poly_n # index of right neighbor212# # unit vector from host to left213# vect_l = (nodes[0][i_l]-nodes[0][i])/dist_neigh[i_l]214# # unit vector bending host node toward inside the polygon from left neighbor215# vect_lb = np.array([vect_l[1], -vect_l[0]]) # rotate vect_l cw for pi/2216# # unit vector from host to right217# vect_r = (nodes[0][i_r]-nodes[0][i])/dist_neigh[i]218# # unit vector bending host node toward inside the polygon from right neighbor219# vect_rb = np.array([-vect_r[1], vect_r[0]]) # rotate vect_r ccw for pi/2220221# # add the pulling or pushing effect from left neighbor222# fb_vect[i] = fb_vect[i] + (dist_neigh[i_l]-loop_space) * linear_const * vect_l223# # add the pulling or pushing effect from right neighbor224# fb_vect[i] = fb_vect[i] + (dist_neigh[i]-loop_space) * linear_const * vect_r225# # add the bending effect from left neighbor226# fb_vect[i] = fb_vect[i] + ((inter_curr[i_l] - inter_targ[(i_l+targ_shift)%poly_n])*227# angular_const * vect_lb)228# # add the bending effect from right neighbor229# fb_vect[i] = fb_vect[i] + ((inter_curr[i_r] - inter_targ[(i_r+targ_shift)%poly_n])*230# angular_const * vect_rb)231232# # update one step of position233# nodes[0][i] = nodes[0][i] + disp_coef * fb_vect[i]234235# second algorithm test236# variable for feedback from all spring effects237fb_vect = [np.zeros([1,2]) for i in range(poly_n)]238for i in range(poly_n):239# get feedback from pulling and pushing of the linear spring240i_l = (i-1)%poly_n # index of left neighbor241i_r = (i+1)%poly_n # index of right neighbor242# unit vector from host to left243vect_l = (nodes[0][i_l]-nodes[0][i])/dist_neigh[i_l]244# unit vector from host to right245vect_r = (nodes[0][i_r]-nodes[0][i])/dist_neigh[i]246# unit vector along central axis pointing inside the polygon247vect_lr = nodes[0][i_r]-nodes[0][i_l] # vector from left neighbor to right248dist_temp = math.sqrt(vect_lr[0]*vect_lr[0]+vect_lr[1]*vect_lr[1])249vect_in = np.array([-vect_lr[1]/dist_temp, vect_lr[0]/dist_temp]) # rotate ccw pi/2250251# add the pulling or pushing effect from left neighbor252fb_vect[i] = fb_vect[i] + (dist_neigh[i_l]-loop_space) * linear_const * vect_l253# add the pulling or pushing effect from right neighbor254fb_vect[i] = fb_vect[i] + (dist_neigh[i]-loop_space) * linear_const * vect_r255# add the bending effect initialize by the host node itself256fb_vect[i] = fb_vect[i] + ((inter_targ[(i+targ_shift)%poly_n]-inter_curr[i])*257bend_const * vect_in)258259# update one step of position260nodes[0][i] = nodes[0][i] + disp_coef * fb_vect[i]261262# graphics update263screen.fill(background_color)264for i in range(2):265# draw the nodes and line segments266disp_pos = [[0,0] for j in range(poly_n)]267# calculate the display pos for all nodes, as red dots268for j in range(0, poly_n):269disp_pos[j] = world_to_display(nodes[i][j], world_size, screen_size)270pygame.draw.circle(screen, robot_color, disp_pos[j], robot_size, 0)271# draw an outer circle to mark the starting node272pygame.draw.circle(screen, robot_color, disp_pos[0], int(robot_size*1.5), 1)273for j in range(poly_n-1):274pygame.draw.line(screen, robot_color, disp_pos[j], disp_pos[j+1])275pygame.draw.line(screen, robot_color, disp_pos[poly_n-1], disp_pos[0])276pygame.display.update()277278279280281282283