1075 lines
31 KiB
C++

// Fill out your copyright notice in the Description page of Project Settings.
#include "MultiRotorDrone.h"
#include "Misc/DateTime.h"
#include <stdlib.h>
#include <math.h>
#include "Kismet/GameplayStatics.h"
// Sets default values
AMultiRotorDrone::AMultiRotorDrone()
{
// Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void AMultiRotorDrone::BeginPlay()
{
Super::BeginPlay();
Kt = AIR_DENCITY * Rotors.Ct * powf(Rotors.Diameter, 4);
Km = AIR_DENCITY * Rotors.Cq * powf(Rotors.Diameter, 5);
minThrust = 0.5 * Mass * GRAVITY;
maxThrustPerMotor = ((MaxRPM / 60) * (MaxRPM / 60)) * Kt;
MaxThrust = maxThrustPerMotor * 4;
SrefZ = sqrtf(Rotors.p1.X * Rotors.p1.X + Rotors.p1.Y * Rotors.p1.Y) * sqrtf(Rotors.p2.X * Rotors.p2.X + Rotors.p2.Y * Rotors.p2.Y);
COMLocation = { -0.02, 0.0, -0.29 };
if (Mode == GUIDED)
{
if (!use_true_state)
{
XYerrTol = 0.75;
}
}
}
void AMultiRotorDrone::SetTrueState(FTrueDroneState true_state)
{
FVector prev_position = TrueState.Position;
FVector prev_euler = TrueState.Orientation;
FVector prev_velocity = TrueState.Velocity;
TrueState.Position = true_state.Position * 0.01;
TrueState.Position.Z *= -1.0;
TrueState.Orientation = true_state.Orientation * (PI / 180);
if (!TrueState.initialised)
{
if (use_true_state)
{
TargetPosition = TrueState.Position;
TargetYaw = TrueState.Orientation.Z;
}
TrueState.initialised = true;
return;
}
//UE_LOG(LogTemp, Warning, TEXT("Prev position %f %f %f"), prev_position.X, prev_position.Y, prev_position.Z)
//UE_LOG(LogTemp, Warning, TEXT("Curr position %f %f %f"), TrueState.Position.X, TrueState.Position.Y, TrueState.Position.Z)
TrueState.Velocity = (TrueState.Position - prev_position) / MjStep;
TrueState.Acceleration = (TrueState.Velocity - prev_velocity) / MjStep;
FVector EulerDt = (TrueState.Orientation - prev_euler) / MjStep;
EulerDt2PQR(EulerDt);
}
void AMultiRotorDrone::SetCurrentState()
{
FVector prev_position = State.Position;
FVector prev_euler = State.Orientation;
FVector prev_velocity = State.Velocity;
if (use_true_state)
{
State = TrueState;
return;
}
State.Position = EstState.position;
State.Orientation = EstState.quat.GetEulerRad();
State.Orientation.Z = State.Orientation.Z;
State.Velocity = EstState.velocity;
//UE_LOG(LogTemp, Display, TEXT("True orientation degrees %f %f %f"), true_state.Orientation.X, true_state.Orientation.Y, true_state.Orientation.Z);
if (!State.initialised)
{
TargetPosition = State.Position;
TargetYaw = State.Orientation.Z;
State.initialised = true;
}
State.Acceleration = EstState.acceleration;
PQR = EstState.pqr;
}
FDroneData AMultiRotorDrone::GetDronInfo()
{
FDroneData drone_data;
for (int32 i = 0; i < 4; i++)
{
drone_data.MotorsRPM[i] = RPM[i] * 60;
}
drone_data.Velocity = State.Velocity;
drone_data.Position = State.Position;
drone_data.Orientation = State.Orientation;
return drone_data;
}
void AMultiRotorDrone::EulerDt2PQR(FVector& EulerDt)
{
float r = State.Orientation.X;
float p = State.Orientation.Y;
float y = State.Orientation.Z;
float srtp = sinf(r) * tanf(p);
float crtp = cosf(r) * tanf(p);
float srsp = sinf(r) / cosf(p);
float crsp = cosf(r) / cosf(p);
Matrix33 R(1, srtp, crtp, 0, cosf(p), -sinf(p), 0, srsp, crsp);
PQR = R.Inverse() * EulerDt;
}
void AMultiRotorDrone::SetImuSensor(UIMUSensor* sensor)
{
Imu_sensor = sensor;
StateEstimator.RegisterBuffer(Sensor::GYRO, Imu_sensor->GyroDataBuffer);
StateEstimator.RegisterBuffer(Sensor::ACCEL, Imu_sensor->AccelDataBuffer);
StateEstimator.RegisterBuffer(Sensor::MAG, Imu_sensor->MagDataBuffer);
}
void AMultiRotorDrone::SetGPSSensor(UGPSSensor* sensor)
{
GPS_sensor = sensor;
StateEstimator.RegisterBuffer(Sensor::GPS, GPS_sensor->DataBuffer);
}
void AMultiRotorDrone::SetBaroSensor(UBarometer* sensor)
{
Baro_sensor = sensor;
StateEstimator.RegisterBuffer(Sensor::BARO, Baro_sensor->DataBuffer);
}
// Called every frame
void AMultiRotorDrone::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
//ProcessIMUData();
if (!StateEstimator.IsInitialised())
{
StateEstimator.InitialiseFilter();
}
else
{
StateEstimator.Update();
StateEstimator.GetState(EstState);
SetCurrentState();
ControllerUpdate();
}
UE_LOG(LogTemp, Warning, TEXT("EKF estimated position: %f, %f, %f!"), EstState.position.X, EstState.position.Y, EstState.position.Z);
UE_LOG(LogTemp, Warning, TEXT("EKF estimated velocity: %f, %f, %f!"), EstState.velocity.X, EstState.velocity.Y, EstState.velocity.Z);
FVector Euler = EstState.quat.GetEulerRad();
UE_LOG(LogTemp, Warning, TEXT("EKF estimated orientation: %f, %f, %f!"), Euler.X, Euler.Y, Euler.Z);
UE_LOG(LogTemp, Warning, TEXT("True position: %f, %f, %f!"), TrueState.Position.X, TrueState.Position.Y, TrueState.Position.Z);
UE_LOG(LogTemp, Warning, TEXT("True velocity: %f, %f, %f!"), TrueState.Velocity.X, TrueState.Velocity.Y, TrueState.Velocity.Z);
UE_LOG(LogTemp, Warning, TEXT("True orientation: %f, %f, %f!"), TrueState.Orientation.X, TrueState.Orientation.Y, TrueState.Orientation.Z);
}
// Called to bind functionality to input
void AMultiRotorDrone::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
//PlayerInputComponent->BindAxis(TEXT("height_increase"), this, &AMultiRotorDrone::IncreaseTargetHeight);
}
void AMultiRotorDrone::IncreaseDecreaseTargetHeight(float value)
{
float dt = UGameplayStatics::GetWorldDeltaSeconds(this);
if (Mode == GUIDED)
{
LandingStarted = false;
TargetPosition.Z -= MaxVelocity * dt * value;
//GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::White, FString::SanitizeFloat(TargetPosition.Z));
}
else if (Mode == MANUAL)
{
if (KeepAltitude) KeepAltitude = false;
if (!AltitudeManualControl) AltitudeManualControl = true;
CurrentThrottle += ThrottleRate * dt * value;
if (CurrentThrottle > PWM_max_us)
{
CurrentThrottle = PWM_max_us;
}
if (CurrentThrottle < PWM_min_us)
{
CurrentThrottle = PWM_min_us;
}
//GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::White, FString::SanitizeFloat(CurrentThrottle));
}
//UE_LOG(LogTemp, Display, TEXT("Current Height Desired: %f %f"), TargetPosition.Z, value);
}
void AMultiRotorDrone::HoldAltitude()
{
//TargetPosition.Z = State.Position.Z;
//TargetPosition.X = State.Position.X;
//TargetPosition.Y = State.Position.Y;
//KeepAltitude = true;
//KeepXYPosition = true;
CurrentThrottle = 1500;
pid.Xvel_int = 0.0;
pid.Yvel_int = 0.0;
}
void AMultiRotorDrone::MoveLeftRight(float value)
{
float dt = UGameplayStatics::GetWorldDeltaSeconds(this);
LandingStarted = false;
if (Mode == GUIDED)
{
TargetPosition.Y += MaxVelocity * dt * value;
//GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::White, FString::SanitizeFloat(TargetPosition.Y));
}
else if (Mode == MANUAL)
{
if (KeepXYPosition) KeepXYPosition = false;
if (!XYmanualControl) XYmanualControl = true;
CurrentRollPWM += RollPitchPWMRate * dt * value;
if (CurrentRollPWM > PWM_max_us)
{
CurrentRollPWM = PWM_max_us;
}
if (CurrentRollPWM < PWM_min_us)
{
CurrentRollPWM = PWM_min_us;
}
//GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::White, FString::SanitizeFloat(CurrentRollPWM));
}
}
void AMultiRotorDrone::MoveForwardBackward(float value)
{
LandingStarted = false;
float dt = UGameplayStatics::GetWorldDeltaSeconds(this);
if (Mode == GUIDED)
{
TargetPosition.X += MaxVelocity * dt * -value;
//GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::White, FString::SanitizeFloat(TargetPosition.X));
}
else if (Mode == MANUAL)
{
if (KeepXYPosition) KeepXYPosition = false;
CurrentPitchPWM += RollPitchPWMRate * dt * value;
if (CurrentPitchPWM > PWM_max_us)
{
CurrentPitchPWM = PWM_max_us;
}
if (CurrentPitchPWM < PWM_min_us)
{
CurrentPitchPWM = PWM_min_us;
}
}
}
void AMultiRotorDrone::HoldXYPosition(bool X, bool Y)
{
//TargetPosition.Z = State.Position.Z;
//TargetPosition.X = State.Position.X;
//TargetPosition.Y = State.Position.Y;
//KeepAltitude = true;
KeepXYPosition = true;
if (X) CurrentPitchPWM = 1500;
if (Y) CurrentRollPWM = 1500;
pid.Xvel_int = 0.0;
pid.Yvel_int = 0.0;
}
void AMultiRotorDrone::UpdateYaw(float value)
{
if (Mode == MANUAL && KeepXYPosition)
{
KeepXYPosition = false;
}
float dt = UGameplayStatics::GetWorldDeltaSeconds(this);
TargetYaw = wrap_PI(TargetYaw + YawRate * dt * value);
//GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::White, FString::SanitizeFloat(TargetYaw));
}
float AMultiRotorDrone::GetJ(FVector PropLoc, float RPS, float D)
{
FQuaternion orient_quat;
orient_quat.InitializeFromEulerAngles(State.Orientation.X, State.Orientation.Y, State.Orientation.Z);
Matrix33 mT = orient_quat.GetMatrix().Transposed();
FVector Vb = mT * (State.Velocity * -1);
FVector localAeroVel = Vb + FVector::CrossProduct(PQR, PropLoc);
FVector direction(0, 0, -1);
float lVeln = FVector::DotProduct(localAeroVel, direction);
FVector vVeln = direction * lVeln;
float V = vVeln.Size();
float J;
if (RPS > 0.01)
{
J = V / (RPS * D);
}
else
{
J = V / D;
}
return J;
}
void AMultiRotorDrone::AltitudeController(float& target_height, float& AltCmd)
{
float current_height = State.Position.Z;;
float vert_velocity = State.Velocity.Z;
float max_rps = MaxRPM / 60;
float min_rps = MinRPM / 60;
float up_limit = 0.5 * 4 * max_rps * max_rps * Kt;
//float up_limit = 1.5 * Mass * GRAVITY;
float low_limit = 0.1 * Mass * GRAVITY;
if (Mode == GUIDED)
{
UE_LOG(LogTemp, Display, TEXT("Target height %f"), target_height);
float height_error = target_height - current_height;
if (!use_true_state && fabs(height_error) <= 0.5)
{
height_error = 0.0;
}
float target_vel = height_error / MjStep;
float tAcc = pid.Kpz * height_error;
float dEr = (height_error - pid.prev_XYZ_error.Z) / MjStep;
pid.prev_XYZ_error.Z = height_error;
float VelError = target_vel - State.Velocity.Z;
//float tAcc = tVel - pid.Kvz * State.Velocity.Z;
//if (current_height > target_height)
//{
tAcc -= pid.Kvz * State.Velocity.Z;
//}
//float tAcc = tVel + pid.Kd_z * dEr;
AltCmd = -tAcc + GRAVITY * Mass;
//AltCmd = -tAcc * Mass;
//UE_LOG(LogTemp, Display, TEXT("Target Z position %f"), TargetPosition.Z);
//UE_LOG(LogTemp, Display, TEXT("Current Z position %f"), State.Position.Z);
}
else
{
float ratio = (CurrentThrottle - PWM_min_us) / (PWM_max_us - PWM_min_us);
AltCmd = ratio * MaxThrust;
float target_vel = -MaxVelocity + 2 * MaxVelocity * ratio;
UE_LOG(LogTemp, Display, TEXT("Target Z velocity %f"), target_vel);
float VelError = -target_vel - State.Velocity.Z;
float accel_ff = VelError / MjStep;
pid.Zvel_int += VelError * MjStep;
//AltCmd = -pid.Kd_z * VelError - pid.Ki_z * pid.Zvel_int - accel_ff;
AltCmd = -pid.Kd_z * VelError - pid.Ki_z * pid.Zvel_int;
AltCmd *= Mass;
//GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::White, FString::SanitizeFloat(target_vel));
}
AltCmd = fmax(fmin(AltCmd, up_limit), low_limit);
ThrustCmd = AltCmd;
UE_LOG(LogTemp, Display, TEXT("Alt CMD %f"), AltCmd);
}
void AMultiRotorDrone::YawController(float& target_yaw, float& tau_yaw)
{
//UE_LOG(LogTemp, Display, TEXT("Target yaw %f"), TargetYaw);
float yaw = State.Orientation.Z;
float r = PQR.Z;
float er = wrap_PI(target_yaw - yaw);
tau_yaw = pid.Kp_yaw * er - pid.Kd_yaw * r;
float max_rps = MaxRPM / 60;
float min_rps = MinRPM / 60;
float up_limit = 2 * max_rps * max_rps * Km - 2 * min_rps * min_rps * Km;
float low_limit = -up_limit;
tau_yaw = fmax(fmin(tau_yaw, up_limit), low_limit);
//UE_LOG(LogTemp, Display, TEXT("Tau yaw %f"), tau_yaw);
}
void AMultiRotorDrone::RollPitchCmdController(float& Xref, float& Yref, float& roll_cmd, float& pitch_cmd)
{
float posX, posY;
float velX, velY;
posX = State.Position.X;
posY = State.Position.Y;
velX = State.Velocity.X;
velY = State.Velocity.Y;
Matrix33 yaw_rot;
yaw_rot.FromEuler(0.0, 0.0, State.Orientation.Z);
float max_roll = MaxRoll;
float max_pitch = MaxPitch;
FVector XYvel(velX, velY, 0);
FVector Kp(pid.Kpx, pid.Kpy, 0);
FVector Kd = { -pid.Kd_x, pid.Kd_y, 0.0 };
FVector Kv(pid.Kvx, pid.Kvy, 0);
FVector rp_cmd;
if (Mode == GUIDED)
{
FVector PosXY(posX, posY, 0);
FVector XYref(Xref, Yref, 0);
//FVector Ki(-pid.I_xy, pid.I_xy, 0.0);
bool insideX = false;
bool insideY = false;
FVector Error = yaw_rot * (XYref - PosXY);
if (fabs(Error.Y) <= XYerrTol)
{
insideY = true;
if (fabsf(XYvel.Y) <= 0.5)
{
Error.Y = 0.0f;
}
}
if (fabs(Error.X) <= XYerrTol)
{
insideX = true;
if (fabsf(XYvel.X) <= 0.5)
{
Error.X = 0.0f;
}
}
float err_norm = Error.Size();
float vel_norm = XYvel.Size();
//UE_LOG(LogTemp, Display, TEXT("Vel size %f"), vel_norm);
if (!insideX || !insideY)
{
if (vel_norm >= 1.0)
{
FVector hit_vector = (XYvel / vel_norm) * err_norm;
FVector hit_dist_vector = Error - hit_vector;
if (hit_dist_vector.Size() < XYerrTol)
{
//UE_LOG(LogTemp, Display, TEXT("Error X Y %f %f"), Error.X, Error.Y);
Error = hit_vector;
//UE_LOG(LogTemp, Display, TEXT("Error X Y %f %f"), Error.X, Error.Y);
}
}
rp_cmd = Kp * Error + Kv * XYvel;
}
else
{
if (vel_norm > 0.5)
{
rp_cmd = Kv * XYvel;
}
else
{
rp_cmd = { 0.0, 0.0, 0.0 };
}
}
//UE_LOG(LogTemp, Display, TEXT("RP command X Y %f %f"), rp_cmd.X, rp_cmd.Y);
//UE_LOG(LogTemp, Display, TEXT("RP command X Y %f %f"), rp_cmd.X, rp_cmd.Y);
/*
float xy_tol = XYerrTol;
if (insideX && insideY)
{
if (abs(velX) < 0.5 && abs(velY) < 0.5)
{
Error.Y = 0.0f;
Error.X = 0.0f;
}
else
{
float md = fmin(fabsf(Error.Y), fabsf(Error.X));
xy_tol = md / 2;
if (fabsf(velX) < 1.0 && fabsf(velY) < 1.0)
{
max_roll = PI / 10;
max_pitch = PI / 10;
}
}
}
*/
float XYclose_radius = 5.0;
//rp_cmd = Kp * Error + Kv * XYvel;
FVector d_error = (Error - pid.prev_XYZ_error) / MjStep;
pid.prev_XYZ_error.X = Error.X;
pid.prev_XYZ_error.Y = Error.Y;
//rp_cmd = Kp * Error + Kd * d_error;
//FVector rp_cmd = Kp * Error;
roll_cmd = fmax(fmin(rp_cmd.Y, max_roll), -max_roll);
pitch_cmd = fmax(fmin(rp_cmd.X, max_pitch), -max_pitch);
UE_LOG(LogTemp, Display, TEXT("Target X position %f"), TargetPosition.X);
//UE_LOG(LogTemp, Display, TEXT("Current X position %f"), State.Position.X);
UE_LOG(LogTemp, Display, TEXT("Target Y position %f"), TargetPosition.Y);
//UE_LOG(LogTemp, Display, TEXT("Current Y position %f"), State.Position.Y);
}
else
{
float bx_c, by_c;
if (KeepXYPosition && XYvel.Size2D() > 0.1)
{
rp_cmd = Kv * XYvel;
//rp_cmd.X = fmax(-MaxAcceleration, fmin(rp_cmd.X, MaxAcceleration));
//rp_cmd.Y = fmax(-MaxAcceleration, fmin(rp_cmd.Y, MaxAcceleration));
//float c = ThrustCmd / Mass;
//pitch_cmd = atan2f(rp_cmd.X, c);
//roll_cmd = atan2f(rp_cmd.Y, c);
roll_cmd = fmax(-MaxRoll, fmin(rp_cmd.Y, MaxRoll));
pitch_cmd = fmax(-MaxPitch, fmin(rp_cmd.X, MaxPitch));
}
else
{
if (KeepXYPosition)
{
KeepXYPosition = false;
}
float roll_ratio = (CurrentRollPWM - PWM_min_us) / (PWM_max_us - PWM_min_us);
float pitch_ratio = (CurrentPitchPWM - PWM_min_us) / (PWM_max_us - PWM_min_us);
roll_cmd = -MaxRoll + roll_ratio * 2 * MaxRoll;
pitch_cmd = -MaxPitch + pitch_ratio * 2 * MaxPitch;
}
FQuaternion roll_pitch_quat;
roll_pitch_quat.InitializeFromEulerAngles(roll_cmd, pitch_cmd, 0);
Matrix33 roll_pitch_mat = roll_pitch_quat.GetMatrix();
FVector z_axis(0, 0, 1);
z_axis = roll_pitch_mat * z_axis;
bx_c = -z_axis.X;
by_c = -z_axis.Y;
//UE_LOG(LogTemp, Display, TEXT("bx by commanded %f %f "), bx_c, by_c);
FVector euler = State.Orientation;
FQuaternion quat;
quat.InitializeFromEulerAngles(euler.X, euler.Y, 0);
Matrix33 mat = quat.GetMatrix();
//Matrix33 mat;
//mat.FromEuler(euler.X, euler.Y, 0);
float bx = mat(0, 2);
float by = mat(1, 2);
//UE_LOG(LogTemp, Display, TEXT("Current bx by %f %f"), bx, by);
float bx_dot = pid2.Kp_roll * (bx_c - bx);
float by_dot = pid2.Kp_pitch * (by_c - by);
Matrix33 mat2(mat(1, 0), -mat(0, 0), 0.0, mat(1, 1), -mat(0, 1), 0.0, 0.0, 0.0, 0.0);
mat2 = mat2 * (1 / mat(2, 2));
FVector b(bx_dot, by_dot, 0.0);
FVector pq_c = mat2 * b;
float p_c = pq_c.X;
float q_c = pq_c.Y;
//UE_LOG(LogTemp, Display, TEXT("Commanded p q : %f %f"), p_c, q_c);
p_c = fmax(-MaxRPrate, fmin(p_c, MaxRPrate));
q_c = fmax(-MaxRPrate, fmin(q_c, MaxRPrate));
float p = PQR.X;
float q = PQR.Y;
float r = PQR.Z;
roll_cmd = pid2.Kp_p * (p_c - p);
pitch_cmd = pid2.Kp_q * (q_c - q);
}
}
void AMultiRotorDrone::AttitudeController(float& roll_cmd, float& pitch_cmd, float& roll_tau, float& pitch_tau)
{
if (Mode == GUIDED)
{
FVector pr(State.Orientation.Y, State.Orientation.X, 0.0);
FVector pr_ref(pitch_cmd, roll_cmd, 0.0);
FVector qp(PQR.Y, PQR.X, 0.0);
//UE_LOG(LogTemp, Display, TEXT("PQR %f %f %f"), PQR.X, PQR.Y, PQR.Z);
FVector Kp(pid.Kpp, pid.Kpr, 0.0);
FVector Kd(pid.Kdp, pid.Kdr, 0.0);
FVector pr_error = pr_ref - pr;
//UE_LOG(LogTemp, Display, TEXT("PR error %f %f"), pr_error.X, pr_error.Y);
FVector prev_int = pid.pr_error_int;
pid.pr_error_int += pr_error * MjStep;
pid.pr_error_int -= prev_int * pid.AntiWU;
//FVector error_dt = (pr_error - pid.prev_pr_error) / MjStep;
pid.prev_pr_error = pr_error;
FVector tau_pr = Kp * pr_error + pid.I_pr * pid.pr_error_int - Kd * qp;
//FVector tau_pr = Kp * pr_error + pid.I_pr * pid.pr_error_int + Kd * error_dt;
pitch_tau = tau_pr.X;
roll_tau = tau_pr.Y;
//pitch_tau = pitch_cmd;
//roll_tau = roll_cmd;
}
else
{
pitch_tau = pitch_cmd;
roll_tau = roll_cmd;
}
//UE_LOG(LogTemp, Display, TEXT("Roll tau Pitch tau raw %f %f "), roll_tau, pitch_tau);
float max_rps = MaxRPM / 60;
float min_rps = MinRPM / 60;
float up_limit = 2 * max_rps * max_rps * Kt - 2 * min_rps * min_rps * Kt;
float low_limit = -up_limit;
pitch_tau = fmax(fmin(pitch_tau, up_limit), low_limit);
roll_tau = fmax(fmin(roll_tau, up_limit), low_limit);
//UE_LOG(LogTemp, Display, TEXT("Roll tau Pitch tau %f %f "), roll_tau, pitch_tau);
}
void AMultiRotorDrone::Mixer(float& tot_thrust, float& roll_tau, float& pitch_tau, float& yaw_tau)
{
//UE_LOG(LogTemp, Display, TEXT("Total thrust %f"), tot_thrust);
float xf = fabs(Rotors.p1.X);
float yf = fabs(Rotors.p1.Y);
float xr = fabs(Rotors.p2.X);
float yr = fabs(Rotors.p2.Y);
//float w1sq = pitch_tau / (2 * Kt * (xf + xr)) - roll_tau / (2 * Kt * (yf + yr)) + (tot_thrust * xr) / (2 * (xf + xr)) + (yaw_tau * yr) / (2 * Km * (yf + yr));
float w1sq = pitch_tau / (2 * Kt * (xf + xr)) - roll_tau / (2 * Kt * (yf + yr)) + (tot_thrust * xr) / (2 * Kt * (xf + xr)) + (yaw_tau * yr) / (2 * Km * (yf + yr));
//float w2sq = roll_tau / (2 * Kt * (yf + yr)) - pitch_tau / (2 * Kt * (xf + xr)) + (tot_thrust * xf) / (2 * (xf + xr)) + (yaw_tau * yf) / (2 * Km * (yf + yr));
float w2sq = roll_tau / (2 * Kt * (yf + yr)) - pitch_tau / (2 * Kt * (xf + xr)) + (tot_thrust * xf) / (2 * Kt * (xf + xr)) + (yaw_tau * yf) / (2 * Km * (yf + yr));
//float w3sq = pitch_tau / (2 * Kt * (xf + xr)) + roll_tau / (2 * Kt * (yf + yr)) + (tot_thrust * xr) / (2 * (xf + xr)) - (yaw_tau * yr) / (2 * Km * (yf + yr));
float w3sq = pitch_tau / (2 * Kt * (xf + xr)) + roll_tau / (2 * Kt * (yf + yr)) + (tot_thrust * xr) / (2 * Kt * (xf + xr)) - (yaw_tau * yr) / (2 * Km * (yf + yr));
//float w4sq = (tot_thrust * xf) / (2 * (xf + xr)) - roll_tau / (2 * Kt * (yf + yr)) - pitch_tau / (2 * Kt * (xf + xr)) - (yaw_tau * yf) / (2 * Km * (yf + yr));
float w4sq = (tot_thrust * xf) / (2 * Kt * (xf + xr)) - roll_tau / (2 * Kt * (yf + yr)) - pitch_tau / (2 * Kt * (xf + xr)) - (yaw_tau * yf) / (2 * Km * (yf + yr));
//UE_LOG(LogTemp, Display, TEXT("Computed omegas %f %f %f %f"), w1sq, w2sq, w3sq, w4sq);
ComputedRPM[0] = w1sq;
ComputedRPM[1] = w2sq;
ComputedRPM[2] = w3sq;
ComputedRPM[3] = w4sq;
float min_level = (0.05 * Mass * GRAVITY) / Kt;
float max_level = (MaxRPM / 60) * (MaxRPM / 60);
float min_wsq = ComputedRPM[0];
for (int32 i = 1; i < 4; i++)
{
//if (ComputedRPM[i] < min_wsq) min_wsq = ComputedRPM[i];
if (ComputedRPM[i] < 0) ComputedRPM[i] = 0;
}
//if (min_wsq < min_level)
//{
//float diff = min_level - min_wsq;
//for (int32 i = 0; i < 4; i++)
//{
//ComputedRPM[i] += diff;
//}
//}
//for (int32 i = 0; i < 4; i++)
//{
//if (ComputedRPM[i] > max_level) ComputedRPM[i] = max_level;
//}
if (w1sq >= 0.1) RPM[0] = sqrtf(w1sq);
if (w2sq > 0.1) RPM[1] = sqrtf(w2sq);
if (w3sq > 0.1) RPM[2] = -sqrtf(w3sq);
if (w4sq > 0.1) RPM[3] = -sqrtf(w4sq);
ComputeForcesAndMoments();
}
void AMultiRotorDrone::ComputeForcesAndMoments()
{
for (int32 i = 0; i < 4; i++)
{
float rps_sq = ComputedRPM[i];
float rps = sqrtf(rps_sq);
FVector prop_loc;
switch (i)
{
case 0:
prop_loc = Rotors.p1;
break;
case 1:
prop_loc = Rotors.p2;
break;
case 2:
prop_loc = Rotors.p3;
break;
case 3:
prop_loc = Rotors.p4;
break;
}
//float J = GetJ(prop_loc, rps, Rotors.Diameter);
//float Ct = Rotors.get_value(Rotors.cThrust, J);
//float Cq = Rotors.get_value(Rotors.cTorque, J);
//UE_LOG(LogTemp, Display, TEXT("Prop %d J Ct Cq %f %f %f"), i+1, J, Ct, Cq);
//Kt = AIR_DENCITY * Ct * powf(Rotors.Diameter, 4);
//Km = AIR_DENCITY * Cq * powf(Rotors.Diameter, 5);
float force = Kt * rps_sq;
ForcesToApply[i] = force;
MomentsToApply[i] = Km * rps_sq;
if (i == 0 || i == 1)
{
MomentsToApply[i] *= -1;
}
}
UE_LOG(LogTemp, Display, TEXT("Forces %f %f %f %f"), ForcesToApply[0], ForcesToApply[1], ForcesToApply[2], ForcesToApply[3]);
UE_LOG(LogTemp, Display, TEXT("Moments %f %f %f %f"), MomentsToApply[0], MomentsToApply[1], MomentsToApply[2], MomentsToApply[3]);
}
void AMultiRotorDrone::ComputedDragForce()
{
FQuaternion Orient;
Orient.InitializeFromEulerAngles(State.Orientation.X, State.Orientation.Y, State.Orientation.Z);
Matrix33 Tl2b = Orient.GetMatrix().Transposed();
FVector Vb = Tl2b * State.Velocity;
double Qbar = 0.5 * AIR_DENCITY * (Vb.X * Vb.X + Vb.Y * Vb.Y + Vb.Z * Vb.Z);
double QbarX = 0.5 * AIR_DENCITY * Vb.X * Vb.X;
double QbarY = 0.5 * AIR_DENCITY * Vb.Y * Vb.Y;
double QbarZ = 0.5 * AIR_DENCITY * Vb.Z * Vb.Z;
//Vb.Normalize();
int sign = Vb.X >= 0 ? -1 : 1;
ComputedDrag[0] = sign * QbarX * SrefX * CdXY;
sign = Vb.Y >= 0 ? -1 : 1;
ComputedDrag[1] = sign * QbarY * SrefY * CdXY;
sign = Vb.Z >= 0 ? 1 : -1;
ComputedDrag[2] = sign * QbarZ * SrefZ * SrefZ * CdZ;
FVector drag(ComputedDrag[0], ComputedDrag[1], -ComputedDrag[2]);
FVector wdrag = Tl2b.Transposed() * drag;
//UE_LOG(LogTemp, Display, TEXT("Computed Drag %f %f %f"), wdrag[0], wdrag[1], wdrag[2]);
}
void AMultiRotorDrone::ControllerUpdate()
{
float total_thrust;
if (LandingStarted)
{
Landing();
}
float target_height = TargetPosition.Z;
AltitudeController(target_height, total_thrust);
float target_yaw = TargetYaw;
//float tau_yaw;
float yaw_cmd;
YawController(target_yaw, yaw_cmd);
float roll_cmd = 0;
float pitch_cmd = 0;
float xref = TargetPosition.X;
float yref = TargetPosition.Y;
RollPitchCmdController(xref, yref, roll_cmd, pitch_cmd);
//UE_LOG(LogTemp, Display, TEXT("Roll Pitch commands %f %f"), target_roll, target_pitch);
float tau_roll, tau_pitch;
AttitudeController(roll_cmd, pitch_cmd, tau_roll, tau_pitch);
//BodyRateController(roll_cmd, pitch_cmd, yaw_cmd, tau_roll, tau_pitch, tau_yaw);
Mixer(total_thrust, tau_roll, tau_pitch, yaw_cmd);
//UE_LOG(LogTemp, Display, TEXT("Commanded Position %f %f %f"), TargetPosition.X, TargetPosition.Y, TargetPosition.Z);
//UE_LOG(LogTemp, Display, TEXT("Current Position %f %f %f"), State.Position.X, State.Position.Y, State.Position.Z);
//ComputedDragForce();
//UE_LOG(LogTemp, Display, TEXT("Motors commands %f %f %f %f"), ComputedRPM[0], ComputedRPM[1], ComputedRPM[2], ComputedRPM[3]);
}
/*
void AMultiRotorDrone::CorrectForces()
{
float minForce = ForcesToApply[0];
for (int32 i = 1; i < 4; i++)
{
if (ForcesToApply[i] < 0)
{
minForce = ForcesToApply[i];
//ForcesToApply[i] = 0.0;
}
}
//float min_per_motor = minThrust * 0.25;
float min_per_motor = 0;
if (minForce < min_per_motor)
{
float diff = min_per_motor - minForce;
for (int32 i = 0; i < 4; i++)
{
ForcesToApply[i] += diff;
}
}
}
*/
void AMultiRotorDrone::Landing()
{
static bool initialized = false;
static float prev_height = 0;
static int height_counter = 0;
if (!initialized)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::White, TEXT("LANDING STARTED"));
prev_height = State.Position.Z;
initialized = true;
}
float height = State.Position.Z;
if (fabsf(height - prev_height) <= 0.001)
{
height_counter += 1;
if (height_counter >= 1000)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("LANDING COMPLETED"));
height_counter = 0;
LandingStarted = false;
initialized = false;
TakeOffStarted = false;
OnGround = true;
}
}
float landing_speed = 3;
if (TargetPosition.Z < 0)
{
TargetPosition.Z += landing_speed * MjStep;
}
prev_height = height;
}
//CONTROLLER 2
void AMultiRotorDrone::LateralController(float& x, float& dx, float& ddx, float& y, float& dy, float& ddy, float& c, float& bx_c, float& by_c)
{
float dt = UGameplayStatics::GetWorldDeltaSeconds(this);
float X = State.Position.X;
float Y = State.Position.Y;
float dX = State.Velocity.X;
float dY = State.Velocity.Y;
FQuaternion yaw_quat;
yaw_quat.InitializeFromEulerAngles(0, 0, State.Orientation.Z);
Matrix33 yaw_rot = yaw_quat.GetMatrix();
float x_err = x - X;
float dx_ff = x_err / dt;
float ddx_ff = dx_ff / dt;
float dx_err = dx_ff - dX;
float y_err = y - Y;
float dy_ff = y_err / dt;
float ddy_ff = dy_ff / dt;
float dy_err = dy_ff - dY;
//FVector pos_error(x_err, y_err, 0);
//FVector vel_error(dx_err, dy_err, 0);
//pos_error = yaw_rot * pos_error;
//vel_error = yaw_rot * vel_error;
//x_err = pos_error.X;
//y_err = pos_error.Y;
//dx_err = vel_error.X;
//dy_err = vel_error.Y;
float ddx_cmd = pid2.xKp * x_err + pid2.xKd * dx_ff + ddx_ff;
ddx_cmd = fmax(fmin(ddx_cmd, 5), -5);
float ddy_cmd = pid2.yKp * y_err + pid2.yKd * dy_ff + ddy_ff;
ddy_cmd = fmax(fmin(ddy_cmd, 5), -5);
//float z_thrust = c * Mass;
//float x_thrust = fabs(ddx_cmd * Mass);
//float y_thrust = fabs(ddy_cmd * Mass);
//FVector tot_thrust(x_thrust, y_thrust, z_thrust);
//double thrust = tot_thrust.Size();
//if (thrust > maxThrustPerMotor * 4)
//{
//tot_thrust.Normalize();
//tot_thrust *= (maxThrustPerMotor * 4);
//float thrust_margin = maxThrustPerMotor * 4 - z_thrust;
//x_thrust = thrust * (x_thrust / tot_thrust.Size2D());
//y_thrust = thrust_margin * (y_thrust / (x_thrust + y_thrust));
//ddx_cmd = tot_thrust.X / Mass;
//ddy_cmd = tot_thrust.Y / Mass;
//c = tot_thrust.Z / Mass;
//}
//int sign_x = ddx_cmd >= 0 ? 1 : -1;
//int sign_y = ddy_cmd >= 0 ? 1 : -1;
//ddx_cmd = (x_thrust * sign_x) / Mass;
//ddy_cmd = (y_thrust * sign_y) / Mass;
UE_LOG(LogTemp, Display, TEXT("X Y accel commands: %f %f"), ddx_cmd, ddy_cmd);
bx_c = -ddx_cmd / c;
by_c = -ddy_cmd / c;
}
void AMultiRotorDrone::RollPitchController(float& bx_c, float& by_c, float& p_c, float& q_c)
{
FVector euler = State.Orientation;
UE_LOG(LogTemp, Display, TEXT("Carrent orientation: %f %f %f"), euler.X, euler.Y, euler.Z);
FQuaternion quat;
quat.InitializeFromEulerAngles(euler.X, euler.Y, euler.Z);
Matrix33 mat = quat.GetMatrix();
float bx = mat(0, 2);
float by = mat(1, 2);
float bx_dot = pid2.Kp_roll * (bx_c - bx);
float by_dot = pid2.Kp_pitch * (by_c - by);
Matrix33 mat2(mat(1, 0), -mat(0, 0), 0.0, mat(1, 1), -mat(0, 1), 0.0, 0.0, 0.0, 0.0);
mat2 = mat2 * (1 / mat(2, 2));
FVector b(bx_dot, by_dot, 0.0);
FVector pq_c = mat2 * b;
float max_tilt = PI / 4;
p_c = pq_c.X;
q_c = pq_c.Y;
UE_LOG(LogTemp, Display, TEXT("Commanded p q : %f %f"), p_c, q_c);
}
void AMultiRotorDrone::BodyRateController(float& p_c, float& q_c, float& r_c, float& u_bar_p, float& u_bar_q, float& u_bar_r)
{
float p = PQR.X;
float q = PQR.Y;
float r = PQR.Z;
u_bar_p = pid2.Kp_p * (p_c - p);
u_bar_q = pid2.Kp_q * (q_c - q);
//u_bar_r = pid2.Kp_r * (r_c - r);
UE_LOG(LogTemp, Display, TEXT("Commanded body rates : %f %f %f"), u_bar_p, u_bar_q, u_bar_r);
float max_rps = MaxRPM / 60;
float min_rps = MinRPM / 60;
float pq_up_limit = 2 * max_rps * max_rps * Kt - 2 * min_rps * min_rps * Kt;
float pq_low_limit = -pq_up_limit;
float r_up_limit = 2 * max_rps * max_rps * Km - 2 * min_rps * min_rps * Km;
float r_low_limit = -r_up_limit;
u_bar_p = fmax(pq_low_limit, fmin(u_bar_p, pq_up_limit));
u_bar_q = fmax(pq_low_limit, fmin(u_bar_p, pq_up_limit));
//u_bar_r = fmax(r_low_limit, fmin(u_bar_p, r_up_limit));
}
void AMultiRotorDrone::HeadingController(float& yaw_c, float& yaw_tau)
{
float yaw = State.Orientation.Z;
yaw_tau = pid2.Kp_yaw * (yaw_c - yaw);
}
void AMultiRotorDrone::HeightController(float& z_t, float& dz_t, float& ddz_t, float& c)
{
float Z = State.Position.Z;
float dZ = State.Velocity.Z;
float dt = UGameplayStatics::GetWorldDeltaSeconds(this);
float z_error = z_t - Z;
float dz_ff = z_error / dt;
float ddz_ff = dz_ff / dt;
float u_1_bar = pid2.zKp * z_error + pid2.zKd * (dz_ff - dZ) + ddz_ff;
FQuaternion quat;
quat.InitializeFromEulerAngles(State.Orientation.X, State.Orientation.Y, State.Orientation.Z);
Matrix33 mat = quat.GetMatrix();
float b_z = mat(2, 2);
c = (-u_1_bar - GRAVITY) / b_z;
if ((c + GRAVITY) * Mass > maxThrustPerMotor * 4)
{
c = ((maxThrustPerMotor * 4) / Mass) - GRAVITY;
}
//UE_LOG(LogTemp, Display, TEXT("Target thrust vs maxThrust %f %f"), c * Mass, maxThrustPerMotor * 4);
}
void AMultiRotorDrone::Controller2Update()
{
UE_LOG(LogTemp, Display, TEXT("Target Height %f"), TargetPosition.Z);
float dt = UGameplayStatics::GetWorldDeltaSeconds(this);
float c = 0;
float target_height = TargetPosition.Z;
float dz_ff = MaxVelocity;
float ddz_ff = MaxVelocity / dt;
HeightController(target_height, dz_ff, ddz_ff, c);
float bx_c = 0;
float by_c = 0;
float target_x = TargetPosition.X;
float dx_ff = MaxVelocity;
float ddx_ff = MaxVelocity / dt;
float target_y = TargetPosition.Y;
float dy_ff = MaxVelocity;
float ddy_ff = MaxVelocity / dt;
LateralController(target_x, dx_ff, ddx_ff, target_y, dy_ff, ddy_ff, c, bx_c, by_c);
float p_c, q_c;
RollPitchController(bx_c, by_c, p_c, q_c);
float yaw_target = TargetYaw;
float r_c;
HeadingController(yaw_target, r_c);
float u_bar_p, u_bar_q, u_bar_r;
BodyRateController(p_c, q_c, r_c, u_bar_p, u_bar_q, u_bar_r);
//float omega_c = c / Kt;
//float omega_p = u_bar_p / Kt;
//float omega_q = u_bar_q / Kt;
//float omega_r = u_bar_r / Km;
ComputedRPM[0] = (0.25 * (c - u_bar_p + u_bar_q + u_bar_r)) / Kt;
ComputedRPM[1] = (0.25 * (c + u_bar_p - u_bar_q + u_bar_r)) / Kt;
ComputedRPM[2] = (0.25 * (c + u_bar_p + u_bar_q - u_bar_r)) / Kt;
ComputedRPM[3] = (0.25 * (c - u_bar_p - u_bar_q - u_bar_r)) / Kt;
//float w1sq = 0.25 * (omega_c - omega_p + omega_q + omega_r);
//float w2sq = 0.25 * (omega_c + omega_p - omega_q + omega_r);
//float w3sq = 0.25 * (omega_c + omega_p + omega_q - omega_r);
//float w4sq = 0.25 * (omega_c - omega_p - omega_q - omega_r);
//ComputedRPM[0] = w1sq;
//ComputedRPM[1] = w2sq;
//ComputedRPM[2] = w3sq;
//ComputedRPM[3] = w4sq;
/*
float min_rps_sq_value = 0;
float min_rps_sq = min_rps_sq_value;
for (int32 i = 0; i < 4; i++)
{
if (ComputedRPM[i] < min_rps_sq)
{
min_rps_sq = ComputedRPM[i];
}
}
float diff = min_rps_sq_value - min_rps_sq;
for (int32 i = 0; i < 4; i++)
{
ComputedRPM[i] += diff;
}
*/
//UE_LOG(LogTemp, Display, TEXT("Motors commands %f %f %f %f"), ComputedRPM[0], ComputedRPM[1], ComputedRPM[2], ComputedRPM[3]);
ComputeForcesAndMoments();
}