Skip to content

Commit 8b467a5

Browse files
committed
Performances improvement #12 and #16
1 parent 6edd0a0 commit 8b467a5

File tree

71 files changed

+6627
-4355
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+6627
-4355
lines changed

src/MIC/Assets/.MeshCloudScripting/Presentation1/App.cs

+51-10
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ public class App : IHostedService, IAsyncDisposable
2929
private readonly AppSettings _appSettings;
3030
private readonly PersonFlow _personFlow;
3131
private readonly List<QLearning> _qLearnings;
32+
private CancellationTokenSource cts1 = new CancellationTokenSource();
33+
private CancellationTokenSource cts2 = new CancellationTokenSource();
34+
private CancellationTokenSource cts3 = new CancellationTokenSource();
35+
private CancellationTokenSource cts4 = new CancellationTokenSource();
36+
private CancellationTokenSource cts5 = new CancellationTokenSource();
37+
private Dictionary<string, Vector3> destinationsList = new Dictionary<string, Vector3>
38+
{
39+
{"Cafe", new Vector3(-2, 0.1f, 3)}, //Machine à café
40+
{"Innover", new Vector3(-27, 0.1f, 5)}, // Innover
41+
{"Loft", new Vector3()}, // Loft
42+
};
3243
public App(ICloudApplication app, ILogger<App> logger)
3344
{
3445
_app = app;
@@ -39,7 +50,7 @@ public App(ICloudApplication app, ILogger<App> logger)
3950
for (int i = 0; i < 5; i++)
4051
{
4152
int numStates = 1800;
42-
_qLearnings.Add(new QLearning(numStates, 8, 0.7, 0.9, 0.6, 0));
53+
_qLearnings.Add(new QLearning(numStates, 8, 0.7, 0.7, 1, 0));
4354
}
4455
}
4556
private AppSettings? LoadSettings()
@@ -80,21 +91,51 @@ public async Task StartAsync(CancellationToken token)
8091
await UploadImageToBlobStorage(2, _appSettings);
8192
btnSphere.IsActive = false;
8293
};
83-
Vector3 destination = new Vector3(-25, 0, 1);
84-
var wall = (TransformNode)_app.Scene.FindChildByPath("QLearning/Wall");
94+
95+
var btnSimulationCafe = (TransformNode)_app.Scene.FindChildByPath("Simulation/ButtonCafe");
96+
var sensorCafe = btnSimulationCafe.FindFirstChild<InteractableNode>();
97+
var btnSImulationInnover = (TransformNode)_app.Scene.FindChildByPath("Simulation/ButtonInnover");
98+
var sensorInnover = btnSImulationInnover.FindFirstChild<InteractableNode>();
99+
sensorCafe.Selected += async (sender, args) =>
100+
{
101+
await StartSimulation("Cafe");
102+
};
103+
sensorInnover.Selected += async (sender, args) =>
104+
{
105+
await StartSimulation("Innover");
106+
};
107+
}
108+
109+
public async Task StartSimulation(string simulationAction) //0 to go to the coffee
110+
{
111+
Vector3 destination = destinationsList[simulationAction];
85112
var npc1 = (TransformNode)_app.Scene.FindChildByPath("HumanMale_Character");
86113
var npc2 = (TransformNode)_app.Scene.FindChildByPath("HumanMale_Character1");
87114
var npc3 = (TransformNode)_app.Scene.FindChildByPath("HumanMale_Character2");
88115
var npc4 = (TransformNode)_app.Scene.FindChildByPath("HumanMale_Character3");
89116
var npc5 = (TransformNode)_app.Scene.FindChildByPath("HumanMale_Character4");
90-
_qLearnings[0].MoveAction(npc1, destination, 0, 10000);
91-
//_qLearnings[1].MoveAction(npc2, destination, 1, 100);
92-
//_qLearnings[2].MoveAction(npc3, destination, 10000);
93-
//_qLearnings[3].MoveAction(npc4, destination, 10000);
94-
//_qLearnings[4].MoveAction(npc5, destination, 10000);
95117

96-
//var move = 5;
97-
//_personFlow.Boucle(npc, move);
118+
cts1.Cancel();
119+
cts2.Cancel();
120+
cts3.Cancel();
121+
cts4.Cancel();
122+
cts5.Cancel();
123+
124+
cts1 = new CancellationTokenSource();
125+
cts2 = new CancellationTokenSource();
126+
cts3 = new CancellationTokenSource();
127+
cts4 = new CancellationTokenSource();
128+
cts5 = new CancellationTokenSource();
129+
130+
_qLearnings[0].MoveAction(npc1, destination, simulationAction, 0, 10000, cts1.Token);
131+
await Task.Delay(50);
132+
_qLearnings[1].MoveAction(npc2, destination, simulationAction, 1, 10000, cts2.Token);
133+
await Task.Delay(50);
134+
_qLearnings[2].MoveAction(npc3, destination, simulationAction, 2, 10000, cts3.Token);
135+
await Task.Delay(50);
136+
_qLearnings[3].MoveAction(npc4, destination, simulationAction, 3, 10000, cts4.Token);
137+
await Task.Delay(50);
138+
_qLearnings[4].MoveAction(npc5, destination, simulationAction, 4, 10000, cts5.Token);
98139
}
99140

