2015-08-11 03:28:44 -03:00
|
|
|
#include "AP_Mount_Servo.h"
|
2022-06-14 01:55:10 -03:00
|
|
|
#if HAL_MOUNT_SERVO_ENABLED
|
2015-01-08 16:11:12 -04:00
|
|
|
|
2022-11-06 23:48:42 -04:00
|
|
|
#include <AP_AHRS/AP_AHRS.h>
|
|
|
|
#include <GCS_MAVLink/GCS_MAVLink.h>
|
|
|
|
|
2015-01-12 07:51:56 -04:00
|
|
|
extern const AP_HAL::HAL& hal;
|
|
|
|
|
2015-01-08 16:11:12 -04:00
|
|
|
// init - performs any required initialisation for this instance
|
2019-08-27 03:23:30 -03:00
|
|
|
void AP_Mount_Servo::init()
|
2015-01-08 16:11:12 -04:00
|
|
|
{
|
|
|
|
if (_instance == 0) {
|
2017-01-05 01:13:02 -04:00
|
|
|
_roll_idx = SRV_Channel::k_mount_roll;
|
|
|
|
_tilt_idx = SRV_Channel::k_mount_tilt;
|
|
|
|
_pan_idx = SRV_Channel::k_mount_pan;
|
|
|
|
_open_idx = SRV_Channel::k_mount_open;
|
2015-01-08 16:11:12 -04:00
|
|
|
} else {
|
|
|
|
// this must be the 2nd mount
|
2017-01-05 01:13:02 -04:00
|
|
|
_roll_idx = SRV_Channel::k_mount2_roll;
|
|
|
|
_tilt_idx = SRV_Channel::k_mount2_tilt;
|
|
|
|
_pan_idx = SRV_Channel::k_mount2_pan;
|
|
|
|
_open_idx = SRV_Channel::k_mount2_open;
|
2015-01-08 16:11:12 -04:00
|
|
|
}
|
2023-04-20 05:22:59 -03:00
|
|
|
AP_Mount_Backend::init();
|
2015-01-08 16:11:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// update mount position - should be called periodically
|
|
|
|
void AP_Mount_Servo::update()
|
|
|
|
{
|
2023-12-29 03:10:52 -04:00
|
|
|
// change to RC_TARGETING mode if RC input has changed
|
|
|
|
set_rctargeting_on_rcinput_change();
|
|
|
|
|
2023-07-21 03:35:42 -03:00
|
|
|
auto mount_mode = get_mode();
|
|
|
|
switch (mount_mode) {
|
2015-01-08 16:11:12 -04:00
|
|
|
// move mount to a "retracted position" or to a position where a fourth servo can retract the entire mount into the fuselage
|
2022-06-23 00:39:10 -03:00
|
|
|
case MAV_MOUNT_MODE_RETRACT: {
|
2023-03-25 01:04:42 -03:00
|
|
|
_angle_bf_output_rad = _params.retract_angles.get() * DEG_TO_RAD;
|
2023-07-21 03:35:42 -03:00
|
|
|
mnt_target.angle_rad.set(_angle_bf_output_rad, false);
|
|
|
|
mnt_target.target_type = MountTargetType::ANGLE;
|
2015-01-08 16:11:12 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// move mount to a neutral position, typically pointing forward
|
2022-06-23 00:39:10 -03:00
|
|
|
case MAV_MOUNT_MODE_NEUTRAL: {
|
2023-03-25 01:04:42 -03:00
|
|
|
_angle_bf_output_rad = _params.neutral_angles.get() * DEG_TO_RAD;
|
2023-07-21 03:35:42 -03:00
|
|
|
mnt_target.angle_rad.set(_angle_bf_output_rad, false);
|
|
|
|
mnt_target.target_type = MountTargetType::ANGLE;
|
2015-01-08 16:11:12 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// point to the angles given by a mavlink message
|
2022-06-23 00:39:10 -03:00
|
|
|
case MAV_MOUNT_MODE_MAVLINK_TARGETING: {
|
2023-07-21 03:35:42 -03:00
|
|
|
// mavlink targets are stored while handling the incoming message and considered valid
|
2015-01-08 16:11:12 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// RC radio manual angle control, but with stabilization from the AHRS
|
2022-06-23 00:39:10 -03:00
|
|
|
case MAV_MOUNT_MODE_RC_TARGETING: {
|
|
|
|
// update targets using pilot's RC inputs
|
2023-07-21 03:35:42 -03:00
|
|
|
MountTarget rc_target;
|
|
|
|
get_rc_target(mnt_target.target_type, rc_target);
|
|
|
|
switch (mnt_target.target_type) {
|
|
|
|
case MountTargetType::ANGLE:
|
|
|
|
mnt_target.angle_rad = rc_target;
|
|
|
|
break;
|
|
|
|
case MountTargetType::RATE:
|
|
|
|
mnt_target.rate_rads = rc_target;
|
|
|
|
break;
|
2022-06-23 00:39:10 -03:00
|
|
|
}
|
2015-01-08 16:11:12 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-07-21 03:35:42 -03:00
|
|
|
// point mount to a GPS point given by the mission planner
|
|
|
|
case MAV_MOUNT_MODE_GPS_POINT:
|
|
|
|
if (get_angle_target_to_roi(mnt_target.angle_rad)) {
|
|
|
|
mnt_target.target_type = MountTargetType::ANGLE;
|
2015-01-08 16:11:12 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2023-07-21 03:35:42 -03:00
|
|
|
// point mount to Home location
|
|
|
|
case MAV_MOUNT_MODE_HOME_LOCATION:
|
|
|
|
if (get_angle_target_to_home(mnt_target.angle_rad)) {
|
|
|
|
mnt_target.target_type = MountTargetType::ANGLE;
|
2020-12-21 05:07:43 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2023-07-21 03:35:42 -03:00
|
|
|
// point mount to another vehicle
|
|
|
|
case MAV_MOUNT_MODE_SYSID_TARGET:
|
|
|
|
if (get_angle_target_to_sysid(mnt_target.angle_rad)) {
|
|
|
|
mnt_target.target_type = MountTargetType::ANGLE;
|
2019-03-16 04:06:02 -03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2015-01-08 16:11:12 -04:00
|
|
|
default:
|
|
|
|
//do nothing
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-07-21 03:35:42 -03:00
|
|
|
// send target angles or rates depending on the target type
|
|
|
|
switch (mnt_target.target_type) {
|
|
|
|
case MountTargetType::RATE:
|
|
|
|
update_angle_target_from_rate(mnt_target.rate_rads, mnt_target.angle_rad);
|
|
|
|
FALLTHROUGH;
|
|
|
|
case MountTargetType::ANGLE:
|
|
|
|
// update _angle_bf_output_rad based on angle target
|
|
|
|
if ((mount_mode != MAV_MOUNT_MODE_RETRACT) & (mount_mode != MAV_MOUNT_MODE_NEUTRAL)) {
|
|
|
|
update_angle_outputs(mnt_target.angle_rad);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-01-08 16:11:12 -04:00
|
|
|
// move mount to a "retracted position" into the fuselage with a fourth servo
|
2023-07-21 03:35:42 -03:00
|
|
|
const bool mount_open = (mount_mode == MAV_MOUNT_MODE_RETRACT) ? 0 : 1;
|
2022-06-23 00:39:10 -03:00
|
|
|
move_servo(_open_idx, mount_open, 0, 1);
|
2015-01-08 16:11:12 -04:00
|
|
|
|
|
|
|
// write the results to the servos
|
2023-03-25 01:04:42 -03:00
|
|
|
move_servo(_roll_idx, degrees(_angle_bf_output_rad.x)*10, _params.roll_angle_min*10, _params.roll_angle_max*10);
|
|
|
|
move_servo(_tilt_idx, degrees(_angle_bf_output_rad.y)*10, _params.pitch_angle_min*10, _params.pitch_angle_max*10);
|
|
|
|
move_servo(_pan_idx, degrees(_angle_bf_output_rad.z)*10, _params.yaw_angle_min*10, _params.yaw_angle_max*10);
|
2015-01-08 16:11:12 -04:00
|
|
|
}
|
|
|
|
|
2024-05-15 23:09:37 -03:00
|
|
|
// returns true if this mount can control its roll
|
|
|
|
bool AP_Mount_Servo::has_roll_control() const
|
|
|
|
{
|
|
|
|
return SRV_Channels::function_assigned(_roll_idx) && roll_range_valid();
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns true if this mount can control its tilt
|
|
|
|
bool AP_Mount_Servo::has_pitch_control() const
|
|
|
|
{
|
|
|
|
return SRV_Channels::function_assigned(_tilt_idx) && pitch_range_valid();
|
|
|
|
}
|
|
|
|
|
2022-06-23 00:39:10 -03:00
|
|
|
// returns true if this mount can control its pan (required for multicopters)
|
|
|
|
bool AP_Mount_Servo::has_pan_control() const
|
2015-01-08 16:11:12 -04:00
|
|
|
{
|
2022-08-26 00:42:17 -03:00
|
|
|
return SRV_Channels::function_assigned(_pan_idx) && yaw_range_valid();
|
2015-01-08 16:11:12 -04:00
|
|
|
}
|
|
|
|
|
2022-07-11 05:07:22 -03:00
|
|
|
// get attitude as a quaternion. returns true on success
|
|
|
|
bool AP_Mount_Servo::get_attitude_quaternion(Quaternion& att_quat)
|
2015-01-08 16:11:12 -04:00
|
|
|
{
|
2024-05-15 23:15:16 -03:00
|
|
|
// No feedback from gimbal so simply report demanded servo angles (which is
|
|
|
|
// not the same as target angles).
|
|
|
|
float roll_rad = 0.0f;
|
|
|
|
float pitch_rad = 0.0f;
|
|
|
|
float yaw_rad = 0.0f;
|
|
|
|
if (has_roll_control()) {
|
|
|
|
roll_rad = constrain_float(_angle_bf_output_rad.x, radians(_params.roll_angle_min), radians(_params.roll_angle_max));
|
|
|
|
}
|
|
|
|
if (has_pitch_control()) {
|
|
|
|
pitch_rad = constrain_float(_angle_bf_output_rad.y, radians(_params.pitch_angle_min), radians(_params.pitch_angle_max));
|
|
|
|
}
|
|
|
|
if (has_pan_control()) {
|
|
|
|
yaw_rad = constrain_float(_angle_bf_output_rad.z, radians(_params.yaw_angle_min), radians(_params.yaw_angle_max));
|
|
|
|
}
|
2023-10-14 07:52:24 -03:00
|
|
|
|
|
|
|
// convert to quaternion
|
2024-05-15 23:15:16 -03:00
|
|
|
att_quat.from_euler(roll_rad, pitch_rad, yaw_rad);
|
2022-07-11 05:07:22 -03:00
|
|
|
return true;
|
2015-01-08 16:11:12 -04:00
|
|
|
}
|
|
|
|
|
2022-07-11 05:07:22 -03:00
|
|
|
// private methods
|
|
|
|
|
2022-06-23 00:39:10 -03:00
|
|
|
// update body-frame angle outputs from earth-frame angle targets
|
|
|
|
void AP_Mount_Servo::update_angle_outputs(const MountTarget& angle_rad)
|
2015-01-08 16:11:12 -04:00
|
|
|
{
|
2022-06-23 00:39:10 -03:00
|
|
|
const AP_AHRS &ahrs = AP::ahrs();
|
|
|
|
|
2023-01-18 20:33:05 -04:00
|
|
|
// get target yaw in body-frame with limits applied
|
2023-03-25 00:50:56 -03:00
|
|
|
const float yaw_bf_rad = constrain_float(angle_rad.get_bf_yaw(), radians(_params.yaw_angle_min), radians(_params.yaw_angle_max));
|
2023-01-18 20:33:05 -04:00
|
|
|
|
|
|
|
// default output to target earth-frame roll and pitch angles, body-frame yaw
|
2023-03-25 01:04:42 -03:00
|
|
|
_angle_bf_output_rad.x = angle_rad.roll;
|
|
|
|
_angle_bf_output_rad.y = angle_rad.pitch;
|
|
|
|
_angle_bf_output_rad.z = yaw_bf_rad;
|
2023-01-18 20:33:05 -04:00
|
|
|
|
|
|
|
// this is sufficient for self-stabilising brushless gimbals
|
|
|
|
if (!requires_stabilization) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// retrieve lean angles from ahrs
|
2024-01-12 08:40:23 -04:00
|
|
|
Vector2f ahrs_angle_rad = {ahrs.get_roll(), ahrs.get_pitch()};
|
2023-01-18 20:33:05 -04:00
|
|
|
|
|
|
|
// rotate ahrs roll and pitch angles to gimbal yaw
|
|
|
|
if (has_pan_control()) {
|
|
|
|
ahrs_angle_rad.rotate(-yaw_bf_rad);
|
2022-08-30 05:20:19 -03:00
|
|
|
}
|
2015-01-08 16:11:12 -04:00
|
|
|
|
2023-01-18 20:33:05 -04:00
|
|
|
// add roll and pitch lean angle correction
|
2023-03-25 01:04:42 -03:00
|
|
|
_angle_bf_output_rad.x -= ahrs_angle_rad.x;
|
|
|
|
_angle_bf_output_rad.y -= ahrs_angle_rad.y;
|
2023-01-18 20:33:05 -04:00
|
|
|
|
2022-08-30 05:20:19 -03:00
|
|
|
// lead filter
|
|
|
|
const Vector3f &gyro = ahrs.get_gyro();
|
2015-01-08 16:11:12 -04:00
|
|
|
|
2024-01-12 08:40:23 -04:00
|
|
|
if (!is_zero(_params.roll_stb_lead) && fabsf(ahrs.get_pitch()) < M_PI/3.0f) {
|
2022-08-30 05:20:19 -03:00
|
|
|
// Compute rate of change of euler roll angle
|
|
|
|
float roll_rate = gyro.x + (ahrs.sin_pitch() / ahrs.cos_pitch()) * (gyro.y * ahrs.sin_roll() + gyro.z * ahrs.cos_roll());
|
2023-03-25 01:04:42 -03:00
|
|
|
_angle_bf_output_rad.x -= roll_rate * _params.roll_stb_lead;
|
2022-08-30 05:20:19 -03:00
|
|
|
}
|
2015-01-08 16:11:12 -04:00
|
|
|
|
2023-01-18 20:33:05 -04:00
|
|
|
if (!is_zero(_params.pitch_stb_lead)) {
|
2022-08-30 05:20:19 -03:00
|
|
|
// Compute rate of change of euler pitch angle
|
|
|
|
float pitch_rate = ahrs.cos_pitch() * gyro.y - ahrs.sin_roll() * gyro.z;
|
2023-03-25 01:04:42 -03:00
|
|
|
_angle_bf_output_rad.y -= pitch_rate * _params.pitch_stb_lead;
|
2015-01-08 16:11:12 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// move_servo - moves servo with the given id to the specified angle. all angles are in degrees * 10
|
|
|
|
void AP_Mount_Servo::move_servo(uint8_t function_idx, int16_t angle, int16_t angle_min, int16_t angle_max)
|
|
|
|
{
|
2022-12-18 22:19:55 -04:00
|
|
|
SRV_Channels::move_servo((SRV_Channel::Aux_servo_function_t)function_idx, angle, angle_min, angle_max);
|
2015-01-08 16:11:12 -04:00
|
|
|
}
|
2022-06-14 01:55:10 -03:00
|
|
|
#endif // HAL_MOUNT_SERVO_ENABLED
|