Skip to content

Commit 2bcaa35

Browse files
committed
partical filter added
1 parent 9bbbc9c commit 2bcaa35

3 files changed

Lines changed: 267 additions & 2 deletions

File tree

launch/uwb_initial_pose.launch

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
<launch>
2-
<include file="$(find pozyx_simulation)/launch/uwb_anchors_set.launch" />
3-
<node pkg="pozyx_simulation" name="uwb_simulation" type="uwb_simulation.py" />
42
<node pkg="advoard_localization" name="kalman_filter_localization" type="kalman_filter_localization.py"/>
53
<node pkg="advoard_localization" name="initialpose_uwb_average" type="initialpose_uwb_average.py" />
64
</launch>
2.36 KB
Binary file not shown.

src/particle_filter_UWB_V3.py

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
#!/usr/bin/env python3
2+
3+
import rospy
4+
import rospkg
5+
6+
from nav_msgs.msg import Odometry
7+
from geometry_msgs.msg import PointStamped
8+
import tf
9+
10+
from pozyx_simulation.msg import uwb_data
11+
import math
12+
13+
import numpy as np
14+
import scipy.stats
15+
16+
import time
17+
18+
19+
rospy.init_node('particle_filter_UWB_node', anonymous=True)
20+
pub = rospy.Publisher('localization_data_topic', PointStamped, queue_size=10)
21+
r = rospy.Rate(1)
22+
23+
global particles
24+
global weights
25+
26+
27+
#add random seed for generating comparable pseudo random numbers
28+
#np.random.seed(123)
29+
30+
def initialize_particles(num_particles, map_limits):
31+
# randomly initialize the particles inside the map limits
32+
33+
created_particles = []
34+
35+
for i in range(num_particles):
36+
particle = dict()
37+
38+
# draw x,y and theta coordinate from uniform distribution
39+
# inside map limits
40+
particle['x'] = np.random.uniform(map_limits[0], map_limits[1])
41+
particle['y'] = np.random.uniform(map_limits[2], map_limits[3])
42+
#particle['theta'] = np.random.uniform(-np.pi, np.pi)
43+
particle['theta'] = 0 + np.random.uniform(-np.pi, np.pi)
44+
45+
created_particles.append(particle)
46+
47+
return created_particles
48+
49+
def mean_pose():
50+
# calculate the mean pose of a particle set.
51+
#
52+
# for x and y, the mean position is the mean of the particle coordinates
53+
#
54+
# for theta, we cannot simply average the angles because of the wraparound
55+
# (jump from -pi to pi). Therefore, we generate unit vectors from the
56+
# angles and calculate the angle of their average
57+
58+
# save x and y coordinates of particles
59+
xs = []
60+
ys = []
61+
62+
# save unit vectors corresponding to particle orientations
63+
vxs_theta = []
64+
vys_theta = []
65+
66+
toplam_nokta = 0
67+
"""
68+
for i in range(len(particles)):
69+
if weights[i]>0.001:
70+
toplam_nokta = toplam_nokta +1
71+
xs.append(particles[i]['x'])
72+
ys.append(particles[i]['y'])
73+
74+
#make unit vector from particle orientation
75+
vxs_theta.append(np.cos(particles[i]['theta']))
76+
vys_theta.append(np.sin(particles[i]['theta']))
77+
print("toplam :" + str(toplam_nokta))
78+
"""
79+
80+
81+
for particle in particles:
82+
xs.append(particle['x'])
83+
ys.append(particle['y'])
84+
85+
#make unit vector from particle orientation
86+
vxs_theta.append(np.cos(particle['theta']))
87+
vys_theta.append(np.sin(particle['theta']))
88+
89+
90+
#calculate average coordinates
91+
mean_x = np.mean(xs)
92+
mean_y = np.mean(ys)
93+
mean_theta = np.arctan2(np.mean(vys_theta), np.mean(vxs_theta))
94+
95+
return [mean_x, mean_y, mean_theta]
96+
97+
def sample_motion_model(Odometry):
98+
global particles
99+
# Samples new particle positions, based on old positions, the odometry measurements and the motion noise
100+
# (probabilistic motion models slide 27)
101+
102+
103+
delta_vel = Odometry.twist.twist.linear.x/30 # redefine r1 odom=>twist=>linear=>x
104+
delta_w = Odometry.twist.twist.angular.z/30 # redefine t odom=>twist=>angular=>z
105+
106+
107+
# "move" each particle according to the odometry measurements plus sampled noise to generate new particle set
108+
109+
new_particles = []
110+
for particle in particles:
111+
new_particle = dict()
112+
# sample noisy motions
113+
noise_x = (np.random.uniform() - 0.5) * abs(delta_vel)
114+
#noise_y = (np.random.uniform()-0.5) * abs(delta_vel)
115+
noise_theta = (np.random.uniform() - 0.5) * abs(delta_w)
116+
noisy_delta_vel = delta_vel + noise_x
117+
#noisy_delta_vel_y = delta_vel + noise_y
118+
noisy_delta_w = delta_w + noise_theta
119+
# calculate new particle pose
120+
new_particle['x'] = particle['x'] + noisy_delta_vel * np.cos(particle['theta'])
121+
new_particle['y'] = particle['y'] + noisy_delta_vel * np.sin(particle['theta'])
122+
new_particle['theta'] = particle['theta'] + noisy_delta_w
123+
new_particles.append(new_particle)
124+
125+
particles = new_particles
126+
127+
128+
129+
def eval_sensor_model():
130+
global weights
131+
global particles
132+
# Computes the observation likelihood of all particles, given the particle and landmark positions and sensor measurements
133+
# (probabilistic sensor models slide 33)
134+
#
135+
# The employed sensor model is range only.
136+
137+
sigma_r = 0.5
138+
139+
while not rospy.is_shutdown():
140+
#measured landmark ids and ranges
141+
ids = g_uwb_data.destination_id
142+
ranges = g_uwb_data.distance
143+
144+
weights_new = []
145+
146+
# rate each particle
147+
for particle in particles:
148+
all_meas_likelihood = 1.0 # for combining multiple measurements
149+
# loop for each observed landmark
150+
for i in range(len(ids)):
151+
lm_id = ids[i]
152+
meas_range = ranges[i]
153+
lx = sensor_pos[i][0]
154+
ly = sensor_pos[i][1]
155+
lz = sensor_pos[i][2]
156+
#calculate expected range
157+
px = particle['x']
158+
py = particle['y']
159+
pz = 0
160+
ptheta = particle['theta']
161+
# calculate expected range measurement
162+
meas_range_exp = np.sqrt((lx - px) ** 2 + (ly - py) ** 2 + (lz - pz) ** 2)
163+
# evaluate sensor model (probability density function of normal distribution)
164+
meas_likelihood = scipy.stats.norm.pdf(meas_range, meas_range_exp, sigma_r)
165+
#print(meas_likelihood)
166+
# combine (independent) measurements
167+
all_meas_likelihood = all_meas_likelihood * meas_likelihood
168+
169+
weights_new.append(all_meas_likelihood)
170+
171+
#normalize weights_new
172+
normalizer = sum(weights_new)
173+
weights = weights_new / normalizer
174+
175+
#print("max weights :" + str(max(weights)))
176+
resample_particles()
177+
[mean_x, mean_y, mean_theta] = mean_pose()
178+
publish_data(mean_x,mean_y)
179+
180+
def resample_particles():
181+
global particles
182+
global weights
183+
# Returns a new set of particles obtained by performing stochastic universal sampling, according to the particle weights.
184+
185+
new_particles = []
186+
#new_weights = []
187+
# distance between pointers
188+
step = 1.0 / len(particles)
189+
# random start of first pointer
190+
u = np.random.uniform(0, step)
191+
# where we are along the weights
192+
c = weights[0]
193+
# index of weight container and corresponding particle
194+
i = 0
195+
# loop over all particle weights
196+
for particle in particles:
197+
# go through the weights until you find the particle to which the pointer points
198+
while u > c:
199+
i = i + 1
200+
c = c + weights[i]
201+
# add that particle
202+
new_particles.append(particles[i])
203+
#new_weights.append(weights[i])
204+
# increase the threshold
205+
u = u + step
206+
207+
#weights = new_weights
208+
particles = new_particles
209+
210+
def subscribe_odom_data(Odometry):
211+
sample_motion_model(Odometry)
212+
213+
def subscribe_uwb_data(uwb_data):
214+
global g_uwb_data
215+
g_uwb_data = uwb_data
216+
217+
#eval_sensor_model(uwb_data,sensor_pos)
218+
#particles = resample_particles()
219+
220+
def publish_data(pose_x,pose_y):
221+
robot_pos = PointStamped()
222+
robot_pos.header.stamp = rospy.Time.now()
223+
robot_pos.header.frame_id = "map"
224+
225+
robot_pos.point.x = float(pose_x)
226+
robot_pos.point.y = float(pose_y)
227+
robot_pos.point.z = 0.0
228+
229+
pub.publish(robot_pos)
230+
231+
def get_anchors_pos():
232+
max_anchor = 100
233+
sensor_pos = []
234+
uwb_id = 'uwb_anchor_'
235+
listener = tf.TransformListener()
236+
237+
for i in range(max_anchor):
238+
try:
239+
time.sleep(0.3)
240+
(trans,rot) = listener.lookupTransform('/map', uwb_id+str(i), rospy.Time(0))
241+
sensor_pos.append(trans)
242+
except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException):
243+
if(i == 0):
244+
rospy.INFO("Firstly have to run pozyx_simulation package uwb_simulation.py file")
245+
break
246+
247+
return sensor_pos
248+
249+
250+
if __name__ == "__main__":
251+
global particles
252+
global sensor_pos
253+
#get uwb anchors postion
254+
sensor_pos = get_anchors_pos()
255+
256+
#1800 0
257+
map_limits = [-3,3,-3,3]
258+
particles = initialize_particles(1000, map_limits)
259+
260+
261+
rospy.Subscriber("odom", Odometry, subscribe_odom_data)
262+
rospy.Subscriber("uwb_data_topic", uwb_data, subscribe_uwb_data)
263+
264+
time.sleep(2)
265+
eval_sensor_model()
266+
rospy.spin()
267+

0 commit comments

Comments
 (0)