This commit is contained in:
NikolajDanger
2022-10-05 11:40:03 +02:00
parent f598801950
commit 640db167fe
3 changed files with 82 additions and 38 deletions

View File

@ -3,7 +3,7 @@ import numpy as np
import time import time
import sys import sys
import threading import threading
import selflocalization.framebuffer as framebuffer import framebuffer as framebuffer
from pkg_resources import parse_version from pkg_resources import parse_version

View File

@ -1,6 +1,6 @@
import random
import numpy as np import numpy as np
import selflocalization.random_numbers as rn import random_numbers as rn
class Particle(object): class Particle(object):
"""Data structure for storing particle information (state and weight)""" """Data structure for storing particle information (state and weight)"""
@ -34,6 +34,9 @@ class Particle(object):
def setWeight(self, val): def setWeight(self, val):
self.weight = val self.weight = val
def copy(self):
return Particle(self.x, self.y, self.theta, self.weight)
def estimate_pose(particles_list): def estimate_pose(particles_list):
"""Estimate the pose from particles by computing the average position and orientation over all particles. """Estimate the pose from particles by computing the average position and orientation over all particles.
@ -64,7 +67,9 @@ def estimate_pose(particles_list):
def move_particle(particle, delta_x, delta_y, delta_theta): def move_particle(particle, delta_x, delta_y, delta_theta):
"""Move the particle by (delta_x, delta_y, delta_theta)""" """Move the particle by (delta_x, delta_y, delta_theta)"""
print("particle.py: move_particle not implemented. You should do this.") particle.setX(particle.getX() + delta_x)
particle.setY(particle.getY() + delta_y)
particle.setTheta(particle.getTheta() + delta_theta)
def add_uncertainty(particles_list, sigma, sigma_theta): def add_uncertainty(particles_list, sigma, sigma_theta):
@ -83,3 +88,11 @@ def add_uncertainty_von_mises(particles_list, sigma, theta_kappa):
particle.x += rn.randn(0.0, sigma) particle.x += rn.randn(0.0, sigma)
particle.y += rn.randn(0.0, sigma) particle.y += rn.randn(0.0, sigma)
particle.theta = np.mod(rn.rand_von_mises(particle.theta, theta_kappa), 2.0 * np.pi) - np.pi particle.theta = np.mod(rn.rand_von_mises(particle.theta, theta_kappa), 2.0 * np.pi) - np.pi
def resample(particle_list):
weights = [p.weight for p in particle_list]
if sum(weights) == 0:
weights = [1/len(particle_list) for _ in particle_list]
new_particles = random.choices(particle_list, weights, k=len(particle_list))
particle_list = [p.copy() for p in new_particles]
return particle_list

View File

