Modular Control System

One of the key features on the boards are that they assemble to a larger control system by just clicking them together. I have around 12 different boards by now and will start adding new boards and variants as I convert all my electronics from Target 3001 to KiCAD. These two board have 5V, Device Bus and CAN on the backbone. Device Bus is designed for higher speed data transfer and communication with Raspberry PI, while CAN is the primary control system.

My preference for CAN is due to its arbitration allowing a no-config device to device communication that in theory can stretch over several kilometers. My CAN loops are high speed and very short. CAN is also excellent for automotive vehicles like the one I am working on now.

One of the returning discussion I have is that these boards was designed as Raspberry PI Hat’s and use that form factor and pin layout on the backbone bus. The fact is that I so far had little usage for Raspberry PI. This is more a statement of how capable these MCU’s are + what they can achieve together.

Also, looking at the new STM32G.. series as well as STM32H.. series I might upgrade MCU as we move forwards.  These MCU’s support CAN FD with much higher data transfer than CAN HS. STH32F405RG was chosen because of it’s high capability on IO, but STM32H753 is much better thought the smallest MCU is 100 pin. The G4 series is interesting because ST have started to improve IO capacity. The MCU’s are slightly faster version of the Cotex M4 and contains up to 3 CAN FD channels.

 

Automotive Control System

The wheels above are cool, specially since they can carry a lot of weight. But, the motors are 2 x 2.5KW and the belt driven design is very open making it easy to be contaminated. The powerfully motors are more designed for speed than for driving slowly, but I will optimize that later. Position system is straight forward, but expensive. I need 2 x ZED-F9P modules with 1cm satelite accuracy – the trouble is that these cost up to 200ich USD each. I have modules with lower accuracy that I will start working with as I also want to work on a non-satelite based position systems.

The diagram above is a schetch of the control system. I will be using an XportHub + Raspberry PI + LoRa board for main controller. It will be a modified version of XPortHub with 2 Galvanic issolated CANbus networks. The actuator network will use galvanic issolated drivers based on ISO1042 or similar – High Speed CAN. The sensor network will be isolated at the main controller, but not at the sensors. I have not illustraded power here, but actuators will have 18V supply while sensors have 5V. Three motor controllers as actuators – one for each wheel and one for the cutter. I will use a high speed propeller engine for the later.

So far this has just been a straight forward design based on available components. I just want to get the drive mechanism up running so I can start working on the position system. Building a lawn mower is just the start here – I have to admit that once I started this I was thinking “just for fun”, but the interest for what I do here + the wealth of ideas of what we can use this for is overwealming.

I still have to work a bit on my motor controllers before I start on the 3D sensor – the first objective is to just run this as a ROV outside so I can start on position reference as I test out infrastructure components.

Using old solder paste

I just started on 2 new motor drivers for my new project and soldered DRV8301 which is a dense 58 pin crab using an old solder paste. Soldering these crabs are easy and done in minutes, but I struggled with this paste and ended up with a driver that had pins not connected causing 2 days of messing around testing and looking for bugs. So, in short – lesson learned – soldering these boards are actually very easy with a bit of training and fresh solder paste. A new cylinder cost ca 3.- USD, so what I will do is to systematically replace my solder paste to make sure I work with fresh one from now on. The difference as I changed paste was extremely noticable to be gentle!

As for my new boards – I have to remove the driver chips and re-solder them with fresh paste. 2 days wasted in testing 🙁

XPortHub – Galvanic Isolated CAN Ports

The plan is to use two separate CAN Networks on my Lawn Mover – one network for motors, and one network for everything else – both using CAN HS at 1Mbps. To do this I consider upgrading XportHub. This picture show XPortHub2 w/2 CAN Ports. ISO1042 is an isolated CAN tranceiver in a SOIC8 a bit wider than the SOP8 used above and it should be realistic to upgrade XPortHub1 or XPortHub2. As I have no experience with ISO1042 I will have to experiment a bit.

My previous experiment with “all in one” galvanic isolated tranceivers is with ADM3053 from Analogue Devices – the buck converter on this used 180Mhz and needed an extra coil to issolate frequencies on + voltage. But, it also created a bit of heat and occupied space. The buck converter on ISO1042Q1 (Aytomotive version) runs on lower frequency, so I hope to see less heat and avoid that coil. But, I will still need more space than the SOP8 packages above.