100141
public async Task<string> GetImage(TransformNode node, string imageUrl)

src/MIC/Assets/.MeshCloudScripting/Presentation1/QLearning.cs

+99-73
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.ComponentModel;
34
using System.IO;
45
using System.Numerics;
56
using Microsoft.Mesh.CloudScripting;
@@ -9,27 +10,38 @@ namespace Presentation1
910
{
1011
public class QLearning
1112
{
12-
private double[,] qTable; // Table for the memory of the npc, works with a reward value
13-
private Dictionary<int, double> maxQValues = new Dictionary<int, double>();
14-
private Dictionary<int, int> maxQActions = new Dictionary<int, int>();
15-
private int numStates, numActions; //numState : number of stats possible | numActions : number of actions posible
16-
private double learningRate, discountFactor, explorationRate; // Parameters for the Q learning algorithm
17-
private float lastDistance = 9999; // Stores the last distance from the choosen destination
18-
private Random rnd = new Random();
1913
const int GRID_SIZE = 60;
2014
const int STATE_MODULUS = 100000;
21-
private Dictionary<int, Vector3> actionDirections = new Dictionary<int, Vector3>
15+
const float DISTANCE_THRESHOLD = 0.1f;
16+
const int REWARD_GOAL = 100;
17+
const int REWARD_FAR = -1;
18+
const int REWARD_CLOSE = 1;
19+
20+
private readonly double[,] qTable; // Table for the memory of the npc, works with a reward value
21+
private readonly int numStates, numActions; //numState : number of stats possible | numActions : number of actions posible
22+
private readonly double learningRate, discountFactor, explorationRate; // Parameters for the Q learning algorithm
23+
private readonly bool[,] gridObstacles;
24+
private readonly object lockObject = new object();
25+
private readonly Random rnd = new Random();
26+
27+
private List<Vector3> npcPositions = new List<Vector3>();
28+
29+
private float lastDistance = 9999; // Stores the last distance from the choosen destination
30+
31+
private readonly Dictionary<int, double> maxQValues = new Dictionary<int, double>();
32+
private readonly Dictionary<int, int> maxQActions = new Dictionary<int, int>();
33+
private readonly Dictionary<float, Vector3> actionDirections = new Dictionary<float, Vector3>
2234
{
23-
{0, new Vector3(0, 0, 1)}, // Move up
24-
{1, new Vector3(0, 0, -1)}, // Move down
25-
{2, new Vector3(-1, 0, 0)}, // Move left
26-
{3, new Vector3(1, 0, 0)}, // Move right
27-
{4, new Vector3(1, 0f, 1)}, // Move up-right
28-
{5, new Vector3(-1, 0f, 1)}, // Move up-left
29-
{6, new Vector3(1, 0f, -1)}, // Move down-right
30-
{7, new Vector3(-1, 0f, -1)} // Move down-left
35+
{0, new Vector3(0, 0, 1f)}, // Move up
36+
{1, new Vector3(0, 0, -1f)}, // Move down
37+
{2, new Vector3(-1f, 0, 0)}, // Move left
38+
{3, new Vector3(1f, 0, 0)}, // Move right
39+
{4, new Vector3(1f, 0f, 1f)}, // Move up-right
40+
{5, new Vector3(-01f, 0f, 1f)}, // Move up-left
41+
{6, new Vector3(1f, 0f, -1f)}, // Move down-right
42+
{7, new Vector3(-1f, 0f, -1f)} // Move down-left
3143
};
32-
private bool[,] gridObstacles;
44+
3345
public QLearning(int numStates, int numActions, double learningRate, double discountFactor, double explorationRate, int npcNum)
3446
{
3547
this.numStates = numStates;
@@ -40,7 +52,6 @@ public QLearning(int numStates, int numActions, double learningRate, double disc
4052

4153
qTable = new double[numStates, numActions];
4254

43-
LoadQTable(npcNum);
4455
gridObstacles = LoadGrid();
4556
}
4657

@@ -55,22 +66,23 @@ public int ChooseAction(int state)
5566
}
5667
else
5768
{
69+
maxQActions.TryGetValue(state, out int action);
5870
// Exploit: select the action with max value
59-
return maxQActions.ContainsKey(state) ? maxQActions[state] : 0;
71+
return action;
6072
}
6173
}
6274

6375
// Update the Q value in the Q table with the reward it gets
6476
public void UpdateQValue(int prevState, int action, float reward, int nextState)
6577
{
66-
double oldValue = qTable[prevState, action];
67-
if (!maxQValues.ContainsKey(prevState) || qTable[prevState, action] > maxQValues[prevState])
68-
{
69-
double learnedValue = reward + discountFactor * (maxQValues.ContainsKey(nextState) ? maxQValues[nextState] : 0);
70-
qTable[prevState, action] += learningRate * (learnedValue - oldValue);
71-
maxQValues[prevState] = qTable[prevState, action];
72-
maxQActions[prevState] = action;
73-
}
78+
double oldValue = qTable[prevState, action];
79+
if (!maxQValues.ContainsKey(prevState) || qTable[prevState, action] > maxQValues[prevState])
80+
{
81+
double learnedValue = reward + discountFactor * (maxQValues.ContainsKey(nextState) ? maxQValues[nextState] : 0);
82+
qTable[prevState, action] += learningRate * (learnedValue - oldValue);
83+
maxQValues[prevState] = qTable[prevState, action];
84+
maxQActions[prevState] = action;
85+
}
7486
}
7587

7688
// Get the position/state of the npc
@@ -86,17 +98,22 @@ public int GetState(TransformNode npc)
8698
}
8799

