MotorController
control.c
1 #include "control.h"
2 #include "coding_wheels.h"
3 #include "motor.h"
4 #include "settings.h"
5 #include "orientation.h"
6 #include "position.h"
7 #include "RTT/SEGGER_RTT.h"
8 #include "math.h"
9 
10 /******************************************************************************/
11 /* Local macros */
12 /******************************************************************************/
13 /* Maximum command value allowed (absolute maximum is 100) */
14 #define MAX_PWM 70
15 
16 /* Reduction factors for the PID coeffs */
17 #define REDUCTION_FACTOR_P 1000
18 #define REDUCTION_FACTOR_I 10000
19 #define REDUCTION_FACTOR_D 10000
20 
21 /* Period of the int_pos thread, in ms */
22 #define INT_POS_PERIOD 10
23 
24 /* Period of current position update, in ms */
25 #define POS_PERIOD 100
26 
27 /* Period of the control thread, in ms */
28 #define CONTROL_PERIOD 1
29 
30 #define RESET_PERIOD 300
31 
32 /* Returns the absolute value of the parameter */
33 #define ABS(x) (((x) > 0) ? (x) : -(x))
34 
35 /* Returns the sign of the parameter (1 or -1) */
36 #define SIGN(x) (((x) < 0) ? (-1) : 1)
37 
38 /* Selects the "urgent stop" strategy */
39 #define BASIC_STOP 0
40 
41 /*
42  * For all variables: goal refers to an instruction sent by the master, target
43  * to an intermediate objective.
44  */
45 /******************************************************************************/
46 /* Public variables */
47 /******************************************************************************/
48 volatile goal_t goal;
49 
50 volatile bool dist_command_received;
51 
52 /* Current distance travelled since last goal_mean_dist update */
53 volatile int32_t current_distance;
54 
55 /* Boolean value to stop motors whatever the commands are */
56 volatile uint8_t master_stop;
57 
58 /* Boolean indicating whether a new distance command has been received from the
59  master. It's set to TRUE in i2c_interface. */
60 volatile bool dist_command_updated;
61 
62 volatile bool translation_ended;
63 volatile bool rotation_ended;
64 
65 BSEMAPHORE_DECL(reset_orientation_sem, TRUE);
66 volatile int8_t reset_orientation_direction;
67 volatile int16_t reset_orientation_orientation;
68 
69 /******************************************************************************/
70 /* Local types */
71 /******************************************************************************/
72 typedef struct {
73  int32_t p;
74  int32_t i;
75  int32_t d;
76 } pid_t;
77 
78 typedef struct {
79  int32_t prev_epsilon;
80  int32_t epsilon;
81  int32_t epsilon_sum;
83 
84 /******************************************************************************/
85 /* Local variables */
86 /******************************************************************************/
87 /* Intermediate value computed by the int_pos thread */
88 static volatile int32_t target_dist;
89 static volatile uint16_t target_heading;
90 
91 static volatile bool orientation_control = TRUE;
92 
93 /* Threads stacks */
94 THD_WORKING_AREA(wa_control, CONTROL_STACK_SIZE);
95 THD_WORKING_AREA(wa_int_pos, INT_POS_STACK_SIZE);
96 THD_WORKING_AREA(wa_reset_pos, INT_POS_STACK_SIZE);
97 
98 
99 /******************************************************************************/
100 /* Local Functions */
101 /******************************************************************************/
102 
103 /*
104 parameters :
105  t : time, in seconds. t = 0 is the begginning of the move
106  must be positive
107  inc_acceleration : acceleration when the speed of the robot goes increases
108  in m / s^2
109  dec_acceleration : acceleration when the speed of the robot goes decreases
110  in m / s^2
111  cruising_speed : maximum speed of the robot
112  in m / s
113  final_x : final destination of the robot, in m
114 
115  NOTE :
116  only the signs of t and final_x matter
117  the appropriate signs for inc_acceleration, dec_acceleration, cruising_speed
118  are calculated
119 
120 returns the intermediate target to reach at time t
121 
122 speed graph :
123  ----------------------
124  / ^ ^\
125  / | | \
126 ------/ | | \--------
127  ^ ^ ^ ^
128  | | | |
129  | | | |
130  t=0 t=t1 t=t3 t=t4
131 
132 */
133 float compute_target(float t, float inc_acceleration, float dec_acceleration,
134  float cruising_speed, float final_x)
135 {
136 
137  /* signs computation */
138  if (final_x >= 0) {
139  inc_acceleration = ABS(inc_acceleration);
140  dec_acceleration = - ABS(dec_acceleration);
141  cruising_speed = ABS(cruising_speed);
142  }
143  else {
144  inc_acceleration = - ABS(inc_acceleration);
145  dec_acceleration = ABS(dec_acceleration);
146  cruising_speed = - ABS(cruising_speed);
147  }
148 
149  /* t1 and t2 computation, see graph above */
150  float t1 = cruising_speed / inc_acceleration;
151  float t2 = final_x / cruising_speed + cruising_speed / 2 * (1 / inc_acceleration + 1 / dec_acceleration);
152 
153  /* if cruising_speed is never reached */
154  if (t2 <= t1){
155  float t4_square = 2 * final_x / (inc_acceleration * (1 - inc_acceleration / dec_acceleration));
156 
157  if (t * t <= t4_square) return inc_acceleration * t * t / 2;
158 
159  float t4_inv = 2 / sqrt(t4_square);
160  float delta_t = t - t4_inv * final_x / inc_acceleration;
161 
162  if (delta_t >= 0) return final_x;
163  return dec_acceleration / 2 * delta_t * delta_t + final_x;
164  }
165 
166  float t3 = t2 - cruising_speed /dec_acceleration;
167 
168  /* the result is computed in function of the part of the graph at which t belongs */
169  if (t < 0) return 0;
170  else if (t <= t1 && t <= t2) return inc_acceleration * t * t / 2;
171  else if (t <= t2) return t1 * cruising_speed / 2 + cruising_speed * (t - t1);
172  else if (t <= t3)
173  return final_x + cruising_speed * cruising_speed / 2 / dec_acceleration \
174  + (t - t2) * (cruising_speed + dec_acceleration / 2 * (t - t2));
175  else return final_x;
176 }
177 
178 /******************************************************************************/
179 /* Public functions */
180 /******************************************************************************/
181 
182 
183 
184 extern THD_FUNCTION(int_pos_thread, p) {
185  (void)p;
186 
187  /*
188  * All times are in seconds.
189  * All distances are in millimeters.
190  */
191 
192  /* Time */
193  float linear_t = 0.0; /* in s */
194  float angular_t = 0.0; /* in s */
195 
196  int16_t prev_goal_heading = 0;
197  int16_t delta_heading = 0;
198  int16_t initial_heading = 0;
199  int16_t tmp_target_heading = 0;
200  uint32_t update_position_counter = 0U;
201 
202  uint32_t start_time;
203 
204  while (TRUE) {
205  start_time = chVTGetSystemTime();
206 
207  /* Acquire sensors data and update localisation */
210  update_position_counter++;
211  //TODO : est-ce qu'on ne peut pas recalculer a chaque fois ?
212  if (update_position_counter > (uint32_t)(POS_PERIOD / INT_POS_PERIOD)) {
213  update_position();
214  update_position_counter = 0U;
215  }
216 
217  /* linear */
218  /* New command received */
219  if (dist_command_received) {
220  /* Acknowledge the "new_command" message */
221  dist_command_received = FALSE;
222  /* Reset time */
223  linear_t = 0.0;
224  /* Warn the control thread that a new command has been received */
225  dist_command_updated = TRUE;
226  }
227  else {
228  linear_t += (float)INT_POS_PERIOD / 1000.0;
229  }
230 
231  /* acceleration and speed are in cm, goal.mean_dist and target_dist in mm
232  as all must be in the same unit, we convert to mm
233  seee inc/settings.h */
234  target_dist = compute_target(linear_t, settings.max_linear_acceleration * 10,
237  goal.mean_dist);
238 
239  /* angular */
240  if (goal.heading != prev_goal_heading) {
241  /* New command received */
242  angular_t = 0.0;
243  prev_goal_heading = goal.heading;
244  initial_heading = orientation;
245 
246  delta_heading = goal.heading - orientation;
247  if (delta_heading < -(HEADING_MAX_VALUE / 2)) {
248  delta_heading += HEADING_MAX_VALUE;
249  } else if (delta_heading > (HEADING_MAX_VALUE / 2)) {
250  delta_heading -= HEADING_MAX_VALUE;
251  }
252  }
253  else {
254  angular_t += (float)INT_POS_PERIOD / 1000.0;
255  }
256 
257  /* Multiply by 16 to convert degree to IMU unit */
258  tmp_target_heading = initial_heading + ((int16_t) compute_target(angular_t,
259  (float) settings.max_angular_acceleration * 16,
260  (float) settings.max_angular_acceleration * 16,
261  (float) settings.cruise_angular_speed * 16,
262  delta_heading));
263 
264  if (tmp_target_heading < 0) {
265  target_heading = tmp_target_heading + HEADING_MAX_VALUE;
266  } else if (tmp_target_heading >= HEADING_MAX_VALUE) {
267  target_heading = tmp_target_heading - HEADING_MAX_VALUE;
268  } else {
269  target_heading = tmp_target_heading;
270  }
271 
272  // counter just not to spam the console
273  static int cpt_print = 0;
274  if (cpt_print++ % 50 == 0) {
275  /* printf("config : %d %d %d %d %d\n", initial_heading, settings.max_angular_acceleration * 16, settings.max_angular_acceleration * 16,
276  settings.cruise_angular_speed * 16,
277  delta_heading);
278  printf("temp : %d %.3f\n", tmp_target_heading, angular_t);
279  printf("cur_pos = (%.3f, %.3f)\n", cur_pos.x, cur_pos.y);*/
280  printf("target %d / %d (%d) %d / %d (%d)\r\n", target_heading, goal.heading, orientation, target_dist, goal.mean_dist, current_distance);
281  }
282  /* Wait to reach the desired period */
283  chThdSleepMilliseconds(INT_POS_PERIOD - ST2MS(chVTGetSystemTime() - start_time));
284  }
285 }
286 
287 extern THD_FUNCTION(control_thread, p) {
288  (void)p;
289 
290  control_values_t linear_control = {0, 0, 0};
291  control_values_t angular_control = {0, 0, 0};
292 
293  pid_t linear_pid;
294  pid_t angular_pid;
295 
296  /* Saved ticks value (to compute current distance) */
297  ticks_t saved_ticks = {0, 0};
298 
299  /* Previous goal heading, used to know whether a new instruction has been received */
300  int16_t prev_goal_heading;
301 
302  /* Current heading to maintain (initial orientation before sync_dist, target_heading after) */
303  int16_t cur_target_heading;
304 
305  /* Commands for motors and other related local variables */
306  int32_t prev_command[2] = {0, 0};
307  int32_t command[2];
308  int32_t prev_linear_command = 0;
309  int32_t linear_command = 0;
310  int32_t prev_angular_command = 0;
311  int32_t angular_command = 0;
312  int32_t tmp_command;
313 
314  /* Maximum delta on each command between two successive loop turn */
315  int32_t max_linear_delta_pwm_command; /* max_linear_acceleration * CONTROL_PERIOD */
316  int32_t max_angular_delta_pwm_command; /* max_angular_acceleration * CONTROL_PERIOD */
317 
318  /* Start time of the current loop */
319  uint32_t start_time;
320 
321  int32_t remaining_time;
322 
323  /* Initialise the variables */
324  prev_goal_heading = goal.heading;
325  cur_target_heading = target_heading;
326 
327  /* Infinite loop */
328  while (TRUE) {
329 
330  start_time = chVTGetSystemTime();
331 
332  /* Reset the linear PID sum and saved_ticks if a new instruction has been
333  received from master */
334  if (dist_command_updated) {
335  /* Acknowledge the "command_updated" message */
336  dist_command_updated = FALSE;
337 
338  /* Reset the measured values */
339  linear_control.epsilon_sum = 0;
340  saved_ticks.left = left_ticks;
341  saved_ticks.right = right_ticks;
342  }
343 
344  /* Reset the angular PID sum if a new instruction has been received from master */
345  if (prev_goal_heading != goal.heading) {
346  prev_goal_heading = goal.heading;
347  angular_control.epsilon_sum = 0;
348  }
349 
350  /* (Re)Compute the settings value, in case max accelerations have changed */
351  max_linear_delta_pwm_command = settings.max_linear_acceleration * CONTROL_PERIOD / 10;
352  max_angular_delta_pwm_command = settings.max_angular_acceleration * CONTROL_PERIOD / 10;
353 
354  /* Update current_distance, in mm */
355  current_distance = 1000 * ((left_ticks - saved_ticks.left) + (right_ticks - saved_ticks.right)) / (2 * settings.ticks_per_m);
356 
357  /* Check whether current move is finished */
358  translation_ended = ((uint32_t)ABS(current_distance - goal.mean_dist) < settings.linear_allowance);
359  rotation_ended = ((ABS(orientation - goal.heading) % (HEADING_MAX_VALUE / 2)) < settings.angular_allowance);
360 
361  /* Compute linear_control.epsilon and related input values */
362  linear_control.prev_epsilon = linear_control.epsilon;
363  linear_control.epsilon = target_dist - current_distance;
364  linear_control.epsilon_sum += linear_control.epsilon;
365 
366  if (master_stop == FALSE) {
367  /* Linear PID */
368  linear_pid.p = (settings.linear_coeff.p * linear_control.epsilon) / REDUCTION_FACTOR_P;
369 
370  linear_pid.i = (settings.linear_coeff.i * linear_control.epsilon_sum) / REDUCTION_FACTOR_I;
371 
372  linear_pid.d = (settings.linear_coeff.d * (linear_control.epsilon - linear_control.prev_epsilon)) / REDUCTION_FACTOR_D;
373 
374  prev_linear_command = linear_command;
375  linear_command = linear_pid.p + linear_pid.i + linear_pid.d;
376 
377  /* Limit linear acceleration/deceleration */
378  if ((int32_t)(linear_command - prev_linear_command) > max_linear_delta_pwm_command) {
379  linear_command = prev_linear_command + max_linear_delta_pwm_command;
380  } else if ((int32_t)(linear_command - prev_linear_command) < -max_linear_delta_pwm_command) {
381  linear_command = prev_linear_command - max_linear_delta_pwm_command;
382  }
383 
384  /* Update current target heading if heading dist sync ref has been reached */
385  if (ABS(current_distance) >= goal.heading_dist_sync_ref) {
386  cur_target_heading = target_heading;
387  }
388 
389  /* Compute angular_control.epsilon and related input values */
390  angular_control.prev_epsilon = angular_control.epsilon;
391  angular_control.epsilon = cur_target_heading - orientation;
392 
393  /* Angles are module HEADING_MAX_VALUE, this case must thus be handled
394  for the robot to turn in the right direction (the shorter one). */
395  if (angular_control.epsilon > (HEADING_MAX_VALUE / 2)) {
396  angular_control.epsilon -= HEADING_MAX_VALUE;
397  } else if (angular_control.epsilon < -(HEADING_MAX_VALUE / 2)) {
398  angular_control.epsilon += HEADING_MAX_VALUE;
399  }
400 
401  angular_control.epsilon_sum += angular_control.epsilon;
402 
403  /* Angular PID */
404  angular_pid.p = (settings.angular_coeff.p * angular_control.epsilon) / REDUCTION_FACTOR_P;
405 
406  angular_pid.i = (settings.angular_coeff.i * angular_control.epsilon_sum) / REDUCTION_FACTOR_I;
407 
408  angular_pid.d = (settings.angular_coeff.d * (angular_control.epsilon - angular_control.prev_epsilon)) / REDUCTION_FACTOR_D;
409 
410  prev_angular_command = angular_command;
411  angular_command = angular_pid.p + angular_pid.i + angular_pid.d;
412 
413  /* Limit angular acceleration/deceleration */
414  if ((int32_t)(angular_command - prev_angular_command) > max_angular_delta_pwm_command) {
415  angular_command = prev_angular_command + max_angular_delta_pwm_command;
416  } else if ((int32_t)(angular_command - prev_angular_command) < -max_angular_delta_pwm_command) {
417  angular_command = prev_angular_command - max_angular_delta_pwm_command;
418  }
419 
420  if (FALSE == orientation_control) {
421  printf(".");
422  angular_command = 0;
423  }
424 
425  /* Motor commands */
426  /* If left wheel required speed is too high, reduce both components */
427  tmp_command = ABS(linear_command + angular_command);
428  if (tmp_command > MAX_PWM) {
429  linear_command *= MAX_PWM;
430  linear_command /= tmp_command;
431 
432  angular_command *= MAX_PWM;
433  angular_command /= tmp_command;
434  }
435 
436  /* If right wheel required speed is too high, reduce both components */
437  tmp_command = ABS(linear_command - angular_command);
438  if (tmp_command > MAX_PWM) {
439  linear_command *= MAX_PWM;
440  linear_command /= tmp_command;
441 
442  angular_command *= MAX_PWM;
443  angular_command /= tmp_command;
444  }
445 
446  /* Compute new commands */
447  prev_command[MOTOR_LEFT] = command[MOTOR_LEFT];
448  command[MOTOR_LEFT] = linear_command + angular_command;
449  if ((command[MOTOR_LEFT] < MIN_COMMAND) && (command[MOTOR_LEFT] > 0)) {
450  command[MOTOR_LEFT] = MIN_COMMAND;
451  } else if ((command[MOTOR_LEFT] > -MIN_COMMAND) && (command[MOTOR_LEFT] < 0)) {
452  command[MOTOR_LEFT] = -MIN_COMMAND;
453  }
454 
455  prev_command[MOTOR_RIGHT] = command[MOTOR_RIGHT];
456  command[MOTOR_RIGHT] = linear_command - angular_command;
457  if ((command[MOTOR_RIGHT] < MIN_COMMAND) && (command[MOTOR_RIGHT] > 0)) {
458  command[MOTOR_RIGHT] = MIN_COMMAND;
459  } else if ((command[MOTOR_RIGHT] > -MIN_COMMAND) && (command[MOTOR_RIGHT] < 0)) {
460  command[MOTOR_RIGHT] = -MIN_COMMAND;
461  }
462 
463  /* Apply new commands */
464  motor_t motor;
465  for (motor = MOTOR_LEFT; motor <= MOTOR_RIGHT; ++motor) {
466  /* Change direction if required */
467  if (((command[motor] < 0) && (prev_command[motor] >= 0))
468  || ((command[motor] >= 0) && (prev_command[motor] < 0))) {
469  motor_toggle_direction(motor);
470  }
471 
472  /* Set new speed */
473  if (command[motor] < 0) {
474  motor_set_speed(motor, -command[motor]);
475  } else {
476  motor_set_speed(motor, command[motor]);
477  }
478  }
479  } else {
480  linear_control.epsilon_sum = 0;
481  angular_control.epsilon_sum = 0;
482 #if BASIC_STOP
485  linear_command = 0;
486  angular_command = 0;
487 #else
488  /* Reduce linear command as much as possible */
489  tmp_command = SIGN(linear_command) * (ABS(linear_command) - max_linear_delta_pwm_command);
490  if (SIGN(tmp_command) != SIGN(linear_command)) {
491  linear_command = 0;
492  } else {
493  linear_command = tmp_command;
494  }
495 
496  /* Reduce angular command as much as possible */
497  tmp_command = SIGN(angular_command) * (ABS(angular_command) - max_angular_delta_pwm_command);
498  if (SIGN(tmp_command) != SIGN(angular_command)) {
499  angular_command = 0;
500  } else {
501  angular_command = tmp_command;
502  }
503 
504  command[MOTOR_RIGHT] = linear_command + angular_command;
505  command[MOTOR_LEFT] = linear_command - angular_command;
506 
509 
510  goal.mean_dist -= current_distance;
511  dist_command_received = TRUE;
512 #endif /* BASIC_STOP */
513  }
514 
515  /* Sleep until next period */
516  remaining_time = CONTROL_PERIOD - ST2MS(chVTGetSystemTime() - start_time);
517  if (remaining_time > 0) {
518  chThdSleepMilliseconds(remaining_time);
519  }
520  }
521 }
522 
523 extern THD_FUNCTION(reset_pos_thread, p) {
524  int32_t saved_current_distance;
525 
526  (void)p;
527 
528  while (TRUE) {
529  chBSemWait(&reset_orientation_sem);
530 
531  /* Disable orientation control */
532  orientation_control = FALSE;
533 
534  /* Move as fast as possible in the required direction */
535  goal.mean_dist = SIGN(reset_orientation_direction) * 0x0FFFFFFF;
536  dist_command_received = TRUE;
537 
538  saved_current_distance = 0;
539 
540  while (TRUE) {
541  chThdSleepMilliseconds(RESET_PERIOD);
542  if (saved_current_distance == current_distance) {
543  /* no move in the last period */
544  set_orientation(reset_orientation_orientation);
545  orientation_control = TRUE;
546  goal.mean_dist = 0;
547  dist_command_received = TRUE;
548  break;
549  } else {
550  saved_current_distance = current_distance;
551  }
552  }
553 
554  }
555 }
uint16_t max_linear_acceleration
Definition: settings.h:44
uint16_t cruise_angular_speed
Definition: settings.h:47
void update_position(void)
Update the position according to the information given by the coding wheels.
Definition: position.c:48
int motor_set_speed(motor_t motor, uint8_t speed)
Set the rotation speed of the specified motor.
Definition: motor.c:77
int16_t orientation
Definition: orientation.c:32
#define MIN_COMMAND
Definition: motor.h:9
void update_orientation(void)
Update the current orientation.
Definition: orientation.c:169
pid_coeffs_t linear_coeff
Definition: settings.h:49
uint16_t i
Definition: settings.h:33
motor_t
Alias to select the motor to work on.
Definition: motor.h:20
pid_coeffs_t angular_coeff
Definition: settings.h:50
uint16_t linear_allowance
Definition: settings.h:56
uint16_t max_angular_acceleration
Definition: settings.h:45
uint16_t d
Definition: settings.h:34
Definition: control.h:18
int32_t set_orientation(int16_t heading)
Set the euler heading angle offset.
Definition: orientation.c:38
void motor_toggle_direction(motor_t motor)
Revert the rotation direction of the specified motor.
Definition: motor.c:145
uint16_t angular_allowance
Definition: settings.h:58
uint16_t ticks_per_m
Definition: settings.h:42
void compute_movement(void)
Compute the coding wheels movements.
Definition: position.c:39
uint16_t p
Definition: settings.h:32
Definition: control.c:72
uint16_t cruise_linear_speed
Definition: settings.h:46
volatile robot_settings_t settings
Global variable used to store the configuration in use.
Definition: settings.c:9