Laelaps  2.3.5
RoadNarrows Robotics Small Outdoor Mobile Robot Project
laePowertrain.cxx
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: Laelaps
4 //
5 // Library: liblaelaps
6 //
7 // File: laePowertrain.cxx
8 //
9 /*! \file
10  *
11  * $LastChangedDate: 2016-02-15 15:44:49 -0700 (Mon, 15 Feb 2016) $
12  * $Rev: 4320 $
13  *
14  * \brief Laelaps powertrain class implementations.
15  *
16  * \author Robin Knight (robin.knight@roadnarrows.com)
17  *
18  * \par Copyright
19  * \h_copy 2015-2017. RoadNarrows LLC.\n
20  * http://www.roadnarrows.com\n
21  * All Rights Reserved
22  */
23 /*
24  * @EulaBegin@
25  *
26  * Unless otherwise stated explicitly, all materials contained are copyrighted
27  * and may not be used without RoadNarrows LLC's written consent,
28  * except as provided in these terms and conditions or in the copyright
29  * notice (documents and software) or other proprietary notice provided with
30  * the relevant materials.
31  *
32  * IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY
33  * MEMBERS/EMPLOYEES/CONTRACTORS OF ROADNARROWS OR DISTRIBUTORS OF THIS SOFTWARE
34  * BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
35  * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
36  * DOCUMENTATION, EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN
37  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38  *
39  * THE AUTHORS AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
40  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
41  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
42  * "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
43  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
44  *
45  * @EulaEnd@
46  */
47 ////////////////////////////////////////////////////////////////////////////////
48 
49 #include <stdio.h>
50 #include <unistd.h>
51 
52 #include <string>
53 
54 #include "rnr/rnrconfig.h"
55 #include "rnr/log.h"
56 
57 #include "Laelaps/RoboClaw.h"
58 
59 #include "Laelaps/laelaps.h"
60 #include "Laelaps/laeTune.h"
61 #include "Laelaps/laeUtils.h"
62 #include "Laelaps/laeMotor.h"
63 #include "Laelaps/laeDesc.h"
64 #include "Laelaps/laeDb.h"
65 #include "Laelaps/laePowertrain.h"
66 
67 using namespace std;
68 using namespace laelaps;
69 using namespace motor::roboclaw;
70 
71 
72 // -----------------------------------------------------------------------------
73 // Class LaePowertrainAttr
74 // -----------------------------------------------------------------------------
75 
76 LaePowertrainAttr::LaePowertrainAttr()
77 {
78  // (derived) attributes
79  m_nMotorId = LaeMotorIdNone;
80  m_nMotorCtlrId = LaeMotorCtlrIdNone;
81  m_nMotorIndex = 0;
82 
83  m_nMotorDir = LaeMotorDirNormal;
84  m_eJointType = LaeJointTypeUnknown;
85 
86  m_uPulsesPerRev = 0.0;
87  m_fGearRatio = 1.0;
88  m_fMaxRps = 0.0;
89  m_uMaxQpps = 1;
90  m_fMotorRadsPerPulse = 0.0;
91  m_fWheelRadsPerPulse = 0.0;
92 
93  m_fMaxAmps = 0.0;
94  m_fStallTorque = 0.0;
95 
96  m_fTireRadius = 0.0;
97  m_fTireWidth = 0.0;
98  m_fMetersPerPulse = 0.0;
99  m_fMetersPerRadian = 0.0;
100 }
101 
102 LaePowertrainAttr::LaePowertrainAttr(const LaePowertrainAttr &src)
103 {
104  // (derived) attributes
105  m_nMotorId = src.m_nMotorId;
106  m_nMotorCtlrId = src.m_nMotorCtlrId;
107  m_nMotorIndex = src.m_nMotorIndex;
108 
109  m_nMotorDir = src.m_nMotorDir;
110  m_eJointType = src.m_eJointType;
111 
112  m_uPulsesPerRev = src.m_uPulsesPerRev;
113  m_fGearRatio = src.m_fGearRatio;
114  m_fMaxRps = src.m_fMaxRps;
115  m_uMaxQpps = src.m_uMaxQpps;
116  m_fMotorRadsPerPulse = src.m_fMotorRadsPerPulse;
117  m_fWheelRadsPerPulse = src.m_fWheelRadsPerPulse;
118 
119  m_fMaxAmps = src.m_fMaxAmps;
120  m_fStallTorque = src.m_fStallTorque;
121 
122  m_fTireRadius = src.m_fTireRadius;
123  m_fTireWidth = src.m_fTireWidth;
124  m_fMetersPerPulse = src.m_fMetersPerPulse;
125  m_fMetersPerRadian = src.m_fMetersPerRadian;
126 }
127 
128 LaePowertrainAttr LaePowertrainAttr::operator=(const LaePowertrainAttr &rhs)
129 {
130  // (derived) attributes
131  m_nMotorId = rhs.m_nMotorId;
132  m_nMotorCtlrId = rhs.m_nMotorCtlrId;
133  m_nMotorIndex = rhs.m_nMotorIndex;
134 
135  m_nMotorDir = rhs.m_nMotorDir;
136  m_eJointType = rhs.m_eJointType;
137 
138  m_uPulsesPerRev = rhs.m_uPulsesPerRev;
139  m_fMaxRps = rhs.m_fMaxRps;
140  m_uMaxQpps = rhs.m_uMaxQpps;
141  m_fMotorRadsPerPulse = rhs.m_fMotorRadsPerPulse;
142  m_fGearRatio = rhs.m_fGearRatio;
143  m_fWheelRadsPerPulse = rhs.m_fWheelRadsPerPulse;
144 
145  m_fMaxAmps = rhs.m_fMaxAmps;
146  m_fStallTorque = rhs.m_fStallTorque;
147 
148  m_fTireRadius = rhs.m_fTireRadius;
149  m_fTireWidth = rhs.m_fTireWidth;
150  m_fMetersPerPulse = rhs.m_fMetersPerPulse;
151  m_fMetersPerRadian = rhs.m_fMetersPerRadian;
152 
153  return *this;
154 }
155 
156 // -----------------------------------------------------------------------------
157 // Class LaePowertrainState
158 // -----------------------------------------------------------------------------
159 
160 LaePowertrainState::LaePowertrainState()
161 {
162  m_nEncoder = 0;
163  m_nSpeed = 0;
164  m_fTemp = 0.0;
165  m_fVolts = 0.0;
166  m_fAmps = 0.0;
167  m_uBufLen = 0;
168  m_uAlarms = LaeMotorAlarmNone;
169  m_uWarnings = LaeMotorWarnNone;
170 
171  m_fPosition = 0.0;
172  m_fVelocity = 0.0;
173  m_fTorque = 0.0;
174  m_fPe = 0.0;
175  m_fPm = 0.0;
176 }
177 
178 LaePowertrainState::LaePowertrainState(const LaePowertrainState &src)
179 {
180  m_nEncoder = src.m_nEncoder;
181  m_nSpeed = src.m_nSpeed;
182  m_fTemp = src.m_fTemp;
183  m_fVolts = src.m_fVolts;
184  m_fAmps = src.m_fAmps;
185  m_uBufLen = src.m_uBufLen;
186  m_uAlarms = src.m_uAlarms;
187  m_uWarnings = src.m_uWarnings;
188 
189  m_fPosition = src.m_fPosition;
190  m_fVelocity = src.m_fVelocity;
191  m_fTorque = src.m_fTorque;
192  m_fPe = src.m_fPe;
193  m_fPm = src.m_fPm;
194 }
195 
196 LaePowertrainState LaePowertrainState::operator=(const LaePowertrainState &rhs)
197 {
198  m_nEncoder = rhs.m_nEncoder;
199  m_nSpeed = rhs.m_nSpeed;
200  m_fTemp = rhs.m_fTemp;
201  m_fVolts = rhs.m_fVolts;
202  m_fAmps = rhs.m_fAmps;
203  m_uBufLen = rhs.m_uBufLen;
204  m_uWarnings = rhs.m_uWarnings;
205 
206  m_fPosition = rhs.m_fPosition;
207  m_fVelocity = rhs.m_fVelocity;
208  m_fTorque = rhs.m_fTorque;
209  m_fPe = rhs.m_fPe;
210  m_fPm = rhs.m_fPm;
211 
212  return *this;
213 }
214 
215 
216 // -----------------------------------------------------------------------------
217 // Class LaePowertrain
218 // -----------------------------------------------------------------------------
219 
220 LaePowertrain::LaePowertrain()
221 {
222 }
223 
224 LaePowertrain::LaePowertrain(const LaePowertrain &src)
225 {
226  m_strName = src.m_strName;
227  m_attr = src.m_attr;
228  m_state = src.m_state;
229 }
230 
231 LaePowertrain LaePowertrain::operator=(const LaePowertrain &rhs)
232 {
233  m_strName = rhs.m_strName;
234  m_attr = rhs.m_attr;
235  m_state = rhs.m_state;
236 
237  return *this;
238 }
239 
240 int LaePowertrain::toMotorId(const int nCtlrId, const int nMotorIndex)
241 {
242  switch( nCtlrId )
243  {
244  case LaeMotorCtlrIdFront:
245  switch( nMotorIndex )
246  {
247  case LaeMotorLeft:
248  return LaeMotorIdLF;
249  case LaeMotorRight:
250  return LaeMotorIdRF;
251  default:
252  break;
253  }
254  break;
255  case LaeMotorCtlrIdRear:
256  switch( nMotorIndex )
257  {
258  case LaeMotorLeft:
259  return LaeMotorIdLR;
260  case LaeMotorRight:
261  return LaeMotorIdRR;
262  default:
263  break;
264  }
265  break;
266  default:
267  break;
268  }
269 
270  return LaeMotorIdNone;
271 }
272 
273 string LaePowertrain::toKey(const int nCtlrId, const int nMotorIndex)
274 {
275  static const char *unknown = "";
276 
277  int nMotorId = LaePowertrain::toMotorId(nCtlrId, nMotorIndex);
278 
279  return nMotorId != LaeMotorIdNone? LaeDesc::KeyPowertrain[nMotorId]: unknown;
280 }
281 
282 string LaePowertrain::toKey(const int nMotorId)
283 {
284  static const char *unknown = "";
285 
286  if( (nMotorId >= 0) && (nMotorId < LaeMotorsNumOf) )
287  {
288  LaeDesc::KeyPowertrain[nMotorId];
289  }
290  else
291  {
292  return unknown;
293  }
294 }
295 
296 int LaePowertrain::configure(const LaeDescPowertrain &desc)
297 {
298  // from description
299  m_strName = desc.m_strKey;
300  m_attr.m_nMotorId = desc.m_nMotorId;
301  m_attr.m_nMotorCtlrId = desc.m_nMotorCtlrId;
302  m_attr.m_nMotorIndex = desc.m_nMotorIndex;
303  m_attr.m_eJointType = desc.m_eJointType;
304  m_attr.m_nMotorDir = desc.m_nDir;
305  m_attr.m_fGearRatio = desc.m_fGearRatio;
306 
307  // from motor specs and derivations
308  m_attr.m_uPulsesPerRev = LaeQuadPulsesPerRev;
309  m_attr.m_fMaxRps = LaeMotorRatedMaxRpm / 60.0;
310  m_attr.m_uMaxQpps = (uint_t)(
311  m_attr.m_fMaxRps *
312  m_attr.m_fGearRatio *
313  (double)m_attr.m_uPulsesPerRev);
314 
315  m_attr.m_fMotorRadsPerPulse = M_TAU / (double)m_attr.m_uPulsesPerRev;
316  m_attr.m_fWheelRadsPerPulse = m_attr.m_fMotorRadsPerPulse /
317  m_attr.m_fGearRatio;
318 
319  m_attr.m_fMaxAmps = LaeMotorRatedAmps;
320  m_attr.m_fStallTorque = LaeMotorStallTorque;
321 
322  return LAE_OK;
323 }
324 
325 int LaePowertrain::configure(const LaeTunes &tunes)
326 {
327  double fTuneTireRadius, fTuneTireWidth; // tire dimensions
328 
329  tunes.getTireDimParams(m_strName, fTuneTireRadius, fTuneTireWidth);
330 
331  m_attr.m_fTireRadius = fTuneTireRadius;
332  m_attr.m_fTireWidth = fTuneTireWidth;
333 
334  m_attr.m_fMetersPerPulse = m_attr.m_fWheelRadsPerPulse * m_attr.m_fTireRadius;
335  m_attr.m_fMetersPerRadian = m_attr.m_fTireRadius;
336 
337  return LAE_OK;
338 }
339 
340 int LaePowertrain::reload(const LaeTunes &tunes)
341 {
342  return configure(tunes);
343 }
344 
345 int LaePowertrain::resetOdometer()
346 {
347  m_state.m_nEncoder = 0;
348  m_state.m_fPosition = 0.0;
349 
350  RtDb.m_kin.m_powertrain[m_attr.m_nMotorId].m_fPosition = m_state.m_fPosition;
351 }
352 
353 int LaePowertrain::updateStateDynamics(s64_t nEncoder,
354  s32_t nSpeed,
355  double fAmps,
356  uint_t uBufLen)
357 {
358  m_state.m_nEncoder = nEncoder;
359  m_state.m_nSpeed = nSpeed;
360 
361  if( fAmps <= motor::roboclaw::ParamAmpMax )
362  {
363  m_state.m_fAmps = fAmps;
364  }
365 
366  // command buffer length (commands)
367  m_state.m_uBufLen = uBufLen;
368 
369  // angular position and velocity
370  m_state.m_fPosition = (double)m_state.m_nEncoder *
371  m_attr.m_fWheelRadsPerPulse;
372  m_state.m_fVelocity = (double)m_state.m_nSpeed *
373  m_attr.m_fWheelRadsPerPulse;
374 
375  // power
376  m_state.m_fPe = m_state.m_fAmps * m_state.m_fVolts;
377  m_state.m_fPm = m_state.m_fPe * 1.0; // TODO need efficiency curve
378 
379  // torque
380  if( fabs(m_state.m_fVelocity) >= 0.0001 )
381  {
382  m_state.m_fTorque = m_state.m_fPm / m_state.m_fVelocity;
383  }
384  else if( m_state.m_fAmps >= (m_attr.m_fMaxAmps * 0.9) )
385  {
386  m_state.m_fTorque = m_attr.m_fStallTorque *
387  m_state.m_fAmps /
388  m_attr.m_fMaxAmps;
389  }
390  else
391  {
392  m_state.m_fTorque = 0.0;
393  }
394 
395  m_state.m_fTorque = fcap(m_state.m_fTorque, -m_attr.m_fStallTorque,
396  m_attr.m_fStallTorque);
397 
398  // update real-time database
399  RtDb.m_kin.m_powertrain[m_attr.m_nMotorId].m_nEncoder = m_state.m_nEncoder;
400  RtDb.m_kin.m_powertrain[m_attr.m_nMotorId].m_nSpeed = m_state.m_nSpeed;
401  RtDb.m_kin.m_powertrain[m_attr.m_nMotorId].m_fPosition = m_state.m_fPosition;
402  RtDb.m_kin.m_powertrain[m_attr.m_nMotorId].m_fVelocity = m_state.m_fVelocity;
403  RtDb.m_kin.m_powertrain[m_attr.m_nMotorId].m_fPe = m_state.m_fPe;
404  RtDb.m_kin.m_powertrain[m_attr.m_nMotorId].m_fTorque = m_state.m_fTorque;
405 }
406 
407 int LaePowertrain::updateHealth(double fVolts, double fTemp, uint_t uCtlrStatus)
408 {
409  m_state.m_fVolts = fVolts;
410  m_state.m_fTemp = fTemp;
411 
412  m_state.m_uAlarms = LaeMotorAlarmNone;
413  m_state.m_uWarnings = LaeMotorWarnNone;
414 
415  //
416  // Errors and/or warnings exist.
417  //
418  if( uCtlrStatus != ParamStatusNormal )
419  {
420  // emergency stopped
421  if( uCtlrStatus & ParamStatusEStopped )
422  {
423  m_state.m_uAlarms |= LaeMotorAlarmEStop;
424  }
425 
426  // temperature alarms
427  if( uCtlrStatus & ParamStatusErrTemp )
428  {
429  m_state.m_uAlarms |= LaeMotorAlarmTemp;
430  }
431 
432  // temperature warnings
433  if( uCtlrStatus & ParamStatusWarnTemp )
434  {
435  m_state.m_uWarnings |= LaeMotorWarnTemp;
436  }
437 
438  // voltage alarms
439  if( uCtlrStatus & ( ParamStatusErrMainBattHigh |
442  {
443  m_state.m_uAlarms |= LaeMotorAlarmVoltage;
444  }
445 
446  // voltage warnings
447  if( uCtlrStatus & ( ParamStatusWarnMainBattHigh |
449  {
450  m_state.m_uWarnings |= LaeMotorWarnVoltage;
451  }
452 
453  if( m_attr.m_nMotorIndex == Motor1 )
454  {
455  // motor 1 drive fault alarm
456  if( uCtlrStatus & ParamStatusErrMot1Fault )
457  {
458  m_state.m_uAlarms |= LaeMotorAlarmFault;
459  }
460 
461  // motor 1 over current warning
462  if( uCtlrStatus & ParamStatusWarnMot1OverCur )
463  {
464  m_state.m_uWarnings |= LaeMotorWarnCurrent;
465  }
466  }
467  else if( m_attr.m_nMotorIndex == Motor2 )
468  {
469  // motor 2 drive fault alarm
470  if( uCtlrStatus & ParamStatusErrMot2Fault )
471  {
472  m_state.m_uAlarms |= LaeMotorAlarmFault;
473  }
474 
475  // motor 2 over current warning
476  if( uCtlrStatus & ParamStatusWarnMot2OverCur )
477  {
478  m_state.m_uWarnings |= LaeMotorWarnCurrent;
479  }
480  }
481  }
482 
483  // TODO torque calculation
484  m_state.m_fTorque = 0.0;
485 
486  m_state.m_fPe = m_state.m_fAmps * m_state.m_fVolts;
487  m_state.m_fPm = m_state.m_fPe * 1.0; // TODO need efficiency curve
488 }
logic battery under volt error
Definition: RoboClaw.h:322
double m_fAmps
motor draw (A)
int m_nMotorCtlrId
motor controller id
Definition: laePowertrain.h:81
Laelaps tuning data class.
Definition: laeTune.h:566
double m_fTemp
motor temperature (C)
int m_eJointType
joint type
Definition: laePowertrain.h:85
uint_t m_uBufLen
command queue length
double m_fGearRatio
gear ratio
Definition: laePowertrain.h:88
double fcap(double a, double min, double max)
Cap value within limits [min, max].
Definition: laeUtils.h:162
double m_fTorque
wheel torque (N-m)
double m_fMaxAmps
maximum rated amps
Definition: laePowertrain.h:94
std::string m_strName
powertrain unique name (key)
int m_nMotorIndex
motor controller unique motor index
Definition: laePowertrain.h:82
RoboClaw motor controller class interface.
double m_fMaxRps
maximum rated output shaft rev/sec
Definition: laePowertrain.h:89
int m_nMotorDir
motor normalized direction
Definition: laePowertrain.h:84
double m_fTireRadius
tire radius
Definition: laePowertrain.h:97
double m_fPm
motor output mechanical power (W)
LaeJointType m_eJointType
powertrain joint type
Definition: laeDesc.h:184
double m_fMotorRadsPerPulse
motor radians per encoder pulse
Definition: laePowertrain.h:91
temperature out-of-range error
Definition: RoboClaw.h:318
uint_t m_uWarnings
motor warnings
int m_nDir
normalize cw/ccw direction.
Definition: laeDesc.h:187
Powertrain attributes class.
Definition: laePowertrain.h:76
double m_fStallTorque
stall torque amps
Definition: laePowertrain.h:95
LaePowertrainAttr m_attr
semi-fixed attribute data
Robotic powertrain description.
Definition: laeDesc.h:177
main battery over voltage error
Definition: RoboClaw.h:320
The <b><i>Laelaps</i></b> namespace encapsulates all <b><i>Laelaps</i></b> related constructs...
Definition: laeAlarms.h:64
Laelaps robotic base mobile platform description class interface.
uint_t m_uMaxQpps
maximum quadrature pulses/second rps
Definition: laePowertrain.h:90
temp out-of-range warning
Definition: RoboClaw.h:327
uint_t m_uAlarms
motor alarms
Laelaps common utilities.
double m_fVolts
input motor voltage (V)
main battery over volt warning
Definition: RoboClaw.h:325
Laelaps powertrain class interfaces.
int m_nMotorId
unique robot motor id
Definition: laeDesc.h:181
Laelaps tuning.
s64_t m_nEncoder
motor encoder position (quad pulses)
double m_fMetersPerRadian
tire meters per radian
double m_fGearRatio
motor gear ratio
Definition: laeDesc.h:186
main battery under volt warning
Definition: RoboClaw.h:326
double m_fMetersPerPulse
tire meters per encoder pulse
Definition: laePowertrain.h:99
int m_nMotorCtlrId
unique motor controller id
Definition: laeDesc.h:182
Laelaps motors, encoder, and controllers hardware abstraction interfaces.
logic battery over volt error
Definition: RoboClaw.h:321
std::string m_strKey
powertrain key
Definition: laeDesc.h:180
motor 1 over current warning
Definition: RoboClaw.h:315
double m_fWheelRadsPerPulse
output shaft radians per encoder pulse
Definition: laePowertrain.h:92
Powertrain state data class.
LaePowertrainState m_state
dynamic state data
double m_fPe
motor input electrical power (W)
double m_fTireWidth
tire width
Definition: laePowertrain.h:98
double m_fVelocity
wheel angular velocity (radians/second)
#define M_TAU
tau = 2 * pi
Definition: laeUtils.h:66
int m_nMotorIndex
motor controller unique motor index
Definition: laeDesc.h:183
uint_t m_uPulsesPerRev
encoder pulses per motor revolution
Definition: laePowertrain.h:87
motor 2 over current warning
Definition: RoboClaw.h:316
double m_fPosition
wheel angular position (radians)
void getTireDimParams(const std::string &strName, double &fTireRadius, double &fTireWidth) const
Get tire dimensions tune parameters.
Definition: laeTune.cxx:382
Powertrain data class.
Laelaps real-time "database".
int m_nSpeed
raw speed (qpps)
Top-level package include file.
static const double ParamAmpMax
maximum amps
Definition: RoboClaw.h:278