Lap Time Simulator Car (MATLAB) - matlab

I'm currently trying to make a lap time simulator for point mass acceleration of a car. I've made a model for the engine and its speed as shown below. However I'm coming across a problem where the rpm(engine_speed) doesn't drop down once it's above 14000 where there would be a gear change. How can I improve this
car.torque = [4 7 10 12.5 14.5 25 30.5 30.5 35.5 45.6 44.8 39.4 33.8 34 13 4];
car.engine_speed = [1 1000 2000 3000 4000 5000 6000 7000 8000 9000 10000 11000 12000 13000 14000 15000] .* 2 * pi / 60;
car.torque_spline = spline(car.engine_speed, car.torque);
%This is within a while loop
model.wheel_speed = model.vx(idx-1) ./ car.wheel_radius;
model.engine_speed = model.wheel_speed .* (car.gearbox .* car.gear_ratio);
model.engine_speed = min(14000 * (2 * pi / 60), max(0, model.engine_speed));

From your code snippet, it appears that as wheel_speed increases, engine_speed can only decrease if gear_ratio or gear_box change. Where are the gear variables set or modeled?
comment: I don't think you need .* or ./ in lines 2 or 4. .* and ./ are only required when multiplying/dividing arrays element-wise. In this case you're multiplying/divding each element of an array by a number.

Related

Resampling multiple data columns from minutes to hours in matlab

I got a big data set of minutly data with multiple columns that needs to be converted from minutes to hours.
I am new to matlab and tried
data_minute = rand(data); % synthetic data
data_hour = mean(reshape(data_minute, 60, []))
which only gives me the hourly data from one row.
I wasnt able to work through every column with something like:
for i = 1:n_columns
data_hour(:,i) = mean(reshape(data_minute(:,i),60, []));
end
Trying a For-Loop to sample every 60 data plots also didn't work out.
Looking at a solution in google didn't give me a result i understood.
Update:
For clarification the data looks something like this:
minute value
1 501
2 479
3 449
4 463
5 404
6 173
7 141
8 141
9 141
10 140
11 140
12 140
13 140
14 202
15 206
16 206
.. ...
525604 120
This sounds like a job for timetable and retime. First make a timetable, using a duration for the "time" variable - it's easy to create a duration array using the minutes function. For example:
>> tt = timetable(minutes(0:1000)', rand(1001, 1));
>> % Just look at the first few rows of 'tt':
>> head(tt)
ans =
8×1 timetable
Time Var1
_____ ________
0 min 0.31907
1 min 0.98605
2 min 0.71818
3 min 0.41318
4 min 0.09863
5 min 0.73456
6 min 0.63731
7 min 0.073842
>> % use 'retime' to get the hourly means:
>> rt = retime(tt, 'hourly', 'mean')
rt =
17×1 timetable
Time Var1
_______ _______
0 min 0.47755
60 min 0.47877
120 min 0.48007
180 min 0.55399
240 min 0.5142
300 min 0.5656
360 min 0.50957
420 min 0.48986
480 min 0.49568
540 min 0.55133
600 min 0.49981
660 min 0.53677
720 min 0.49343
780 min 0.53409
840 min 0.47901
900 min 0.55287
960 min 0.48173
We want to: Downsample the data with an aggregation or an interpolation of all the measurements grouped by hour.
If we take this example data matrice:
M = [10, 3,4,5,6;
2000, 3,4,3,5;
5000, 4,4,4,4]
And we say that the first column correspond to the time in second, and the other columns correspond to your measurements.
Solution 1: Aggregation with accumarray
% we start by calculating the time in hour (3600 seconds in one hour).
hour = ceil(M(:,1)/3600)
% We extract the measurements
val = M(:,2:end)
% nrow = How many different measurements ?
nrow = size(val,2);
% How many unique hour ?
[uid,~,id] = unique(hour);
% creation of a sub index grouping the measurements by hour and by column
sub = [repmat(id,nrow,1),kron(1:nrow,ones(1,length(id))).']
sub =
1 1
1 1
2 1
1 2
1 2
2 2
1 3
1 3
2 3
1 4
1 4
2 4
%We calculate the result using accumarray (first column = hour):
RES = [uid,accumarray(sub,val(:),[],#median)] %if you want the mean choose #mean
RES =
1.0000 3.0000 4.0000 4.0000 5.5000
2.0000 4.0000 4.0000 4.0000 4.0000
Solution 2: Interpolation with interp1
You can interpolate your data with interp1
interp_second = unique(floor(M(:,1)/3600))*3600
%création of an unique index
uid = unique(ceil(M(:,1)/3600))
% We extract the measurements
val = M(:,2:end)
% Result (first column = hour)
RES = [uid,interp1(M(:,1),val,interp_second)]
Conclusion
I would recommand the solution 1, because the method is more robust.

MATLAB - Correlation with Vectors

I have two vectors XYZ with different sizes. We can call it Data1 and Data2, where:
Data1 = [1000 3:55 2000; ...
950 2200 4.5; ...
1050 2350 5.5; ...
1025 2500 6; ...
1075 2600 7; ...
1000 2700 8];
Data2 = [1000 2650 7.95; ...
1000 2750 8.16; ...
1000 2700 9; ...
1025 3000 10];
The minimum acceptable difference between the points is 100 meters to the position (X, Y) and 0.2 for the depth (Z).
In this case, the points between the vectors will be P_Data1 = [1000 2700 8] and P_Data2 = [1000 2650 7.95], because the distance is acceptable and the depth is the nearest.
Does anyone know a function that can do this correlation to help me? I think in Matalab there is some function to the problem and a high performance, for I will do this calculation for thousands of points.
I'm currently using a nested loop, but the performance is very bad, because I calculate all distances, then all the differences between the depths for every point and filter the matrix.
In short, I want to find the points with lower and lower depths between two vectors of different sizes to the defined ranges.
I thank you for all the help!
Data1 = [950 2200 4.5; ...
1050 2350 5.5; ...
1025 2500 6; ...
1075 2600 7; ...
1000 2700 8];
Data2 = [1000 2650 7.95; ...
1000 2750 8.16; ...
1000 2700 9; ...
1025 3000 10];
vec1 = Data1(:,3);
vec2 = Data2(:,3);
[p,q] = meshgrid(vec1, vec2);
output1 = 0; %initial set
while output1 == 0
sub = [abs(p(:)-q(:))];
[M,I] = min(sub);
IndData1 = floor(I/4);
IndData2 = mod(I,IndData1);
%this basically computes the smallest possible Z
%Check if it works for condition 2:
checkcolumn1 = abs(Data1(IndData1,1) - Data2(IndData2,1));
checkcolumn2 = abs(Data1(IndData1,2) - Data2(IndData2,2));
if checkcolumn1 < 200 && checkcolumn2 <200
output1 = Data1(IndData1,:);
output2 = Data2(IndData2,:);
else
min(sub) = 1000000 %huge high number to basically remove the min
end
end
So this program should do what you ask, basically, it first calculates the minimum of the Z column, you can add a condition that it has to be less than 0.2 by the way, I just assumed that there has to be some value smaller than 0.2. Then it tries to see if the first condition can be fulfilled. Although it uses a loop for the search, it is actually very efficient, as it will jump out of the loop as soon as it finds the correct values.

Multiply each value in rows of Matrix A by each corresponding value of a specfic row in Matrix B

I have a A=[m,n] matrix and a B=[n,l] matrix.
A =
[1 2 3
4 5 6
7 8 9
10 11 12]
For the sake of simplicity, let's assume l=1, so B is in fact a vector B=[n,1]
B = [100 10 1]
I would like multiply all the values in each row of A by a corresponding value of B - column-wise.
I know how to do it "manually":
C=[A(:,1)*B(:,1), A(:,2)*B(:,2), A(:,3)*B(:,3)]
This is the result I want:
C = [100 20 3
400 50 6
700 80 9
1000 110 12]
Unfortunately my real life matrices are a bit bigger e.g. (D=[888,1270]) so I'm looking for smarter/faster way to do this.
Pre R2016b:
C=bsxfun(#times,A,B)
C =
100 20 3
400 50 6
700 80 9
1000 110 12
R2016b and later:
In MATLAB® R2016b and later, you can directly use operators instead of bsxfun , since the operators independently support implicit expansion of arrays.
C = A .* B
If I > 1, then you will have to reorder the dimensions of B first with a permute,
>> B = [100 10 1; 1 10 100];
>> C = bsxfun(#times, A, permute(B, [3 2 1]));
>> C
C(:,:,1) =
100 20 3
400 50 6
700 80 9
1000 110 12
C(:,:,2) =
1 20 300
4 50 600
7 80 900
10 110 1200

Limited Sum in Matlab

Hi lets say that i have matrix size 5x5.
B=[1 2 3 4 5; 10 20 30 40 50; 100 200 300 400 500; 1000 2000 3000 4000 5000; 10000 20000 30000 40000 50000];
How do i use function sum, to sum rows between 2 and 4 and have result:
A = [1110;2220;3330;4440]
You'll find some useful information about matrix indexing in the documentation at http://www.mathworks.co.uk/help/matlab/math/matrix-indexing.html
To illustrate your example, you can use B(2:4,:) to retreive the following:
ans =
10 20 30 40 50
100 200 300 400 500
1000 2000 3000 4000 5000
You can then use the sum function as follows to achieve your desired result:
A = sum(B(2:4,:))
I hope this helps!
All the best,
Matt
MATLAB>> sum(B(2:4,1:4))
ans =
1110 2220 3330 4440
If you want to transpose the result, add ' at the end.

How can I divide each row of a matrix by a fixed row?

Suppose I have a matrix like:
100 200 300 400 500 600
1 2 3 4 5 6
10 20 30 40 50 60
...
I wish to divide each row by the second row (each element by the corresponding element), so I'll get:
100 100 100 100 100 100
1 1 1 1 1 1
10 10 10 10 10 10
...
Hw can I do it (without writing an explicit loop)?
Use bsxfun:
outMat = bsxfun (#rdivide, inMat, inMat(2,:));
The 1st argument to bsxfun is a handle to the function you want to apply, in this case right-division.
Here's a couple more equivalent ways:
M = [100 200 300 400 500 600
1 2 3 4 5 6
10 20 30 40 50 60];
%# BSXFUN
MM = bsxfun(#rdivide, M, M(2,:));
%# REPMAT
MM = M ./ repmat(M(2,:),size(M,1),1);
%# repetition by multiplication
MM = M ./ ( ones(size(M,1),1)*M(2,:) );
%# FOR-loop
MM = zeros(size(M));
for i=1:size(M,1)
MM(i,:) = M(i,:) ./ M(2,:);
end
The best solution is the one using BSXFUN (as posted by #Itamar Katz)
You can now use array vs matrix operations.
This will do the trick :
mat = [100 200 300 400 500 600
1 2 3 4 5 6
10 20 30 40 50 60];
result = mat ./ mat(2,:)
which will output :
result =
100 100 100 100 100 100
1 1 1 1 1 1
10 10 10 10 10 10
This will work in Octave and Matlab since R2016b.