PX4 Firmware
PX4 Autopilot Software http://px4.io
HelicopterMixer.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2012-2017 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 mixer_helicopter.cpp
36  *
37  * Helicopter mixers.
38  */
39 
40 #include "HelicopterMixer.hpp"
41 
42 #include <mathlib/mathlib.h>
43 #include <cstdio>
44 #include <px4_platform_common/defines.h>
45 
46 #define debug(fmt, args...) do { } while(0)
47 //#define debug(fmt, args...) do { printf("[mixer] " fmt "\n", ##args); } while(0)
48 //#include <debug.h>
49 //#define debug(fmt, args...) lowsyslog(fmt "\n", ##args)
50 
51 using math::constrain;
52 
53 HelicopterMixer::HelicopterMixer(ControlCallback control_cb, uintptr_t cb_handle, mixer_heli_s mixer_info) :
54  Mixer(control_cb, cb_handle),
55  _mixer_info(mixer_info)
56 {
57 }
58 
60 HelicopterMixer::from_text(Mixer::ControlCallback control_cb, uintptr_t cb_handle, const char *buf, unsigned &buflen)
61 {
62  mixer_heli_s mixer_info;
63  unsigned swash_plate_servo_count = 0;
64  unsigned u[5];
65  int s[5];
66  int used;
67 
68  /* enforce that the mixer ends with a new line */
69  if (!string_well_formed(buf, buflen)) {
70  return nullptr;
71  }
72 
73  if (sscanf(buf, "H: %u%n", &swash_plate_servo_count, &used) != 1) {
74  debug("helicopter parse failed on '%s'", buf);
75  return nullptr;
76  }
77 
78  if (swash_plate_servo_count < 3 || swash_plate_servo_count > 4) {
79  debug("only supporting swash plate with 3 or 4 servos");
80  return nullptr;
81  }
82 
83  if (used > (int)buflen) {
84  debug("OVERFLOW: helicopter spec used %d of %u", used, buflen);
85  return nullptr;
86  }
87 
88  buf = skipline(buf, buflen);
89 
90  if (buf == nullptr) {
91  debug("no line ending, line is incomplete");
92  return nullptr;
93  }
94 
95  buf = findtag(buf, buflen, 'T');
96 
97  if ((buf == nullptr) || (buflen < 12)) {
98  debug("control parser failed finding tag, ret: '%s'", buf);
99  return nullptr;
100  }
101 
102  if (sscanf(buf, "T: %u %u %u %u %u",
103  &u[0], &u[1], &u[2], &u[3], &u[4]) != 5) {
104  debug("control parse failed on '%s'", buf);
105  return nullptr;
106  }
107 
108  for (unsigned i = 0; i < HELI_CURVES_NR_POINTS; i++) {
109  mixer_info.throttle_curve[i] = ((float) u[i]) / 10000.0f;
110  }
111 
112  buf = skipline(buf, buflen);
113 
114  if (buf == nullptr) {
115  debug("no line ending, line is incomplete");
116  return nullptr;
117  }
118 
119  buf = findtag(buf, buflen, 'P');
120 
121  if ((buf == nullptr) || (buflen < 12)) {
122  debug("control parser failed finding tag, ret: '%s'", buf);
123  return nullptr;
124  }
125 
126  if (sscanf(buf, "P: %d %d %d %d %d",
127  &s[0], &s[1], &s[2], &s[3], &s[4]) != 5) {
128  debug("control parse failed on '%s'", buf);
129  return nullptr;
130  }
131 
132  for (unsigned i = 0; i < HELI_CURVES_NR_POINTS; i++) {
133  mixer_info.pitch_curve[i] = ((float) s[i]) / 10000.0f;
134  }
135 
136  buf = skipline(buf, buflen);
137 
138  if (buf == nullptr) {
139  debug("no line ending, line is incomplete");
140  return nullptr;
141  }
142 
143  mixer_info.control_count = swash_plate_servo_count;
144 
145  /* Now loop through the servos */
146  for (unsigned i = 0; i < mixer_info.control_count; i++) {
147 
148  buf = findtag(buf, buflen, 'S');
149 
150  if ((buf == nullptr) || (buflen < 12)) {
151  debug("control parser failed finding tag, ret: '%s'", buf);
152  return nullptr;
153  }
154 
155  if (sscanf(buf, "S: %u %u %d %d %d %d",
156  &u[0],
157  &u[1],
158  &s[0],
159  &s[1],
160  &s[2],
161  &s[3]) != 6) {
162  debug("control parse failed on '%s'", buf);
163  return nullptr;
164  }
165 
166  mixer_info.servos[i].angle = ((float) u[0]) * M_PI_F / 180.0f;
167  mixer_info.servos[i].arm_length = ((float) u[1]) / 10000.0f;
168  mixer_info.servos[i].scale = ((float) s[0]) / 10000.0f;
169  mixer_info.servos[i].offset = ((float) s[1]) / 10000.0f;
170  mixer_info.servos[i].min_output = ((float) s[2]) / 10000.0f;
171  mixer_info.servos[i].max_output = ((float) s[3]) / 10000.0f;
172 
173  buf = skipline(buf, buflen);
174 
175  if (buf == nullptr) {
176  debug("no line ending, line is incomplete");
177  return nullptr;
178  }
179  }
180 
181  debug("remaining in buf: %d, first char: %c", buflen, buf[0]);
182 
183  HelicopterMixer *hm = new HelicopterMixer(control_cb, cb_handle, mixer_info);
184 
185  if (hm != nullptr) {
186  debug("loaded heli mixer with %d swash plate input(s)", mixer_info.control_count);
187 
188  } else {
189  debug("could not allocate memory for mixer");
190  }
191 
192  return hm;
193 }
194 
195 unsigned
196 HelicopterMixer::mix(float *outputs, unsigned space)
197 {
198  if (space < _mixer_info.control_count + 1u) {
199  return 0;
200  }
201 
202  /* Find index to use for curves */
203  float thrust_cmd = get_control(0, 3);
204  int idx = (thrust_cmd / 0.25f);
205 
206  /* Make sure idx is in range */
207  if (idx < 0) {
208  idx = 0;
209 
210  } else if (idx > HELI_CURVES_NR_POINTS - 2) {
211  /* We access idx + 1 below, so max legal index is (size - 2) */
212  idx = HELI_CURVES_NR_POINTS - 2;
213  }
214 
215  /* Local throttle curve gradient and offset */
216  float tg = (_mixer_info.throttle_curve[idx + 1] - _mixer_info.throttle_curve[idx]) / 0.25f;
217  float to = (_mixer_info.throttle_curve[idx]) - (tg * idx * 0.25f);
218  float throttle = constrain(2.0f * (tg * thrust_cmd + to) - 1.0f, -1.0f, 1.0f);
219 
220  /* Local pitch curve gradient and offset */
221  float pg = (_mixer_info.pitch_curve[idx + 1] - _mixer_info.pitch_curve[idx]) / 0.25f;
222  float po = (_mixer_info.pitch_curve[idx]) - (pg * idx * 0.25f);
223  float collective_pitch = constrain((pg * thrust_cmd + po), -0.5f, 0.5f);
224 
225  float roll_cmd = get_control(0, 0);
226  float pitch_cmd = get_control(0, 1);
227 
228  outputs[0] = throttle;
229 
230  for (unsigned i = 0; i < _mixer_info.control_count; i++) {
231  outputs[i + 1] = collective_pitch
232  + cosf(_mixer_info.servos[i].angle) * pitch_cmd * _mixer_info.servos[i].arm_length
233  - sinf(_mixer_info.servos[i].angle) * roll_cmd * _mixer_info.servos[i].arm_length;
234  outputs[i + 1] *= _mixer_info.servos[i].scale;
235  outputs[i + 1] += _mixer_info.servos[i].offset;
236  outputs[i + 1] = constrain(outputs[i + 1], _mixer_info.servos[i].min_output, _mixer_info.servos[i].max_output);
237  }
238 
239  return _mixer_info.control_count + 1;
240 }
constexpr _Tp constrain(_Tp val, _Tp min_val, _Tp max_val)
Definition: Limits.hpp:66
#define debug(fmt, args...)
static const char * skipline(const char *buf, unsigned &buflen)
Skip a line.
Definition: Mixer.cpp:90
float throttle_curve[HELI_CURVES_NR_POINTS]
int(* ControlCallback)(uintptr_t handle, uint8_t control_group, uint8_t control_index, float &control)
Fetch a control value.
Definition: Mixer.hpp:154
mixer_heli_servo_s servos[4]
up to four inputs
static bool string_well_formed(const char *buf, unsigned &buflen)
Check wether the string is well formed and suitable for parsing.
Definition: Mixer.cpp:105
mixer_heli_s _mixer_info
static HelicopterMixer * from_text(Mixer::ControlCallback control_cb, uintptr_t cb_handle, const char *buf, unsigned &buflen)
Factory method.
#define HELI_CURVES_NR_POINTS
Vector< float, 6 > f(float t, const Matrix< float, 6, 1 > &, const Matrix< float, 3, 1 > &)
Definition: integration.cpp:8
uint8_t control_count
number of inputs
float get_control(uint8_t group, uint8_t index)
Invoke the client callback to fetch a control value.
Definition: Mixer.cpp:50
helicopter swash plate mixer
static const char * findtag(const char *buf, unsigned &buflen, char tag)
Find a tag.
Definition: Mixer.cpp:60
float pitch_curve[HELI_CURVES_NR_POINTS]
HelicopterMixer(ControlCallback control_cb, uintptr_t cb_handle, mixer_heli_s mixer_info)
Constructor.
#define M_PI_F
Definition: ashtech.cpp:44
Abstract class defining a mixer mixing zero or more inputs to one or more outputs.
Definition: Mixer.hpp:136
Generic helicopter mixer for helicopters with swash plate.
unsigned mix(float *outputs, unsigned space) override
Perform the mixing function.