2015-02-24 01:16:05 -04:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
from aircraft import Aircraft
|
|
|
|
import util, time, math
|
2015-04-17 20:25:01 -03:00
|
|
|
from math import degrees, radians, sin, cos
|
2015-02-24 01:16:05 -04:00
|
|
|
from rotmat import Vector3, Matrix3
|
|
|
|
|
|
|
|
class HeliCopter(Aircraft):
|
|
|
|
'''a HeliCopter simulator'''
|
|
|
|
def __init__(self, frame='+',
|
2015-03-10 12:22:46 -03:00
|
|
|
hover_throttle=0.65,
|
|
|
|
terminal_velocity=40.0,
|
2015-02-24 01:16:05 -04:00
|
|
|
frame_height=0.1,
|
2015-04-17 20:25:01 -03:00
|
|
|
hover_lean=8.0,
|
|
|
|
yaw_zero=0.1,
|
|
|
|
rotor_rot_accel=radians(20),
|
|
|
|
mass=2.13):
|
2015-02-24 01:16:05 -04:00
|
|
|
Aircraft.__init__(self)
|
|
|
|
self.mass = mass # Kg
|
|
|
|
self.hover_throttle = hover_throttle
|
|
|
|
self.terminal_velocity = terminal_velocity
|
|
|
|
self.terminal_rotation_rate = 4*radians(360.0)
|
|
|
|
self.frame_height = frame_height
|
|
|
|
self.last_time = time.time()
|
2015-04-17 20:25:01 -03:00
|
|
|
self.roll_rate_max = radians(1400)
|
|
|
|
self.pitch_rate_max = radians(1400)
|
|
|
|
self.yaw_rate_max = radians(1400)
|
2015-03-10 12:22:46 -03:00
|
|
|
self.rsc_setpoint = 0.8
|
2015-02-24 01:16:05 -04:00
|
|
|
self.thrust_scale = (self.mass * self.gravity) / self.hover_throttle
|
2015-04-17 20:25:01 -03:00
|
|
|
self.rotor_rot_accel=rotor_rot_accel
|
|
|
|
|
|
|
|
# calculate lateral thrust ratio for tail rotor
|
|
|
|
self.tail_thrust_scale = sin(radians(hover_lean)) * self.thrust_scale / yaw_zero
|
2015-02-24 01:16:05 -04:00
|
|
|
|
|
|
|
def update(self, servos):
|
|
|
|
# how much time has passed?
|
|
|
|
t = time.time()
|
|
|
|
delta_time = t - self.last_time
|
|
|
|
self.last_time = t
|
|
|
|
|
|
|
|
# get swash proportions (these are from 0 to 1)
|
|
|
|
swash1 = servos[0]
|
|
|
|
swash2 = servos[1]
|
|
|
|
swash3 = servos[2]
|
|
|
|
tail_rotor = servos[3]
|
|
|
|
rsc = servos[7]
|
|
|
|
|
|
|
|
# get total thrust from 0 to 1
|
|
|
|
thrust = (rsc/self.rsc_setpoint)*(swash1+swash2+swash3)/3.0
|
|
|
|
|
|
|
|
# very simplistic mapping to body euler rates
|
|
|
|
roll_rate = swash1 - swash2
|
|
|
|
pitch_rate = (swash1 + swash2)/2.0 - swash3
|
|
|
|
yaw_rate = tail_rotor - 0.5
|
|
|
|
|
|
|
|
#print(swash1, swash2, swash3, roll_rate, pitch_rate, yaw_rate, thrust, rsc)
|
|
|
|
|
2015-04-17 20:25:01 -03:00
|
|
|
rsc_scale = rsc/self.rsc_setpoint
|
|
|
|
|
|
|
|
roll_rate *= rsc_scale
|
|
|
|
pitch_rate *= rsc_scale
|
|
|
|
yaw_rate *= rsc_scale
|
2015-03-10 12:22:46 -03:00
|
|
|
|
2015-02-24 01:16:05 -04:00
|
|
|
rot_accel = Vector3(roll_rate*self.roll_rate_max,
|
|
|
|
pitch_rate*self.pitch_rate_max,
|
|
|
|
yaw_rate*self.yaw_rate_max)
|
|
|
|
|
|
|
|
# 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
|
2015-03-10 12:22:46 -03:00
|
|
|
rot_accel.z -= self.gyro.z * radians(5000.0) / self.terminal_rotation_rate
|
2015-02-24 01:16:05 -04:00
|
|
|
|
2015-04-17 20:25:01 -03:00
|
|
|
rot_accel.z += (rsc_scale+thrust) * self.rotor_rot_accel
|
|
|
|
|
2015-02-24 01:16:05 -04:00
|
|
|
# update rotational rates in body frame
|
|
|
|
self.gyro += rot_accel * delta_time
|
|
|
|
|
|
|
|
# update attitude
|
|
|
|
self.dcm.rotate(self.gyro * delta_time)
|
|
|
|
self.dcm.normalize()
|
|
|
|
|
|
|
|
# air resistance
|
|
|
|
air_resistance = - self.velocity * (self.gravity/self.terminal_velocity)
|
|
|
|
|
|
|
|
thrust *= self.thrust_scale
|
|
|
|
|
2015-04-17 20:25:01 -03:00
|
|
|
accel_body = Vector3(0, yaw_rate * rsc_scale * self.tail_thrust_scale, -thrust / self.mass)
|
2015-02-24 01:16:05 -04:00
|
|
|
accel_earth = self.dcm * accel_body
|
|
|
|
accel_earth += Vector3(0, 0, self.gravity)
|
|
|
|
accel_earth += air_resistance
|
|
|
|
|
|
|
|
# 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() and 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 + Vector3(0, 0, -self.gravity))
|
|
|
|
|
|
|
|
# new velocity vector
|
|
|
|
self.velocity += accel_earth * delta_time
|
|
|
|
|
|
|
|
# new position vector
|
|
|
|
old_position = self.position.copy()
|
|
|
|
self.position += self.velocity * delta_time
|
|
|
|
|
|
|
|
# constrain height to the ground
|
|
|
|
if self.on_ground():
|
|
|
|
if not self.on_ground(old_position):
|
|
|
|
print("Hit ground at %f m/s" % (self.velocity.z))
|
|
|
|
|
|
|
|
self.velocity = Vector3(0, 0, 0)
|
|
|
|
# zero roll/pitch, but keep yaw
|
|
|
|
(r, p, y) = self.dcm.to_euler()
|
|
|
|
self.dcm.from_euler(0, 0, y)
|
|
|
|
|
|
|
|
self.position = Vector3(self.position.x, self.position.y,
|
|
|
|
-(self.ground_level + self.frame_height - self.home_altitude))
|
|
|
|
|
|
|
|
# update lat/lon/altitude
|
2015-04-01 13:11:25 -03:00
|
|
|
self.update_position()
|