Generating N random points with certain predefined distance between them - matlab

I have to create highway scenario in MATLAB. I have to generate random points (i.e. vehicles) on highway. By using randn() command, random points are overlapping on each other. I want to generate random points such that a minimum distance between random points is maintained.
Could anybody help me in generating this kind of scenario..

You might consider Poisson disc (a.k.a. disk) sampling. Basically, Poisson-disc sampling produces points that are tightly-packed, but no closer to each other than a specified minimum distance, resulting in a more natural pattern.
My matlab is rusty, sorry, no code, but links
http://www.cs.sandia.gov/~samitch/papers/cccg-present.pdf
https://www.jasondavies.com/poisson-disc/

This is not an elegant solution, but it satisfies your minimum distance constraint.
% Highway dimensions
lx = 1000;
ly = 1000;
% Minimum distance
d = 100;
% Number of points to generate
n = 50;
points = [rand(1, 2) .* [lx ly]];
d2 = d ^ 2;
% Keep adding points until we have n points.
while (size(points, 1) < n)
% Randomly generate a new point
point = rand(1, 2) .* [lx ly];
% Calculate squared distances to all other points
dist2 = sum((points - repmat(point, size(points, 1), 1)) .^ 2, 2);
% Only add this point if it is far enough away from all others.
if (all(dist2 > d2))
points = [points; point];
end
end
plot(points(:,1), points(:,2), 'o')

Related

Averaging periodic data with a variable sampling rate

