PX4 Firmware
PX4 Autopilot Software http://px4.io
motor_ramp.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2016 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /**
35  * @file motor_ramp.cpp
36  *
37  * @author Andreas Antener <andreas@uaventure.com>
38  * @author Roman Bapst <bapstroman@gmail.com>
39  */
40 
41 #include <px4_platform_common/px4_config.h>
42 #include <px4_platform_common/defines.h>
43 #include <px4_platform_common/module.h>
44 #include <px4_platform_common/tasks.h>
45 #include <px4_platform_common/posix.h>
46 #include <px4_platform_common/getopt.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include <errno.h>
52 #include <math.h>
53 #include <poll.h>
54 
55 #include <arch/board/board.h>
56 #include <drivers/drv_hrt.h>
57 #include <drivers/drv_pwm_output.h>
58 
59 #include "systemlib/err.h"
61 
62 enum RampState {
67 };
68 
69 enum Mode {
73 };
74 
75 static bool _thread_should_exit = false; /**< motor_ramp exit flag */
76 static bool _thread_running = false; /**< motor_ramp status flag */
77 static int _motor_ramp_task; /**< Handle of motor_ramp task / thread */
78 static float _ramp_time;
79 static int _min_pwm;
80 static int _max_pwm;
81 static Mode _mode;
82 static const char *_mode_c;
83 static const char *_pwm_output_dev = "/dev/pwm_output0";
84 
85 /**
86  * motor_ramp management function.
87  */
88 extern "C" __EXPORT int motor_ramp_main(int argc, char *argv[]);
89 
90 /**
91  * Mainloop of motor_ramp.
92  */
93 int motor_ramp_thread_main(int argc, char *argv[]);
94 
95 bool min_pwm_valid(int pwm_value);
96 
97 bool max_pwm_valid(int pwm_value);
98 
99 int set_min_pwm(int fd, unsigned long max_channels, int pwm_value);
100 
101 int set_out(int fd, unsigned long max_channels, float output);
102 
103 int prepare(int fd, unsigned long *max_channels);
104 
105 /**
106  * Print the correct usage.
107  */
108 static void usage(const char *reason);
109 
110 static void
111 usage(const char *reason)
112 {
113  if (reason) {
114  PX4_ERR("%s", reason);
115  }
116 
117  PRINT_MODULE_DESCRIPTION(
118  R"DESCR_STR(
119 ### Description
120 Application to test motor ramp up.
121 
122 Before starting, make sure to stop any running attitude controller:
123 $ mc_rate_control stop
124 $ fw_att_control stop
125 
126 When starting, a background task is started, runs for several seconds (as specified), then exits.
127 
128 ### Example
129 $ motor_ramp sine -a 1100 -r 0.5
130 )DESCR_STR");
131 
132 
133  PRINT_MODULE_USAGE_NAME_SIMPLE("motor_ramp", "command");
134  PRINT_MODULE_USAGE_ARG("ramp|sine|square", "mode", false);
135  PRINT_MODULE_USAGE_PARAM_STRING('d', "/dev/pwm_output0", nullptr, "Pwm output device", true);
136  PRINT_MODULE_USAGE_PARAM_INT('a', 0, 900, 1500, "Select minimum pwm duty cycle in usec", false);
137  PRINT_MODULE_USAGE_PARAM_INT('b', 2000, 901, 2100, "Select maximum pwm duty cycle in usec", true);
138  PRINT_MODULE_USAGE_PARAM_FLOAT('r', 1.0f, 0.0f, 65536.0f, "Select motor ramp duration in sec", true);
139 
140  PRINT_MODULE_USAGE_PARAM_COMMENT("WARNING: motors will ramp up to full speed!");
141 
142 }
143 
144 /**
145  * The motor_ramp app only briefly exists to start
146  * the background job. The stack size assigned in the
147  * Makefile does only apply to this management task.
148  *
149  * The actual stack size should be set in the call
150  * to task_create().
151  */
152 int motor_ramp_main(int argc, char *argv[])
153 {
154  int myoptind = 1;
155  int ch;
156  const char *myoptarg = nullptr;
157  bool error_flag = false;
158  bool set_pwm_min = false;
159  _max_pwm = 2000;
160  _ramp_time = 1.0f;
161 
162  if (_thread_running) {
163  PX4_WARN("motor_ramp already running\n");
164  /* this is not an error */
165  return 0;
166  }
167 
168  if (argc < 4) {
169  usage("missing parameters");
170  return 1;
171  }
172 
173  while ((ch = px4_getopt(argc, argv, "d:a:b:r:", &myoptind, &myoptarg)) != EOF) {
174  switch (ch) {
175 
176  case 'd':
177  if(!strcmp(myoptarg, "/dev/pwm_output0") || !strcmp(myoptarg, "/dev/pwm_output1")){
178  _pwm_output_dev = myoptarg;
179  } else {
180  usage("pwm output device not found");
181  error_flag = true;
182  }
183  break;
184 
185  case 'a':
186  _min_pwm = atoi(myoptarg);
187 
188  if (!min_pwm_valid(_min_pwm)) {
189  usage("min PWM not in range");
190  error_flag = true;
191  } else {
192  set_pwm_min = true;
193  }
194 
195  break;
196 
197  case 'b':
198  _max_pwm = atoi(myoptarg);
199 
200  if (!max_pwm_valid(_max_pwm)) {
201  usage("max PWM not in range");
202  error_flag = true;
203  }
204 
205  break;
206 
207  case 'r':
208  _ramp_time = atof(myoptarg);
209 
210  if (_ramp_time <= 0) {
211  usage("ramp time must be greater than 0");
212  error_flag = true;
213  }
214 
215  break;
216 
217  default:
218  PX4_WARN("unrecognized flag");
219  error_flag = true;
220  break;
221  }
222  }
223 
224  _thread_should_exit = false;
225 
226  if(!set_pwm_min){
227  PX4_WARN("pwm_min not set. use -a flag");
228  error_flag = true;
229  }
230 
231 
232  if (!strcmp(argv[myoptind], "ramp")) {
233  _mode = RAMP;
234 
235  } else if (!strcmp(argv[myoptind], "sine")) {
236  _mode = SINE;
237 
238  } else if (!strcmp(argv[myoptind], "square")) {
239  _mode = SQUARE;
240 
241  } else {
242  usage("selected mode not valid");
243  error_flag = true;
244  }
245 
246  _mode_c = myoptarg;
247 
248  if(error_flag){
249  return 1;
250  }
251 
252  _motor_ramp_task = px4_task_spawn_cmd("motor_ramp",
253  SCHED_DEFAULT,
254  SCHED_PRIORITY_DEFAULT + 40,
255  2000,
257  (argv) ? (char *const *)&argv[2] : (char *const *)nullptr);
258 
259  return 0;
260 }
261 
262 bool min_pwm_valid(int pwm_value)
263 {
264  return pwm_value >= 900 && pwm_value <= 1500;
265 }
266 
267 bool max_pwm_valid(int pwm_value)
268 {
269  return pwm_value <= 2100 && pwm_value > _min_pwm;
270 }
271 
272 int set_min_pwm(int fd, unsigned long max_channels, int pwm_value)
273 {
274  int ret;
275 
276  struct pwm_output_values pwm_values {};
277 
278  pwm_values.channel_count = max_channels;
279 
280  for (unsigned i = 0; i < max_channels; i++) {
281  pwm_values.values[i] = pwm_value;
282  }
283 
284  ret = px4_ioctl(fd, PWM_SERVO_SET_MIN_PWM, (long unsigned int)&pwm_values);
285 
286  if (ret != OK) {
287  PX4_ERR("failed setting min values");
288  return 1;
289  }
290 
291  return 0;
292 }
293 
294 int set_out(int fd, unsigned long max_channels, float output)
295 {
296  int ret;
297  int pwm = (_max_pwm - _min_pwm) * output + _min_pwm;
298 
299  for (unsigned i = 0; i < max_channels; i++) {
300 
301  ret = ioctl(fd, PWM_SERVO_SET(i), pwm);
302 
303  if (ret != OK) {
304  PX4_ERR("PWM_SERVO_SET(%d), value: %d", i, pwm);
305  return 1;
306  }
307  }
308 
309  return 0;
310 }
311 
312 int prepare(int fd, unsigned long *max_channels)
313 {
314  /* make sure no other source is publishing control values now */
315  struct actuator_controls_s actuators;
317 
318  /* clear changed flag */
319  orb_copy(ORB_ID_VEHICLE_ATTITUDE_CONTROLS, act_sub, &actuators);
320 
321  /* wait 50 ms */
322  px4_usleep(50000);
323 
324  /* now expect nothing changed on that topic */
325  bool orb_updated;
326  orb_check(act_sub, &orb_updated);
327 
328  if (orb_updated) {
329  PX4_ERR("ABORTING! Attitude control still active. Please ensure to shut down all controllers:\n"
330  "\tmc_rate_control stop\n"
331  "\tfw_att_control stop\n");
332  return 1;
333  }
334 
335  /* get number of channels available on the device */
336  if (px4_ioctl(fd, PWM_SERVO_GET_COUNT, (unsigned long)max_channels) != OK) {
337  PX4_ERR("PWM_SERVO_GET_COUNT");
338  return 1;
339  }
340 
341  /* tell IO/FMU that its ok to disable its safety with the switch */
342  if (px4_ioctl(fd, PWM_SERVO_SET_ARM_OK, 0) != OK) {
343  PX4_ERR("PWM_SERVO_SET_ARM_OK");
344  return 1;
345  }
346 
347  /* tell IO/FMU that the system is armed (it will output values if safety is off) */
348  if (px4_ioctl(fd, PWM_SERVO_ARM, 0) != OK) {
349  PX4_ERR("PWM_SERVO_ARM");
350  return 1;
351  }
352 
353  /* tell IO to switch off safety without using the safety switch */
355  PX4_ERR("PWM_SERVO_SET_FORCE_SAFETY_OFF");
356  return 1;
357  }
358 
359  return 0;
360 }
361 
362 int motor_ramp_thread_main(int argc, char *argv[])
363 {
364  _thread_running = true;
365 
366  unsigned long max_channels = 0;
367  static struct pwm_output_values last_spos;
368  static struct pwm_output_values last_min;
369  static unsigned servo_count;
370 
371  int fd = px4_open(_pwm_output_dev, 0);
372 
373  if (fd < 0) {
374  PX4_ERR("can't open %s", _pwm_output_dev);
375  _thread_running = false;
376  return 1;
377  }
378 
379  /* get the number of servo channels */
380  if (px4_ioctl(fd, PWM_SERVO_GET_COUNT, (unsigned long)&servo_count) < 0) {
381  PX4_ERR("PWM_SERVO_GET_COUNT");
382  px4_close(fd);
383  _thread_running = false;
384  return 1;
385 
386  }
387 
388  /* get current servo values */
389  for (unsigned i = 0; i < servo_count; i++) {
390 
391  if (px4_ioctl(fd, PWM_SERVO_GET(i), (unsigned long)&last_spos.values[i]) < 0) {
392  PX4_ERR("PWM_SERVO_GET(%d)", i);
393  px4_close(fd);
394  _thread_running = false;
395  return 1;
396  }
397  }
398 
399  /* get current pwm min */
400  if (px4_ioctl(fd, PWM_SERVO_GET_MIN_PWM, (long unsigned int)&last_min) < 0) {
401  PX4_ERR("failed getting pwm min values");
402  px4_close(fd);
403  _thread_running = false;
404  return 1;
405  }
406 
408  PX4_ERR("Failed to Enter pwm test mode");
409  px4_close(fd);
410  _thread_running = false;
411  return 1;
412  }
413 
414  if (prepare(fd, &max_channels) != OK) {
415  _thread_should_exit = true;
416  }
417 
418  set_out(fd, max_channels, 0.0f);
419 
420  float dt = 0.001f; // prevent division with 0
421  float timer = 0.0f;
422  hrt_abstime start = 0;
423  hrt_abstime last_run = 0;
424 
425  enum RampState ramp_state = RAMP_INIT;
426  float output = 0.0f;
427 
428  while (!_thread_should_exit) {
429 
430  if (last_run > 0) {
431  dt = hrt_elapsed_time(&last_run) * 1e-6;
432 
433  } else {
434  start = hrt_absolute_time();
435  }
436 
437  last_run = hrt_absolute_time();
438  timer = hrt_elapsed_time(&start) * 1e-6;
439 
440  switch (ramp_state) {
441  case RAMP_INIT: {
442  PX4_INFO("setting pwm min: %d", _min_pwm);
443  set_min_pwm(fd, max_channels, _min_pwm);
444  ramp_state = RAMP_MIN;
445  break;
446  }
447 
448  case RAMP_MIN: {
449  if (timer > 3.0f) {
450  PX4_INFO("starting %s: %.2f sec", _mode_c, (double)_ramp_time);
451  start = hrt_absolute_time();
452  ramp_state = RAMP_RAMP;
453  }
454 
455  set_out(fd, max_channels, output);
456  break;
457  }
458 
459  case RAMP_RAMP: {
460  if (_mode == RAMP) {
461  output += 1000.0f * dt / (_max_pwm - _min_pwm) / _ramp_time;
462 
463  } else if (_mode == SINE) {
464  // sine outpout with period T = _ramp_time and magnitude between [0,1]
465  // phase shift makes sure that it starts at zero when timer is zero
466  output = 0.5f * (1.0f + sinf(M_TWOPI_F * timer / _ramp_time - M_PI_2_F));
467 
468  } else if (_mode == SQUARE) {
469  output = fmodf(timer, _ramp_time) > (_ramp_time * 0.5f) ? 1.0f : 0.0f;
470  }
471 
472  if ((output > 1.0f && _mode == RAMP) || (timer > 3.0f * _ramp_time)) {
473  // for ramp mode we set output to 1, for others we just leave it as is
474  output = _mode != RAMP ? output : 1.0f;
475  start = hrt_absolute_time();
476  ramp_state = RAMP_WAIT;
477  PX4_INFO("%s finished, waiting", _mode_c);
478  }
479 
480  set_out(fd, max_channels, output);
481  break;
482  }
483 
484  case RAMP_WAIT: {
485  if (timer > 0.5f) {
486  _thread_should_exit = true;
487  PX4_INFO("stopping");
488  break;
489  }
490 
491  set_out(fd, max_channels, output);
492  break;
493  }
494  }
495 
496  // rate limit
497  px4_usleep(2000);
498  }
499 
500  if (fd >= 0) {
501  /* set current pwm min */
502  if (px4_ioctl(fd, PWM_SERVO_SET_MIN_PWM, (long unsigned int)&last_min) < 0) {
503  PX4_ERR("failed setting pwm min values");
504  px4_close(fd);
505  _thread_running = false;
506  return 1;
507  }
508 
509  /* set previous servo values */
510  for (unsigned i = 0; i < servo_count; i++) {
511 
512  if (px4_ioctl(fd, PWM_SERVO_SET(i), (unsigned long)last_spos.values[i]) < 0) {
513  PX4_ERR("PWM_SERVO_SET(%d)", i);
514  px4_close(fd);
515  _thread_running = false;
516  return 1;
517  }
518  }
519 
521  PX4_ERR("Failed to Exit pwm test mode");
522  px4_close(fd);
523  _thread_running = false;
524  return 1;
525  }
526 
527  px4_close(fd);
528  }
529 
530  _thread_running = false;
531 
532  return 0;
533 }
int orb_copy(const struct orb_metadata *meta, int handle, void *buffer)
Definition: uORB.cpp:90
#define PWM_SERVO_SET_ARM_OK
set the &#39;ARM ok&#39; bit, which activates the safety switch
static void usage(const char *reason)
Print the correct usage.
Definition: motor_ramp.cpp:111
__EXPORT int motor_ramp_main(int argc, char *argv[])
motor_ramp management function.
Definition: motor_ramp.cpp:152
#define PWM_SERVO_GET_COUNT
get the number of servos in *(unsigned *)arg
#define PWM_SERVO_ARM
arm all servo outputs handle by this driver
RampState
Definition: motor_ramp.cpp:62
bool min_pwm_valid(int pwm_value)
Definition: motor_ramp.cpp:262
static const char * _mode_c
Definition: motor_ramp.cpp:82
int motor_ramp_thread_main(int argc, char *argv[])
Mainloop of motor_ramp.
Definition: motor_ramp.cpp:362
Definition: I2C.hpp:51
static Mode _mode
Definition: motor_ramp.cpp:81
static bool _thread_running
motor_ramp status flag
Definition: motor_ramp.cpp:76
High-resolution timer with callouts and timekeeping.
static const char * _pwm_output_dev
Definition: motor_ramp.cpp:83
#define PWM_SERVO_SET(_servo)
set a single servo to a specific value
static int _motor_ramp_task
Handle of motor_ramp task / thread.
Definition: motor_ramp.cpp:77
int orb_subscribe(const struct orb_metadata *meta)
Definition: uORB.cpp:75
uint16_t values[PWM_OUTPUT_MAX_CHANNELS]
#define ORB_ID_VEHICLE_ATTITUDE_CONTROLS
Definition: uORB.h:256
#define PWM_SERVO_ENTER_TEST_MODE
Vector< float, 6 > f(float t, const Matrix< float, 6, 1 > &, const Matrix< float, 3, 1 > &)
Definition: integration.cpp:8
int set_min_pwm(int fd, unsigned long max_channels, int pwm_value)
Definition: motor_ramp.cpp:272
static hrt_abstime hrt_elapsed_time(const hrt_abstime *then)
Compute the delta between a timestamp taken in the past and now.
Definition: drv_hrt.h:102
uint32_t channel_count
static bool _thread_should_exit
motor_ramp exit flag
Definition: motor_ramp.cpp:75
Simple error/warning functions, heavily inspired by the BSD functions of the same names...
#define PWM_SERVO_SET_FORCE_SAFETY_OFF
force safety switch off (to disable use of safety switch)
__BEGIN_DECLS typedef uint64_t hrt_abstime
Absolute time, in microsecond units.
Definition: drv_hrt.h:58
#define PWM_SERVO_GET(_servo)
get a single specific servo value
int fd
Definition: dataman.cpp:146
#define PWM_SERVO_SET_MIN_PWM
set the minimum PWM value the output will send
static int start()
Definition: dataman.cpp:1452
#define PWM_SERVO_SET_MODE
int px4_open(const char *path, int flags,...)
int prepare(int fd, unsigned long *max_channels)
Definition: motor_ramp.cpp:312
#define PWM_SERVO_GET_MIN_PWM
get the minimum PWM value the output will send
float dt
Definition: px4io.c:73
int orb_check(int handle, bool *updated)
Definition: uORB.cpp:95
static int _max_pwm
Definition: motor_ramp.cpp:80
static int _min_pwm
Definition: motor_ramp.cpp:79
bool max_pwm_valid(int pwm_value)
Definition: motor_ramp.cpp:267
#define OK
Definition: uavcan_main.cpp:71
static float _ramp_time
Definition: motor_ramp.cpp:78
Mode
Definition: motor_ramp.cpp:69
int set_out(int fd, unsigned long max_channels, float output)
Definition: motor_ramp.cpp:294
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
int px4_close(int fd)
#define PWM_SERVO_EXIT_TEST_MODE
int px4_ioctl(int fd, int cmd, unsigned long arg)