88100
// Main function that make the npc move and calls all the subfunctions
89-
public async void MoveAction(TransformNode npc, Vector3 destination, int npcNum, int numIterations)
101+
public async void MoveAction(TransformNode npc, Vector3 destination, string destinationName, int npcNum, int numIterations, CancellationToken cancellationToken)
90102
{
103+
LoadQTable(npcNum, destinationName);
91104
for (int i = 0; i < numIterations; i++)
92105
{
106+
if (cancellationToken.IsCancellationRequested)
107+
{
108+
return;
109+
}
93110
int prevState = GetState(npc);
94111
int action = ChooseAction(prevState);
95112

96113
Vector3 direction = actionDirections[action];
97114

98-
await RotateNpc(npc, direction);
99-
await MoveNpc(npc, direction, npcNum);
115+
await RotateNpc(npc, direction, cancellationToken);
116+
await MoveNpc(npc, direction, npcNum, cancellationToken);
100117

101118
// Calculate the reward
102119
float reward = CalculateReward(npc, destination);
@@ -107,39 +124,51 @@ public async void MoveAction(TransformNode npc, Vector3 destination, int npcNum,
107124
UpdateQValue(prevState, action, reward, nextState);
108125
if (i % 100 == 0)
109126
{
110-
SaveQTable(npcNum);
127+
SaveQTable(npcNum, destinationName);
128+
}
129+
if (Vector3.Distance(npc.Position, destination) <= DISTANCE_THRESHOLD) // 0.1f is a small threshold to account for floating point precision
130+
{
131+
break; // Stop the movement
111132
}
112133
}
113134
}
114135

115-
public async Task MoveNpc(TransformNode npc, Vector3 direction, int npcNum)
136+
public async Task MoveNpc(TransformNode npc, Vector3 direction, int npcNum, CancellationToken cancellationToken)
116137
{
117138
float duration = 2f;
118-
float remainingTime = duration;
119139
Vector3 desiredPosition = npc.Position + direction;
120140

141+
if (npcPositions.Any(pos => Vector3.Distance(pos, desiredPosition) < DISTANCE_THRESHOLD))
142+
{
143+
// If there is a collision, return without moving the NPC
144+
return;
145+
}
146+
121147
if (desiredPosition.X >= -GRID_SIZE / 2 && desiredPosition.X < GRID_SIZE / 2 && desiredPosition.Z >= -GRID_SIZE / 2 && desiredPosition.Z < GRID_SIZE / 2 && !gridObstacles[(int)desiredPosition.X + GRID_SIZE / 2, (int)desiredPosition.Z + GRID_SIZE / 2])
122148
{
123149
float t = 0f;
124150
while (t < 1f)
125151
{
126-
float delatTime = 0.01f;
127-
float stepSize = delatTime / duration;
152+
if (cancellationToken.IsCancellationRequested)
153+
{
154+
return;
155+
}
156+
float deltaTime = 0.02f;
157+
float stepSize = deltaTime / duration;
128158
t += stepSize;
129159
npc.Position = Vector3.Lerp(npc.Position, desiredPosition, t);
130160

131161
if (t > 1f) t = 1f;
132-
await Task.Delay((int)(delatTime * 1000));
133-
remainingTime -= delatTime;
162+
await Task.Delay((int)(deltaTime * 100));
134163
}
135164
}
136165

137166
}
138167

139-
public async Task RotateNpc(TransformNode npc, Vector3 direction)
168+
public async Task RotateNpc(TransformNode npc, Vector3 direction, CancellationToken cancellationToken)
140169
{
141170
Vector3 normalizedDirection = Vector3.Normalize(direction);
142-
171+
143172
float rotationAngleRadians = MathF.Atan2(normalizedDirection.X, normalizedDirection.Z);
144173
if (rotationAngleRadians == 0)
145174
{
@@ -151,21 +180,23 @@ public async Task RotateNpc(TransformNode npc, Vector3 direction)
151180
Quaternion rotation = new Quaternion(rotationAxis.X, rotationAxis.Y, rotationAxis.Z, MathF.Cos(rotationAngleRadians / 2));
152181
if (Equals(npc.Rotation, rotation)) return;
153182

154-
float duration = 1f;
155-
float remainingTime = duration;
183+
float duration = 0.5f;
156184
float t = 0f;
157185
while (t < 1f)
158186
{
159-
float deltaTime = 0.01f; // Adjust as needed
187+
if (cancellationToken.IsCancellationRequested)
188+
{
189+
return;
190+
}
191+
float deltaTime = 0.02f; // Adjust as needed
160192
float stepSize = deltaTime / duration;
161193

162194
t += stepSize;
163-
npc.Rotation = Quaternion.Slerp(npc.Rotation, rotation, t);
195+
npc.Rotation = Quaternion.Slerp(npc.Rotation, rotation, t * t * (3 - 2 * t));
164196

165197
if (t > 1f) t = 1f;
166198

167-
await Task.Delay((int)(deltaTime * 1000)); // Convert deltaTime to milliseconds
168-
remainingTime -= deltaTime;
199+
await Task.Delay((int)(deltaTime * 100));
169200
}
170201
}
171202

@@ -177,52 +208,47 @@ public int CalculateReward(TransformNode npc, Vector3 destination)
177208

178209
if (distance == 0)
179210
{
180-
return 100; // Big reward for reaching the goal
181-
}
182-
else if (distance >= lastDistance)
183-
{
184-
lastDistance = distance;
185-
return -1;
211+
return REWARD_GOAL; // Big reward for reaching the goal
186212
}
213+
else
187214
{
188-
lastDistance = distance;
189-
return 1;
215+
return -1 * (int)distance;
190216
}
191217
}
192218

193219
// At the end of the movement, save the Q table in a json to exploit it at the next launchs
194-
public void SaveQTable(int npcNum)
220+
public void SaveQTable(int npcNum, string destinationName)
195221
{
196-
var qTableList = new List<List<double>>();
197-
for (int i = 0; i < numStates; i++)
198-
{
199-
var row = new List<double>();
200-
for (int j = 0; j < numActions; j++)
222+
var qTableList = new List<List<double>>();
223+
for (int i = 0; i < numStates; i++)
201224
{
202-
row.Add(qTable[i, j]);
225+
var row = new List<double>();
226+
for (int j = 0; j < numActions; j++)
227+
{
228+
row.Add(qTable[i, j]);
229+
}
230+
qTableList.Add(row);
203231
}
204-
qTableList.Add(row);
205-
}
206-
string finalFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "qtable" + npcNum + ".json"); ;
207-
File.WriteAllText(finalFilePath, JsonConvert.SerializeObject(qTableList));
232+
string finalFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "qtable" + npcNum + destinationName + ".json"); ;
233+
File.WriteAllText(finalFilePath, JsonConvert.SerializeObject(qTableList));
208234
}
209235