I have a long array of [x,y] coordinate values of a toy car running around a track for 5-6 laps. The number of data points is inconsistent per lap (the laps have anywhere between 50-60 [x,y] points). The data plotted in Matlab makes sense, and it plots the car as it moves around the track:
However I need to somehow average the noisy laps to create a more accurate single map of the track.
I have tried marking the beginning the the track in order to identify the start of a new lap and then average each corresponding point from each lap however due to the different amount of data points for each lap this leads to more errors.
I thought about sorting the [x,y] data to join all of the points into one lap, but this doesn't work since the track is circular.
Does anyone know of a way to somehow average together my data to merge the laps together?
One way to do this is to define the beginning of the track and then parameterize each traversal around the loop by the normalized arc-length of the path. You can then interpolate each of the curves at specific intervals along the track using this parameterization and average the result.
% Assume that the first point is the start point (t = 0)
start_point = path(1,:);
% Compute the distance to this point for all data points
distances = sqrt(sum(bsxfun(#minus, path, start_point).^2, 2));
% Find the minima of this curve (these are all the times that the car passed the start)
% We apply some smoothing to get rid of necessary noise. Really depends on your data
[~, locs] = findpeaks(smooth(-distances, 20));
% Make sure we include the first and last point
locs = [1; locs; numel(distances)];
% Desired samples for each loop
nSamples = 1000;
% Pre-allocate the outputs
xpoints = zeros(numel(locs) - 1, nSamples);
ypoints = zeros(numel(locs) - 1, nSamples);
for k = 1:(numel(locs) - 1)
% Get the coordinates recorded for this particular loop
loop_points = path(locs(k):locs(k+1),:);
% Compute the cumulative arc-length using these points
arc_length = cumsum([0; sum(diff(loop_points, [], 1).^2, 2)]);
% Normalize the arc_length between 0 and 1
arc_length = arc_length ./ arc_length(end);
% Interpolate along the curve
xpoints(k,:) = interp1(arc_length, loop_points(:,1), linspace(0, 1, nSamples));
ypoints(k,:) = interp1(arc_length, loop_points(:,2), linspace(0, 1, nSamples));
end
% Average all the x and y locations
X = mean(xpoints, 1);
Y = mean(ypoints, 1);
plot(X, Y)
We can test this by going in a perfect circle and add some noise to each circuit and change the number of samples each time
nLoops = 10;
x = [];
y = [];
for k = 1:nLoops
nSamples = randi([50, 70]);
t = linspace(0, 2*pi, nSamples + 1);
t(end) = [];
x = cat(1, x(:), cos(t(:)) + 0.1 * (rand(size(t(:))) - 0.5));
y = cat(1, y(:), sin(t(:)) + 0.1 * (rand(size(t(:))) - 0.5));
end
path = [x(:), y(:)];
NOTE: findpeaks and smooth are toolbox functions that can likely be replaced with functions from the MATLAB File Exchange. Alternately, if you know when the car passes the beginning already, you can remove the usage of findpeaks altogether.

I need to spectral clustering for two donuts shape dataset.(Matlab)

I have tried hours but I cannot find solution.
I have "two Donuts" Data sample (variable "X")
you can download file below link
donut dataset(rings.mat)
which spreads to 2D shape like below image
First 250pts are located inside donuts and last 750 pts are located outside donuts.
and I need to perform spectral clustering.
I made (similarity matrix "W") with Gaussian similarity distance.
and I made degree matrix by sum of each raw of "W"
and then I computed eigen value(E) and eigen Vector(V)
and the shape of "V" is not good.
what is wrong with my trial???
I cannot figure out.
load rings.mat
[D, N] = size(X); % data stored in X
%initial plot data
figure; hold on;
for i=1:N,
plot(X(1,i), X(2,i),'o');
end
% perform spectral clustering
W = zeros(N,N);
D = zeros(N,N);
sigma = 1;
for i=1:N,
for j=1:N,
xixj2 = (X(1,i)-X(1,j))^2 + (X(2,i)-X(2,j))^2 ;
W(i,j) = exp( -1*xixj2 / (2*sigma^2) ) ; % compute weight here
% if (i==j)
% W(i,j)=0;
% end;
end;
D(i,i) = sum(W(i,:)) ;
end;
L = D - W ;
normL = D^-0.5*L*D^-0.5;
[u,s,v] = svd(normL);
If you use the Laplacian like it is in your code (the "real" laplacian), then to cluster your points into two sets you will want the eigenvector corresponding to second smallest eigenvalue.
The intuitive idea is to connect all of your points to each other with springs, where the springs are stiffer if the points are near each other, and less stiff for points far away. The eigenvectors of the Laplacian are the modes of vibration if you hit your spring network with a hammer and watch it oscillate - smaller eigenvalues corresponding to lower frequency "bulk" modes, and larger eigenvalues corresponding to higher frequency oscillations. You want the eigenvalue corresponding to the second smallest eigenvalue, which will be like the second mode in a drum, with a positive clustered together, and negative part clustered together.
Now there is some confusion in the comments about whether to use the largest or smallest eigenvalue, and it is because the laplacian in the paper linked there by dave is slightly different, being the identity minus your laplacian. So there they want the largest ones, whereas you want the smallest. The clustering in the paper is also a bit more advanced, and better, but not as easy to implement.
Here is your code, modified to work:
load rings.mat
[D, N] = size(X); % data stored in X
%initial plot data
figure; hold on;
for i=1:N,
plot(X(1,i), X(2,i),'o');
end
% perform spectral clustering
W = zeros(N,N);
D = zeros(N,N);
sigma = 0.3; % <--- Changed to be smaller
for i=1:N,
for j=1:N,
xixj2 = (X(1,i)-X(1,j))^2 + (X(2,i)-X(2,j))^2 ;
W(i,j) = exp( -1*xixj2 / (2*sigma^2) ) ; % compute weight here
% if (i==j)
% W(i,j)=0;
% end;
end;
D(i,i) = sum(W(i,:)) ;
end;
L = D - W ;
normL = D^-0.5*L*D^-0.5;
[u,s,v] = svd(normL);
% New code below this point
cluster1 = find(u(:,end-1) >= 0);
cluster2 = find(u(:,end-1) < 0);
figure
plot(X(1,cluster1),X(2,cluster1),'.b')
hold on
plot(X(1,cluster2),X(2,cluster2),'.r')
hold off
title(sprintf('sigma=%d',sigma))
Here is the result:
Now notice that I changed sigma to be smaller - from 1.0 to 0.3. When I left it at 1.0, I got the following result:
which I assume is because with sigma=1, the points in the inner cluster were able to "pull" on the outer cluster (which they are about distance 1 away from) enough so that it was more energetically favorable to split both circles in half like a solid vibrating drum, rather than have two different circles.

MATLAB - Plotting random points in a circle using a congruential RNG

I am aiming at plotting some random numbers in a circle using MATLAB. My code:
c = 3; p = 31; x = [7];
% generating random numbers (z) in the range [0,1) using
% congruential random number generator (multiplicative)
for i = 2:200;
x(i) = mod(c*x(i-1),p);
end;
z = x/p;
% plot unit circle
hold on;
theta = 0:pi/50:2*pi;
plot(cos(theta),sin(theta),'.');
hold off;
% plotting random points in the unit circle using in-built rand function
phi = 2*pi*rand(1,200);
r = 1*sqrt(rand(1,200));
% plotting random points using the RNG above
% phi = 2*pi*z;
% r = 1*sqrt(z);
hold on;
x = 0 + r.*cos(phi);
y = 0 + r.*sin(phi);
plot(x,y,'r*');
hold off;
clear;
The problem I am facing is that both z and rand consist of random numbers in the range [0,1). However, when I plot using rand I get the ideal result -
while z gives me a helix sorta thing -
What could be the problem?
Besides Ander's good point about the RNG there is also the problem of using z for both phi and r. Check it by using z=rand(200,1) and then creating your plot:
gives the same result as you had before. If you let z be different for both, you get "true" randomness, to some extend in your RNG. I used this RNG:
c = 991;
p = 997;
x=zeros(400,1);
x(1,1) = 7;
for ii = 2:400;
x(ii,1) = mod(c*x(ii-1),p);
end;
z = x/p;
phi2 = 2*pi*z(1:200,1);
r2 = 1*sqrt(z(201:400,1));
where I let your RNG run a bit longer and then used the first 200 for phi and the last 200 for r:
As you can see there's still some kind of swirl visible, but that's due to your RNG. The larger you pick your c and p the less that will be.
Just to show you how pretty your RNG becomes by setting c=3 and p=31 and using the full 400 range of z as above. Isn't that a great swirl?
Easy! Your random number generator is good to some excent.
Random number generators based on prime division do generally have a period. After a number of samples they repeat themselves.
In your case, try to plot(z)
You will notice that that set of numbers is periodic, and has a period of 31. Coincidence?
I THINK NOT!
Thus, remember that when you want to generate pseudorandom numbers, you need a p bigger than the amount of samples you want to generate.
For example if we choose another set of coprime numbers to generate z
c = 991; p = 997;
The plot(z) will be:
And the final plot:

Ideas for reducing the complexity of a 3D density function for generating a ternary surface plot in Matlab

I have a 3D density function q(x,y,z) that I am trying to plot in Matlab 8.3.0.532 (R2014a).
The domain of my function starts at a and ends at b, with uniform spacing ds. I want to plot the density on a ternary surface plot, where each dimension in the plot represents the proportion of x,y,z at a given point. For example, if I have a unit of density on the domain at q(1,1,1) and another unit of density on the domain at q(17,17,17), in both cases there is equal proportions of x,y,z and I will therefore have two units of density on my ternary surface plot at coordinates (1/3,1/3,1/3). I have code that works using ternsurf. The problem is that the number of proportion points grows exponentially fast with the size of the domain. At the moment I can only plot a domain of size 10 (in each dimension) with unit spacing (ds = 1). However, I need a much larger domain than this (size 100 in each dimension) and much smaller than unit spacing (ideally as small as 0.1) - this would lead to 100^3 * (1/0.1)^3 points on the grid, which Matlab just cannot handle. Does anyone have any ideas about how to somehow bin the density function by the 3D proportions to reduce the number of points?
My working code with example:
a = 0; % start of domain
b = 10; % end of domain
ds = 1; % spacing
[x, y, z] = ndgrid((a:ds:b)); % generate 3D independent variables
n = size(x);
q = zeros(n); % generate 3D dependent variable with some norm distributed density
for i = 1:n(1)
for j = 1:n(2)
for k = 1:n(2)
q(i,j,k) = exp(-(((x(i,j,k) - 10)^2 + (y(i,j,k) - 10)^2 + (z(i,j,k) - 10)^2) / 20));
end
end
end
Total = x + y + z; % calculate the total of x,y,z at every point in the domain
x = x ./ Total; % find the proportion of x at every point in the domain
y = y ./ Total; % find the proportion of y at every point in the domain
z = z ./ Total; % find the proportion of z at every point in the domain
x(isnan(x)) = 0; % set coordinate (0,0,0) to 0
y(isnan(y)) = 0; % set coordinate (0,0,0) to 0
z(isnan(z)) = 0; % set coordinate (0,0,0) to 0
xP = reshape(x,[1, numel(x)]); % create a vector of the proportions of x
yP = reshape(y,[1, numel(y)]); % create a vector of the proportions of y
zP = reshape(z,[1, numel(z)]); % create a vector of the proportions of z
q = reshape(q,[1, numel(q)]); % create a vector of the dependent variable q
ternsurf(xP, yP, q); % plot the ternary surface of q against proportions
shading(gca, 'interp');
colorbar
view(2)
I believe you meant n(3) in your innermost loop. Here are a few tips:
1) Loose the loops:
q = exp(- ((x - 10).^2 + (y - 10).^2 + (z - 10).^2) / 20);
2) Loose the reshapes:
xP = x(:); yP = y(:); zP = z(:);
3) Check Total once, instead of doing three checks on x,y,z:
Total = x + y + z; % calculate the total of x,y,z at every point in the domain
Total( abs(Total) < eps ) = 1;
x = x ./ Total; % find the proportion of x at every point in the domain
y = y ./ Total; % find the proportion of y at every point in the domain
z = z ./ Total; % find the proportion of z at every point in the domain
PS: I just recognized your name.. it's Jonathan ;)
Discretization method probably depends on use of your plot, maybe it make sense to clarify your question from this point of view.
Overall, you probably struggling with an "Out of memory" error, a couple of relevant tricks are described here http://www.mathworks.nl/help/matlab/matlab_prog/resolving-out-of-memory-errors.html?s_tid=doc_12b?refresh=true#brh72ex-52 . Of course, they work only up to certain size of arrays.
A more generic solution is too save parts of arrays on hard drive, it makes processing slower but it'll work. E.g., you can define several q functions with the scale-specific ngrids (e.g. ngridOrder0=[0:10:100], ngridOrder10=[1:1:9], ngridOrder11=[11:1:19], etc... ), and write an accessor function which will load/save the relevant grid and q function depending on the part of the plot you're looking.