Yet another option is to ditch the serial ports and Ethernet and add a LoRa connection – that will be a new board, but it will be a dedicated automotive controller and save me from adding a separate LoRa Hat LoRa has a much longer range than WiFI which will be attractive as I might have challenges with Wifi coverage everythere.

A LoRa module cost around 8ich USD and each ISO1042 cost 4-5 USD + I would like the TF card back if I do a new board. This will not be a cheap board.

Just to remind everyone – doing this without galvanic isolation will work just fine until you have an issue with one of the motor controllers and find your entire system dead. I will be reusing this infrastructure on heavy drones, so it is worth getting critical bits isolated.

32 x IO/Servo Hat – rev 1.5

This is a new version of my 32 x IO/Servo Hat. I have always been a bit unhappy with pin-header connectors since they can be disconnected easily, so I decided to try how a surface mounted 2-pin JST worked out and voila – 32 2 pin connectors with signal and ground. This means I drop the +Voltage signal and expect that to be fed from somwhere else – this is actually good as it makes it easy to mix 5V, 12V and 24V servo solutions + these connectors are good as they will not drop out due to vibration.

This is one of my simplest, but yet most usable Hat’s and this connector solution makes a great addition and enable it to be used as part of an automobile stack without being worried about connectors falling off.

New Motor Driver – DRV8353RS

I am currently using DRV8301 from TI and is very happy with that one, but I want to add a 3rd Current Sensor so I can detect sensor errors and my eyes fell on DRV8353 and DRV8323 series. DRV8323 is 60V, while DRV8353 is 100V. I don’t mind the increase in voltage. The numbering is a bit confusing at first, but I concluded on the following:

DRV8053S or DRV8353H have Current amplifiers, but lack Buck Converter.

DRV8053R have both current and buck.

DRV8353RH have Hardware Interface

DRV8353RS have SPI Interface

I will be using DRV8353RS (I think). The SPI opens for a bit more config options and use less pins that the Hardware version. Below is an anotated block diagram of the new driver.

23 external components (MOSFET’s excluded) and a smaller 7x7mm package will be interesting. I need to study this and find reference schematics before I start. Cost of this is ca 4ich USD.

This might be on Revision 1.4 of MC3P60V50A – which actually become MC3P100V… to be accurate.

 

ISO1042 Galvanic CANbus

I recently discovered this chip (ISO1042) and wanted to share a circuit I found. This circuit is excellent, but it has two flaws:

Firstly you need a small isolation coil 10-20uH between ISO 1042 and VCC1. This act as a filter so that higher frequencies used by the buck converter in ISO1042 do not disturb the MCU. I need to check frequencies, but my previous experience indicate that a 10uH coil will do the job.

Secondly I have marked a red cross over D1 – This is a common way of protecting non-isolated CAN, but I have always been against it. Usually the tranceiver circuits can handle very hard pulses and are designed to deal with CANH and CANL beeing floating, differential signals. If you put D1 on you will be forced to include GND2 as the 3rd wire, but without D1 you only need CANH/CANL. In this case we use a galvanic isolated circuit that is far more capable that D1 and can handle pulses in KV area.

As for K1 (TLP175A) this is a nice way of making the terminator switchable through software. I have never bothered much about these, but it can be nice if you connect several devices in a network and need to select whom is switched on/off.

ISO1042 speaks for itself – it is 5 x 7mm, so this is a SOIC8, but notice that it has no external components except two standard filter caps on voltage. I am curious to test this and see how much heat it generate. I previously used chips from a different vendor and was a bit surpriced by the heat and noise from the buck converter.

Another feature on ISO1042 is that it is CAN FD (5Mbps) capable.

Motor Algorithm – Part 4 – PWM Output

My previous entry show how to pre-calculate a Sinus table so we avoid doing this full speed because the next step is to convert this into a PWM pulse. A PWM pulse is measured in time – length – so we need to know the max length of a pulse. That is decided by the frequency we use. 4000Hz is really a minimum, thought if you drive a slow motor you can get away with a slower algorithm. This is the frequency of the timer interrupt we will use to re-calculate PWM output, so a pulse of 1 is 1/4000 in length.

The second is that we need to apply torque where “1” will be 100% torque, 0.5 will be 50% torque etc.

The third element is to scale to a length number matching the timer we use. To sum this up I pre-calculate a pwmFactor as follows:

pwmFactor = 1/4000*timerScale*torqueFactor;

I only need to update this if I change torque. This now gives me a factor I can multiply with the vector to calculate the length of the PWM pulse for each phase.

aPWM = vector[x].a * pwmFactor;
bPWM = vector[x].b * pwmFactor;
cPWM = vector[x].c * pwmFactor;

The final step is to output this pulse by switching pins on/off. Assuming I did not have a Hardware timer I might need to create a much faster interrupt that only switched pins on/off, but luckily the motor timers on MCU’s like STM32F405 will do this for us – I will be using Timer 1 that is a specialized motor timer that will do a lot of the work that otherwise would be hard to achieve – hard, not impossible. We run motors using slow AVR’s and PIC’s, but using a modern MCU with a motor timer is just so much easier.

As I am driving blind-folded with no knowledge of my current rotor position I just do exactly the same as in my Trapezoidal example and iterate through the table. The speed I iterate  (change sinus entry) is now the motor speed. Assuming you use 4000 Hertz and have a motor with only 3 coils you can routhly acieve 10 rotations per sec without skipping vector entryies. This is ca 600 RPM, so if you use a faster motor you really should increase freuency, but increased frequency means more CPU used for math and more loss in the MOSFET’s. You also have an upper limit of what driver, MOSFET and motor will support. A common range is 4000 to 20,000 Hertz.

At this point I don’t know the current rotor position so I just use the vector table knowing that as I iterate the motor will be moving most efficient on a 90 degree vector as illustrated below:

If I had known the current rotor position I would have looked up the vector table 90 degrees before or after the current rotor position. But, to do so I need to use BEMF, Hall or current sensors to calculate my position. In theory we could calculate this every time we create a PWM output, but we face two challenges (1) CPU hungry math and (2) inaccurate input.

Phase currents can in theory be measured calculated for every PWM output, but you usually have so much noise that you end up filtering – meaning you will not have ADC measured currents as often as you output PWM. Hall have a lower accuracy. So the real algorithm usually use a trick where we use sensors to correct rotor position.

By adding rotor vector calculations I basically are doing FOC (Field Oriented Control). I will be using both Current- and Hall sensors. My cutter motor have no Hall and it will be driving fast so this is excellent for current sensors. The wheel drivers do however have Hall sensors and will be driving slow – I do not expect any valid input from current sensors on the wheels, but we will see – so I will be driving based on Hall only.

One advice – before starting putting on PWM on a motor you need to activate temperature- and current- damage thresholds. I have four temperature sensors and two of them are located in between MOSFET’s. If the temperature raise fast or we ever achieve a selected threshold we simply cut the motor to avoid that electronics get damaged.

I also need to do this on phase currents – the MOSFET I use have a maximum of 100A, so if we ever reach – lets say 75A – we cut the motor. Maximum pulse is 400A. This is MOSFET specific data and I have used a wide SOP8 with padding underneath – a package used by several MOSFET’s so I can adapt MOSFET to application – I have 60V MOSFET’s, but I am using IRFP5300 since I had a bunch of them – this have a RDS=1.1mOhm, 30V, 100A etc – excellent for my current applications since I will be using 18V batteries from a local DIY shop.

Two numbers on a MOSFET are very important – (1) RDS that needs to be as low as possible and (2) switching time that needs to be as fast (short) as possible. As we switch we move into an area where the MOSFET will consume more heat – a low frequence is good as we switch more seldom, but a low frequency is no good for faster motors – this is a tradeoff you need to make knowing that higher freuencies will increasingly heat up your MOSFETs. For a SOP8 style package I assume max 1W dissipation without heatsink – meaning that if we burn more than 1W on the MOSFET temperature starts to raise fast. This imporves with heatsink that I have on each driver – but we are now into the discussions about my boards limitations – my target was 50A, but at some point I will destroy boards to learn these numbers.

I just tested PWM outputs on my board and is happy to see they work, so I only need to get temperature- and current- sensors working and I will be spinning the larger motors.

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.