/**************************************************************************** * * Copyright (c) 2022 PX4 Development Team. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name PX4 nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ #include "ActuatorEffectivenessHelicopter.hpp" #include using namespace matrix; ActuatorEffectivenessHelicopter::ActuatorEffectivenessHelicopter(ModuleParams *parent) : ModuleParams(parent) { for (int i = 0; i < NUM_SWASH_PLATE_SERVOS_MAX; ++i) { char buffer[17]; snprintf(buffer, sizeof(buffer), "CA_SP0_ANG%u", i); _param_handles.swash_plate_servos[i].angle = param_find(buffer); snprintf(buffer, sizeof(buffer), "CA_SP0_ARM_L%u", i); _param_handles.swash_plate_servos[i].arm_length = param_find(buffer); } _param_handles.num_swash_plate_servos = param_find("CA_SP0_COUNT"); for (int i = 0; i < NUM_CURVE_POINTS; ++i) { char buffer[17]; snprintf(buffer, sizeof(buffer), "CA_HELI_THR_C%u", i); _param_handles.throttle_curve[i] = param_find(buffer); snprintf(buffer, sizeof(buffer), "CA_HELI_PITCH_C%u", i); _param_handles.pitch_curve[i] = param_find(buffer); } _param_handles.yaw_collective_pitch_scale = param_find("CA_HELI_YAW_CP_S"); _param_handles.yaw_throttle_scale = param_find("CA_HELI_YAW_TH_S"); updateParams(); } void ActuatorEffectivenessHelicopter::updateParams() { ModuleParams::updateParams(); int32_t count = 0; if (param_get(_param_handles.num_swash_plate_servos, &count) != 0) { PX4_ERR("param_get failed"); return; } _geometry.num_swash_plate_servos = math::constrain((int)count, 3, NUM_SWASH_PLATE_SERVOS_MAX); for (int i = 0; i < _geometry.num_swash_plate_servos; ++i) { float angle_deg{}; param_get(_param_handles.swash_plate_servos[i].angle, &angle_deg); _geometry.swash_plate_servos[i].angle = math::radians(angle_deg); param_get(_param_handles.swash_plate_servos[i].arm_length, &_geometry.swash_plate_servos[i].arm_length); } for (int i = 0; i < NUM_CURVE_POINTS; ++i) { param_get(_param_handles.throttle_curve[i], &_geometry.throttle_curve[i]); param_get(_param_handles.pitch_curve[i], &_geometry.pitch_curve[i]); } param_get(_param_handles.yaw_collective_pitch_scale, &_geometry.yaw_collective_pitch_scale); param_get(_param_handles.yaw_throttle_scale, &_geometry.yaw_throttle_scale); } bool ActuatorEffectivenessHelicopter::getEffectivenessMatrix(Configuration &configuration, EffectivenessUpdateReason external_update) { if (external_update == EffectivenessUpdateReason::NO_EXTERNAL_UPDATE) { return false; } // As the allocation is non-linear, we use updateSetpoint() instead of the matrix configuration.addActuator(ActuatorType::MOTORS, Vector3f{}, Vector3f{}); // Tail (yaw) motor configuration.addActuator(ActuatorType::MOTORS, Vector3f{}, Vector3f{}); // N swash plate servos _first_swash_plate_servo_index = configuration.num_actuators_matrix[0]; for (int i = 0; i < _geometry.num_swash_plate_servos; ++i) { configuration.addActuator(ActuatorType::SERVOS, Vector3f{}, Vector3f{}); } return true; } void ActuatorEffectivenessHelicopter::updateSetpoint(const matrix::Vector &control_sp, int matrix_index, ActuatorVector &actuator_sp) { // throttle/collective pitch curve const float throttle = math::interpolateN(-control_sp(ControlAxis::THRUST_Z), _geometry.throttle_curve) * throttleSpoolupProgress(); const float collective_pitch = math::interpolateN(-control_sp(ControlAxis::THRUST_Z), _geometry.pitch_curve); actuator_sp(0) = throttle; actuator_sp(1) = control_sp(ControlAxis::YAW) + fabsf(collective_pitch) * _geometry.yaw_collective_pitch_scale + throttle * _geometry.yaw_throttle_scale; for (int i = 0; i < _geometry.num_swash_plate_servos; i++) { actuator_sp(_first_swash_plate_servo_index + i) = collective_pitch + control_sp(ControlAxis::PITCH) * cosf(_geometry.swash_plate_servos[i].angle) * _geometry.swash_plate_servos[i].arm_length - control_sp(ControlAxis::ROLL) * sinf(_geometry.swash_plate_servos[i].angle) * _geometry.swash_plate_servos[i].arm_length; } }