class IMUBarometerFusion(QMainWindow): FACTOR = 1 KP1 = 0.55 * FACTOR # PI observer velocity gain KP2 = 1.0 * FACTOR # PI observer position gain KI = 0.001 / FACTOR # PI observer integral gain (bias cancellation) def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.initialized = False self.altitude_error_i = 0 self.acc_scale = 0.0 self.start_time = time.time() self.altitude_error = 0 self.inst_acceleration = 0.0 self.delta = 0 self.estimated_velocity = 0.0 self.estimated_altitude = 0.0 self.last_time = time.time() self.last_orig_altitude = 0 self.last_estimated_altitude = 0 ipcon = IPConnection() self.imu = IMU(UID_IMU, ipcon) self.barometer = Barometer(UID_BAROMETER, ipcon) ipcon.connect(HOST, PORT) # Turn leds and orientation calculation off, to save calculation time # for the IMU Brick. This makes sure that the measurements are taken # in equidistant 2ms intervals self.imu.leds_off() self.imu.orientation_calculation_off() # Turn averaging of in the Barometer Bricklet to make sure that # the data is without delay self.barometer.set_averaging(0, 0, 0) red_pen = QPen(Qt.red) red_pen.setWidth(5) plot_list = [ #['', Qt.blue, self.get_orig_value], ['', red_pen, self.get_estimated_value] ] self.plot_widget = PlotWidget('Height [m]', plot_list) self.plot_widget.stop = False self.setCentralWidget(self.plot_widget) self.timer = QTimer() self.timer.timeout.connect(self.update) self.timer.start(6) def get_orig_value(self): return self.last_orig_altitude def get_estimated_value(self): return self.last_estimated_altitude # Update measurements and compute new altitude every 6ms. def update(self): q = self.imu.get_quaternion() acc = self.imu.get_acceleration() alt = self.barometer.get_altitude() / 100.0 compensated_acc_q = self.compute_compensated_acc(q, acc) compensated_acc_q_earth = self.compute_dynamic_acceleration_vector( q, compensated_acc_q) self.last_orig_altitude = alt self.last_estimated_altitude = self.compute_altitude( compensated_acc_q_earth[2], alt) # Remove gravity from accelerometer measurements def compute_compensated_acc(self, q, a): g = (2 * (q.x * q.z - q.w * q.y), 2 * (q.w * q.x + q.y * q.z), q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z) return (a[0] / 1000.0 - g[0], a[1] / 1000.0 - g[1], a[2] / 1000.0 - g[2], 0.0) # Rotate dynamic acceleration vector from sensor frame to earth frame def compute_dynamic_acceleration_vector(self, q, compensated_acc_q): def q_conj(q): return -q[0], -q[1], -q[2], q[3] def q_mult(q1, q2): x1, y1, z1, w1 = q1 x2, y2, z2, w2 = q2 w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2 x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2 y = w1 * y2 + y1 * w2 + z1 * x2 - x1 * z2 z = w1 * z2 + z1 * w2 + x1 * y2 - y1 * x2 return x, y, z, w tmp = q_mult(q, compensated_acc_q) return q_mult(tmp, q_conj(q)) # Computes estimation of altitude based on barometer and accelerometer measurements # Code is based on blog post from Fabio Varesano: http://www.varesano.net/blog/fabio/complementary-filtering-high-res-barometer-and-accelerometer-reliable-altitude-estimation # He seems to have got the idea from the MultiWii project def compute_altitude(self, compensated_acceleration, altitude): self.current_time = time.time() # Initialization if not self.initialized: self.initialized = True self.estimated_altitude = altitude self.estimated_velocity = 0 self.altitude_error_i = 0 # Estimation Error self.altitude_error = altitude - self.estimated_altitude self.altitude_error_i = self.altitude_error_i + self.altitude_error self.altitude_error_i = min(2500.0, max(-2500.0, self.altitude_error_i)) self.inst_acceleration = compensated_acceleration * 9.80665 + self.altitude_error_i * self.KI dt = self.current_time - self.last_time # Integrators self.delta = self.inst_acceleration * dt + (self.KP1 * dt) * self.altitude_error self.estimated_altitude += (self.estimated_velocity / 5.0 + self.delta) * (dt / 2) + ( self.KP2 * dt) * self.altitude_error self.estimated_velocity += self.delta * 10.0 self.last_time = self.current_time return self.estimated_altitude
class IMUBarometerFusion(QMainWindow): FACTOR = 1 KP1 = 0.55*FACTOR # PI observer velocity gain KP2 = 1.0*FACTOR # PI observer position gain KI = 0.001/FACTOR # PI observer integral gain (bias cancellation) def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.initialized = False self.altitude_error_i = 0 self.acc_scale = 0.0 self.start_time = time.time() self.altitude_error = 0 self.inst_acceleration = 0.0 self.delta = 0 self.estimated_velocity = 0.0 self.estimated_altitude = 0.0 self.last_time = time.time() self.last_orig_altitude = 0 self.last_estimated_altitude = 0 ipcon = IPConnection() self.imu = IMU(UID_IMU, ipcon) self.barometer = Barometer(UID_BAROMETER, ipcon) ipcon.connect(HOST, PORT) # Turn leds and orientation calculation off, to save calculation time # for the IMU Brick. This makes sure that the measurements are taken # in equidistant 2ms intervals self.imu.leds_off() self.imu.orientation_calculation_off() # Turn averaging of in the Barometer Bricklet to make sure that # the data is without delay self.barometer.set_averaging(0, 0, 0) red_pen = QPen(Qt.red) red_pen.setWidth(5) plot_list = [#['', Qt.blue, self.get_orig_value], ['', red_pen, self.get_estimated_value] ] self.plot_widget = PlotWidget('Height [m]', plot_list) self.plot_widget.stop = False self.setCentralWidget(self.plot_widget) self.timer = QTimer() self.timer.timeout.connect(self.update) self.timer.start(6) def get_orig_value(self): return self.last_orig_altitude def get_estimated_value(self): return self.last_estimated_altitude # Update measurements and compute new altitude every 6ms. def update(self): q = self.imu.get_quaternion() acc = self.imu.get_acceleration() alt = self.barometer.get_altitude()/100.0 compensated_acc_q = self.compute_compensated_acc(q, acc) compensated_acc_q_earth = self.compute_dynamic_acceleration_vector(q, compensated_acc_q) self.last_orig_altitude = alt self.last_estimated_altitude = self.compute_altitude(compensated_acc_q_earth[2], alt) # Remove gravity from accelerometer measurements def compute_compensated_acc(self, q, a): g = (2*(q.x*q.z - q.w*q.y), 2*(q.w*q.x + q.y*q.z), q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z) return (a[0]/1000.0 - g[0], a[1]/1000.0 - g[1], a[2]/1000.0 - g[2], 0.0) # Rotate dynamic acceleration vector from sensor frame to earth frame def compute_dynamic_acceleration_vector(self, q, compensated_acc_q): def q_conj(q): return -q[0], -q[1], -q[2], q[3] def q_mult(q1, q2): x1, y1, z1, w1 = q1 x2, y2, z2, w2 = q2 w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2 x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2 y = w1 * y2 + y1 * w2 + z1 * x2 - x1 * z2 z = w1 * z2 + z1 * w2 + x1 * y2 - y1 * x2 return x, y, z, w tmp = q_mult(q, compensated_acc_q) return q_mult(tmp, q_conj(q)) # Computes estimation of altitude based on barometer and accelerometer measurements # Code is based on blog post from Fabio Varesano: http://www.varesano.net/blog/fabio/complementary-filtering-high-res-barometer-and-accelerometer-reliable-altitude-estimation # He seems to have got the idea from the MultiWii project def compute_altitude(self, compensated_acceleration, altitude): self.current_time = time.time() # Initialization if not self.initialized: self.initialized = True self.estimated_altitude = altitude self.estimated_velocity = 0 self.altitude_error_i = 0 # Estimation Error self.altitude_error = altitude - self.estimated_altitude self.altitude_error_i = self.altitude_error_i + self.altitude_error self.altitude_error_i = min(2500.0, max(-2500.0, self.altitude_error_i)) self.inst_acceleration = compensated_acceleration * 9.80665 + self.altitude_error_i * self.KI dt = self.current_time - self.last_time # Integrators self.delta = self.inst_acceleration * dt + (self.KP1 * dt) * self.altitude_error self.estimated_altitude += (self.estimated_velocity/5.0 + self.delta) * (dt / 2) + (self.KP2 * dt) * self.altitude_error self.estimated_velocity += self.delta*10.0 self.last_time = self.current_time return self.estimated_altitude