DC motor 2 speed control - owntech-foundation/Tutorials GitHub Wiki
Objectives
The goal of this tutorial is get the data from an encoder while running an average current PID. The encoder data will not be used to control the current in this example, but it can be used as a basis for creating DC motor control.
For this tutorial we will build on your knowledge of the previous tutorials while adding the encoder related elements. We will start from the Buck 3 Tutorial project.
Required hardware
- O2
v_1_1_2
- STLinkV3
- PC (windows or linux)
- power supply (40 V, 2 A)
- An encoder
Required software
- serialPlot
Create the project
- We will create the project by copying the communication project in a new branch, that will be called buck. In the terminal,
git status
git add --all
git commit -m "work done on the dc_motor_1"
git branch dc_motor_2
git checkout dc_motor_2
- On the bottom menu, check that you are now in the encoder branch.
![step1]
- Define the variables
In src/main.cpp
, add the following code to define the variables that will be used to determine if the converter is in buck mode.
//--------------USER VARIABLES DECLARATIONS----------------------
static uint32_t counter = 0; //counter variable
static float32_t duty_cycle = 0.5; //duty cycle (comm task)
static float32_t duty_cycle_step = 0.05; //duty cycle step (comm task)
static bool pwm_enable = false; //state of the PWM (ctrl task)
static float32_t V1_low_value; //store value of V1_low (app task)
static float32_t V2_low_value; //store value of V2_low (app task)
static float32_t Vhigh_value; //store value of Vhigh (app task)
static float32_t i1_low_value; //store value of i1_low (app task)
static float32_t i2_low_value; //store value of i2_low (app task)
static float32_t ihigh_value; //store value of ihigh (app task)
static float32_t meas_data; //temp storage meas value (ctrl task)
static float32_t voltage_reference = 10; //voltage reference (app task)
static int cpt_step = 0; //counter for voltage reference (app task)
static float32_t current_reference = 1; //current reference (app task)
static uint32_t speed_reference = 100; //speed reference in rad/sec (comm task)
static uint32_t speed_reference_step = 10; //speed reference step in rad/sec (comm task / ctrl task)
static uint32_t encoder_value; //store value from the encoder (ctrl task)
static uint32_t encoder_count = 0; //counts the number of control tasks interruptions
static uint32_t encoder_counter_acquisition_period = 10; //number of interruptions needed prior to next encoder value calculation
static uint32_t encoder_value_old; //stores old encoder value
static uint32_t encoder_speed; //stores the new speed in rad/sec
static uint32_t encoder_gain = 6; //stores the gain to be multiplied into the encoder values to calculate rad/sec angular speed
static float32_t error_motor; //stores the value of the error of the pid for the motor speed control
static float32_t int_error_curr_motor; //stores the value of the integral of the motor speed control
static float32_t kp_motor = 0.3; //stores the value of the kp for the motor pid speed control
static float32_t ki_motor = 1; //stores the value of the ki for the motor pid speed control
- Configure the hardware peripherals
In src/main.cpp
, in the setup_hardware()
function, do not change anything.
- Configure the software scheduling
In src/main.cpp
, in the function setup_software()
function, do not modify anything.
- Loop communication task
In the communication task you will change the commands to enable the closed-loop buck mode. To do so you will create a new mode called POWERMODE
.
Find the enum serial_interface_menu_mode
. Add the following code to declare a new mode called BUCKMODE.
enum serial_interface_menu_mode //LIST OF POSSIBLE MODES FOR THE OWNTECH CONVERTER
{
IDLEMODE =0,
SERIALMODE,
POWERMODE,
BUCKMODE,
MOTORMODE,
};
In src/main.cpp
, in the function loop_communication_task()
, add the following code. This code listens to the Serial port. Depending on the character it has received, it switches between IDLEMODE, POWERMODE and BUCKMODE.
void loop_communication_task()
{
while(1) {
received_serial_char = console_getchar();
switch (received_serial_char) {
case 'h':
//----------SERIAL INTERFACE MENU-----------------------
printk(" ________________________________________\n");
printk("| Communication task Tutorial |\n");
printk("| ------- MENU --------- |\n");
printk("| press i : idle mode on |\n");
printk("| press s : serial mode on |\n");
printk("| press p : power mode on |\n");
printk("| press u : duty cycle up by 0.05 |\n");
printk("| press d : duty cycle down by 0.05 |\n");
printk("| press b : closed-loop buck mode on |\n");
printk("| press m : motor mode on |\n");
printk("| press a : speed reference UP |\n");
printk("| press z : speed reference DOWN |\n");
printk("|________________________________________|\n\n");
//------------------------------------------------------
break;
case 'i':
printk("idle mode\n");
mode = IDLEMODE;
break;
case 's':
printk("serial mode on!\n");
mode = SERIALMODE;
break;
case 'p':
printk("power mode on!\n");
mode = POWERMODE;
break;
case 'u':
printk("Duty Cycle UP: %f\n", duty_cycle);
duty_cycle = duty_cycle + duty_cycle_step;
break;
case 'd':
printk("Duty Cycle DOWN: %f\n", duty_cycle);
duty_cycle = duty_cycle - duty_cycle_step;
break;
case 'b':
printk("closed-loop buck mode on!\n");
mode = BUCKMODE;
break;
case 'm':
printk("motor mode on!\n");
mode = MOTORMODE;
break;
case 'a':
printk("Speed UP: %f\n", speed_reference);
speed_reference = speed_reference+speed_reference_step;
break;
case 'z':
printk("Speed DOWN: %f\n", speed_reference);
speed_reference = speed_reference-speed_reference_step;
break;
default:
break;
}
}
}
- Loop application task
In src/main.cpp
, in the function loop_application_task()
, add the following code to define the application task. In this example, the LED will be turned ON when the power is flowing. When the led is ON the O2 converter will print on the serial the value of:
- duty cycle
- VHigh
- V1low
- V2low
- iHigh
- i1low
- i2low
- speed
- speed reference
void loop_application_task()
{
while(1){
if(mode==IDLEMODE) {
hwConfig.setLedOff();
}else if(mode==SERIALMODE) {
hwConfig.setLedOn();
}else if(mode==POWERMODE) {
hwConfig.setLedOn();
}else if(mode==BUCKMODE) {
hwConfig.setLedOn();
if(cpt_step==10)
voltage_reference = 15;
if(cpt_step==20) {
voltage_reference = 10;
cpt_step=0;
}
cpt_step ++;
}else if(mode==MOTORMODE) {
hwConfig.setLedOn();
}
printk("%f:", duty_cycle);
printk("%f:", Vhigh_value);
printk("%f:", V1_low_value);
printk("%f:", V2_low_value);
printk("%f:", ihigh_value);
printk("%f:", i1_low_value);
printk("%f:", i2_low_value);
printk("%f:", (float32_t)encoder_speed); //casts the encoder speed as a float for printing
printk("%f\n", speed_reference);
}
k_msleep(100);
}
}
- Loop control task
In src/main.cpp
, in the function loop_control_task()
, add the following code. In MOTORMODE, the PID tracks the speed_reference
. The speed reference can be changed using the a
and z
keys.
void loop_control_task()
{
meas_data = dataAcquisition.getVHigh();
if(meas_data!=-10000) Vhigh_value = meas_data;
meas_data = dataAcquisition.getV1Low();
if(meas_data!=-10000) V1_low_value = meas_data;
meas_data = dataAcquisition.getV2Low();
if(meas_data!=-10000) V2_low_value= meas_data;
meas_data = dataAcquisition.getIHigh();
if(meas_data!=-10000) ihigh_value = meas_data;
meas_data = dataAcquisition.getI1Low();
if(meas_data!=-10000) i1_low_value = meas_data;
meas_data = dataAcquisition.getI2Low();
if(meas_data!=-10000) i2_low_value = meas_data;
if(mode==IDLEMODE || mode==SERIALMODE) {
pwm_enable = false;
hwConfig.setInterleavedOff();
}else if(mode==POWERMODE || mode==BUCKMODE || mode==MOTORMODE)) {
encoder_count++;
if(encoder_count==encoder_counter_acquisition_period){
encoder_value_old = encoder_value;
encoder_value = hwConfig.getIncrementalEncoderValue();
if((encoder_value-encoder_value_old)<200) encoder_speed = (encoder_value-encoder_value_old)*encoder_gain; //Convert the measurement in rad/sec
encoder_count = 0;
// motor speed PI control
if(mode==MOTORMODE) {
error_motor = speed_reference-encoder_speed; //calculates the motor speed error
int_error_curr_motor += (50e-6)*error_motor; //calculates the integral of the motor speed error
current_reference = kp_motor*(error_motor) + ki_motor*(int_error_curr_motor); //calculates the new current reference based on the motor pid
} else {
int_error_curr_motor =0; //zeroes the integral if the motor mode is off
}
}
if(!pwm_enable) {
pwm_enable = true;
hwConfig.setInterleavedOn();
}
if(mode==BUCKMODE) duty_cycle = opalib_control_interleaved_pid_calculation(voltage_reference, V1_low_value);
if(mode==MOTORMODE) duty_cycle = opalib_control_interleaved_pid_calculation(current_reference, i1_low_value+i2_low_value);
//Sends the PWM to the switches
hwConfig.setInterleavedDutyCycle(duty_cycle);
}
}
- Connect hardware
Now we will connect OwnTech’s O2 to the power supply and to the PC.
- Connect the pins Vhigh and GND of the O2 to the 40 V power supply (set the current limitation at 1 A).
- Connect the micro-JTAG connector of the O2 to the PC thanks to the STLinkV3. The leds PWR and COM should be ON.
- Switch on the power supply. The led2 of the O2 should be ON.
- Connect the encoder according to the image below
For the encoder provided by the OwnTech team, here is the connection to be made:
- In the bottom menu, click on the Build icon . This will launch the compilation of the code. When the compilation is completed, you should see [SUCCESS] in the terminal.
- On the bottom menu, click on the Upload icon . This will flash the compiled code on the microcontroller of the O2. When the process is completed, you should see [SUCCESS] in the terminal.
- On serialPlot, choose the STLinkV3 and set the baud to 115200. Click on Open button.
- When you press the send by the
m
command, data will start to flow in the screen, the LED will turn ON. Data from V1low, i1low, encoder and the duty cycle will appear on the screen.
Expected outputs
- The led1 of the O2 should start as off.
- The led1 of the O2 should turn on when you press the
p
button. - The led1 of the O2 should turn off when you press the
i
button. - The voltage ratio between Vhigh and V1low changes when you press either
u
ord
. - The voltage will converge to 10V if you press
b
. - The motor will start running when you press the
m
button.
That’s it!
Contributors
- Romain Delpoux, 2021.11.04
- Loic Queval, 2021.11.05
- Adrien Prévost, 2021.11.05
- Luiz Villa, 2021.11.07
- Antoine Boche, 2021.11.08
- Luiz Villa, 2022.01.24
- Luiz Villa, 2022.02.22