Motor Algorithm – Part 3 – Vector Table

As mentioned in part2 I want to create a 360 degree vector table with PWM duties for A,B and C. This allows me to drive sinusoidal by simply stepping 0 to 359. To do this I create a small C application as follows:

#include "stdio.h"
#include "stdlib.h"
#include "math.h"
int main(int argc, char* argv)
{
	FILE* fd = fopen("vectors.cpp", "w");
	if (fd != NULL)
	{
		fprintf(fd, "alSinusVector _vector[AL3P_VECTOR_SIZE]=\n");
		fprintf(fd, "{\n");
		for (int i = 0; i < 360; i++)
		{
			double vA, vB, vC;
			
			vA = sin(i*3.14/180.0);
			vB = sin((i + 120)*3.14/180.0);
			vC = sin((i + 120 + 120)*3.14/180.0);
			fprintf(fd, "    %f, %f, %f,		// %d\n",vA,vB,vC, i);
		}
		fprintf(fd, "};\n");
 
		fclose(fd);
	}
}

The math is vA = sin (radians(degrees)), while vB is +120 degrees etc. This will generate a table with values -1 to +1. I use the – kow if I should swith on High or Low MOSFET and the value to compute a duty. The only thing I need to do is actually to multiply with torque + I control speed with how fast I step this. The generated table is below – I have to test this, but looking at the values I think it should work. Note that I generate all 3 vectors here, but you can actually manage with only one since B is A+120 etc.