surfnorm function more efficient way Matlab

After constructing the point cloud I want to get the normal of each point and I used the built-in matlab function surfnorm but its takes a lot of processing time. So if anyone could assist me do this a better and more efficient way.
I wonder if the following code would help you. There are three steps here.
Create 500 randomly spaced points (x,y), and compute a corresponding value z (the height of the surface) for which I chose a sinc like function
Resample the random points using the TriScatteredInterp function - this permits me to obtain points on an evenly sampled grid that "roughly correspond" to the initial surface
Compute the normal to "some points" on that grid (since there are 480x640 points, computing the normal at every point would just create an impossibly dense "forest of vectors"; by sampling "every 10th point" you can actually see what you are doing
The code I used was as follows:
randomX = rand(1,500);
randomY = rand(1,500);
r = 5*sqrt(randomX.^2 + randomY.^2);
randomZ = sin(r) ./ r;
% resample the data:
[xx yy] = meshgrid(linspace(0,1,640), linspace(0,1,480));
F = TriScatteredInterp(randomX(:), randomY(:), randomZ(:));
zz = F(xx, yy);
%% at each point, the normal is cross product of vectors to neighbors
xyz=reshape([xx yy zz],[size(xx) 3]);
xv = 10:30:479; yv = 10:30:639; % points at which to compute normals
dx = xyz(xv, yv+1, :) - xyz(xv, yv, :);
dy = xyz(xv+1, yv, :) - xyz(xv, yv, :);
normVecs = cross(dx, dy); % here we compute the normals.
normVecs = normVecs ./ repmat(sqrt(sum(normVecs.^2, 3)), [1 1 3]);
figure;
quiver3(xx(xv, yv), yy(xv, yv), zz(xv, yv), ...
normVecs(:,:,1), normVecs(:,:,2), normVecs(:,:,3));
axis equal
view([56 22]);
And the resulting plot: