mirror of https://github.com/ArduPilot/ardupilot
264 lines
9.9 KiB
C#
264 lines
9.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using YLScsDrawing.Drawing3d;
|
|
|
|
namespace ArdupilotMega.HIL
|
|
{
|
|
public class QuadCopter : Aircraft
|
|
{
|
|
const float ft2m = (float)(1.0 / 3.2808399);
|
|
const float rad2deg = (float)(180 / Math.PI);
|
|
const float deg2rad = (float)(1.0 / rad2deg);
|
|
const float kts2fps = (float)1.68780986;
|
|
|
|
int framecount = 0;
|
|
DateTime seconds = DateTime.Now;
|
|
|
|
double[] motor_speed = null;
|
|
|
|
double hover_throttle;
|
|
double terminal_velocity;
|
|
|
|
Vector3d old_position;
|
|
|
|
|
|
//# scaling from total motor power to Newtons. Allows the copter
|
|
//# to hover against gravity when each motor is at hover_throttle
|
|
double thrust_scale;
|
|
|
|
DateTime last_time;
|
|
|
|
public QuadCopter()
|
|
{
|
|
mass = 1.0;// # Kg
|
|
frame_height = 0.1;
|
|
motor_speed = new double[] { 0.0, 0.0, 0.0, 0.0 };
|
|
hover_throttle = 0.37;
|
|
terminal_velocity = 30.0;
|
|
|
|
thrust_scale = (mass * gravity) / (4.0 * hover_throttle);
|
|
|
|
last_time = DateTime.Now;
|
|
}
|
|
|
|
double scale_rc(int sn, float servo, float min, float max)
|
|
{
|
|
float min_pwm = 1000;
|
|
float max_pwm = 2000;
|
|
//'''scale a PWM value''' # default to servo range of 1000 to 2000
|
|
if (MainV2.comPort.param.Count > 0)
|
|
{
|
|
min_pwm = int.Parse(MainV2.comPort.param["RC3_MIN"].ToString());
|
|
max_pwm = int.Parse(MainV2.comPort.param["RC3_MAX"].ToString());
|
|
}
|
|
|
|
float p = (servo - min_pwm) / (max_pwm - min_pwm);
|
|
|
|
float v = min + p * (max - min);
|
|
|
|
if (v < min)
|
|
v = min ;
|
|
if (v > max)
|
|
v = max ;
|
|
return v;
|
|
}
|
|
|
|
public void update(ref double[] servos, ArdupilotMega.GCSViews.Simulation.FGNetFDM fdm)
|
|
{
|
|
for (int i = 0; i < servos.Length; i++)
|
|
{
|
|
if (servos[i] <= 0.0)
|
|
{
|
|
motor_speed[i] = 0;
|
|
}
|
|
else
|
|
{
|
|
motor_speed[i] = scale_rc(i,(float)servos[i], 0.0f, 1.0f);
|
|
//servos[i] = motor_speed[i];
|
|
}
|
|
}
|
|
double[] m = motor_speed;
|
|
|
|
/*
|
|
roll = 0;
|
|
pitch = 0;
|
|
yaw = 0;
|
|
roll_rate = 0;
|
|
pitch_rate = 0;
|
|
yaw_rate = 0;
|
|
*/
|
|
|
|
// Console.WriteLine("\nin m {0:0.000000} {1:0.000000} {2:0.000000} {3:0.000000}", m[0], m[1], m[2], m[3]);
|
|
// Console.WriteLine("in vel {0:0.000000} {1:0.000000} {2:0.000000}", velocity.X, velocity.Y, velocity.Z);
|
|
//Console.WriteLine("in r {0:0.000000} p {1:0.000000} y {2:0.000000} - r {3:0.000000} p {4:0.000000} y {5:0.000000}", roll, pitch, yaw, roll_rate, pitch_rate, yaw_rate);
|
|
|
|
|
|
// m[0] *= 0.5;
|
|
|
|
//# how much time has passed?
|
|
DateTime t = DateTime.Now;
|
|
TimeSpan delta_time = t - last_time; // 0.02
|
|
last_time = t;
|
|
|
|
if (delta_time.TotalMilliseconds > 100) // somethings wrong / debug
|
|
{
|
|
delta_time = new TimeSpan(0, 0, 0, 0, 20);
|
|
}
|
|
|
|
//# rotational acceleration, in degrees/s/s
|
|
double roll_accel = (m[1] - m[0]) * 5000.0;
|
|
double pitch_accel = (m[2] - m[3]) * 5000.0;
|
|
double yaw_accel = -((m[2] + m[3]) - (m[0] + m[1])) * 400.0;
|
|
|
|
// Console.WriteLine("roll {0} {1} {2}", roll_accel, roll_rate, roll);
|
|
|
|
//# update rotational rates
|
|
roll_rate += roll_accel * delta_time.TotalSeconds;
|
|
pitch_rate += pitch_accel * delta_time.TotalSeconds;
|
|
yaw_rate += yaw_accel * delta_time.TotalSeconds;
|
|
|
|
// Console.WriteLine("roll {0} {1} {2}", roll_accel, roll_rate, roll);
|
|
|
|
//# update rotation
|
|
roll += roll_rate * delta_time.TotalSeconds;
|
|
pitch += pitch_rate * delta_time.TotalSeconds;
|
|
yaw += yaw_rate * delta_time.TotalSeconds;
|
|
|
|
// Console.WriteLine("roll {0} {1} {2}", roll_accel, roll_rate, roll);
|
|
|
|
//Console.WriteLine("r {0:0.0} p {1:0.0} y {2:0.0} - r {3:0.0} p {4:0.0} y {5:0.0} ms {6:0.000}", roll, pitch, yaw, roll_rate, pitch_rate, yaw_rate, delta_time.TotalSeconds);
|
|
|
|
//# air resistance
|
|
Vector3d air_resistance = -velocity * (gravity / terminal_velocity);
|
|
|
|
//# normalise rotations
|
|
normalise();
|
|
|
|
double thrust = (m[0] + m[1] + m[2] + m[3]) * thrust_scale;//# Newtons
|
|
double accel = thrust / mass;
|
|
|
|
//Console.WriteLine("in {0:0.000000} {1:0.000000} {2:0.000000} {3:0.000000}", roll, pitch, yaw, accel);
|
|
|
|
Vector3d accel3D = RPY_to_XYZ(roll, pitch, yaw, accel);
|
|
//Console.WriteLine("accel3D " + accel3D.X + " " + accel3D.Y + " " + accel3D.Z);
|
|
accel3D += new Vector3d(0, 0, -gravity);
|
|
accel3D += air_resistance;
|
|
|
|
|
|
//# new velocity vector
|
|
velocity += accel3D * delta_time.TotalSeconds;
|
|
this.accel = accel3D;
|
|
|
|
//# new position vector
|
|
old_position = new Vector3d(position);
|
|
position += velocity * delta_time.TotalSeconds;
|
|
|
|
// Console.WriteLine(fdm.agl + " "+ fdm.altitude);
|
|
|
|
//Console.WriteLine("Z {0} halt {1} < gl {2} fh {3}" ,position.Z , home_altitude , ground_level , frame_height);
|
|
|
|
if (home_latitude == 0)
|
|
{
|
|
this.home_latitude = fdm.latitude * rad2deg;
|
|
this.home_longitude = fdm.longitude * rad2deg;
|
|
this.home_altitude = fdm.altitude * ft2m;
|
|
this.ground_level = this.home_altitude;
|
|
|
|
this.altitude = fdm.altitude * ft2m;
|
|
this.latitude = fdm.latitude * rad2deg;
|
|
this.longitude = fdm.longitude * rad2deg;
|
|
}
|
|
|
|
//# constrain height to the ground
|
|
if (position.Z + home_altitude < ground_level + frame_height)
|
|
{
|
|
if (old_position.Z + home_altitude > ground_level + frame_height)
|
|
{
|
|
// Console.WriteLine("Hit ground at {0} m/s", (-velocity.Z));
|
|
}
|
|
velocity = new Vector3d(0, 0, 0);
|
|
roll_rate = 0;
|
|
pitch_rate = 0;
|
|
yaw_rate = 0;
|
|
roll = 0;
|
|
pitch = 0;
|
|
position = new Vector3d(position.X, position.Y,
|
|
ground_level + frame_height - home_altitude + 0);
|
|
// Console.WriteLine("here " + position.Z);
|
|
}
|
|
|
|
//# update lat/lon/altitude
|
|
update_position();
|
|
|
|
// send to apm
|
|
ArdupilotMega.MAVLink.__mavlink_gps_raw_t gps = new ArdupilotMega.MAVLink.__mavlink_gps_raw_t();
|
|
|
|
ArdupilotMega.MAVLink.__mavlink_attitude_t att = new ArdupilotMega.MAVLink.__mavlink_attitude_t();
|
|
|
|
ArdupilotMega.MAVLink.__mavlink_vfr_hud_t asp = new ArdupilotMega.MAVLink.__mavlink_vfr_hud_t();
|
|
|
|
att.roll = (float)roll * deg2rad;
|
|
att.pitch = (float)pitch * deg2rad;
|
|
att.yaw = (float)yaw * deg2rad;
|
|
att.rollspeed = (float)roll_rate * deg2rad;
|
|
att.pitchspeed = (float)pitch_rate * deg2rad;
|
|
att.yawspeed = (float)yaw_rate * deg2rad;
|
|
|
|
gps.alt = ((float)(altitude));
|
|
gps.fix_type = 3;
|
|
gps.v = ((float)Math.Sqrt((velocity.X * velocity.X) + (velocity.Y * velocity.Y)));
|
|
gps.hdg = (float)(((Math.Atan2(velocity.Y, velocity.X) * rad2deg) + 360) % 360); ;
|
|
gps.lat = ((float)latitude);
|
|
gps.lon = ((float)longitude);
|
|
gps.usec = ((ulong)DateTime.Now.Ticks);
|
|
|
|
asp.airspeed = gps.v;
|
|
|
|
MainV2.comPort.generatePacket(ArdupilotMega.MAVLink.MAVLINK_MSG_ID_ATTITUDE, att);
|
|
|
|
MAVLink.__mavlink_raw_pressure_t pres = new MAVLink.__mavlink_raw_pressure_t();
|
|
double calc = (101325 * Math.Pow(1 - 2.25577 * Math.Pow(10, -5) * gps.alt, 5.25588));
|
|
pres.press_diff1 = (short)(int)(calc); // 0 alt is 0 pa
|
|
|
|
MainV2.comPort.generatePacket(ArdupilotMega.MAVLink.MAVLINK_MSG_ID_RAW_PRESSURE, pres);
|
|
|
|
MainV2.comPort.generatePacket(ArdupilotMega.MAVLink.MAVLINK_MSG_ID_VFR_HUD, asp);
|
|
|
|
if (framecount % 10 == 0)
|
|
{// 50 / 10 = 5 hz
|
|
MainV2.comPort.generatePacket(ArdupilotMega.MAVLink.MAVLINK_MSG_ID_GPS_RAW, gps);
|
|
//Console.WriteLine(DateTime.Now.Millisecond + " GPS" );
|
|
}
|
|
|
|
framecount++;
|
|
}
|
|
|
|
public static Vector3d RPY_to_XYZ(double roll, double pitch, double yaw, double length)
|
|
{
|
|
Vector3d v = new Vector3d(0, 0, length);
|
|
v = new_rotate_euler(-deg2rad * (pitch), 0, -deg2rad * (roll)) * v;
|
|
v = new_rotate_euler(0, deg2rad * (yaw), 0) * v;
|
|
return v;
|
|
}
|
|
|
|
static Quaternion new_rotate_euler(double heading, double attitude, double bank)
|
|
{
|
|
Quaternion Q = new Quaternion();
|
|
double c1 = Math.Cos(heading / 2);
|
|
double s1 = Math.Sin(heading / 2);
|
|
double c2 = Math.Cos(attitude / 2);
|
|
double s2 = Math.Sin(attitude / 2);
|
|
double c3 = Math.Cos(bank / 2);
|
|
double s3 = Math.Sin(bank / 2);
|
|
|
|
Q.W = c1 * c2 * c3 - s1 * s2 * s3;
|
|
Q.X = s1 * s2 * c3 + c1 * c2 * s3;
|
|
Q.Y = s1 * c2 * c3 + c1 * s2 * s3;
|
|
Q.Z = c1 * s2 * c3 - s1 * c2 * s3;
|
|
return Q;
|
|
}
|
|
}
|
|
}
|