https://jsfiddle.net/v9xemn86/3/ A JavaScript example of the basics of localization using the Histogram method. This provides a discrete, multi-modal tracking of position, finding he probability of each possible position being the correct one. This allows for multiple positions to have a high probability of being where we are now. However, it requires an impossible amount of memory when the number of positions increases to a real scale.
The code takes a single point sensor input and applies it to a pattern over multiple movements and readings. Note that the pattern can be a known map of the world, or a pattern we are looking for in the world, e.g. the dark door opening with light trim and dark walls. In an real application, it would be possible to take multiple readings without moving, by calling the sense method without the move. Uncertainty in movement is compensated, as is sensor reading. Note that the uncertainty in movement could be caused by physical movement, or by uncertainty in the range to the target causing different spacing between pixels taken from an image. The results are shown as a probability map where grey scale represents the likelihood that the robot is sensing the pattern. The disadvantage of the Histogram method is that it's need for memory scales exponentially with the number of position variables. Here we use only one (e.g. angle between the robot and target) but in real life localization requires far more dimensions.
Code:
function print(msg) { //quick and dirty way to get data out. document.body.innerHTML+="<br>"+msg; } function print_image(msg,v) { // print message and image from the vector for (var i=0,s=""; i<v.length; i++) { var c=Math.floor(v[i]*200+50); // expand to 0...255 and make integer s+="<td style='background-color:rgb("+c+","+c+","+c+")'> </td>" } //the background of the cell displays the color. print("<table cellpadding=0><tr>"+s+"<td>"+msg+"</td></tr></table>") } try { // This is the world as we have been told it exists... or... // the pattern we are looking for in the world. pattern = [1,1,0,1,0,1,1]; print_image("Starting pattern",pattern); // This is what we remember about what we have seen. memory = new Array(pattern.length); // It starts uniformly unsure. Each cell is equally likely, total is 1 for (var i=0;i<pattern.length;i++) memory[i]=1/pattern.length; //print(memory); //should show uniform probability, total=1 function mod(i, m) { //circular modulus is var r = i % m; //regular modulus if (r<0) r+=m; //with a wrap if negative return r; } // Sensors arn't always totally accurate. wHit = 0.9; //weight of a match between sensor and memory wMiss = 0.1; //weight of a mismatch function sense(memory, pattern, input) { output = []; //new memory state var s=0; for (var i=0; i<memory.length;i++) { var d = 1-Math.abs(pattern[i] - input); //compare pattern with input var p = memory[i] * (d * wHit + (1-d) * wMiss); //new probabilities output.push(p); s += p; //sum probabilities. } for (var i=0;i<output.length;i++) output[i] /= s; //normalize return output; } print_image("See a zero, or",sense(memory, pattern, 0)) // Robots don't alway move exactly as commanded. Wheels slip, etc... wExactMove = 0.8; // probibility that we will move exactly 1 cell wOvershoot = (1-wExactMove) / 2; // Remaining chance is half over... wUndershoot = (1-wExactMove) / 2; // and half undershoot when moving. function move(memory, dp) { //update memory based on delta position. output = []; //new memory state var s=0; // sum of the probability of the input we expect in this cell var l=memory.length; for (var i=0; i<l;i++) { // each new cell is based on one before the move s = memory[mod(i-dp,l)] * wExactMove; // cell we moved from s += memory[mod(i-dp+1,l)] * wUndershoot; // cell one short s += memory[mod(i-dp-1,l)] * wOvershoot; // cell one farther output.push(s); } return output; } //Note: We are just testing, so using pattern as memory. print_image("Move right one",move(pattern,1)); events = [['right',1],['right',0],['right',1],['right',0],['left',1]]; for (var i=0; i<events.length; i++) { if ('left'==events[i][0]) { memory=move(memory,-1) } if ('right'==events[i][0]) { memory=move(memory,1) } memory=sense(memory,pattern,events[i][1]); print_image("Moved "+events[i][0]+" saw "+events[i][1], memory) } } catch(e) { print("ERROR: "+e.message);}
Code:
A Python version in 2 dimensions. Clear intent was valued over compactness of code.
# The function localize takes the following arguments: # # colors: # 2D list, each entry either 'R' (for red cell) or 'G' (for green cell) # # measurements: # list of measurements taken by the robot, each entry either 'R' or 'G' # # motions: # list of actions taken by the robot, each entry of the form [dy,dx], # where dx refers to the change in the x-direction (positive meaning # movement to the right) and dy refers to the change in the y-direction # (positive meaning movement downward) # NOTE: the *first* coordinate is change in y; the *second* coordinate is # change in x # # sensor_right: # float between 0 and 1, giving the probability that any given # measurement is correct; the probability that the measurement is # incorrect is 1-sensor_right # # p_move: # float between 0 and 1, giving the probability that any given movement # command takes place; the probability that the movement command fails # (and the robot remains still) is 1-p_move; the robot will NOT overshoot # its destination in this exercise # # The function should RETURN (not just show or print) a 2D list (of the same # dimensions as colors) that gives the probabilities that the robot occupies # each cell in the world. # # Compute the probabilities by assuming the robot initially has a uniform # probability of being in any cell. # # Also assume that at each step, the robot: # 1) first makes a movement, # 2) then takes a measurement. # # Motion: # [0,0] - stay # [0,1] - right # [0,-1] - left # [1,0] - down # [-1,0] - up def localize(colors,measurements,motions,sensor_right,p_move): # initializes p to a uniform distribution over a grid of the same dimensions as colors pinit = 1.0 / float(len(colors)) / float(len(colors[0])) p = [[pinit for row in range(len(colors[0]))] for col in range(len(colors))] #p = [[0 for row in range(len(colors[0]))] for col in range(len(colors))] #p[1][2]=1 # >>> Insert your code here <<< cols = len(p[0]) rows = len(p) for i in range(len(measurements)): #move o=[] #new list for output (don't overwrite p until done) s=0 #zero out the total for normalization to start each event for row in range(rows): c=[] #start a new row, as a list of columns for col in range(cols): m = p[(row-motions[i][0]) % rows][(col-motions[i][1]) % cols]*p_move m = m + p[row][col]*(1-p_move) c.append(m) s = s + m o.append(c) for row in range(rows): #actually not necessary to normalize... included for clarity. for col in range(cols): o[row][col] = o[row][col] / s #normalize output p=o #now we overwrite p with our move output #sense o=[] #new list for output (don't overwrite p until done) s=0 #zero out the total for normalization to start each event for row in range(rows): c=[] #start a new row, as a list of columns for col in range(cols): if measurements[i]==colors[row][col]: h=p[row][col]*sensor_right #hit else: h=p[row][col]*(1-sensor_right) #miss c.append(h) s = s + h o.append(c) for row in range(rows): for col in range(cols): o[row][col] = o[row][col] / s #normalize output p=o #now we overwrite p with our move output #show(p) return p def show(p): rows = ['[' + ','.join(map(lambda x: '{0:.5f}'.format(x),r)) + ']' for r in p] print '[' + ',\n '.join(rows) + ']' ############################################################# # For the following test case, your output should be # [[0.01105, 0.02464, 0.06799, 0.04472, 0.02465], # [0.00715, 0.01017, 0.08696, 0.07988, 0.00935], # [0.00739, 0.00894, 0.11272, 0.35350, 0.04065], # [0.00910, 0.00715, 0.01434, 0.04313, 0.03642]] # (within a tolerance of +/- 0.001 for each entry) colors = [['R','G','G','R','R'], ['R','R','G','R','R'], ['R','R','G','G','R'], ['R','R','R','R','R']] measurements = ['G','G','G','G','G'] motions = [[0,0],[0,1],[1,0],[1,0],[0,1]] p = localize(colors,measurements,motions,sensor_right = 0.7, p_move = 0.8) show(p) # displays your answer
See also: