1. Introduction
There is no shortage of literature online discussing FOC (Field Oriented Control). However, resources that explain how to build a simulation model from scratch using MATLAB are surprisingly rare. Therefore, I plan to write a short series documenting the entire process—partly as a summary of my own learning, and partly to help others who want to get started. If you notice any mistakes, please feel free to point them out.
Let’s get straight to the topic.
The core idea of FOC is to simplify traditional three-phase AC motor control into a form similar to DC motor control. Through vector control, FOC decouples the motor’s torque and flux, allowing for more precise speed and torque regulation.
In an FOC system, coordinate transformations are applied to convert the stator currents and voltages from the stationary three-phase (abc) coordinate system into the rotating d-q coordinate system. This results in two independently controlled components:
q-axis current → torque-producing current
d-axis current → flux-producing current
With this decoupling, the controller can regulate torque and flux independently through closed-loop control. This greatly improves dynamic response, stability, and flexibility of motor control.
A simple conceptual diagram (Figure 1) is shown below:

Figure 1. Schematic Diagram of the FOC Principle
The model is implemented using MATLAB scripting. A simplified example of the code is given as follows:
% Initialization parameters
theta_vals = linspace(0, 2*pi, 100); % Motor rotor position angle variation (0 to 2π cycle)
iq_ref = 1; % Set torque current
id_ref = 0; % Set magnetic flux current
gif_filename = 'FOC_animation.gif'; % Save GIF file name
% Create new image window
figure;
axis equal;
xlim([-1.5, 1.5]);
ylim([-1.5, 1.5]);
hold on;
grid on;
% Plot the baseline of the α-β coordinate system
plot([-1.5, 1.5], [0, 0], 'k--'); % α axis
plot([0, 0], [-1.5, 1.5], 'k--'); % β axis
text(1.4, 0, 'α', 'FontSize', 12);
text(0, 1.4, 'β', 'FontSize', 12);
% Draw the baseline of the d-q coordinate system
dq_axis_d = plot([0, 0], [0, 0], 'b', 'LineWidth', 1.5); % d axis
dq_axis_q = plot([0, 0], [0, 0], 'r', 'LineWidth', 1.5); % q axis
current_vector = plot([0, 0], [0, 0], 'g', 'LineWidth', 2); % Current vector
% Plot the armature magnetic field vector
magnetic_field_vector = plot([0, 0], [0, 0], 'm', 'LineWidth', 2); % Magnetic field vector
% Dynamic labels used to tag the id and iq on the d-q axis
id_label = text(0, 0, 'i_d', 'Color', 'b', 'FontSize', 12, 'HorizontalAlignment', 'center');
iq_label = text(0, 0, 'i_q', 'Color', 'r', 'FontSize', 12, 'HorizontalAlignment', 'center');
% Animation loop
for k = 1:3 % Loop the animation 3 times to increase the length of the GIF
for theta = theta_vals
% Current vector in the α-β coordinate system
ia = iq_ref * cos(theta) - id_ref * sin(theta); % α-axis components
ib = iq_ref * sin(theta) + id_ref * cos(theta); % β-axis components
% Update the orientation of the d-q coordinate system
set(dq_axis_d, 'XData', [0, cos(theta)], 'YData', [0, sin(theta)]);
set(dq_axis_q, 'XData', [0, -sin(theta)], 'YData', [0, cos(theta)]);
% Update the position of the current vector
set(current_vector, 'XData', [0, ia], 'YData', [0, ib]);
% Update the armature magnetic field vector so that it always leads the d-axis by 90°, i.e., is aligned with the q-axis direction
set(magnetic_field_vector, 'XData', [0, -sin(theta)], 'YData', [0, cos(theta)]);
% Update the label's position and rotation to align it with the d-q axis
set(id_label, 'Position', [0.8*cos(theta), 0.8*sin(theta)], 'Rotation', rad2deg(theta));
set(iq_label, 'Position', [-0.8*sin(theta), 0.8*cos(theta)], 'Rotation', rad2deg(theta) + 90);
% Get the current frame
frame = getframe(gcf);
img = frame2im(frame);
[imind, cm] = rgb2ind(img, 256);
% Write frames to a GIF file
if theta == theta_vals(1) && k == 1
imwrite(imind, cm, gif_filename, 'gif', 'Loopcount', inf, 'DelayTime', 0.05);
else
imwrite(imind, cm, gif_filename, 'gif', 'WriteMode', 'append', 'DelayTime', 0.05);
end
end
end
% Persistent display after animation ends
hold off;2. Basic Principles of FOC
As shown in Figure 2, this is a classic block diagram of the Field-Oriented Control (FOC) scheme:

