Tetrapod Project
motor_driver.cpp
Go to the documentation of this file.
1 #include "motor_driver.h"
2 
3 MotorControl::MotorControl(uint8_t _id, uint8_t _can_port_id, int _number_of_inner_motor_rotations, double _position_offset)
4 {
5  id = _id;
6 
8 
9  can_message.id = address;
10 
11  can_port_id = _can_port_id;
12 
13  position_offset = _position_offset;
14 
16 
18 
20 
21  // We set the initial encoder value to be just to the left of the shaft pointing straight forward.
23 
24  // This value is somewhat arbitrary. It should be scaled relative to the
25  // encoder resolution and large enough so that no inner motor turns are skipped
27 
28  initial_number_of_inner_motor_rotations = _number_of_inner_motor_rotations;
29 
30  number_of_inner_motor_rotations = _number_of_inner_motor_rotations;
31 
32  // Set the PID parameters in the motor
33  /*
34  while(!writePIDParametersToRAM(10, 1, 50, 5, 50, 5))
35  {
36  delay_microseconds(1000000.0);
37  }
38  */
39 
40  // Get the initial states from the motor
41  while(!readMotorStatus())
42  {
43  #if SERIAL_PRINT
44  Serial.println("Trying to read motor states");
45  #endif
46  delay_microseconds(1000000.0);
47  }
48 
49  while(!readMultiTurnAngle())
50  {
51  #if SERIAL_PRINT
52  Serial.println("Constructor - Trying read multi turn angle");
53  #endif
54  }
55 
56  // Use the sign of the multi turn angle to decide the whether or not to add a target position offset
57  /*
58  if (multi_turn_angle_32_bit_001lsb >= 0)
59  {
60  target_position_offset = 60.0*M_PI/180.0;
61  }
62  else
63  {
64  target_position_offset = 0.0;
65  }
66  */
67  /*
68  Serial.println("");
69  Serial.print("Multiturn angle raw: "); Serial.println(multi_turn_angle_32_bit_001lsb);
70  Serial.print("Target_position_offset: "); Serial.println(target_position_offset);
71  while(true);
72  */
73 }
74 
76 {
77  // Create a CAN message for requesting the motor PID parameters
79 
80  // Send CAN message
82 
83  // Wait 0.010 seconds for a reply from the motor
84  delay_microseconds(10000.0);
85 
86  // Check if we received a reply
88  {
90  {
91  // Store the PI parameters in the class
98 
99  // Report that we successfully managed to read the PID parameters
100  return true;
101  }
102  else
103  {
104  #if SERIAL_PRINT
105  errorMessage();
106  Serial.println("In the function readPIDParameters an incorrect answer was received");
107  #endif
108 
109  return false;
110  }
111  }
112  else
113  {
114  // Report that we failed to read the PID parameters
115  #if SERIAL_PRINT
116  errorMessage();
117  Serial.println("In the function readPIDParameters no reply was received");
118  #endif
119 
120  return false;
121  }
122 }
123 
125 (
126  double _kp_pos,
127  double _ki_pos,
128  double _kp_speed,
129  double _ki_speed,
130  double _kp_torque,
131  double _ki_torque
132 )
133 {
134  // Create a CAN message for writing PID parameters to RAM
136  _kp_pos,
137  _ki_pos,
138  _kp_speed,
139  _ki_speed,
140  _kp_torque,
141  _ki_torque
142  );
143 
144  //clearCANBuffer();
145 
146  // Send CAN message
148 
149  // Wait 0.10 seconds for a reply from the motor
150  delay_microseconds(100000.0);
151 
152  // Check if we received a reply from the motor
154  {
155  // Check that the incomming message is the one we anticipated
157  {
158  // Update the PID parameters to the new ones from the motor
159  kp_pos = received_can_message.buf[2];
160  ki_pos = received_can_message.buf[3];
165  return true;
166  }
167  else
168  {
169  // Report that the reply we received was incorrect
170  #if SERIAL_PRINT
171  errorMessage();
172  Serial.println("In the function writePIDParametersToRAM an incorrect reply was received");
173  #endif
174 
175  return false;
176  }
177  }
178  else
179  {
180  // Report that we failed to write the PID parameters to RAM
181  #if SERIAL_PRINT
182  errorMessage();
183  Serial.println("In the function writePIDParametersToRAM no reply was received");
184  #endif
185 
186  return false;
187  }
188 }
189 
191 {
192  // Convert the desired shaft angle in radians into an inner motor angle in 0.01 degrees
193  double inner_motor_reference_angle = -(_angle - position_offset)*180.0/M_PI*GEAR_REDUCTION*100;
194 
195  // Store the postion reference for debugging
196  raw_position_reference = inner_motor_reference_angle;
197 
198  // Create a position control can message
199  make_can_msg::positionControl1(can_message.buf, inner_motor_reference_angle);
200 
201  // Send the CAN message
203 }
204 
206 {
207  // The motor has defined a CW rotation as the positive rotation direction. We use CCW as the positve rotation direction.
208  _speed = -_speed;
209 
210  // Convert speed in rad/s to 0.01 deg/sec.
211  int32_t speed_001dps = (int)round(GEAR_REDUCTION*_speed*100.0*180.0/M_PI);
212 
213  // Create a CAN message for motor speed control
214  make_can_msg::speedControl(can_message.buf, speed_001dps);
215 
216  // Send CAN message
218 }
219 
221 {
222  // The motor has defined a CW rotation as the positive rotation direction. We use CCW as the positve rotation direction.
223  _torque = -_torque;
224 
225  // Saturate desired torque to be within maximum limits
226  if (_torque > max_torque)
227  {
228  _torque = max_torque;
229  }
230  else if (_torque < -max_torque)
231  {
232  _torque = -max_torque;
233  }
234 
235  // Scale the torque (Nm) to torque currents between [-max_torque_current, max_torque_current]
236  int16_t current_torque = (int) round(max_torque_current*_torque/max_torque);
237 
238  torque_current_reference = current_torque;
239 
240  // Create a CAN message for current torque control
241  make_can_msg::torqueCurrentControl(can_message.buf, current_torque);
242 
243  // Send CAN message
245 }
246 
247 void MotorControl::readMotorControlCommandReply(unsigned char* _can_message)
248 {
249  // Encoder position. 16 bits represents one inner motor rotation
250  uint16_t new_encoder_value = _can_message[7]*256 + _can_message[6];
251 
252  // Update the number of completed turns for the inner motor
254 
255  // Update the latest received encoder value as the innerMotorTurnCompleted function has already been executed
256  previous_encoder_value = new_encoder_value;
257 
258  // Update the shaft position in radians
259  //position = (number_of_inner_motor_rotations + 1.0 - (double)new_encoder_value/(double)max_encoder_value)*ROTATION_DISTANCE*M_PI/180.0 - position_offset;
260 
261  position = -((double)new_encoder_value/(double)max_encoder_value - 1.0 - number_of_inner_motor_rotations)*ROTATION_DISTANCE*M_PI/180.0 + position_offset;
262 
263  // Get the speed of the inner motor in degrees per second
264  int16_t speed_motor_dps = _can_message[5]*256 + _can_message[4];
265 
266  // Convert the motor speed to shaft speed in radians per second
267  speed = -(double)(speed_motor_dps)*M_PI/(180.0*GEAR_REDUCTION);
268 
269  // Get the torque current
270  int16_t torque_current = _can_message[3]*256 + _can_message[2];
271 
272  torque_current_measured = torque_current;
273 
274  // Convert the torque current into torque through a linear scaling
275  torque = -max_torque*torque_current/max_torque_current;
276 }
277 
279 {
280  // Create a CAN Message instructing the motor to stop
282 
283  // Send CAN message
285 
286  // Wait 0.010 seconds for a reply from the motor
287  delay_microseconds(10000.0);
288 
290  {
292  {
293  return true;
294  }
295  else
296  {
297  // Report that the reply we received was incorrect
298  #if SERIAL_PRINT
299  errorMessage();
300  Serial.println("In the function stopMotor an incorrect reply was received");
301  #endif
302 
303  return false;
304  }
305  }
306  else
307  {
308  // Report that we failed to stop the motor
309  #if SERIAL_PRINT
310  errorMessage();
311  Serial.println("In the function stopMotor an incorrect reply was received");
312  #endif
313 
314  return false;
315  }
316 }
317 
319 {
320  // Create a CAN message instructing the motor to turn off
322 
323  // Send CAN message
325 
326  // Wait 0.010 seconds for a reply from the motor
327  delay_microseconds(10000.0);
328 
330  {
332  {
333  return true;
334  }
335  else
336  {
337  // Report that the reply we received was incorrect
338  #if SERIAL_PRINT
339  errorMessage();
340  Serial.println("In the function stopMotor an incorrect reply was received");
341  #endif
342 
343  return false;
344  }
345  }
346  else
347  {
348  // Report that we failed to turn off the motor
349  #if SERIAL_PRINT
350  errorMessage();
351  Serial.println("In the function turnOffMotor no reply was received");
352  #endif
353 
354  return false;
355  }
356 }
357 
359 {
360  // Create a CAN message which requests the motor to send back its multi turn angle
362 
363  // Send the CAN message
365 
366  // Wait 0.010 seconds for a reply from the motor
367  delay_microseconds(5000.0);
368 
370  {
372  {
374  *(uint8_t *)(& multi_turn_angle_001lsb) = received_can_message.buf[1];
375  *((uint8_t *)(& multi_turn_angle_001lsb)+1) = received_can_message.buf[2];
376  *((uint8_t *)(& multi_turn_angle_001lsb)+2) = received_can_message.buf[3];
377  *((uint8_t *)(& multi_turn_angle_001lsb)+3) = received_can_message.buf[4];
378  *((uint8_t *)(& multi_turn_angle_001lsb)+4) = received_can_message.buf[5];
379  *((uint8_t *)(& multi_turn_angle_001lsb)+5) = received_can_message.buf[6];
380  *((uint8_t *)(& multi_turn_angle_001lsb)+6) = received_can_message.buf[7];
381 
382  // Convert the multi turn angle from 0.01 deg representation to a 1.0 degree representation
384 
385  // Convert the multi turn angle into a 32 bit int for printing
386  if(multi_turn_angle_001lsb >= 0)
387  {
389  }
390  else
391  {
392  int64_t multi_turn_angle_64_bit_001lsb = - multi_turn_angle_001lsb;
393  multi_turn_angle_32_bit_001lsb = multi_turn_angle_64_bit_001lsb;
395  }
397 
398  return true;
399  }
400  else
401  {
402  // Report that the reply we received was incorrect
403  #if SERIAL_PRINT
404  errorMessage();
405  Serial.println("In the function readMultiTurnAngle an incorrect reply was received");
406  #endif
407 
408  return false;
409  }
410  }
411  else
412  {
413  // Report that no reply was received
414  #if SERIAL_PRINT
415  errorMessage();
416  Serial.println("In the function readMultiTurnAngle no reply was received");
417  #endif
418 
419  return false;
420  }
421 }
422 
424 {
425  //can_message.id = 0x141;
427  //can_port_1.write(can_message);
428 
430 
431  delay_microseconds(10000.0);
432 
433 
434  //Serial.print("can msg id: "); Serial.print(can_message.id, HEX); Serial.print("\t can_msg type:\t"); Serial.print(can_message.buf[0], HEX); Serial.println("");
435 
437  {
438  //Serial.println("Reply received");
440  {
442  return true;
443  }
444  else
445  {
446  // Report that the reply we received was incorrect
447  #if SERIAL_PRINT
448  errorMessage();
449  Serial.println("In the function readMotorStatus an incorrect reply was received");
450  #endif
451 
452  return false;
453  }
454  }
455  else
456  {
457  // Report that no reply was received
458  #if (SERIAL_PRINT)
459  errorMessage();
460  Serial.println("In the function readMotorStatus no reply was received");
461  #endif
462 
463  return false;
464  }
465 }
466 
468 {
469  // Create a CAN message which requests the motor to send back a reply
471 
472  // Send the CAN message
474 }
475 
477 {
479 
481 
482  delay_microseconds(10000.0);
483 
485  {
487  {
488  double encoder_with_offset = received_can_message.buf[3]*256 + received_can_message.buf[0];
489  double encoder_raw = received_can_message.buf[5]*256 + received_can_message.buf[4];
490  double encoder_offset = received_can_message.buf[7]*256 + received_can_message.buf[6];
491 
492  //Serial.print("ENC W/O:\t"); Serial.print(encoder_with_offset); Serial.print("\t");
493  //Serial.print("ENC RAW:\t"); Serial.print(encoder_raw); Serial.print("\t");
494  //Serial.print("ENC OFF:\t"); Serial.print(encoder_offset); Serial.print("\t");
495  //Serial.println("");
496  }
497  else
498  {
499  #if SERIAL_PRINT
500  errorMessage();
501  Serial.println("getCompleteEncoderPosition - wrong reply received.");
502  #endif
503 
504  return false;
505  }
506  }
507  else
508  {
509  #if SERIAL_PRINT
510  errorMessage();
511  Serial.println("getCompleteEncoderPosition - no reply received.");
512  #endif
513 
514  return false;
515  }
516 }
517 
519  double &_kp_pos,
520  double &_ki_pos,
521  double &_kp_speed,
522  double &_ki_speed,
523  double &_kp_torque,
524  double &_ki_torque)
525 {
526  _kp_pos = kp_pos;
527  _ki_pos = ki_pos;
528  _kp_speed = kp_speed;
529  _ki_speed = ki_speed;
530  _kp_torque = kp_torque;
531  _ki_torque = ki_torque;
532 }
533 
535 {
536  #if SERIAL_PRINT
537  Serial.print("Motor "); Serial.print(id); Serial.print(" - CAN "); Serial.print(can_port_id); Serial.println(" ");
538  Serial.print("kp_pos:\t"); Serial.println(kp_pos);
539  Serial.print("ki_pos:\t"); Serial.println(ki_pos);
540  Serial.print("kp_vel:\t"); Serial.println(kp_speed);
541  Serial.print("kp_vel:\t"); Serial.println(ki_speed);
542  Serial.print("kp_tor:\t"); Serial.println(kp_torque);
543  Serial.print("kp_tor:\t"); Serial.println(ki_torque);
544  #endif
545 }
546 
548 {
549  #if SERIAL_PRINT
550  Serial.print("Motor "); Serial.print(id); Serial.print(", CAN "); Serial.print(can_port_id); Serial.print(" - ");
551  Serial.print("Pos: "); Serial.print(position); Serial.print("[rad]\t");
552  Serial.print("Vel: "); Serial.print(speed); Serial.print("[rad/s]\t");
553  Serial.print("Tor: "); Serial.print(torque); Serial.print("[Nm]\t");
554  Serial.print("MTA: "); Serial.print(multi_turn_angle_32_bit); Serial.print("\t");
555  Serial.print("Enc: "); Serial.print(previous_encoder_value); Serial.print("\t");
556  Serial.print("IMR: "); Serial.print(number_of_inner_motor_rotations); Serial.print("\t");
557  Serial.println("");
558  #endif
559 
560 }
561 
562 void MotorControl::setTorqueCurrent(int _torque_current)
563 {
564  _torque_current = -_torque_current;
565 
566  if(_torque_current > max_torque_current)
567  {
568  _torque_current = max_torque_current;
569  }
570  else if(_torque_current < -max_torque_current)
571  {
572  _torque_current = -max_torque_current;
573  }
574 
575  //Serial.print(_torque_current); Serial.print("\t");
576 
577  make_can_msg::torqueCurrentControl(can_message.buf, _torque_current);
578 
580 }
581 
582 double MotorControl::innerMotorTurnCompleted(uint16_t _previous_encoder_value, uint16_t _new_encoder_value)
583 {
584  // If old >> new a CW turn was completed
585  if(_previous_encoder_value - _new_encoder_value > encoder_turn_threshold)
586  {
587  return -1.0;
588  }
589  // If new >> old a CCW turn was completed
590  else if(_new_encoder_value - _previous_encoder_value > encoder_turn_threshold)
591  {
592  return 1.0;
593  }
594  // No turn was completed
595  else
596  {
597  return 0.0;
598  }
599 }
600 
601 void MotorControl::sendMessage(CAN_message_t _can_message)
602 {
603  if(can_port_id == CAN_PORT_1)
604  {
605  can_port_1.write(_can_message);
606  }
607  else if(can_port_id == CAN_PORT_2)
608  {
609  can_port_2.write(_can_message);
610  }
611  else
612  {
613  #if SERIAL_PRINT
614  errorMessage();
615  Serial.println("In the function sendMessage, an invalid CAN port was selected");
616  #endif
617 
618  while(true);
619  }
620 }
621 
622 int MotorControl::readMessage(CAN_message_t &_can_message)
623 {
624  if(can_port_id == 1)
625  {
626  return can_port_1.read(_can_message);
627  }
628  else if(can_port_id == 2)
629  {
630  return can_port_2.read(_can_message);
631  }
632  else
633  {
634  #if SERIAL_PRINT
635  errorMessage();
636  Serial.println("In the function readMessage, an invalid CAN port was selected");
637  #endif
638  }
639 }
640 
642 {
643  //Serial.print("ERROR - Motor "); Serial.print(id);
644  //Serial.print(", CAN "); Serial.print(can_port_id);
645  //Serial.print("-\t");
646 }
double kp_speed
Definition: motor_driver.h:164
double initial_number_of_inner_motor_rotations
Number of rotations of the inner motor during startup This is needed to create position commands.
Definition: motor_driver.h:171
void setSpeedReference(double _speed)
Set the desired motor speed.
void errorMessage()
int16_t torque_current_measured
The measured torque current of the motor.
Definition: motor_driver.h:220
bool turnOffMotor()
The motor stops exerting a control output. The motor's operating state and previously received contro...
bool stopMotor()
The motor stops exerting a control output. The motor will remain idle until a new control command or ...
double max_torque
The torque in Newton meter has to lie in the interval [-max_torque, max_torque].
Definition: motor_driver.h:233
uint16_t address
Motor address = ID + 0x140. Declared for convenience.
Definition: motor_driver.h:156
CAN_message_t can_message
CAN message for this motor. Only buf field is set for every message sent.
Definition: motor_driver.h:224
void printPIDParameters()
The function prints the PI parameters of the motor.
bool readMotorStatus()
A request is sent to the motor asking it to send back its state. The private state varibles position,...
int64_t multi_turn_angle_001lsb
This is the 64 bit representation of the raw int64_t multi turn angle in 0.01 degrees.
Definition: motor_driver.h:191
uint16_t previous_encoder_value
Keep track of previous encoder position. This way you can detect turns. See innerMotorTurnCompleted()...
Definition: motor_driver.h:179
double ki_torque
Definition: motor_driver.h:167
double kp_torque
Definition: motor_driver.h:166
double number_of_inner_motor_rotations
Number of rotations of the built-in motor, not the output-shaft.
Definition: motor_driver.h:175
void setTorqueCurrent(int _torque_current)
Set the desired motor torque current. Scales linearly with torque.
uint8_t can_port_id
Used to decide which CAN port to use. Possible values {1, 2}.
Definition: motor_driver.h:159
void readMotorControlCommandReply(unsigned char *_can_message)
The state variables position, velocity, and torque are updated based on the latest information from t...
int readMessage(CAN_message_t &_can_message)
This function ensures that the message on the correct CAN port is read.
MotorControl()
Default constructor needed for initializing array.
Definition: motor_driver.h:17
int max_torque_current
All torque values are scaled to the interval [-max_torque_current, max_torque_current].
Definition: motor_driver.h:237
int16_t torque_current_reference
The torque current reference set for the motor.
Definition: motor_driver.h:217
bool readPIDParameters()
Read the motor PI parameters and update the private PI values.
double torque
Latest measured shaft torque in Nm.
Definition: motor_driver.h:211
double innerMotorTurnCompleted(uint16_t _previous_encoder_value, uint16_t _new_encoder_value)
This function checks if the inner motor completed a turn. Depending on the difference between the new...
double ki_speed
Definition: motor_driver.h:165
double speed
Latest measured speed of the shaft in radians/second.
Definition: motor_driver.h:208
bool writePIDParametersToRAM(double _kp_pos, double _ki_pos, double _kp_speed, double _ki_speed, double _kp_torque, double _ki_torque)
Set the motor PI parameters and store them temporary in RAM.
double raw_position_reference
Definition: motor_driver.h:150
void requestMotorStatus()
A request is sent to the motor asking it to send back its state, position, velocity,...
void getPIDParameters(double &_kp_pos, double &_ki_pos, double &_kp_speed, double &_ki_speed, double &_kp_torque, double &_ki_torque)
The input parameters are updated to match the PI parameters of the motor.
int32_t multi_turn_angle_32_bit_001lsb
This variable contains the 32 bit representation of the raw 0.01 degree multi turn angle.
Definition: motor_driver.h:185
double position
Latest measured position of the shaft in radians.
Definition: motor_driver.h:205
double position_offset
position offset = true position - position
Definition: motor_driver.h:199
uint16_t encoder_turn_threshold
If the change in encoder value is larger than this it is safe to assume that the inner motor complete...
Definition: motor_driver.h:245
int64_t multi_turn_angle_64_bit
This is the true 64 bit representation of the multi turn angle in degrees.
Definition: motor_driver.h:188
bool readCompleteEncoderPosition()
All the encoder data from the motor is read and printed. This includes the encoder value,...
CAN_message_t received_can_message
CAN message object used to receive incomming CAN messages.
Definition: motor_driver.h:227
void setPositionReference(double _angle)
Set the desired multi turn motor angle.
void sendMessage(CAN_message_t _can_message)
This function ensures that the message is sent to the correct CAN port.
void printState()
The ID, encoder value, multi turn angle, position, speed and torque of the motor are printed.
void setTorqueReference(double _torque)
Set the desired motor torque.
bool readMultiTurnAngle()
The multi turn angle of the motor updated.
int32_t multi_turn_angle_32_bit
This variable contains the multi turn angle read by the motor in degrees.
Definition: motor_driver.h:182
double kp_pos
Motor PID Parameters.
Definition: motor_driver.h:162
uint16_t max_encoder_value
The 16 bit encoder measures the position of the inner DC motor, not the shaft.
Definition: motor_driver.h:241
#define MOTOR_ADDRESS_OFFSET
const int MAX_ENCODER_VALUE
const double MAX_TORQUE
#define MOTOR_COMMAND_MOTOR_STOP
#define MOTOR_COMMAND_MOTOR_OFF
#define MOTOR_COMMAND_READ_MULTI_TURN_ANGLE
const int MAX_TORQUE_CURRENT
#define MOTOR_COMMAND_READ_MOTOR_STATUS_2
#define MOTOR_COMMAND_READ_PID_PARAMETERS
#define MOTOR_COMMAND_WRITE_PID_PARAMETERS_TO_RAM
const double GEAR_REDUCTION
#define MOTOR_COMMAND_READ_ENCODER_POSITION
const double ROTATION_DISTANCE
void motorStop(unsigned char *_can_message)
The function creates a CAN data message that can be used to request the motor to stop its controller ...
void readMotorStatus2(unsigned char *_can_message)
The function creates a CAN data message that can be used to request the motor to send back a CAN mess...
void torqueCurrentControl(unsigned char *_can_message, int16_t _torque_current)
The function creates a CAN data message that can be used to set the motor's torque current....
void speedControl(unsigned char *_can_message, int32_t speed)
The function creates a CAN data message that can be used to set the motor's speed....
void positionControl1(unsigned char *_can_message, int32_t _multiturn_angle)
The function creates a CAN data message that can be used to set the motor's multi turn position....
void motorOff(unsigned char *_can_message)
The function creates a CAN data message that can be used to request the motor to turn off its control...
void readMultiTurnAngle(unsigned char *_can_message)
The function creates a CAN data message that can be used to request the motor to send back a CAN mess...
void readPIDParameters(unsigned char *_can_message)
The function creates a CAN data message that can be used to request the motor to send back a CAN mess...
Definition: make_can_msg.cpp:3
void readEncoderPosition(unsigned char *_can_message)
The function creates a CAN data message that can be used to request the motor to send back a CAN mess...
void writePIDParametersToRAM(unsigned char *_can_message, unsigned char _k_p_position, unsigned char _k_i_position, unsigned char _k_p_speed, unsigned char _k_i_speed, unsigned char _k_p_torque, unsigned char _k_i_torque)
The function creates a CAN data message that can be used to set the PI parameters of the motor in RAM...
Definition: make_can_msg.cpp:9
const uint8_t CAN_PORT_1
static FlexCAN_T4< CAN1, RX_SIZE_256, TX_SIZE_16 > can_port_1
static FlexCAN_T4< CAN2, RX_SIZE_256, TX_SIZE_16 > can_port_2
const uint8_t CAN_PORT_2
void delay_microseconds(double microsecond_delay)
Performs a nonblocking delay in the program Functions such as delay might block some interrupts.
Definition: utilities.cpp:3