You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
311 lines
11 KiB
311 lines
11 KiB
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; |
|
|
|
int framecount = 0; |
|
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); |
|
} |
|
} |
|
} |