Figure 2 Block diagram of FOC control principle
For the digital control section, the main components include:
A current controller composed of two proportional - integral (PI) controllers
An optional outer-loop speed controller and current-reference generator
Clarke and Park transformations, as well as their inverse transforms, used for converting between the stationary and rotating synchronous reference frames
A Space Vector Modulation (SVM) algorithm for converting the

commands into PWM signals applied to the stator windings
Protection functions and auxiliary functions, including start-up and shutdown logic
An optional observer used to estimate the rotor electrical angle when a sensorless control strategy is required
During my learning process, one question that repeatedly confused me was why PI controllers are used instead of PID controllers. Based on my own experiments and the related literature, a reasonable explanation is as follows:
The motor’s speed response is relatively slow. If the derivative (D) term were used, it would cause excessive fluctuations in the PWM duty cycle. In addition, motor control systems are often subject to high-frequency noise. The D term is highly sensitive to such noise and tends to amplify these small high-frequency disturbances. Together, these effects would degrade the control performance and may even lead to undesirable events such as overcurrent trips. Therefore, in most motor-control applications, PI controllers—not PID controllers—are used.
Among the many modules mentioned above, beginners are often confused by the following points:
The origin of the Clarke and Park transformations and their inverse transforms—particularly the seemingly odd multiplication by 2/3 in the Clarke transform.
The algorithmic principles of Space Vector Modulation (SVM), which is arguably the most confusing part for newcomers.
How the PI parameters of the current loop and speed loop should be tuned—whether there exists a rigorous theoretical derivation or if tuning must rely on engineering trial-and-error.
Let's analyze the reasons one by one:
3. Derivation of Clarke Transform and its Inverse Transform
For ease of subsequent discussion, we establish a rectangular coordinate system with the α-axis as the horizontal axis and the β-axis as the vertical axis, as shown in Figure 3. A, B, and C correspond to the three-phase windings of the brushless motor, and phase A coincides with the α-axis. The angle between the axis of the N pole of the rotor magnet and the positive direction of the α-axis is Θ. Counterclockwise rotation of the rotor is considered forward rotation.

Figure 3 Clarke transformed coordinate system
The above image was implemented using MATLAB script code, as follows:
% Initialize the canvas
figure;
hold on;
axis equal;
xlim([-2, 2]);
ylim([-2, 2]);
grid on;
% Plot the alpha and beta axes
quiver(0, 0, 1.5, 0, 'k', 'LineWidth', 1, 'MaxHeadSize', 0.5); % alpha axis
text(1.6, 0, 'alpha', 'FontSize', 12);
quiver(0, 0, 0, 1.5, 'k', 'LineWidth', 1, 'MaxHeadSize', 0.5); % beta axis
text(0, 1.6, 'beta', 'FontSize', 12);
% Draw the three-phase current vectors of A, B, and C
quiver(0, 0, 1, 0, 'r', 'LineWidth', 1, 'MaxHeadSize', 0.5); % Phase A
text(1.1, 0, 'A', 'FontSize', 12);
quiver(0, 0, -0.5, sqrt(3)/2, 'b', 'LineWidth', 1, 'MaxHeadSize', 0.5); % Phase B
text(-0.6, sqrt(3)/2, 'B', 'FontSize', 12);
quiver(0, 0, -0.5, -sqrt(3)/2, 'g', 'LineWidth', 1, 'MaxHeadSize', 0.5); % Phase C
text(-0.6, -sqrt(3)/2, 'C', 'FontSize', 12);
% Plot the rotor position and angle
quiver(0, 0, cos(pi/6), sin(pi/6), '--', 'LineWidth', 1, 'Color', 'r'); % Rotor direction
quiver(0, 0, cos(7*pi/6), sin(7*pi/6), '--', 'LineWidth', 1, 'Color', 'b'); % Rotor direction
text(cos(pi/6), sin(pi/6), 'N', 'FontSize', 12);
text(-cos(pi/6), -sin(pi/6), 'S', 'FontSize', 12);
text(0, 0, 'O', 'FontSize', 12, 'VerticalAlignment', 'bottom');
text(cos(pi/6)/2, sin(pi/6)/2, 'rotor', 'FontSize', 10, 'HorizontalAlignment', 'right');
% Drawing angle θ
theta = linspace(0, pi/6, 50);
plot(cos(theta), sin(theta), 'k:');
text(cos(pi/12), sin(pi/12), '\theta', 'FontSize', 12);
title('Clarke transform diagram');
hold off;Essentially, the Clarke transform is a mathematical tool used to convert three-phase quantities (such as current or voltage) into two orthogonal stationary reference coordinate systems, often called the α-β coordinate system. This transformation simplifies the analysis and control of AC motors by reducing a three-phase system to a two-phase equivalent system. In our practical digital control systems, we can digitize the two-phase or three-phase currents

