SimpleFOClibrary  2.1
StepperMotor.cpp
Go to the documentation of this file.
1 #include "StepperMotor.h"
2 
3 // StepperMotor(int pp)
4 // - pp - pole pair number
5 // - R - motor phase resistance
6 StepperMotor::StepperMotor(int pp, float _R)
7 : FOCMotor()
8 {
9  // number od pole pairs
10  pole_pairs = pp;
11  // save phase resistance number
12  phase_resistance = _R;
13 
14  // torque control type is voltage by default
15  // current and foc_current not supported yet
17 }
18 
23  driver = _driver;
24 }
25 
26 // init hardware pins
28  if(monitor_port) monitor_port->println(F("MOT: Init"));
29 
30  // if set the phase resistance of the motor use current limit to calculate the voltage limit
32  float new_voltage_limit = current_limit * (phase_resistance); // v_lim = current_lim / (3/2 phase resistance) - worst case
33  // use it if it is less then voltage_limit set by the user
34  voltage_limit = new_voltage_limit < voltage_limit ? new_voltage_limit : voltage_limit;
35  }
36  // sanity check for the voltage limit configuration
38  // constrain voltage for sensor alignment
40 
41  // update the controller limits
43  // velocity control loop controls current
45  }else{
47  }
49 
50  _delay(500);
51  // enable motor
52  if(monitor_port) monitor_port->println(F("MOT: Enable driver."));
53  enable();
54  _delay(500);
55 
56 }
57 
58 
59 // disable motor driver
61 {
62  // set zero to PWM
63  driver->setPwm(0, 0);
64  // disable driver
65  driver->disable();
66  // motor status update
67  enabled = 0;
68 }
69 // enable motor driver
71 {
72  // disable enable
73  driver->enable();
74  // set zero to PWM
75  driver->setPwm(0, 0);
76  // motor status update
77  enabled = 1;
78 }
79 
80 
84 // FOC initialization function
85 int StepperMotor::initFOC( float zero_electric_offset, Direction _sensor_direction ) {
86  int exit_flag = 1;
87  // align motor if necessary
88  // alignment necessary for encoders!
89  if(_isset(zero_electric_offset)){
90  // abosolute zero offset provided - no need to align
91  zero_electric_angle = zero_electric_offset;
92  // set the sensor direction - default CW
93  sensor_direction = _sensor_direction;
94  }
95 
96  // sensor and motor alignment - can be skipped
97  // by setting motor.sensor_direction and motor.zero_electric_angle
98  _delay(500);
99  if(sensor) exit_flag = alignSensor();
100  else if(monitor_port) monitor_port->println(F("MOT: No sensor."));
101 
102  if(exit_flag){
103  if(monitor_port) monitor_port->println(F("MOT: Ready."));
104  }else{
105  if(monitor_port) monitor_port->println(F("MOT: Init FOC failed."));
106  disable();
107  }
108 
109  return exit_flag;
110 }
111 
112 // Encoder alignment to electrical 0 angle
113 int StepperMotor::alignSensor() {
114  int exit_flag = 1; //success
115  if(monitor_port) monitor_port->println(F("MOT: Align sensor."));
116 
117  // if unknown natural direction
118  if(!_isset(sensor_direction)){
119  // check if sensor needs zero search
120  if(sensor->needsSearch()) exit_flag = absoluteZeroSearch();
121  // stop init if not found index
122  if(!exit_flag) return exit_flag;
123 
124  // find natural direction
125  // move one electrical revolution forward
126  for (int i = 0; i <=500; i++ ) {
127  float angle = _3PI_2 + _2PI * i / 500.0;
128  setPhaseVoltage(voltage_sensor_align, 0, angle);
129  _delay(2);
130  }
131  // take and angle in the middle
132  float mid_angle = sensor->getAngle();
133  // move one electrical revolution backwards
134  for (int i = 500; i >=0; i-- ) {
135  float angle = _3PI_2 + _2PI * i / 500.0 ;
136  setPhaseVoltage(voltage_sensor_align, 0, angle);
137  _delay(2);
138  }
139  float end_angle = sensor->getAngle();
140  setPhaseVoltage(0, 0, 0);
141  _delay(200);
142  // determine the direction the sensor moved
143  if (mid_angle == end_angle) {
144  if(monitor_port) monitor_port->println(F("MOT: Failed to notice movement"));
145  return 0; // failed calibration
146  } else if (mid_angle < end_angle) {
147  if(monitor_port) monitor_port->println(F("MOT: sensor_direction==CCW"));
149  } else{
150  if(monitor_port) monitor_port->println(F("MOT: sensor_direction==CW"));
152  }
153  // check pole pair number
154  if(monitor_port) monitor_port->print(F("MOT: PP check: "));
155  float moved = fabs(mid_angle - end_angle);
156  if( fabs(moved*pole_pairs - _2PI) > 0.5 ) { // 0.5 is arbitrary number it can be lower or higher!
157  if(monitor_port) monitor_port->print(F("fail - estimated pp:"));
158  if(monitor_port) monitor_port->println(_2PI/moved,4);
159  }else if(monitor_port) monitor_port->println(F("OK!"));
160 
161  }else if(monitor_port) monitor_port->println(F("MOT: Skip dir calib."));
162 
163  // zero electric angle not known
165  // align the electrical phases of the motor and sensor
166  // set angle -90(270 = 3PI/2) degrees
167  setPhaseVoltage(voltage_sensor_align, 0, _3PI_2);
168  _delay(700);
170  _delay(20);
171  if(monitor_port){
172  monitor_port->print(F("MOT: Zero elec. angle: "));
174  }
175  // stop everything
176  setPhaseVoltage(0, 0, 0);
177  _delay(200);
178  }else if(monitor_port) monitor_port->println(F("MOT: Skip offset calib."));
179  return exit_flag;
180 }
181 
182 // Encoder alignment the absolute zero angle
183 // - to the index
184 int StepperMotor::absoluteZeroSearch() {
185 
186  if(monitor_port) monitor_port->println(F("MOT: Index search..."));
187  // search the absolute zero with small velocity
188  float limit_vel = velocity_limit;
189  float limit_volt = voltage_limit;
192  shaft_angle = 0;
193  while(sensor->needsSearch() && shaft_angle < _2PI){
194  angleOpenloop(1.5*_2PI);
195  // call important for some sensors not to loose count
196  // not needed for the search
197  sensor->getAngle();
198  }
199  // disable motor
200  setPhaseVoltage(0, 0, 0);
201  // reinit the limits
202  velocity_limit = limit_vel;
203  voltage_limit = limit_volt;
204  // check if the zero found
205  if(monitor_port){
206  if(sensor->needsSearch()) monitor_port->println(F("MOT: Error: Not found!"));
207  else monitor_port->println(F("MOT: Success!"));
208  }
209  return !sensor->needsSearch();
210 }
211 
212 
213 // Iterative function looping FOC algorithm, setting Uq on the Motor
214 // The faster it can be run the better
216  // if disabled do nothing
217  if(!enabled) return;
218  // if open-loop do nothing
220 
221  // shaft angle
224 
225  // set the phase voltage - FOC heart function :)
226  setPhaseVoltage(voltage.q, voltage.d, electrical_angle);
227 }
228 
229 // Iterative function running outer loop of the FOC algorithm
230 // Behavior of this function is determined by the motor.controller variable
231 // It runs either angle, velocity or voltage loop
232 // - needs to be called iteratively it is asynchronous function
233 // - if target is not set it uses motor.target value
234 void StepperMotor::move(float new_target) {
235  // if disabled do nothing
236  if(!enabled) return;
237  // downsampling (optional)
238  if(motion_cnt++ < motion_downsample) return;
239  motion_cnt = 0;
240  // set internal target variable
241  if(_isset(new_target) ) target = new_target;
242  // get angular velocity
244  // choose control loop
245  switch (controller) {
247  if(!_isset(phase_resistance)) voltage.q = target; // if voltage torque control
249  voltage.d = 0;
250  break;
252  // angle set point
254  // calculate velocity set point
256  // calculate the torque command
257  current_sp = PID_velocity(shaft_velocity_sp - shaft_velocity); // if voltage torque control
258  // if torque controlled through voltage
259  // use voltage if phase-resistance not provided
262  voltage.d = 0;
263  break;
265  // velocity set point
267  // calculate the torque command
268  current_sp = PID_velocity(shaft_velocity_sp - shaft_velocity); // if current/foc_current torque control
269  // if torque controlled through voltage control
270  // use voltage if phase-resistance not provided
273  voltage.d = 0;
274  break;
276  // velocity control in open loop
278  voltage.q = velocityOpenloop(shaft_velocity_sp); // returns the voltage that is set to the motor
279  voltage.d = 0;
280  break;
282  // angle control in open loop
284  voltage.q = angleOpenloop(shaft_angle_sp); // returns the voltage that is set to the motor
285  voltage.d = 0;
286  break;
287  }
288 }
289 
290 
291 // Method using FOC to set Uq and Ud to the motor at the optimal angle
292 // Function implementing Sine PWM algorithms
293 // - space vector not implemented yet
294 //
295 // Function using sine approximation
296 // regular sin + cos ~300us (no memory usaage)
297 // approx _sin + _cos ~110us (400Byte ~ 20% of memory)
298 void StepperMotor::setPhaseVoltage(float Uq, float Ud, float angle_el) {
299  // Sinusoidal PWM modulation
300  // Inverse Park transformation
301 
302  // angle normalization in between 0 and 2pi
303  // only necessary if using _sin and _cos - approximation functions
304  angle_el = _normalizeAngle(angle_el);
305  float _ca = _cos(angle_el);
306  float _sa = _sin(angle_el);
307  // Inverse park transform
308  Ualpha = _ca * Ud - _sa * Uq; // -sin(angle) * Uq;
309  Ubeta = _sa * Ud + _ca * Uq; // cos(angle) * Uq;
310 
311  // set the voltages in hardware
313 }
314 
315 // Function (iterative) generating open loop movement for target velocity
316 // - target_velocity - rad/s
317 // it uses voltage_limit variable
318 float StepperMotor::velocityOpenloop(float target_velocity){
319  // get current timestamp
320  unsigned long now_us = _micros();
321  // calculate the sample time from last call
322  float Ts = (now_us - open_loop_timestamp) * 1e-6;
323  // quick fix for strange cases (micros overflow + timestamp not defined)
324  if(Ts <= 0 || Ts > 0.5) Ts = 1e-3;
325 
326  // calculate the necessary angle to achieve target velocity
327  shaft_angle = _normalizeAngle(shaft_angle + target_velocity*Ts);
328  // for display purposes
329  shaft_velocity = target_velocity;
330 
331  // use voltage limit or current limit
332  float Uq = voltage_limit;
334 
335  // set the maximal allowed voltage (voltage_limit) with the necessary angle
336  setPhaseVoltage(Uq, 0, _electricalAngle(shaft_angle, pole_pairs));
337 
338  // save timestamp for next call
339  open_loop_timestamp = now_us;
340 
341  return Uq;
342 }
343 
344 // Function (iterative) generating open loop movement towards the target angle
345 // - target_angle - rad
346 // it uses voltage_limit and velocity_limit variables
347 float StepperMotor::angleOpenloop(float target_angle){
348  // get current timestamp
349  unsigned long now_us = _micros();
350  // calculate the sample time from last call
351  float Ts = (now_us - open_loop_timestamp) * 1e-6;
352  // quick fix for strange cases (micros overflow + timestamp not defined)
353  if(Ts <= 0 || Ts > 0.5) Ts = 1e-3;
354 
355  // calculate the necessary angle to move from current position towards target angle
356  // with maximal velocity (velocity_limit)
357  if(abs( target_angle - shaft_angle ) > abs(velocity_limit*Ts)){
358  shaft_angle += _sign(target_angle - shaft_angle) * abs( velocity_limit )*Ts;
360  }else{
361  shaft_angle = target_angle;
362  shaft_velocity = 0;
363  }
364 
365  // use voltage limit or current limit
366  float Uq = voltage_limit;
368  // set the maximal allowed voltage (voltage_limit) with the necessary angle
369  setPhaseVoltage(Uq, 0, _electricalAngle(shaft_angle, pole_pairs));
370 
371  // save timestamp for next call
372  open_loop_timestamp = now_us;
373 
374  return Uq;
375 }
velocity_openloop
@ velocity_openloop
Definition: FOCMotor.h:31
StepperMotor::StepperMotor
StepperMotor(int pp, float R=NOT_SET)
Definition: StepperMotor.cpp:6
_sin
float _sin(float a)
Definition: foc_utils.cpp:14
FOCMotor::current_sp
float current_sp
target current ( q current )
Definition: FOCMotor.h:135
StepperMotor::initFOC
int initFOC(float zero_electric_offset=NOT_SET, Direction sensor_direction=Direction::CW) override
Definition: StepperMotor.cpp:85
CW
@ CW
Definition: Sensor.h:8
FOCMotor
Definition: FOCMotor.h:58
FOCMotor::zero_electric_angle
float zero_electric_angle
absolute zero electric angle - if available
Definition: FOCMotor.h:180
StepperDriver
Definition: StepperDriver.h:4
StepperDriver::voltage_limit
float voltage_limit
limiting voltage set to the motor
Definition: StepperDriver.h:16
Sensor::needsSearch
virtual int needsSearch()
Definition: Sensor.cpp:10
voltage
@ voltage
Torque control using voltage.
Definition: FOCMotor.h:39
FOCMotor::shaftAngle
float shaftAngle()
Definition: FOCMotor.cpp:56
FOCMotor::voltage_limit
float voltage_limit
Voltage limitting variable - global limit.
Definition: FOCMotor.h:150
StepperMotor::init
void init() override
Definition: StepperMotor.cpp:27
angle
@ angle
Position/angle motion control.
Definition: FOCMotor.h:30
_isset
#define _isset(a)
Definition: foc_utils.h:11
_delay
void _delay(unsigned long ms)
Definition: time_utils.cpp:5
DQVoltage_s::d
float d
Definition: foc_utils.h:48
StepperMotor::Ubeta
float Ubeta
Phase voltages U alpha and U beta used for inverse Park and Clarke transform.
Definition: StepperMotor.h:78
FOCMotor::electricalAngle
float electricalAngle()
Definition: FOCMotor.cpp:68
FOCMotor::electrical_angle
float electrical_angle
current electrical angle
Definition: FOCMotor.h:133
FOCMotor::shaft_velocity_sp
float shaft_velocity_sp
current target velocity
Definition: FOCMotor.h:136
velocity
@ velocity
Velocity motion control.
Definition: FOCMotor.h:29
torque
@ torque
Torque control.
Definition: FOCMotor.h:28
StepperDriver::setPwm
virtual void setPwm(float Ua, float Ub)=0
StepperMotor::linkDriver
void linkDriver(StepperDriver *driver)
Definition: StepperMotor.cpp:22
StepperMotor::loopFOC
void loopFOC() override
Definition: StepperMotor.cpp:215
FOCMotor::shaft_velocity
float shaft_velocity
current motor velocity
Definition: FOCMotor.h:134
FOCMotor::enabled
int8_t enabled
enabled or disabled motor flag
Definition: FOCMotor.h:155
FOCMotor::shaft_angle
float shaft_angle
current motor angle
Definition: FOCMotor.h:132
FOCMotor::target
float target
current target value - depends of the controller
Definition: FOCMotor.h:131
FOCMotor::phase_resistance
float phase_resistance
motor phase resistance
Definition: FOCMotor.h:146
FOCMotor::torque_controller
TorqueControlType torque_controller
parameter determining the torque control type
Definition: FOCMotor.h:163
_normalizeAngle
float _normalizeAngle(float angle)
Definition: foc_utils.cpp:47
FOCMotor::sensor_direction
int sensor_direction
if sensor_direction == Direction::CCW then direction will be flipped to CW
Definition: FOCMotor.h:181
StepperMotor::disable
void disable() override
Definition: StepperMotor.cpp:60
StepperDriver::disable
virtual void disable()=0
PIDController::limit
float limit
Maximum output value.
Definition: pid.h:31
StepperMotor::Ualpha
float Ualpha
Definition: StepperMotor.h:78
_electricalAngle
float _electricalAngle(float shaft_angle, int pole_pairs)
Definition: foc_utils.cpp:53
FOCMotor::current_limit
float current_limit
Current limitting variable - global limit.
Definition: FOCMotor.h:151
StepperMotor::move
void move(float target=NOT_SET) override
Definition: StepperMotor.cpp:234
FOCMotor::motion_cnt
unsigned int motion_cnt
counting variable for downsampling for move commad
Definition: FOCMotor.h:176
StepperMotor::driver
StepperDriver * driver
Definition: StepperMotor.h:41
StepperMotor.h
FOCMotor::sensor
Sensor * sensor
Definition: FOCMotor.h:206
_2PI
#define _2PI
Definition: foc_utils.h:23
FOCMotor::P_angle
PIDController P_angle
parameter determining the position PID configuration
Definition: FOCMotor.h:172
CCW
@ CCW
Definition: Sensor.h:9
FOCMotor::voltage
DQVoltage_s voltage
current d and q voltage set to the motor
Definition: FOCMotor.h:138
StepperDriver::enable
virtual void enable()=0
Direction
Direction
Definition: Sensor.h:7
FOCMotor::controller
MotionControlType controller
parameter determining the control loop to be used
Definition: FOCMotor.h:164
FOCMotor::pole_pairs
int pole_pairs
motor pole pairs number
Definition: FOCMotor.h:147
FOCMotor::monitor_port
Print * monitor_port
Serial terminal variable if provided.
Definition: FOCMotor.h:213
_micros
unsigned long _micros()
Definition: time_utils.cpp:21
FOCMotor::voltage_sensor_align
float voltage_sensor_align
sensor and motor align voltage parameter
Definition: FOCMotor.h:142
FOCMotor::PID_velocity
PIDController PID_velocity
parameter determining the velocity PID configuration
Definition: FOCMotor.h:171
_cos
float _cos(float a)
Definition: foc_utils.cpp:39
DQVoltage_s::q
float q
Definition: foc_utils.h:49
FOCMotor::velocity_limit
float velocity_limit
Velocity limitting variable - global limit.
Definition: FOCMotor.h:152
FOCMotor::motion_downsample
unsigned int motion_downsample
parameter defining the ratio of downsampling for move commad
Definition: FOCMotor.h:175
angle_openloop
@ angle_openloop
Definition: FOCMotor.h:32
FOCMotor::velocity_index_search
float velocity_index_search
target velocity for index search
Definition: FOCMotor.h:143
_sign
#define _sign(a)
Definition: foc_utils.h:7
StepperMotor::enable
void enable() override
Definition: StepperMotor.cpp:70
_3PI_2
#define _3PI_2
Definition: foc_utils.h:24
FOCMotor::shaftVelocity
float shaftVelocity()
Definition: FOCMotor.cpp:62
Sensor::getAngle
virtual float getAngle()=0
FOCMotor::shaft_angle_sp
float shaft_angle_sp
current target angle
Definition: FOCMotor.h:137