lib_observer.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. /****************************************************************************
  2. * control/lib_observer.c
  3. *
  4. * Copyright (C) 2018 Gregory Nutt. All rights reserved.
  5. * Author: Mateusz Szafoni <raiden00@railab.me>
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in
  15. * the documentation and/or other materials provided with the
  16. * distribution.
  17. * 3. Neither the name NuttX nor the names of its contributors may be
  18. * used to endorse or promote products derived from this software
  19. * without specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  24. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  25. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  26. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  27. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  28. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  29. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  31. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  32. * POSSIBILITY OF SUCH DAMAGE.
  33. *
  34. ****************************************************************************/
  35. /****************************************************************************
  36. * Included Files
  37. ****************************************************************************/
  38. #include <dsp.h>
  39. /****************************************************************************
  40. * Pre-processor Definitions
  41. ****************************************************************************/
  42. #define ANGLE_DIFF_THR (M_PI_F)
  43. /****************************************************************************
  44. * Public Functions
  45. ****************************************************************************/
  46. /****************************************************************************
  47. * Name: motor_observer_init
  48. *
  49. * Description:
  50. * Initialize motor observer
  51. *
  52. * Input Parameters:
  53. * observer - pointer to the common observer data
  54. * ao - pointer to the angle specific observer data
  55. * so - pointer to the speed specific observer data
  56. * per - observer execution period
  57. *
  58. * Returned Value:
  59. * None
  60. *
  61. ****************************************************************************/
  62. void motor_observer_init(FAR struct motor_observer_s *observer,
  63. FAR void *ao, FAR void *so, float per)
  64. {
  65. DEBUGASSERT(observer != NULL);
  66. DEBUGASSERT(ao != NULL);
  67. DEBUGASSERT(so != NULL);
  68. DEBUGASSERT(per > 0.0f);
  69. /* Reset observer data */
  70. memset(observer, 0, sizeof(struct motor_observer_s));
  71. /* Set observer period */
  72. observer->per = per;
  73. /* Connect angle estimation observer data */
  74. observer->ao = ao;
  75. /* Connect speed estimation observer data */
  76. observer->so = so;
  77. }
  78. /****************************************************************************
  79. * Name: motor_observer_smo_init
  80. *
  81. * Description:
  82. * Initialize motor sliding mode observer.
  83. *
  84. * Input Parameters:
  85. * smo - pointer to the sliding mode observer private data
  86. * kslide - SMO gain
  87. * err_max - linear region upper limit
  88. *
  89. * Returned Value:
  90. * None
  91. *
  92. ****************************************************************************/
  93. void motor_observer_smo_init(FAR struct motor_observer_smo_s *smo,
  94. float kslide,
  95. float err_max)
  96. {
  97. DEBUGASSERT(smo != NULL);
  98. DEBUGASSERT(kslide > 0.0f);
  99. DEBUGASSERT(err_max > 0.0f);
  100. /* Reset structure */
  101. memset(smo, 0, sizeof(struct motor_observer_smo_s));
  102. /* Initialize structure */
  103. smo->k_slide = kslide;
  104. smo->err_max = err_max;
  105. }
  106. /****************************************************************************
  107. * Name: motor_observer_smo
  108. *
  109. * Description:
  110. * One step of the SMO observer.
  111. * REFERENCE: http://ww1.microchip.com/downloads/en/AppNotes/01078B.pdf
  112. *
  113. * Below some theoretical backgrounds about SMO.
  114. *
  115. * The digitalized motor model can be represent as:
  116. *
  117. * d(i_s.)/dt = (-R/L)*i_s. + (1/L)*(v_s - e_s. - z)
  118. *
  119. * We compare estimated current (i_s.) with measured current (i_s):
  120. *
  121. * err = i_s. - i_s
  122. *
  123. * and get correction factor (z):
  124. *
  125. * sign = sing(err)
  126. * z = sign*K_SLIDE
  127. *
  128. * Once the digitalized model is compensated, we estimate BEMF (e_s.) by
  129. * filtering z:
  130. *
  131. * e_s. = low_pass(z)
  132. *
  133. * The estimated BEMF is filtered once again and used to approximate the
  134. * motor angle:
  135. *
  136. * e_filtered_s. = low_pass(e_s.)
  137. * theta = arctan(-e_alpha/e_beta)
  138. *
  139. * The estimated theta is phase-shifted due to low pass filtration, so we
  140. * need some phase compensation. More details below.
  141. *
  142. * where:
  143. * v_s - phase input voltage vector
  144. * i_s. - estimated phase current vector
  145. * i_s - phase current vector
  146. * e_s. - estimated phase BEMF vector
  147. * R - motor winding resistance
  148. * L - motor winding inductance
  149. * z - output correction factor voltage
  150. *
  151. * Input Parameters:
  152. * o - (in/out) pointer to the common observer data
  153. * i_ab - (in) inverter alpha-beta current
  154. * v_ab - (in) inverter alpha-beta voltage
  155. * phy - (in) pointer to the motor physical parameters
  156. * dir - (in) rotation direction (1.0 for CW, -1.0 for CCW)
  157. *
  158. * Returned Value:
  159. * None
  160. *
  161. ****************************************************************************/
  162. void motor_observer_smo(FAR struct motor_observer_s *o, FAR ab_frame_t *i_ab,
  163. FAR ab_frame_t *v_ab,
  164. FAR struct motor_phy_params_s *phy, float dir)
  165. {
  166. DEBUGASSERT(o != NULL);
  167. DEBUGASSERT(i_ab != NULL);
  168. DEBUGASSERT(v_ab != NULL);
  169. DEBUGASSERT(phy != NULL);
  170. FAR struct motor_observer_smo_s *smo =
  171. (FAR struct motor_observer_smo_s *)o->ao;
  172. FAR ab_frame_t *emf = &smo->emf;
  173. FAR ab_frame_t *emf_f = &smo->emf_f;
  174. FAR ab_frame_t *z = &smo->z;
  175. FAR ab_frame_t *i_est = &smo->i_est;
  176. FAR ab_frame_t *v_err = &smo->v_err;
  177. FAR ab_frame_t *i_err = &smo->i_err;
  178. FAR ab_frame_t *sign = &smo->sign;
  179. float i_err_a_abs = 0.0f;
  180. float i_err_b_abs = 0.0f;
  181. float angle = 0.0f;
  182. float filter = 0.0f;
  183. /* REVISIT: observer works only when IQ current is high enough */
  184. /* Calculate observer gains */
  185. smo->F = (1.0f - o->per * phy->res * phy->one_by_ind);
  186. smo->G = o->per * phy->one_by_ind;
  187. /* Saturate F gain */
  188. if (smo->F < 0.0f)
  189. {
  190. smo->F = 0.0f;
  191. }
  192. /* Saturate G gain */
  193. if (smo->G > 0.999f)
  194. {
  195. smo->G = 0.999f;
  196. }
  197. /* Configure low pass filters
  198. *
  199. * We tune low-pass filters to achieve cutoff frequency equal to
  200. * input singal frequency. This gives us constant phase shift between
  201. * input and outpu signals equals to:
  202. *
  203. * phi = -arctan(f_in/f_c) = -arctan(1) = -45deg = -PI/4
  204. *
  205. * Input signal frequency is equal to the frequency of the motor currents,
  206. * which give us:
  207. *
  208. * f_c = omega_e/(2*PI)
  209. * omega_m = omega_e/pole_pairs
  210. * f_c = omega_m*pole_pairs/(2*PI)
  211. *
  212. * filter = T * (2*PI) * f_c
  213. * filter = T * omega_m * pole_pairs
  214. *
  215. * T - [s] period at which the digital filter is being
  216. * calculated
  217. * f_in - [Hz] input frequency of the filter
  218. * f_c - [Hz] cutoff frequency of the filter
  219. * omega_m - [rad/s] mechanical angular velocity
  220. * omega_e - [rad/s] electrical angular velocity
  221. * pole_pairs - pole pairs
  222. *
  223. */
  224. filter = o->per * o->speed * phy->p;
  225. /* Limit SMO filters
  226. * REVISIT: lowest filter limit should depend on minimum speed:
  227. * filter = T * (2*PI) * f_c = T * omega0
  228. *
  229. */
  230. if (filter >= 1.0f)
  231. {
  232. filter = 0.99f;
  233. }
  234. else if (filter <= 0.0f)
  235. {
  236. filter = 0.005f;
  237. }
  238. smo->emf_lp_filter1 = filter;
  239. smo->emf_lp_filter2 = smo->emf_lp_filter1;
  240. /* Get voltage error: v_err = v_ab - emf */
  241. v_err->a = v_ab->a - emf->a;
  242. v_err->b = v_ab->b - emf->b;
  243. /* Estimate stator current */
  244. i_est->a = smo->F * i_est->a + smo->G * (v_err->a - z->a);
  245. i_est->b = smo->F * i_est->b + smo->G * (v_err->b - z->b);
  246. /* Get motor current error */
  247. i_err->a = i_ab->a - i_est->a;
  248. i_err->b = i_ab->b - i_est->b;
  249. /* Slide-mode controller */
  250. sign->a = (i_err->a > 0.0f ? 1.0f : -1.0f);
  251. sign->b = (i_err->b > 0.0f ? 1.0f : -1.0f);
  252. /* Get current error absolute value - just multiply value with its sign */
  253. i_err_a_abs = i_err->a * sign->a;
  254. i_err_b_abs = i_err->b * sign->b;
  255. /* Calculate new output correction factor voltage */
  256. if (i_err_a_abs < smo->err_max)
  257. {
  258. /* Enter linear region if error is small enough */
  259. z->a = i_err->a * smo->k_slide / smo->err_max;
  260. }
  261. else
  262. {
  263. /* Non-linear region */
  264. z->a = sign->a * smo->k_slide;
  265. }
  266. if (i_err_b_abs < smo->err_max)
  267. {
  268. /* Enter linear region if error is small enough */
  269. z->b = i_err->b * smo->k_slide / smo->err_max;
  270. }
  271. else
  272. {
  273. /* Non-linear region */
  274. z->b = sign->b * smo->k_slide;
  275. }
  276. /* Filter z to obtain estimated emf */
  277. LP_FILTER(emf->a, z->a, smo->emf_lp_filter1);
  278. LP_FILTER(emf->b, z->b, smo->emf_lp_filter1);
  279. /* Filter emf one more time before angle stimation */
  280. LP_FILTER(emf_f->a, emf->a, smo->emf_lp_filter2);
  281. LP_FILTER(emf_f->b, emf->b, smo->emf_lp_filter2);
  282. /* Estimate phase angle according to:
  283. * emf_a = -|emf| * sin(th)
  284. * emf_b = |emf| * cos(th)
  285. * th = atan2(-emf_a, emf->b)
  286. */
  287. angle = fast_atan2(-emf->a, emf->b);
  288. #if 1
  289. /* Some assertions
  290. * TODO: simplify
  291. */
  292. if (angle != angle) angle = 0.0f;
  293. if (emf->a != emf->a) emf->a = 0.0f;
  294. if (emf->b != emf->b) emf->b = 0.0f;
  295. if (z->a != z->a) z->a = 0.0f;
  296. if (z->b != z->b) z->b = 0.0f;
  297. if (i_est->a != i_est->a) i_est->a = 0.0f;
  298. if (i_est->b != i_est->b) i_est->b = 0.0f;
  299. #endif
  300. /* Angle compensation.
  301. * Due to low pass filtering we have some delay in estimated phase angle.
  302. *
  303. * Adaptive filters introduced above cause -PI/4 phase shift for each
  304. * filter. We use 2 times filtering which give us constant -PI/2 (-90deg)
  305. * phase shift.
  306. */
  307. angle = angle + dir * M_PI_2_F;
  308. /* Normalize angle to range <0, 2PI> */
  309. angle_norm_2pi(&angle, 0.0f, 2.0f*M_PI_F);
  310. /* Store estimated angle in observer data */
  311. o->angle = angle;
  312. }
  313. /****************************************************************************
  314. * Name: motor_sobserver_div_init
  315. *
  316. * Description:
  317. * Initialize DIV speed observer
  318. *
  319. * Input Parameters:
  320. * so - (in/out) pointer to the DIV speed observer data
  321. * sample - (in) number of mechanical angle samples
  322. * filter - (in) low-pass filter for final omega
  323. * per - (in) speed observer execution period
  324. *
  325. * Returned Value:
  326. * None
  327. *
  328. ****************************************************************************/
  329. void motor_sobserver_div_init(FAR struct motor_sobserver_div_s *so,
  330. uint8_t samples,
  331. float filter,
  332. float per)
  333. {
  334. DEBUGASSERT(so != NULL);
  335. DEBUGASSERT(samples > 0);
  336. DEBUGASSERT(filter > 0.0f);
  337. /* Reset observer data */
  338. memset(so, 0, sizeof(struct motor_sobserver_div_s));
  339. /* Store number of samples for DIV observer */
  340. so->samples = samples;
  341. /* Store low-pass filter for DIV observer speed */
  342. so->filter = filter;
  343. /* */
  344. so->one_by_dt = 1.0f / (so->samples * per);
  345. }
  346. /****************************************************************************
  347. * Name: motor_sobserver_div
  348. *
  349. * Description:
  350. * Estimate motor mechanical speed based on motor mechanical angle
  351. * difference.
  352. *
  353. * Input Parameters:
  354. * o - (in/out) pointer to the common observer data
  355. * angle - (in) mechanical angle normalized to <0.0, 2PI>
  356. * dir - (in) mechanical rotation direction. Valid values:
  357. * DIR_CW (1.0f) or DIR_CCW(-1.0f)
  358. *
  359. ****************************************************************************/
  360. void motor_sobserver_div(FAR struct motor_observer_s *o,
  361. float angle, float dir)
  362. {
  363. DEBUGASSERT(o != NULL);
  364. DEBUGASSERT(angle >= 0.0f && angle <= 2*M_PI_F);
  365. DEBUGASSERT(dir == DIR_CW || dir == DIR_CCW);
  366. FAR struct motor_sobserver_div_s *so =
  367. (FAR struct motor_sobserver_div_s *)o->so;
  368. volatile float omega = 0.0f;
  369. /* Get angle diff */
  370. so->angle_diff = angle - so->angle_prev;
  371. /* Correct angle if we crossed angle boundary
  372. * REVISIT:
  373. */
  374. if ((dir == DIR_CW && so->angle_diff < -ANGLE_DIFF_THR) ||
  375. (dir == DIR_CCW && so->angle_diff > ANGLE_DIFF_THR))
  376. {
  377. /* Correction sign depends on rotation direction */
  378. so->angle_diff += dir * 2 * M_PI_F;
  379. }
  380. /* Get absoulte value */
  381. if (so->angle_diff < 0.0f)
  382. {
  383. so->angle_diff = -so->angle_diff;
  384. }
  385. /* Accumulate angle only if sample is valid */
  386. so->angle_acc += so->angle_diff;
  387. /* Increase counter */
  388. so->cntr += 1;
  389. /* Accumulate angle until we get configured number of samples */
  390. if (so->cntr >= so->samples)
  391. {
  392. /* Estimate omega using accumulated angle samples.
  393. * In this case use simple estimation:
  394. *
  395. * omega = delta_theta/delta_time
  396. * speed_now = low_pass(omega)
  397. *
  398. */
  399. omega = so->angle_acc*so->one_by_dt;
  400. /* Store filtered omega.
  401. *
  402. * REVISIT: cut-off frequency for this filter should be
  403. * (probably) set according to minimum supported omega:
  404. *
  405. * filter = T * (2*PI) * f_c = T * omega0
  406. *
  407. * where:
  408. * omega0 - minimum angular speed
  409. * T - speed estimation period (samples*one_by_dt)
  410. */
  411. LP_FILTER(o->speed, omega, so->filter);
  412. /* Reset samples counter and accumulated angle */
  413. so->cntr = 0;
  414. so->angle_acc = 0.0f;
  415. }
  416. /* Store current angle as previous angle */
  417. so->angle_prev = angle;
  418. }
  419. /****************************************************************************
  420. * Name: motor_observer_speed_get
  421. *
  422. * Description:
  423. * Get the estmiated motor mechanical speed from the observer
  424. *
  425. * Input Parameters:
  426. * o - (in/out) pointer to the common observer data
  427. *
  428. * Returned Value:
  429. * Return estimated motor mechanical speed from observer
  430. *
  431. ****************************************************************************/
  432. float motor_observer_speed_get(FAR struct motor_observer_s *o)
  433. {
  434. DEBUGASSERT(o != NULL);
  435. return o->speed;
  436. }
  437. /****************************************************************************
  438. * Name: motor_observer_angle_get
  439. *
  440. * Description:
  441. * Get the estmiated motor electrical angle from the observer
  442. *
  443. * Input Parameters:
  444. * o - (in/out) pointer to the common observer data
  445. *
  446. * Returned Value:
  447. * Return estimated motor mechanical angle from observer
  448. *
  449. ****************************************************************************/
  450. float motor_observer_angle_get(FAR struct motor_observer_s *o)
  451. {
  452. DEBUGASSERT(o != NULL);
  453. return o->angle;
  454. }