amplified by the current sampling circuit using an ADC peripheral. In a symmetrical three-phase circuit, according to Kirchhoff's current theorem,

so knowing any two phase currents allows us to calculate the current in the third phase. There are two types of Clarke transforms: equal-amplitude transforms (with a transform factor of 2/3) and equal-power transforms (with a transform factor of
FOC typically adopts the equal-amplitude version. As the name suggests, the amplitudes of the relevant variables are equal before and after the equal-amplitude transform, which is beneficial for microcontrollers to perform integer calculations.
There are two main points worth considering here. The first is how to derive the transformation matrices of the Clarke transform and its inverse transform. We can analyze this using Figure 3, and based on fundamental trigonometric functions, we know that:

After simplification, we get:

Based on the above formula, we can know that the transformation matrix of the Clarke transform is:

Of course, the Clarke transform alone is far from sufficient; we also need the inverse transform (however, we may not need the inverse Clarke transform when performing vector control). As shown in Figure 3, since α and A-axis are coaxial, we often need to set

when performing the inverse transform. However, if we now have a current with an amplitude of 1A in phase A, for a Y-connected motor, according to Kirchhoff's current theorem, we can easily know

Applying the three-phase current to our Clarke transform matrix, we have:

At this time:

Clearly, this doesn't match the settings we used for the inverse transform, and such calculations would significantly increase the computational load, hindering the microcontroller's integer calculation capabilities. Therefore, we typically multiply by a coefficient 2/3 for tuning when performing the Clarke transform.
Next, let's derive the Clarke inverse transform:

Figure 4. Inverse Clarke Transformation
Therefore, we can obtain the matrix form of the Clarke inverse transform as:

4. Park Transformation and its Inverse Derivation
The fundamental function of the Park transform is to transform the stationary two-phase orthogonal α-β coordinate system into a rotating two-phase orthogonal d-q coordinate system, aligning the rotor's N-pole axis with the positive direction of the d-axis. This keeps the rotating two-phase d-q coordinate system and the rotor magnetic field relatively stationary, allowing the current

to be adjusted separately using a linear PI controller. The angle of the Park transform is derived from the position obtained in the previous position estimation.

Figure 5. Illustration of the Park Transformation
Based on the above figure, trigonometric calculations show that:

Therefore, we obtain the Park transformation matrix as follows:

And its corresponding inverse Park transformation simply requires finding its inverse transformation:

5. Concept of Sectors
Using a typical three-phase half-bridge inverter as an example, as shown in Figure 6:
When Switch 1 is ON and Switch 2 is OFF, the terminal voltage of phase A is equal to the DC bus voltage. This state is denoted as 1.
When Switch 1 is OFF and Switch 2 is ON, the terminal voltage of phase A is pulled to ground, and this state is denoted as 0.
With complementary PWM and dead-time insertion, each motor phase terminal (A, B, C) can only be in state 1 or 0. Therefore, the three phases A, B, and C together can form 2³ = 8 possible switching states.
However:
When all high-side switches are ON and all low-side switches are OFF, no current flows through the motor.
When all high-side switches are OFF and all low-side switches are ON, no current flows through the motor either.
Thus, only six of the eight states produce actual current flow, and these six states are referred to as the active vectors.

Figure 6. Schematic of a Three-Phase Half-Bridge Inverter
As described above, when the upper arm of phase A is open and the upper arms of phases B and C are closed, we record this state as (1 0 0). Its binary representation converted to decimal is 4. Similarly, we have five other basic vectors, represented as shown in Figure 7:

Figure 7. Hexagonal Vector Diagram of SVPWM
The above image was implemented using MATLAB script code, as follows:
% Clear the workspace, close all graphics windows, and clear the command window
clear all;
close all;
clc;
% Define the magnitude of the voltage vector
V_mag = 1;
% Define the angles (in degrees) of the six effective vectors
angles_deg = [0, 60, 120, 180, 240, 300];
% Convert the angle from degrees to radians because MATLAB's trigonometric functions use radians
angles_rad = angles_deg * pi / 180;
% Calculate the x (alpha axis) and y (beta axis) components of the voltage vector
Vx = V_mag * cos(angles_rad); % x component
Vy = V_mag * sin(angles_rad); % y component
% Create a new graphics window for drawing
figure;
hold on; % Preserve the current drawing so that subsequent drawing commands do not overwrite previous content
% Draw the zero vector (origin)
plot(0, 0, 'ko', 'MarkerSize', 8, 'LineWidth', 2); % Draw a black dot at the origin
% Use the quiver function to plot six valid vectors
for i = 1:length(Vx)
quiver(0, 0, Vx(i), Vy(i), 0, 'LineWidth', 2, 'MaxHeadSize', 0.5, 'Color', 'b');
end
% Draw the outline of the hexagon by connecting the endpoints of the vectors
x_tips = [Vx, Vx(1)];
y_tips = [Vy, Vy(1)];
% Use the plot function to draw the outline of a hexagon
plot(x_tips, y_tips, 'k--', 'LineWidth', 1.5);
% Add three-phase current coordinates for phases A, B, and C
text(1.1, 0, 'A', 'FontSize', 12, 'Color', 'g', 'FontWeight', 'bold'); % Phase A
text(-0.55, 0.95, 'B', 'FontSize', 12, 'Color', 'g', 'FontWeight', 'bold'); % Phase B
text(-0.55, -0.95, 'C', 'FontSize', 12, 'Color', 'g', 'FontWeight', 'bold'); % Phase C
% Configure axis properties for better visualization
axis equal;
grid on;
xlabel('Alpha axis');
ylabel('Beta axis');
title('SVPWM hexagonal vector image');
% Define the label order and detailed description
labels = {'V4', 'V6', 'V2', 'V3', 'V1', 'V5'};
details = {'(1, 0, 0)', '(0, 1, 0)', '(0, 0, 1)', '(1, -1, 0)', '(0, 1, -1)', '(1, 0, -1)'};
% Add labels and detailed descriptions to each vector
for i = 1:length(Vx)
% Add text slightly offset from the vector endpoint
text(Vx(i)*1.1, Vy(i)*1.1, [labels{i} ' ' details{i}], 'FontSize', 10, 'Color', 'r');
end
% Draw six sectors from I to VI, in a counter-clockwise direction
sector_labels = {'I', 'II', 'III', 'IV', 'V', 'VI'};
sector_angles = [30, 90, 150, 210, 270, 330]; % The midpoint angle of each sector
for i = 1:length(sector_angles)
% Sector label location (place the label inside the sector)
x_sector = 0.8 * cos(sector_angles(i) * pi / 180);
y_sector = 0.8 * sin(sector_angles(i) * pi / 180);
text(x_sector, y_sector, sector_labels{i}, 'FontSize', 12, 'Color', 'm', 'FontWeight', 'bold');
end
% Draw short straight linesL1:y = sqrt(3) * x and L2:y = -sqrt(3) * x
x_line = linspace(-0.7, 0.7, 100); % x-coordinate range (slightly outside the hexagon)
y_L1 = sqrt(3) * x_line; % L1's y-coordinate
y_L2 = -sqrt(3) * x_line; % L2's y-coordinate
plot(x_line, y_L1, 'r--', 'LineWidth', 1.5); % L1 straight line
plot(x_line, y_L2, 'r--', 'LineWidth', 1.5); % L2 straight line
% Add labels to L1 and L2
text(0.6, sqrt(3) * 0.6, 'L1: y = \surd{3}x', 'FontSize', 10, 'Color', 'r');
text(0.6, -sqrt(3) * 0.6, 'L2: y = -\surd{3}x', 'FontSize', 10, 'Color', 'r');
% Release drawing hold so that subsequent drawing commands will not be added to the current drawing
hold off;6.Sector Determination
As shown in Figure 7, the modulation space is divided into six basic sectors using six fundamental vectors. As mentioned earlier, a key difference between FOC and six-step commutation is that its synthesized magnetic field always leads the rotor by 90°. This avoids torque pulsation and increases efficiency. To generate a magnetic field vector that always leads the rotor by 90°, a precise understanding of the rotor’s position is crucial. Knowing the rotor’s position, we then use these vectors to synthesize the desired vector. As shown in Figure 8, we synthesize the desired vector Vs using V4 and V6, with Vs leading the rotor by 90° to support its rotation.

Figure 8. Illustration of the Synthesized Vector
In summary, to synthesize a magnetic field vector that leads the rotor magnetic field by 90°, we need to know the rotor's position. Looking back at Figure 3, it's easy to conclude that knowing the magnitude of

tells us the rotor's position. (This also explains why the control loop doesn't need the inverse Clarke transform; after the inverse Parke transform, we know Vα and Vβ, thus knowing the rotor's position. We can then synthesize the desired vector to drive the motor's rotation, thus eliminating the need for the inverse Clarke transform). To synthesize the desired vector, the sector containing the rotor's position must be clear, allowing us to synthesize the required vector using the basic vectors within that sector.
Observing Figure 7, the x-axis and L1 and L2 already divide the modulation space into six sectors. Specifically:
If L1>0, L2<0, then the vector to be synthesized is in sector 2;
If L1>0, L2<0, Y>0, then the vector to be synthesized is in sector 3;
If L1>0, L2>0, Y<0, then the vector to be synthesized is in sector 4;
If L1<0, L2>0, then the vector to be synthesized is in sector 5;
If L1<0, L2<0, Y<0, then the vector to be synthesized is in sector 6;
If L1<0, L2<0, Y>0, then the vector to be synthesized is in sector 1.
This method often requires multiple if-else statements for evaluation in a program, which is inefficient. Therefore, we abstract the above conditions and mimic the six-step commutation method, letting

