ardupilot/Tools/ArdupilotMegaPlanner/HIL/QuadCopter.cs
Michael Oborne 32832dd296 Mission Planner 1.2.5
add experimental antenna tracker find
add new apparam eeprom reader
add ground alt display to hud
mod stats
modify guided mode alt selection.
test flight planner tab on flight data tab
move some functions to the right click menu
add xplanes data in/out setup to be automatic.
add better mission upload handeling.
2012-08-12 12:25:22 +08:00

310 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using log4net;
using YLScsDrawing.Drawing3d;
using ArdupilotMega.HIL;
namespace ArdupilotMega.HIL
{
public class Motor : Utils
{
const bool True = true;
const bool False = false;
public Motor self;
public double angle;
public bool clockwise;
public double servo;
public Motor(double angle, bool clockwise, double servo)
{
self = this;
self.angle = angle;
self.clockwise = clockwise;
self.servo = servo;
}
public static Motor[] build_motors(string frame)
{
Motor[] motors = new HIL.Motor[8];
frame = frame.ToLower();
if (frame.Contains("quad") || frame.Contains("quadx"))
{
motors = new HIL.Motor[] {
new Motor(90, False, 1),
new Motor(270, False, 2),
new Motor(0, True, 3),
new Motor(180, True, 4),
};
if (frame.Contains("quadx"))
{
foreach (int i in range(4))
motors[i].angle -= 45.0;
}
}
else if (frame.Contains("y6"))
{
motors = new HIL.Motor[] {
new Motor(60, False, 1),
new Motor(60, True, 7),
new Motor(180, True, 4),
new Motor(180, False, 8),
new Motor(-60, True, 2),
new Motor(-60, False, 3),
};
}
else if (frame.Contains("hexa") || frame.Contains("hexax"))
{
motors = new HIL.Motor[] {
new Motor(0, True, 1),
new Motor(60, False, 4),
new Motor(120, True, 8),
new Motor(180, False, 2),
new Motor(240, True, 3),
new Motor(300, False, 7),
};
}
else if (frame.Contains("hexax"))
{
motors = new HIL.Motor[] {
new Motor(30, False, 7),
new Motor(90, True, 1),
new Motor(150, False, 4),
new Motor(210, True, 8),
new Motor(270, False, 2),
new Motor(330, True, 3),
};
}
else if (frame.Contains("octa") || frame.Contains("octax"))
{
motors = new HIL.Motor[] {
new Motor(0, True, 1),
new Motor(180, True, 2),
new Motor(45, False, 3),
new Motor(135, False, 4),
new Motor(-45, False, 7),
new Motor(-135, False, 8),
new Motor(270, True, 10),
new Motor(90, True, 11),
};
if (frame.Contains("octax"))
{
foreach (int i in range(8))
motors[i].angle += 22.5;
}
}
return motors;
}
}
public class QuadCopter : Aircraft
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
QuadCopter self;
DateTime seconds = DateTime.Now;
double[] motor_speed = null;
double hover_throttle;
double terminal_velocity;
double terminal_rotation_rate;
Motor[] motors;
Vector3 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(string frame = "quad")
{
self = this;
motors = Motor.build_motors(frame);
mass = 1.0;// # Kg
frame_height = 0.1;
motor_speed = new double[motors.Length];
hover_throttle = 0.37;
terminal_velocity = 30.0;
terminal_rotation_rate = 4 * 360.0 * deg2rad;
thrust_scale = (mass * gravity) / (motors.Length * hover_throttle);
last_time = DateTime.Now;
}
double scale_rc(int sn, float servo, float min, float max)
{
return ((servo - 1000) / 1000.0);
}
public void update(ref double[] servos, ArdupilotMega.GCSViews.Simulation.FGNetFDM fdm)
{
for (int i = 0; i < servos.Length; i++)
{
var servo = servos[(int)self.motors[i].servo - 1];
if (servo <= 0.0)
{
motor_speed[i] = 0;
}
else
{
motor_speed[i] = scale_rc(i, (float)servo, 0.0f, 1.0f);
//servos[i] = motor_speed[i];
}
}
double[] m = motor_speed;
//# 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, in body frame
Vector3 rot_accel = new Vector3(0, 0, 0);
double thrust = 0.0;
foreach (var i in range((self.motors.Length)))
{
rot_accel.x += -radians(5000.0) * sin(radians(self.motors[i].angle)) * m[i];
rot_accel.y += radians(5000.0) * cos(radians(self.motors[i].angle)) * m[i];
if (self.motors[i].clockwise)
{
rot_accel.z -= m[i] * radians(400.0);
}
else
{
rot_accel.z += m[i] * radians(400.0);
}
thrust += m[i] * self.thrust_scale; // newtons
}
// rotational air resistance
rot_accel.x -= self.gyro.x * radians(5000.0) / self.terminal_rotation_rate;
rot_accel.y -= self.gyro.y * radians(5000.0) / self.terminal_rotation_rate;
rot_accel.z -= self.gyro.z * radians(400.0) / self.terminal_rotation_rate;
// Console.WriteLine("rot_accel " + rot_accel.ToString());
// update rotational rates in body frame
self.gyro += rot_accel * delta_time.TotalSeconds;
// Console.WriteLine("gyro " + gyro.ToString());
// update attitude
self.dcm.rotate(self.gyro * delta_time.TotalSeconds);
self.dcm.normalize();
// air resistance
Vector3 air_resistance = -self.velocity * (self.gravity / self.terminal_velocity);
accel_body = new Vector3(0, 0, -thrust / self.mass);
Vector3 accel_earth = self.dcm * accel_body;
accel_earth += new Vector3(0, 0, self.gravity);
accel_earth += air_resistance;
// add in some wind
// accel_earth += self.wind.accel(self.velocity);
// if we're on the ground, then our vertical acceleration is limited
// to zero. This effectively adds the force of the ground on the aircraft
if (self.on_ground() && accel_earth.z > 0)
accel_earth.z = 0;
// work out acceleration as seen by the accelerometers. It sees the kinematic
// acceleration (ie. real movement), plus gravity
self.accel_body = self.dcm.transposed() * (accel_earth + new Vector3(0, 0, -self.gravity));
// new velocity vector
self.velocity += accel_earth * delta_time.TotalSeconds;
if (double.IsNaN(velocity.x) || double.IsNaN(velocity.y) || double.IsNaN(velocity.z))
velocity = new Vector3();
// new position vector
old_position = self.position.copy();
self.position += self.velocity * delta_time.TotalSeconds;
if (home_latitude == 0)
{
home_latitude = fdm.latitude * rad2deg;
home_longitude = fdm.longitude * rad2deg;
home_altitude = altitude;
}
// constrain height to the ground
if (self.on_ground())
{
if (!self.on_ground(old_position))
Console.WriteLine("Hit ground at {0} m/s", (self.velocity.z));
self.velocity = new Vector3(0, 0, 0);
// zero roll/pitch, but keep yaw
double r = 0;
double p = 0;
double y = 0;
self.dcm.to_euler(ref r, ref p, ref y);
self.dcm.from_euler(0, 0, y);
self.position = new Vector3(self.position.x, self.position.y,
-(self.ground_level + self.frame_height - self.home_altitude));
}
// update lat/lon/altitude
self.update_position(delta_time.TotalSeconds);
// send to apm
MAVLink.mavlink_hil_state_t hilstate = new MAVLink.mavlink_hil_state_t();
hilstate.time_usec = (UInt64)DateTime.Now.Ticks; // microsec
hilstate.lat = (int)(latitude * 1e7); // * 1E7
hilstate.lon = (int)(longitude * 1e7); // * 1E7
hilstate.alt = (int)(altitude * 1000); // mm
self.dcm.to_euler(ref roll, ref pitch, ref yaw);
if (double.IsNaN(roll))
{
self.dcm.identity();
}
hilstate.roll = (float)roll;
hilstate.pitch = (float)pitch;
hilstate.yaw = (float)yaw;
Vector3 earth_rates = Utils.BodyRatesToEarthRates(dcm, gyro);
hilstate.rollspeed = (float)earth_rates.x;
hilstate.pitchspeed = (float)earth_rates.y;
hilstate.yawspeed = (float)earth_rates.z;
hilstate.vx = (short)(velocity.y * 100); // m/s * 100
hilstate.vy = (short)(velocity.x * 100); // m/s * 100
hilstate.vz = 0; // m/s * 100
hilstate.xacc = (short)(accelerometer.x * 1000); // (mg)
hilstate.yacc = (short)(accelerometer.y * 1000); // (mg)
hilstate.zacc = (short)(accelerometer.z * 1000); // (mg)
MainV2.comPort.sendPacket(hilstate);
}
}
}