@ -1,6 +1,6 @@
import cv2 import cv2
import selflocalization.particle as particle import particle as particle
import selflocalization.camera as camera import camera as camera
import numpy as np import numpy as np
import time import time
from timeit import default_timer as timer from timeit import default_timer as timer
@ -8,7 +8,7 @@ import sys
# Flags # Flags
showGUI = True # Whether or not to open GUI windows showGUI = False # Whether or not to open GUI windows
onRobot = True # Whether or not we are running on the Arlo robot onRobot = True # Whether or not we are running on the Arlo robot
@ -20,8 +20,7 @@ def isRunningOnArlo():
if isRunningOnArlo(): if isRunningOnArlo():
# XXX: You need to change this path to point to where your robot.py file is located sys.path.append("..")
sys.path.append("../../../../Arlo/python")
try: try:
@ -32,6 +31,14 @@ except ImportError:
onRobot = False onRobot = False
POWER = 70
TURN_T = 7.9 # 1 degree
DRIVE_T = 22 # 1 centimeter
RIGHT_WHEEL_OFFSET = 4
CLOCKWISE_OFFSET = 0.96
# Some color constants in BGR format # Some color constants in BGR format
@ -46,16 +53,23 @@ CBLACK = (0, 0, 0)
# Landmarks. # Landmarks.
# The robot knows the position of 2 landmarks. Their coordinates are in the unit centimeters [cm]. # The robot knows the position of 2 landmarks. Their coordinates are in the unit centimeters [cm].
landmarkIDs = [1, 2] landmarkIDs = [4, 6]
landmarks = { landmarks = {
1: (0.0, 0.0), # Coordinates for landmark 1 4: (0.0, 0.0), # Coordinates for landmark 1
2: (300.0, 0.0) # Coordinates for landmark 2 6: (300.0, 0.0) # Coordinates for landmark 2
} }
landmark_colors = [CRED, CGREEN] # Colors used when drawing the landmarks landmark_colors = [CRED, CGREEN] # Colors used when drawing the landmarks
SIGMA = 1
SIGMA_THETA = 0.2
def normal(x, mean, std):
return (
(np.exp(-(((x - mean)**2)/(2 * std**2))))/(np.sqrt(2 * np.pi) * std)
)
def jet(x): def jet(x):
"""Colour map for drawing particles. This function determines the colour of """Colour map for drawing particles. This function determines the colour of
@ -118,6 +132,29 @@ def initialize_particles(num_particles):
return particles return particles
def dist(particle, landmark):
return np.sqrt((landmark[0]-particle.x)**2+(landmark[1]-particle.y)**2)
def calc_angle(particle, landmark, dist):
e_theta = np.array([np.cos(particle.theta), np.sin(particle.theta)]).T
e_landmark = (np.array([landmark[0]-particle.x, landmark[1]-particle.y]).T)/dist
e_hat_theta = np.array([-np.sin(particle.theta), np.cos(particle.theta)]).T
return np.sign(np.dot(e_landmark, e_hat_theta)) * np.arccos(np.dot(e_landmark, e_theta))
def calc_weight(particle, landmark_values):
if landmark_values == []:
return
weights = []
for values in landmark_values:
dist_to_landmark = dist(particle, landmarks[values[0]])
dist_weight = normal(values[1], dist_to_landmark, SIGMA)
angle_to_landmark = calc_angle(particle, landmarks[values[0]], dist_to_landmark)
angle_weight = normal(values[2], angle_to_landmark, SIGMA_THETA)
weights.append(dist_weight * angle_weight)
particle.setWeight(np.product(weights))
# Main program # # Main program #
try: try:
@ -142,7 +179,7 @@ try:
velocity = 0.0 # cm/sec velocity = 0.0 # cm/sec
angular_velocity = 0.0 # radians/sec angular_velocity = 0.0 # radians/sec
# Initialize the robot (XXX: You do this) arlo = robot.Robot()
# Allocate space for world map # Allocate space for world map
world = np.zeros((500,500,3), dtype=np.uint8) world = np.zeros((500,500,3), dtype=np.uint8)
@ -162,44 +199,38 @@ try:
action = cv2.waitKey(10) action = cv2.waitKey(10)
if action == ord('q'): # Quit if action == ord('q'): # Quit
break break
if not isRunningOnArlo():
if action == ord('w'): # Forward
velocity += 4.0
elif action == ord('x'): # Backwards
velocity -= 4.0
elif action == ord('s'): # Stop
velocity = 0.0
angular_velocity = 0.0
elif action == ord('a'): # Left
angular_velocity += 0.2
elif action == ord('d'): # Right
angular_velocity -= 0.2
arlo.stop()
if action == ord(','): # Forward
arlo.go_diff(POWER, POWER + RIGHT_WHEEL_OFFSET, 1, 1)
# Use motor controls to update particles elif action == ord('o'): # Backwards
# XXX: Make the robot drive arlo.go_diff(POWER, POWER, 0, 0)
# XXX: You do this elif action == ord('a'): # Left
arlo.go_diff(POWER, POWER, 0, 1)
elif action == ord('e'): # Right
arlo.go_diff(POWER, POWER, 1, 0)
# Fetch next frame # Fetch next frame
colour = cam.get_next_frame() colour = cam.get_next_frame()
# Detect objects # Detect objects
objectIDs, dists, angles = cam.detect_aruco_objects(colour) objectIDs, dists, angles = cam.detect_aruco_objects(colour)
if not isinstance(objectIDs, type(None)): if not isinstance(objectIDs, type(None)):
landmark_values = []
# List detected objects # List detected objects
for i in range(len(objectIDs)): for i in range(len(objectIDs)):
print("Object ID = ", objectIDs[i], ", Distance = ", dists[i], ", angle = ", angles[i]) print("Object ID = ", objectIDs[i], ", Distance = ", dists[i], ", angle = ", angles[i])
# XXX: Do something for each detected object - remember, the same ID may appear several times if objectIDs[i] in landmarkIDs:
landmark_values.append((objectIDs[i], dists[i], angles[i]))
# Compute particle weights # Compute particle weights
# XXX: You do this for p in particles:
calc_weight(p, landmark_values)
# Resampling # Resampling
# XXX: You do this particles = particle.resample(particles)
# Draw detected objects # Draw detected objects
cam.draw_aruco_objects(colour) cam.draw_aruco_objects(colour)
@ -208,13 +239,13 @@ try:
for p in particles: for p in particles:
p.setWeight(1.0/num_particles) p.setWeight(1.0/num_particles)
particle.add_uncertainty(particles, SIGMA, SIGMA_THETA)
est_pose = particle.estimate_pose(particles) # The estimate of the robots current pose est_pose = particle.estimate_pose(particles) # The estimate of the robots current pose
if showGUI: if showGUI:
# Draw map # Draw map
draw_world(est_pose, particles, world) draw_world(est_pose, particles, world)
# Show frame # Show frame
cv2.imshow(WIN_RF1, colour) cv2.imshow(WIN_RF1, colour)