, and N = 4*A+2*B+C. Based on the above analysis, let:
When L1>0, A = 1, otherwise A = 0;
When L2>0, B = 1, otherwise B = 0;
When Y>0, C = 0, otherwise C = 1.
Applying the above definitions to the analysis yields the following table:
N | 1 | 2 | 3 | 4 | 5 | 6 |
Sector | 5 | 1 | 6 | 3 | 4 | 2 |
7. Composite Vector
Our control system is always based on a digital control system. Therefore, we cannot obtain a continuous value for the rotor position, but only a discrete one. In existing control systems, this value is often updated once per carrier cycle. Therefore, the composite vector should also be updated once per carrier cycle. If one carrier cycle is T8, from Figure 8 and the relevant mathematical knowledge, we know that:

Here, T4 and T6 are the durations of the basic vector's action. Since the sum of T4 and T6 is often not equal to T8, the remaining action time is usually assigned to V0 and V7, thus the above equation becomes:

In other words, within a carrier cycle, we can synthesize the desired vector by determining the duration of action of the two fundamental vectors. This raises two questions: first, how to determine the duration of action of the fundamental vectors? Second, when should we switch the fundamental vectors? Regarding the second question, there are often two methods for wave modulation: the well-known five-segment and seven-segment modulations. Their advantages and disadvantages are widely available online, and I have summarized them as follows:
Features | Five-segment chopper | Seven-segment chopper |
Switching cycles | Fewer | More |
Output waveform quality | Average | Higher |
Switching losses | Lower | Higher |
Applications | Applications sensitive to switching losses | Applications requiring high output waveform quality |
Let's analyze why: For example, if we want to synthesize vector Vs in the first sector, we can use two waveform generation methods as follows: Seven-segment: 0(0 0 0 ) -> 4(1 0 0) -> 6(1 1 0) -> 7(1 1 1) -> 6(1 1 0) -> 4(1 0 0) -> 0(0 0 0); Five-segment: 4(1 0 0) -> 6(1 1 0) -> 7(1 1 1) -> 6(1 1 0) -> 4(1 0 0). The above analysis shows that the five-segment method involves fewer switching operations, which helps reduce the inverter's switching losses, and therefore is more widely used in applications with high efficiency requirements. The seven-segment chopper method switches more times within the PWM cycle, enabling more precise synthesis of the target vector, resulting in higher output waveform quality and fewer harmonic components.
Next, we analyze the duration of action of each fundamental vector in the six sectors:
The first sector is shown in Figure 9:

Figure 9. Vector Dwell Time Analysis for Sector 1
According to Figure 9, and using the knowledge of trigonometric functions, we have:

therefore,

For the remaining sectors, the analysis follows the same approach, as shown in Figures 10, 11, and 12.

Figure 10. Vector Dwell Time Analysis for Sector 2

Figure 11. Vector Dwell Time Analysis for Sectors 3 to 5

Figure 12. Vector Dwell Time Analysis for Sector 6
With the above analysis, we have essentially completed the theoretical framework of FOC control.