210236

211237
// Load the Q table
212-
public void LoadQTable(int npcNum)
238+
public void LoadQTable(int npcNum, string destinationName)
213239
{
214-
string finalFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "qtable" + npcNum + ".json");
215-
if (File.Exists(finalFilePath))
216-
{
217-
var qTableList = JsonConvert.DeserializeObject<List<List<double>>>(File.ReadAllText(finalFilePath));
218-
for (int i = 0; i < numStates; i++)
240+
string finalFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "qtable" + npcNum + destinationName + ".json");
241+
if (File.Exists(finalFilePath))
219242
{
220-
for (int j = 0; j < numActions; j++)
243+
var qTableList = JsonConvert.DeserializeObject<List<List<double>>>(File.ReadAllText(finalFilePath));
244+
for (int i = 0; i < numStates; i++)
221245
{
222-
qTable[i, j] = qTableList[i][j];
246+
for (int j = 0; j < numActions; j++)
247+
{
248+
qTable[i, j] = qTableList[i][j];
249+
}
223250
}
224251
}
225-
}
226252
}
227253

228254
public bool[,] LoadGrid()
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
version https://git-lfs.github.com/spec/v1
2-
oid sha256:21908272f16162328564e39fff144c0b4ecdc1edc7a6ae813c5d210d8e141361
3-
size 193024
2+
oid sha256:2ef67b9979c0048009c769e734c33debd5a9dc3231d6dc12f61c8f799e239d78
3+
size 200704
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
version https://git-lfs.github.com/spec/v1
2-
oid sha256:14b0293276f3d0a25280e3e4f417a9099ec434c929d7ca095e3f2939de0a41d6
2+
oid sha256:a8ad1ba9b7e5b243a020d2df139f19dbc73f798cfe0cefe6c5a42453c7016d42
33
size 149504

0 commit comments

Comments
 (0)