alSinusVector al3PhaseMotor::m_vector[AL3P_VECTOR_SIZE]=
{
0.000000, 0.866556, -0.864962, // 0
0.017444, 0.857718, -0.873584, // 1
0.034882, 0.848620, -0.881940, // 2
0.052309, 0.839263, -0.890028, // 3
0.069721, 0.829651, -0.897846, // 4
0.087112, 0.819786, -0.905390, // 5
0.104476, 0.809672, -0.912658, // 6
0.121808, 0.799311, -0.919649, // 7
0.139103, 0.788708, -0.926360, // 8
0.156356, 0.777864, -0.932789, // 9
0.173561, 0.766783, -0.938934, // 10
0.190713, 0.755470, -0.944793, // 11
0.207808, 0.743926, -0.950365, // 12
0.224839, 0.732156, -0.955648, // 13
0.241802, 0.720163, -0.960640, // 14
0.258691, 0.707951, -0.965339, // 15
0.275501, 0.695523, -0.969745, // 16
0.292228, 0.682884, -0.973856, // 17
0.308866, 0.670038, -0.977670, // 18
0.325409, 0.656987, -0.981187, // 19
0.341854, 0.643736, -0.984406, // 20
0.358194, 0.630289, -0.987324, // 21
0.374426, 0.616651, -0.989943, // 22
0.390544, 0.602825, -0.992260, // 23
0.406543, 0.588816, -0.994275, // 24
0.422418, 0.574627, -0.995988, // 25
0.438164, 0.560263, -0.997397, // 26
0.453778, 0.545729, -0.998503, // 27
0.469253, 0.531029, -0.999305, // 28
0.484585, 0.516168, -0.999803, // 29
0.499770, 0.501149, -0.999997, // 30
0.514803, 0.485978, -0.999887, // 31
0.529679, 0.470659, -0.999472, // 32
0.544394, 0.455196, -0.998753, // 33
0.558943, 0.439595, -0.997730, // 34
0.573323, 0.423861, -0.996404, // 35
0.587528, 0.407997, -0.994774, // 36
0.601554, 0.392009, -0.992842, // 37
0.615396, 0.375902, -0.990607, // 38
0.629052, 0.359681, -0.988072, // 39
0.642516, 0.343350, -0.985235, // 40
0.655785, 0.326915, -0.982099, // 41
0.668854, 0.310380, -0.978663, // 42
0.681720, 0.293751, -0.974930, // 43
0.694378, 0.277032, -0.970901, // 44
0.706825, 0.260229, -0.966575, // 45
0.719057, 0.243347, -0.961956, // 46
0.731070, 0.226391, -0.957044, // 47
0.742861, 0.209365, -0.951841, // 48
0.754425, 0.192277, -0.946348, // 49
0.765760, 0.175129, -0.940567, // 50
0.776862, 0.157929, -0.934500, // 51
0.787727, 0.140680, -0.928149, // 52
0.798353, 0.123389, -0.921515, // 53
0.808736, 0.106059, -0.914600, // 54
0.818873, 0.088698, -0.907408, // 55
0.828760, 0.071310, -0.899939, // 56
0.838396, 0.053900, -0.892196, // 57
0.847776, 0.036473, -0.884182, // 58
0.856898, 0.019036, -0.875899, // 59
0.865760, 0.001593, -0.867350, // 60
0.874358, -0.015851, -0.858536, // 61
0.882690, -0.033290, -0.849461, // 62
0.890753, -0.050719, -0.840128, // 63
0.898546, -0.068132, -0.830539, // 64
0.906065, -0.085525, -0.820697, // 65
0.913308, -0.102892, -0.810605, // 66
0.920273, -0.120227, -0.800267, // 67
0.926958, -0.137526, -0.789686, // 68
0.933361, -0.154783, -0.778864, // 69
0.939481, -0.171992, -0.767805, // 70
0.945314, -0.189150, -0.756512, // 71
0.950859, -0.206250, -0.744989, // 72
0.956116, -0.223287, -0.733240, // 73
0.961081, -0.240256, -0.721267, // 74
0.965754, -0.257152, -0.709075, // 75
0.970133, -0.273970, -0.696667, // 76
0.974217, -0.290704, -0.684047, // 77
0.978004, -0.307350, -0.671219, // 78
0.981494, -0.323903, -0.658187, // 79
0.984685, -0.340357, -0.644954, // 80
0.987576, -0.356707, -0.631525, // 81
0.990167, -0.372949, -0.617904, // 82
0.992456, -0.389077, -0.604095, // 83
0.994444, -0.405087, -0.590102, // 84
0.996129, -0.420974, -0.575930, // 85
0.997511, -0.436732, -0.561582, // 86
0.998589, -0.452358, -0.547063, // 87
0.999363, -0.467846, -0.532378, // 88
0.999834, -0.483191, -0.517531, // 89
1.000000, -0.498390, -0.502527, // 90
0.999861, -0.513437, -0.487369, // 91
0.999419, -0.528328, -0.472063, // 92
0.998672, -0.543057, -0.456614, // 93
0.997622, -0.557622, -0.441025, // 94
0.996268, -0.572017, -0.425303, // 95
0.994610, -0.586238, -0.409451, // 96
0.992650, -0.600281, -0.393474, // 97
0.990388, -0.614140, -0.377378, // 98
0.987825, -0.627813, -0.361167, // 99
0.984961, -0.641295, -0.344846, // 100
0.981797, -0.654582, -0.328419, // 101
0.978335, -0.667670, -0.311894, // 102
0.974575, -0.680554, -0.295273, // 103
0.970518, -0.693231, -0.278562, // 104
0.966166, -0.705698, -0.261766, // 105
0.961520, -0.717949, -0.244891, // 106
0.956581, -0.729982, -0.227942, // 107
0.951351, -0.741793, -0.210923, // 108
0.945832, -0.753379, -0.193839, // 109
0.940025, -0.764735, -0.176697, // 110
0.933932, -0.775858, -0.159501, // 111
0.927555, -0.786745, -0.142257, // 112
0.920895, -0.797393, -0.124969, // 113
0.913955, -0.807798, -0.107643, // 114
0.906737, -0.817958, -0.090284, // 115
0.899244, -0.827868, -0.072898, // 116
0.891476, -0.837527, -0.055490, // 117
0.883437, -0.846930, -0.038065, // 118
0.875130, -0.856076, -0.020628, // 119
0.866556, -0.864962, -0.003185, // 120
0.857718, -0.873584, 0.014259, // 121
0.848620, -0.881940, 0.031698, // 122
0.839263, -0.890028, 0.049128, // 123
0.829651, -0.897846, 0.066543, // 124
0.819786, -0.905390, 0.083938, // 125
0.809672, -0.912658, 0.101307, // 126
0.799311, -0.919649, 0.118646, // 127
0.788708, -0.926360, 0.135948, // 128
0.777864, -0.932789, 0.153209, // 129
0.766783, -0.938934, 0.170423, // 130
0.755470, -0.944793, 0.187586, // 131
0.743926, -0.950365, 0.204691, // 132
0.732156, -0.955648, 0.221734, // 133
0.720163, -0.960640, 0.238710, // 134
0.707951, -0.965339, 0.255613, // 135
0.695523, -0.969745, 0.272438, // 136
0.682884, -0.973856, 0.289180, // 137
0.670038, -0.977670, 0.305834, // 138
0.656987, -0.981187, 0.322396, // 139
0.643736, -0.984406, 0.338859, // 140
0.630289, -0.987324, 0.355219, // 141
0.616651, -0.989943, 0.371471, // 142
0.602825, -0.992260, 0.387609, // 143
0.588816, -0.994275, 0.403630, // 144
0.574627, -0.995988, 0.419528, // 145
0.560263, -0.997397, 0.435299, // 146
0.545729, -0.998503, 0.450937, // 147
0.531029, -0.999305, 0.466438, // 148
0.516168, -0.999803, 0.481796, // 149
0.501149, -0.999997, 0.497009, // 150
0.485978, -0.999887, 0.512070, // 151
0.470659, -0.999472, 0.526975, // 152
0.455196, -0.998753, 0.541719, // 153
0.439595, -0.997730, 0.556299, // 154
0.423861, -0.996404, 0.570710, // 155
0.407997, -0.994774, 0.584947, // 156
0.392009, -0.992842, 0.599006, // 157
0.375902, -0.990607, 0.612883, // 158
0.359681, -0.988072, 0.626573, // 159
0.343350, -0.985235, 0.640072, // 160
0.326915, -0.982099, 0.653377, // 161
0.310380, -0.978663, 0.666483, // 162
0.293751, -0.974930, 0.679386, // 163
0.277032, -0.970901, 0.692083, // 164
0.260229, -0.966575, 0.704568, // 165
0.243347, -0.961956, 0.716840, // 166
0.226391, -0.957044, 0.728893, // 167
0.209365, -0.951841, 0.740724, // 168
0.192277, -0.946348, 0.752330, // 169
0.175129, -0.940567, 0.763708, // 170
0.157929, -0.934500, 0.774852, // 171
0.140680, -0.928149, 0.785761, // 172
0.123389, -0.921515, 0.796431, // 173
0.106059, -0.914600, 0.806858, // 174
0.088698, -0.907408, 0.817040, // 175
0.071310, -0.899939, 0.826974, // 176
0.053900, -0.892196, 0.836655, // 177
0.036473, -0.884182, 0.846082, // 178
0.019036, -0.875899, 0.855252, // 179
0.001593, -0.867350, 0.864161, // 180
-0.015851, -0.858536, 0.872808, // 181
-0.033290, -0.849461, 0.881188, // 182
-0.050719, -0.840128, 0.889301, // 183
-0.068132, -0.830539, 0.897143, // 184
-0.085525, -0.820697, 0.904712, // 185
-0.102892, -0.810605, 0.912006, // 186
-0.120227, -0.800267, 0.919022, // 187
-0.137526, -0.789686, 0.925759, // 188
-0.154783, -0.778864, 0.932213, // 189
-0.171992, -0.767805, 0.938385, // 190
-0.189150, -0.756512, 0.944270, // 191
-0.206250, -0.744989, 0.949868, // 192
-0.223287, -0.733240, 0.955178, // 193
-0.240256, -0.721267, 0.960196, // 194
-0.257152, -0.709075, 0.964923, // 195
-0.273970, -0.696667, 0.969355, // 196
-0.290704, -0.684047, 0.973493, // 197
-0.307350, -0.671219, 0.977335, // 198
-0.323903, -0.658187, 0.980879, // 199
-0.340357, -0.644954, 0.984124, // 200
-0.356707, -0.631525, 0.987070, // 201
-0.372949, -0.617904, 0.989716, // 202
-0.389077, -0.604095, 0.992061, // 203
-0.405087, -0.590102, 0.994104, // 204
-0.420974, -0.575930, 0.995844, // 205
-0.436732, -0.561582, 0.997281, // 206
-0.452358, -0.547063, 0.998415, // 207
-0.467846, -0.532378, 0.999245, // 208
-0.483191, -0.517531, 0.999770, // 209
-0.498390, -0.502527, 0.999992, // 210
-0.513437, -0.487369, 0.999909, // 211
-0.528328, -0.472063, 0.999522, // 212
-0.543057, -0.456614, 0.998831, // 213
-0.557622, -0.441025, 0.997836, // 214
-0.572017, -0.425303, 0.996538, // 215
-0.586238, -0.409451, 0.994936, // 216
-0.600281, -0.393474, 0.993031, // 217
-0.614140, -0.377378, 0.990824, // 218
-0.627813, -0.361167, 0.988316, // 219
-0.641295, -0.344846, 0.985506, // 220
-0.654582, -0.328419, 0.982397, // 221
-0.667670, -0.311894, 0.978989, // 222
-0.680554, -0.295273, 0.975283, // 223
-0.693231, -0.278562, 0.971281, // 224
-0.705698, -0.261766, 0.966983, // 225
-0.717949, -0.244891, 0.962390, // 226
-0.729982, -0.227942, 0.957505, // 227
-0.741793, -0.210923, 0.952328, // 228
-0.753379, -0.193839, 0.946861, // 229
-0.764735, -0.176697, 0.941107, // 230
-0.775858, -0.159501, 0.935066, // 231
-0.786745, -0.142257, 0.928740, // 232
-0.797393, -0.124969, 0.922132, // 233
-0.807798, -0.107643, 0.915243, // 234
-0.817958, -0.090284, 0.908076, // 235
-0.827868, -0.072898, 0.900632, // 236
-0.837527, -0.055490, 0.892915, // 237
-0.846930, -0.038065, 0.884925, // 238
-0.856076, -0.020628, 0.876667, // 239
-0.864962, -0.003185, 0.868141, // 240
-0.873584, 0.014259, 0.859351, // 241
-0.881940, 0.031698, 0.850300, // 242
-0.890028, 0.049128, 0.840990, // 243
-0.897846, 0.066543, 0.831425, // 244
-0.905390, 0.083938, 0.821606, // 245
-0.912658, 0.101307, 0.811537, // 246
-0.919649, 0.118646, 0.801221, // 247
-0.926360, 0.135948, 0.790662, // 248
-0.932789, 0.153209, 0.779862, // 249
-0.938934, 0.170423, 0.768824, // 250
-0.944793, 0.187586, 0.757553, // 251
-0.950365, 0.204691, 0.746051, // 252
-0.955648, 0.221734, 0.734322, // 253
-0.960640, 0.238710, 0.722369, // 254
-0.965339, 0.255613, 0.710197, // 255
-0.969745, 0.272438, 0.697809, // 256
-0.973856, 0.289180, 0.685208, // 257
-0.977670, 0.305834, 0.672399, // 258
-0.981187, 0.322396, 0.659385, // 259
-0.984406, 0.338859, 0.646170, // 260
-0.987324, 0.355219, 0.632759, // 261
-0.989943, 0.371471, 0.619156, // 262
-0.992260, 0.387609, 0.605363, // 263
-0.994275, 0.403630, 0.591387, // 264
-0.995988, 0.419528, 0.577231, // 265
-0.997397, 0.435299, 0.562899, // 266
-0.998503, 0.450937, 0.548396, // 267
-0.999305, 0.466438, 0.533726, // 268
-0.999803, 0.481796, 0.518893, // 269
-0.999997, 0.497009, 0.503903, // 270
-0.999887, 0.512070, 0.488759, // 271
-0.999472, 0.526975, 0.473467, // 272
-0.998753, 0.541719, 0.458030, // 273
-0.997730, 0.556299, 0.442454, // 274
-0.996404, 0.570710, 0.426744, // 275
-0.994774, 0.584947, 0.410903, // 276
-0.992842, 0.599006, 0.394938, // 277
-0.990607, 0.612883, 0.378852, // 278
-0.988072, 0.626573, 0.362651, // 279
-0.985235, 0.640072, 0.346340, // 280
-0.982099, 0.653377, 0.329923, // 281
-0.978663, 0.666483, 0.313406, // 282
-0.974930, 0.679386, 0.296794, // 283
-0.970901, 0.692083, 0.280091, // 284
-0.966575, 0.704568, 0.263303, // 285
-0.961956, 0.716840, 0.246435, // 286
-0.957044, 0.728893, 0.229492, // 287
-0.951841, 0.740724, 0.212479, // 288
-0.946348, 0.752330, 0.195402, // 289
-0.940567, 0.763708, 0.178264, // 290
-0.934500, 0.774852, 0.161073, // 291
-0.928149, 0.785761, 0.143833, // 292
-0.921515, 0.796431, 0.126549, // 293
-0.914600, 0.806858, 0.109226, // 294
-0.907408, 0.817040, 0.091870, // 295
-0.899939, 0.826974, 0.074487, // 296
-0.892196, 0.836655, 0.057080, // 297
-0.884182, 0.846082, 0.039656, // 298
-0.875899, 0.855252, 0.022221, // 299
-0.867350, 0.864161, 0.004778, // 300
-0.858536, 0.872808, -0.012666, // 301
-0.849461, 0.881188, -0.030106, // 302
-0.840128, 0.889301, -0.047537, // 303
-0.830539, 0.897143, -0.064954, // 304
-0.820697, 0.904712, -0.082351, // 305
-0.810605, 0.912006, -0.099723, // 306
-0.800267, 0.919022, -0.117064, // 307
-0.789686, 0.925759, -0.134370, // 308
-0.778864, 0.932213, -0.151635, // 309
-0.767805, 0.938385, -0.168854, // 310
-0.756512, 0.944270, -0.186021, // 311
-0.744989, 0.949868, -0.203132, // 312
-0.733240, 0.955178, -0.220181, // 313
-0.721267, 0.960196, -0.237163, // 314
-0.709075, 0.964923, -0.254073, // 315
-0.696667, 0.969355, -0.270905, // 316
-0.684047, 0.973493, -0.287655, // 317
-0.671219, 0.977335, -0.304318, // 318
-0.658187, 0.980879, -0.320888, // 319
-0.644954, 0.984124, -0.337360, // 320
-0.631525, 0.987070, -0.353729, // 321
-0.617904, 0.989716, -0.369991, // 322
-0.604095, 0.992061, -0.386141, // 323
-0.590102, 0.994104, -0.402173, // 324
-0.575930, 0.995844, -0.418082, // 325
-0.561582, 0.997281, -0.433864, // 326
-0.547063, 0.998415, -0.449515, // 327
-0.532378, 0.999245, -0.465028, // 328
-0.517531, 0.999770, -0.480400, // 329
-0.502527, 0.999992, -0.495626, // 330
-0.487369, 0.999909, -0.510701, // 331
-0.472063, 0.999522, -0.525620, // 332
-0.456614, 0.998831, -0.540380, // 333
-0.441025, 0.997836, -0.554975, // 334
-0.425303, 0.996538, -0.569401, // 335
-0.409451, 0.994936, -0.583654, // 336
-0.393474, 0.993031, -0.597730, // 337
-0.377378, 0.990824, -0.611623, // 338
-0.361167, 0.988316, -0.625331, // 339
-0.344846, 0.985506, -0.638848, // 340
-0.328419, 0.982397, -0.652171, // 341
-0.311894, 0.978989, -0.665295, // 342
-0.295273, 0.975283, -0.678217, // 343
-0.278562, 0.971281, -0.690932, // 344
-0.261766, 0.966983, -0.703437, // 345
-0.244891, 0.962390, -0.715728, // 346
-0.227942, 0.957505, -0.727802, // 347
-0.210923, 0.952328, -0.739654, // 348
-0.193839, 0.946861, -0.751280, // 349
-0.176697, 0.941107, -0.762678, // 350
-0.159501, 0.935066, -0.773845, // 351
-0.142257, 0.928740, -0.784775, // 352
-0.124969, 0.922132, -0.795467, // 353
-0.107643, 0.915243, -0.805917, // 354
-0.090284, 0.908076, -0.816121, // 355
-0.072898, 0.900632, -0.826077, // 356
-0.055490, 0.892915, -0.835782, // 357
-0.038065, 0.884925, -0.845232, // 358
-0.020628, 0.876667, -0.854426, // 359
};

Motor Algorithm – Part 2 – Sinusoidal

In the Trapezoidal algorithm we drove the field using six steps which works, but it is very inaccurate. I would like more steps and to do that I need to use a Sinusoidal algorithm.

Sinusoidal means we create a sinus wave using PWM duty, in fact we create 3 sinus waves 120 degrees apart to rotate the field with more steps. To illustrate this I will build on the Trapezoidal algorithm and expand the number of steps it uses:

  • A+ (100% duty), B- (100% duty) C (off)
  • A+ (100% duty), (B-  off), C- (100% duty)

This is out starting point. In the Trapezoidal example we applied the next step A+(100%/C-(100), but what we now will do is to move more gracefuly between B- to C- by adding 50% duty steps.

  • A+ (100% duty), B- (100% duty) C (off)
  • A+(100% duty), (B- 50% duty), C- (50% duty)
  • A+(100% duty), (B-  off), C- (100% duty)

By doing this we have basically modified a 6 step Trapezoidal to be a 12 step Sinusoidal and illustrated how we can use PWM duty to create a full sinusoidal algorithm.

The illustrations above illustrate the original Trapezoidal algorithm with the steps A+/B- and A+/C-. Vectors will in this case jump 45 degrees.

The allistration above show the difference and what we achieve by introducing a new 50% duty step as we now have 22,5 degree jumps. We can now build on this and create a full 360 degree sinusoidal algorithm. Some Sinusoidal algorithms pre-calculate a 360 entry vector table with PWM out duty for A.B and C using index as the input vector. Assuming we use a 4 byte duty number (float) and 360 entries we end up with a 4320 byte lookup table. This is a decent tradeoff to avoid doing all the math real-time.

Just to remind everyone – the picture above is a common propeller motor and while it is still 3-phase it have something like 36 coils which will be A,B and C repeated over and over. This means that a 360 degree Sinusoidal and even a 6 step Trapezoidal might be far more accurate than you expect based on the theoretical 3-phase drawings. You need to know the number of coils to know your speed.

Motor Algorithm – Part 1 – Trapezoidal

I will try to annotate the motor driver algorithms starting with a simple, brute force Trapezoidal algorithm. If you try to read papers on motor drivers you will see a lot of advanced math, but you will find very few papers explaining how simple it actually is, so I will try to do that here.

I borrowed the excellent drawing below that illustrate the 3-phase motors with windiings A,B and C. Actual motors have more windigs. You will find 6, 9, 12 windings and more on actual motors, but the concept is the same. To drive this we need to apply a pulse on A, B and C in sequence.

If you look at the windings you will see that A alone can’t drive anything, so to actually have a coil you will need to apply + on A and – on B or C. This leads us to the simplest of the algorithms where you just apply pulses in sequences over and over again.

  1. A+ B- (C is off)
  2. A+ C- (B is off)
  3. B+ C- (A is off)
  4. B+ A- (C is off)
  5. C+ A- (B is off)
  6. C+ B- (A is off)

A simple Trapezoidal will apply the pulse in sufficient length so the motor is garanteed to step one step. But, as you don’t know the current position you might have 5 steps before your motor starts. As we drive blindfolded we increase speed by making the steps faster. To drive the other direction we just reverse the sequence.

Trapezoldal is excellent to drive a motor very slowly and it is easy to code a working example. As we in this example drive without any sensors whatsoever we just have to assume that the motor follow our directions. This can be a bit tricky as we will not detect if the motor stalls and as the sequence goes wrong we just add the the problem. To cope with this we can add sensors.

BEMF basically measure the voltage on the phase we don’t use as this can tell us the actual position.

Phase Current is the current in/out of each coil that can be used to compute the rotor position. The challenge with this is that it needs a bit of speed before the currents become notifyable + it can be very sensitive for noise situations.

Hall sensors are magnetic delectors that will create a sinus as the motor rotates. This can be measured and used to compute rotor position.

Encoders can be put on shafts to accurate measure position.

Lawn Mover – Motor Algorithm

Running a BLDC (3-phase motor) you need to insert and tune some parameters with regards to size of motor, number of winding, how much current do you output etc. But, more important is the algorithm and technique you chose matching the job at hand. You have three different algorithms (FOC, Sinusoidal, Trapezoidal) and a variety of input sensors techiques (BEMF, Hall, Current and position encoders).

FOC (Field Oriented Vector) is excellent for running fast motors, but the algorithm requires heavy math and current sensors that don’t work that well on slow speeds giving me a problem on the lawn mower wheels. Classic Trapezoidal or Sinusoidal combined with Hall sensors do actually have an advantage at very low speeds. Both are also table driven, meaning we can act more or less as a stepper motor. The accuracy of Hall sensors are not even close to that of a stepper, but we have a belt that introduce a gear ratio that in effect will increase the accuracy.

Sinusoidal can be calculated, but a neat trick is to pre-calculate x number of vectors in a table.

Vector driven means we use current, hall, bemf or a position encoder to detect rotator position and calculate an output vector that is 90 degrees – this is where FOC is good as it is more efficient than the other algorithms assuming it has accurate current sensors. All methods can be used without any sensor input – running a BLDC with Trapezoidal with no sensors is very easy as you just rotate the field based on timing outputting one PWM combination at the time. It work decently well assuming the motor follow your output, but as you are blind for the actual position you get a glitch at start or if the motor is stuck. It is also very difficult to get up in higher speeds, but this work excellent at low speeds.

I have current sensors on the drivers, but as I will be driving slow I expect there to be more noise than input on the wheel drivers. Hall Sensors are far more reliable as they work even at stand-still.

In my case I will just use Trapezoidal on the wheels for now since it is dead easy to code up and fits well with what I need to do. Trapezoidal combined with Hall sensors should work just fine. Having three Hall sensors I will get an encoder that givers me 0 to 7 as input – 8 positions. These will give me the ca position of the rotor and should be sufficient to index the next step in a Trapezoidal algorithm that easily can be adjusted to this.

Changing subject to the grass cutter I basically need a different approach as I in this case is interested in running at 80% possible speed (80% speed is ca 50% efficiency) on a 1,5KW motor. In this case I don’t have hall sensors, but as I run faster I should have working current sensors.

One challenge is however that as I start the cutter I don’t know the position, so I do a trick and start running the motor Trapezoidal until I get readings and from there I run FOC (or sinusoidal). By doing this I take advantage of the fact that slow Trapezoidal will more or less force the motor from stand-still up in a minimum speed where I can start accelerating based on phase current readings.

At this point I am only interested in spinning the motors – I will implement more optimized algorithm’s later. My decition is basically not to use time on this at precent because motor algorithms and optimization can be very time consuming.

BasicPI Firmware Stack – Abstraction Layer

This is the block diagram of AL (Abstraction Layer) modules I drew som time ago. I need to review this as the list is far longer, but you get the idea. I actually started on the AL a year ago and managed to destroy my work due to a bug in STM32CubeIDE at the time, so I need to start from scratch more or less – yes I do feel the pain! But, I have myself to blame for bad backup procedures.

alOS Overview

alOS embed a RTOS (Real Time Operating System) so that the rest of the code can be independent of what OS we use. The terminology Thread and Task is used to distinguish between actual Threads that need a stack and Tasks running within a thread or main.

alOS provides four bulks of functionality that is important in any system. These are static functions so they can be called anywhere in code and guarantee portability of code. Their actual implementation is different from OS to OS.

Embedded will typically use FreeRTOS (or similar) to create a threading OS, while we use a linear scheduler for tasks. Timers are a combination of HS and SW timers.

Windows will use WinAPI for threads and the same linear scheduler for Tasks and Timers.

Static Member Description
alOS::sleep() Sleep in ms.
alOS::millis() Get time in ms.
alOS::micros() Get time in ys.
alOS::FIFOCreate()

Create a one way byte or message FIFO. The array used must be created before calling this function. typically a uint8_t array should be declared and used as FIFO buffer.

alOS::FIFOSend()

Send bytes or a message. Will also signal the Receive Task (if any) to execute. Will either insert all bytes in the fifo or none. The caller must handle full fifo signals (returning false).

alOS::FIFOReceive()

Receive bytes or a message. Can be used polling in which case it will return 0 if no bytes/messages was found.

alOS::FIFOReceiveTask() Set Task to receive a signal for each call to Send.
alOS::AddThread()

Add a thread. A thread execute in parallel on a timer interrupt and need a separate stack.

alOS::StartThread() Start thread. This enables the thread to be called.
alOS::StopThread() Stop a thread. This stops the thread from executing.
alOS::SignalThread() This signals the thread to execute once.
alOS::AddTask()

Add a function callback that can be executed on time or signals.  A Task need to execute and return so the next task can execute.

alOS::StartTask() Start a task.
alOS::StopTask(); Stop a Task.
alOS::SignalTask(); Signal a task to execute.
alOS::AddTimer()

Start a timer. alOS will run itself as a task checking timers and signals ca 1000 times per second.

alOS::StartTimer() Start a timer that will call a task in n ms. This is excellent for timeout style functionality.
alOS::StartLongTimer() Start a long timer lasting more than a day.
alOS::StopTimer() Stop a timer.

FIFO, Timer, Task and Thread reference numbers are unique.

Time

All systems will as a minimum have an elapsed timer counting uS from MCU start. The accuracy of this depends on crystals used and what source is used to maintain the clocks.

alOS guarantee a set of functions related to elapsed timers with uS accuracy. See alRTC for Real Time Clock options.

Threads and Tasks

alOS uses Thread and Task as described here.

A thread need a separate stack and is executed on a system interrupt. It needs to run in a loop and can use techniques like delay() since this allows the OS to execute another thread. Basically threads execute in “parallel” with the RTOS using a time interrupt to switch content usually 1000 times a sec.

A Task is a single function that must do its job and exit before the scheduler can start the next Task. Tasks are lists of functions that are called on timers or signals within a thread. The difference is that they run in a loop executing in sequence and must be written different from a thread. Tasks are however far more scalable than Threads since you only use a single stack. Usually you will have multiple Tasks running in a Thread.

Timers

alOS support 3 types of timers:

  • Hardware timers supported by the MCU.
  • RTOS Timers supported by FreeRTOS.
  • SW Task Timers supported by the linear scheduler.

Using RTOS timers are not recommended, but FreeRTOS (as an example) have their own proprietary timers that can be used if needed.

Hardware timers are  subject to the MCU involved, but STM32F405RG (as an example) have 14 hardware timers. Keep in mind that these are called on actual HW interrupts, so they need an ISR type of function.

Task Timers are basically tasks called on time intervals. A normal task will execute once per ms or each 10th ms depending on what you set, but a timer will execute once the timeout event is raised. Task timers can also exist in much higher numbers and it is not any real difference between a task and a task timer. A timer task can be signalled etc.

Hardware timers should however be used for things that require exact timing. Servo pulse control is an example. A SW timer will have some variance in accuracy causing the pulse to vary from second to second. On a servo this will be observed as the servo making small, unexpected moves. A hardware timer is more exact and capable of giving the same pulse from second to second making the servo stable – this is just one example. But, keep in mind that a HW timer is far more expensive to use than a SW timer, so it is not recommended to use a HW timer to blink a led etc.

Task signals

alOS support a scheme with signal counters, meaning that a task is executed once for each signal you send. This was designed with message queues in mind there you need to process once per message received. Since the timer function will prevent other tasks in the same thread it is healthy to process in bulks – hence the signal counter scheme.

Queues

The main queuing mechanism in alOS is easyIPC, meaning you can create a queue between local tasks, threads or to a different device.

3-Phase Motor Driver w/Hall Sensors – 60V/50A

Many of you have seen this before – it’s my 60V/50A 3-Phase Motor Driver “Thunderstick”. It was a messy first assembly with greece coming through PCB holes, but I am all in all very happy with this design and will be using three of these controllers on the lawn mower. These are quite advanced drivers and similar to the Vedder (VESC) design so we can borrow that code – except that I will be using the Hall Sensors, so I need to verify if these works.

  1. RS485 Interface. I am seriously considering replacing that with a 2nd CAN interface.
  2. Terminator for RS485.
  3. CAN HS interface.
  4. Terminator jumper for CAN.
  5. STM32F405RG
  6. IO port
  7. IO port
  8. SWD. This is compatible with my other SWD ports, but it is a weird design that I will not use again.
  9. Power lane – designed so I can add a wire to take more current In.
  10. MOSFET’s.
  11. Ground Power In.
  12. Current Shunts. This only have 2 current sensors.
  13. Mounting holes.
  14. Ground power lane.
  15. Temperature sensors.
  16. +60V Power In.
  17. DRV8301 – 3-Phase driver.
  18. PSU + Buick Converter. DRV8301 contains a Buick Converter that gives 5V and we use SPX3819 to deliver 3.3V.
  19. Crystal.
  20. Hall Sensors w/5V Output.

This show my drone motor that is perfect for the grass cutter.

This shows the larger 3KW Scooter motor with hall sensors. The picture says 190KV, but I have 2 x 280KV. Will be running them at either 18V or 36V so I can use standard – off the shelf battery packages for DIY tools. These should fit perfectly with the wheel frames I have ordered.

I will need to make a revision of this driver and port it to KiKad in the process. At this point I also need to consider 4 or even 6 layers + I need to consider galvanic isolation as I add 3 motor controllers, several sensors and a main controller into a network.

3D Position System

A module like ZED-F9P cost 148.- EUR and cover all these with an accuracy of 10 cm.

The cost of thus module is currently a limitations, but cost will come down. I am more interested in the fact that it announce 10cm accuracy which open up a lot of usability options. The classic 2.5 meters are ok for many applications, but not for a lawn mover.

With two of these units you can also detect the direction of the unit. But, I am planning to add two or three “3D sensors” on my lawn mower. Satellite position is only one option here as I can add Ultra sound /LIDAR to detect surroundings, 9 DOF to detect acceleration, gyroscope and compass signals as well as temperature, humidity, pressure etc.

The last trick is to fix reference signals on house corners using ultrasound, light or rf signals. The idea is that the robot will detect these and be able to detect difference between the signals. I need to dig a bit into this, but it should be doable.

I have so far focused on using Raspberry PI Hat format on many of my modules and I believe this still is optional for a 3D module since it might be advantageous to actually add a RPI with camera and more advanced position algorithms.

3D sensors like Acceleration, gyro and compass will help tracking relative movement once they have a reference position. This is why a 2m accuracy on GPS still can be workable. To compensate for errors I can add multiple units + I plan to test multiple cameras to see if I can reference IR light positions. Cameras also have the option that we can teach the robot to actually see and recognize it’s surroundings – that said the later is complicated and require a bit of work.

I think position accuracy looks doable, but it will be some work. I also think multiple systems is a must together with the capability to detect/reject errors.

Building my own Robotic Lawn Mover

I recently studied a robotic lawn mover and realized that I can easily build one of those myself.

To get started I need two wheels with separate motors and the picture above is the driving mechanism for an electric scateboard. The wheels are 200mm in diameter and entire construction is ca 400mm wide. It is perfect as base for a lawn mover. It will cost ca 300 USD. The motors on this picture is 350W 3-phase and I would prefer stepper motors or motors with hall sensors in this case, so I will try to buy components separate + I am not fuzzed about that scateboard mounting in the middle.

To make this work I can either add a 3rd supporting wheel or simply attempt a balancing robot design. The later would be cool, but I am not sure how stable it would be with the cutter below, so I will probably use a 3 wheel design. A 4 wheel design would also be cool and add some value as my garden is large and have several levels.

The cutter itself is dead easy – most robots use a rotating plate with 4 loose blades that cut the grass. You can buy those blades in most DIY shops now, and making a round plate fixed to a motor that can spin with some speed is not difficult. I can also make the bottom of the robot flat to avoid grass coming in everywhere – with only the cutter sticking out below.

To box this I can easily find a plastic storage unit of correct size and mount it over the design.

Batteries are no challenge as you can buy tool batteries in most shops. I accidentally bought 36V/4Ah today for 70.- USD – this is for standard DIY tools and I can make a fitting for these. I can just add as many batteries I want or even build my own using 18650 or similar cells. I like the idea of having these replaceable.

The next is the sensors. A standard GPS have ca 2.5meter accuracy so that alone is not sufficient. I can buy separate kits to put down cables, but as I have a large and complicated garden I would like an easier system where I teach the robot where to cut and in what pattern. To do so I need a position system accurate down to ca 100mm. I need to return to this part.

This leaves the charger – I am thinking of making an inductive charger – where one coil is in my house wall and the other on the unit – not exactly rocket science.

As for control system I can use 3 x Thunder sticks and a rack of Hat’s as needed – this is actually a very cool project and parts have been ordered. It would have been cheaper to just buy a ready to go lawn mover, but I would have needed 5 of them to cover my entire garden. This one I hope to be able to program to cover it all.  It will take me a while to get parts, assemnble and program this unit, but it will be fun.

The position system on the lawn mover is critical. I would like an accurate position system so I can teach the robot the limits of my garden.

The first and obvious is to use 2 x GPS modules located in each end of the robot. They will only be 40 cm apart, but that together with the standard set of sensors (3 axis gyro, 3 axis accelerometer, 3 axis magnetometer, temperature, humidity, pressure) will all give me info on position.

The next is to add “eyes” in form of ultrasonic sensors as well as lasers and obviously cameras. I want one of these at each corner mounted of a pan & tilt camera unit so it can do a 180 degree coverage on each corner. Lasers and ultrasonic can detect surrounding terrain, but camera can detect IR leds I put on the house and based on them triangulate it’s own position. This means I need to set up IR senders that send a blink sequence. That blink sequence is reasonable to detect in a picture and by measuring the distance to other signals you should be able to triangulate it’s position. The blink sequence identify the sender and thus it’s reference position.

I can also put up IR senders on places in the garden if I need to + I can use BLE sensors. Needless to say, my robot will use Wifi to connect to a control unit inside the house.

I am just spinning ideas here, so I need to work a bit on the position system.

A normal lawn mover work more or less as an old fasioned “bump & go” car – it runs in one direction until the sensor detect a cable at which point it turn in a rando direction and continue. But, they are able to find their “home”. More expensive units can do more, but to really do this properly you need an accurat position system. If you got a position system then the rest is easy.

Arduino NANO BLE 33 Sense

Arduino recently launched a new version of NANO with a powerfully 32-bit ARM M4 core, 64Mhz speed, 1Mb Flash and 256 Mb SRAM. And this is just the beginning.

It’s MCU is nRF52840 that also contains a BLE Radio and SW stack. It has all the features you expect from a modern M4, but it is more on this board than just the MCU w/BLE.

Located on the Sense version NANO 33 you will find five different components:

  • LSM9DS1 with  3D accelerometer, 3D gyroscope and 3D magnetometer.
  • MP34DT05-A with microphone.
  • APDS-9960 with Digital Proximity, Ambient light, RGB and Gesture Sensor.
  • LPS22HB with Altimeter and Barometer.
  • HTS221 with Humidity and Temperature.

The board is a bit expensive – 27.- EUR + P&P + another 35.- EUR in customs to Norway. I probably paid ca 80.- EUR for my sample, so I will not be buying more.