diff options
author | Lorenz Meier <lm@inf.ethz.ch> | 2013-03-23 22:39:54 +0100 |
---|---|---|
committer | Lorenz Meier <lm@inf.ethz.ch> | 2013-03-23 22:39:54 +0100 |
commit | 482cada59ba87bc1ac538f7f165a57880e7fbeda (patch) | |
tree | cbc8939fb22e9aae5a0e7b0b39f52fad71285f5c /apps/position_estimator_mc/position_estimator_mc_main.c | |
parent | 0dc96dbd891cf108a4ecc01539f5f710c6dd92e9 (diff) | |
download | px4-firmware-482cada59ba87bc1ac538f7f165a57880e7fbeda.tar.gz px4-firmware-482cada59ba87bc1ac538f7f165a57880e7fbeda.tar.bz2 px4-firmware-482cada59ba87bc1ac538f7f165a57880e7fbeda.zip |
Butchered position estimator from Damian Aregger into shape, publishes now global position estimate as well. Compiling, needs HIL testing
Diffstat (limited to 'apps/position_estimator_mc/position_estimator_mc_main.c')
-rwxr-xr-x | apps/position_estimator_mc/position_estimator_mc_main.c | 510 |
1 files changed, 510 insertions, 0 deletions
diff --git a/apps/position_estimator_mc/position_estimator_mc_main.c b/apps/position_estimator_mc/position_estimator_mc_main.c new file mode 100755 index 000000000..10dee3f22 --- /dev/null +++ b/apps/position_estimator_mc/position_estimator_mc_main.c @@ -0,0 +1,510 @@ +/**************************************************************************** + * + * Copyright (C) 2008-2012 PX4 Development Team. All rights reserved. + * Author: Damian Aregger <daregger@student.ethz.ch> + * Tobias Naegeli <naegelit@student.ethz.ch> +* Lorenz Meier <lm@inf.ethz.ch> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file position_estimator_main.c + * Model-identification based position estimator for multirotors + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdbool.h> +#include <fcntl.h> +#include <float.h> +#include <string.h> +#include <nuttx/config.h> +#include <nuttx/sched.h> +#include <sys/prctl.h> +#include <termios.h> +#include <errno.h> +#include <limits.h> +#include <math.h> +#include <uORB/uORB.h> +#include <uORB/topics/sensor_combined.h> +#include <uORB/topics/parameter_update.h> +#include <uORB/topics/vehicle_attitude.h> +#include <uORB/topics/actuator_outputs.h> +#include <uORB/topics/actuator_controls_effective.h> +#include <uORB/topics/vehicle_status.h> +#include <uORB/topics/vehicle_vicon_position.h> +#include <uORB/topics/vehicle_local_position.h> +#include <uORB/topics/vehicle_global_position.h> +#include <uORB/topics/vehicle_local_position_setpoint.h> +#include <uORB/topics/vehicle_gps_position.h> +#include <mavlink/mavlink_log.h> +#include <poll.h> +#include <systemlib/geo/geo.h> +#include <systemlib/err.h> +#include <systemlib/systemlib.h> + +#include <drivers/drv_hrt.h> + +#include "position_estimator_mc_params.h" +//#include <uORB/topics/debug_key_value.h> +#include "codegen/kalman_dlqe2.h" +#include "codegen/kalman_dlqe2_initialize.h" +#include "codegen/kalman_dlqe3.h" +#include "codegen/kalman_dlqe3_initialize.h" + +static bool thread_should_exit = false; /**< Deamon exit flag */ +static bool thread_running = false; /**< Deamon status flag */ +static int position_estimator_mc_task; /**< Handle of deamon task / thread */ + +__EXPORT int position_estimator_mc_main(int argc, char *argv[]); + +int position_estimator_mc_thread_main(int argc, char *argv[]); +/** + * Print the correct usage. + */ +static void usage(const char *reason); + +static void +usage(const char *reason) +{ + if (reason) + warnx("%s\n", reason); + warnx("usage: position_estimator_mc {start|stop|status}"); + exit(1); +} + +/** + * The position_estimator_mc_thread only briefly exists to start + * the background job. The stack size assigned in the + * Makefile does only apply to this management task. + * + * The actual stack size should be set in the call + * to task_create(). + */ +int position_estimator_mc_main(int argc, char *argv[]) +{ + if (argc < 1) + usage("missing command"); + + if (!strcmp(argv[1], "start")) { + + if (thread_running) { + printf("position_estimator_mc already running\n"); + /* this is not an error */ + exit(0); + } + + thread_should_exit = false; + position_estimator_mc_task = task_spawn("position_estimator_mc", + SCHED_RR, + SCHED_PRIORITY_MAX - 5, + 4096, + position_estimator_mc_thread_main, + (argv) ? (const char **)&argv[2] : (const char **)NULL); + exit(0); + } + if (!strcmp(argv[1], "stop")) { + thread_should_exit = true; + exit(0); + } + + if (!strcmp(argv[1], "status")) { + if (thread_running) { + warnx("position_estimator_mc is running"); + } else { + warnx("position_estimator_mc not started"); + } + exit(0); + } + + usage("unrecognized command"); + exit(1); +} + +/**************************************************************************** + * main + ****************************************************************************/ +int position_estimator_mc_thread_main(int argc, char *argv[]) +{ + /* welcome user */ + warnx("[position_estimator_mc] started"); + int mavlink_fd; + mavlink_fd = open(MAVLINK_LOG_DEVICE, 0); + mavlink_log_info(mavlink_fd, "[position_estimator_mc] started"); + + /* initialize values */ + float z[3] = {0, 0, 0}; /* output variables from tangent plane mapping */ + // float rotMatrix[4] = {1.0f, 0.0f, 0.0f, 1.0f}; + float x_x_aposteriori_k[3] = {1.0f, 0.0f, 0.0f}; + float x_y_aposteriori_k[3] = {1.0f, 0.0f, 0.0f}; + float x_z_aposteriori_k[3] = {1.0f, 0.0f, 0.0f}; + float x_x_aposteriori[3] = {0.0f, 0.0f, 0.0f}; + float x_y_aposteriori[3] = {1.0f, 0.0f, 0.0f}; + float x_z_aposteriori[3] = {1.0f, 0.0f, 0.0f}; + + // XXX this is terribly wrong and should actual dT instead + const float dT_const_50 = 1.0f/50.0f; + + float addNoise = 0.0f; + float sigma = 0.0f; + //computed from dlqe in matlab + const float K_vicon_50Hz[3] = {0.5297f, 0.9873f, 0.9201f}; + // XXX implement baro filter + const float K_baro[3] = {0.0248f, 0.0377f, 0.0287f}; + float K[3] = {0.0f, 0.0f, 0.0f}; + int baro_loop_cnt = 0; + int baro_loop_end = 70; /* measurement for 1 second */ + float p0_Pa = 0.0f; /* to determin while start up */ + float rho0 = 1.293f; /* standard pressure */ + const float const_earth_gravity = 9.81f; + + float posX = 0.0f; + float posY = 0.0f; + float posZ = 0.0f; + + double lat_current; + double lon_current; + float alt_current; + + float gps_origin_altitude = 0.0f; + + /* Initialize filter */ + kalman_dlqe2_initialize(); + kalman_dlqe3_initialize(); + + /* declare and safely initialize all structs */ + struct sensor_combined_s sensor; + memset(&sensor, 0, sizeof(sensor)); + struct vehicle_attitude_s att; + memset(&att, 0, sizeof(att)); + struct vehicle_status_s vehicle_status; + memset(&vehicle_status, 0, sizeof(vehicle_status)); /* make sure that baroINITdone = false */ + struct vehicle_vicon_position_s vicon_pos; + memset(&vicon_pos, 0, sizeof(vicon_pos)); + struct actuator_controls_effective_s act_eff; + memset(&act_eff, 0, sizeof(act_eff)); + struct vehicle_gps_position_s gps; + memset(&gps, 0, sizeof(gps)); + struct vehicle_local_position_s local_pos_est; + memset(&local_pos_est, 0, sizeof(local_pos_est)); + struct vehicle_global_position_s global_pos_est; + memset(&global_pos_est, 0, sizeof(global_pos_est)); + + /* subscribe */ + int sensor_sub = orb_subscribe(ORB_ID(sensor_combined)); + int sub_params = orb_subscribe(ORB_ID(parameter_update)); + int vehicle_attitude_sub = orb_subscribe(ORB_ID(vehicle_attitude)); + int vicon_pos_sub = orb_subscribe(ORB_ID(vehicle_vicon_position)); + int actuator_eff_sub = orb_subscribe(ORB_ID(actuator_controls_effective_0)); + int vehicle_gps_sub = orb_subscribe(ORB_ID(vehicle_gps_position)); + int vehicle_status_sub = orb_subscribe(ORB_ID(vehicle_status)); + + /* advertise */ + orb_advert_t local_pos_est_pub = 0; + orb_advert_t global_pos_est_pub = 0; + + struct position_estimator_mc_params pos1D_params; + struct position_estimator_mc_param_handles pos1D_param_handles; + /* initialize parameter handles */ + parameters_init(&pos1D_param_handles); + + bool flag_use_gps = false; + bool flag_use_baro = false; + bool flag_baro_initialized = false; /* in any case disable baroINITdone */ + /* FIRST PARAMETER READ at START UP*/ + struct parameter_update_s update; + orb_copy(ORB_ID(parameter_update), sub_params, &update); /* read from param to clear updated flag */ + /* FIRST PARAMETER UPDATE */ + parameters_update(&pos1D_param_handles, &pos1D_params); + flag_use_baro = pos1D_params.baro; + sigma = pos1D_params.sigma; + addNoise = pos1D_params.addNoise; + /* END FIRST PARAMETER UPDATE */ + + /* try to grab a vicon message - if it fails, go for GPS. */ + + /* make sure the next orb_check() can't return true unless we get a timely update */ + orb_copy(ORB_ID(vehicle_vicon_position), vicon_pos_sub, &vicon_pos); + /* allow 200 ms for vicon to come in */ + usleep(200000); + /* check if we got vicon */ + bool update_check; + orb_check(vicon_pos_sub, &update_check); + /* if no update was available, use GPS */ + flag_use_gps = !update_check; + + if (flag_use_gps) { + mavlink_log_info(mavlink_fd, "[pos_est_mc] GPS locked"); + /* wait until gps signal turns valid, only then can we initialize the projection */ + + // XXX magic number + float hdop_threshold_m = 4.0f; + float vdop_threshold_m = 8.0f; + + /* + * If horizontal dilution of precision (hdop / eph) + * and vertical diluation of precision (vdop / epv) + * are below a certain threshold (e.g. 4 m), AND + * home position is not yet set AND the last GPS + * GPS measurement is not older than two seconds AND + * the system is currently not armed, set home + * position to the current position. + */ + + while (!(gps.fix_type == 3 + && (gps.eph_m < hdop_threshold_m) + && (gps.epv_m < vdop_threshold_m) + && (hrt_absolute_time() - gps.timestamp_position < 2000000))) { + + struct pollfd fds1[2] = { + { .fd = vehicle_gps_sub, .events = POLLIN }, + { .fd = sub_params, .events = POLLIN }, + }; + + /* wait for GPS updates, BUT READ VEHICLE STATUS (!) + * this choice is critical, since the vehicle status might not + * actually change, if this app is started after GPS lock was + * aquired. + */ + if (poll(fds1, 2, 5000)) { + if (fds1[0].revents & POLLIN){ + /* Read gps position */ + orb_copy(ORB_ID(vehicle_gps_position), vehicle_gps_sub, &gps); + } + if (fds1[1].revents & POLLIN){ + /* Read out parameters to check for an update there, e.g. useGPS variable */ + /* read from param to clear updated flag */ + struct parameter_update_s updated; + orb_copy(ORB_ID(parameter_update), sub_params, &updated); + /* update parameters */ + parameters_update(&pos1D_param_handles, &pos1D_params); + } + } + static int printcounter = 0; + if (printcounter == 100) { + printcounter = 0; + warnx("[pos_est_mc] wait for GPS fix"); + } + printcounter++; + } + + /* get gps value for first initialization */ + orb_copy(ORB_ID(vehicle_gps_position), vehicle_gps_sub, &gps); + lat_current = ((double)(gps.lat)) * 1e-7d; + lon_current = ((double)(gps.lon)) * 1e-7d; + alt_current = gps.alt * 1e-3f; + gps_origin_altitude = alt_current; + /* initialize coordinates */ + map_projection_init(lat_current, lon_current); + /* publish global position messages only after first GPS message */ + printf("[pos_est_mc] initialized projection with: lat: %.10f, lon:%.10f\n", lat_current, lon_current); + + } else { + mavlink_log_info(mavlink_fd, "[pos_est_mc] I'm NOT using GPS - I use VICON"); + /* onboard calculated position estimations */ + } + thread_running = true; + + struct pollfd fds2[3] = { + { .fd = vehicle_gps_sub, .events = POLLIN }, + { .fd = vicon_pos_sub, .events = POLLIN }, + { .fd = sub_params, .events = POLLIN }, + }; + + bool vicon_updated = false; + bool gps_updated = false; + + /**< main_loop */ + while (!thread_should_exit) { + int ret = poll(fds2, 3, 20); //wait maximal this 20 ms = 50 Hz minimum rate + if (ret < 0) { + /* poll error */ + } else { + if (fds2[2].revents & POLLIN){ + /* new parameter */ + /* read from param to clear updated flag */ + struct parameter_update_s updated; + orb_copy(ORB_ID(parameter_update), sub_params, &updated); + /* update parameters */ + parameters_update(&pos1D_param_handles, &pos1D_params); + flag_use_baro = pos1D_params.baro; + sigma = pos1D_params.sigma; + addNoise = pos1D_params.addNoise; + } + vicon_updated = false; /* default is no vicon_updated */ + if (fds2[1].revents & POLLIN) { + /* new vicon position */ + orb_copy(ORB_ID(vehicle_vicon_position), vicon_pos_sub, &vicon_pos); + posX = vicon_pos.x; + posY = vicon_pos.y; + posZ = vicon_pos.z; + vicon_updated = true; /* set flag for vicon update */ + } /* end of poll call for vicon updates */ + gps_updated = false; + if (fds2[0].revents & POLLIN) { + /* new GPS value */ + orb_copy(ORB_ID(vehicle_gps_position), vehicle_gps_sub, &gps); + /* Project gps lat lon (Geographic coordinate system) to plane*/ + map_projection_project(((double)(gps.lat)) * 1e-7d, ((double)(gps.lon)) * 1e-7d, &(z[0]), &(z[1])); + posX = z[0]; + posY = z[1]; + posZ = (float)(gps.alt * 1e-3f); + gps_updated = true; + } + + /* Main estimator loop */ + orb_copy(ORB_ID(actuator_controls_effective_0), actuator_eff_sub, &act_eff); + orb_copy(ORB_ID(vehicle_attitude), vehicle_attitude_sub, &att); + orb_copy(ORB_ID(vehicle_status), vehicle_status_sub, &vehicle_status); + orb_copy(ORB_ID(sensor_combined), sensor_sub, &sensor); + // barometric pressure estimation at start up + if (!flag_baro_initialized){ + // mean calculation over several measurements + if (baro_loop_cnt<baro_loop_end) { + p0_Pa += (sensor.baro_pres_mbar*100); + baro_loop_cnt++; + } else { + p0_Pa /= (float)(baro_loop_cnt); + flag_baro_initialized = true; + char *baro_m_start = "barometer initialized with p0 = "; + char p0_char[15]; + sprintf(p0_char, "%8.2f", (double)(p0_Pa/100)); + char *baro_m_end = " mbar"; + char str[80]; + strcpy(str,baro_m_start); + strcat(str,p0_char); + strcat(str,baro_m_end); + mavlink_log_info(mavlink_fd, str); + } + } + if (flag_use_gps) { + /* initialize map projection with the last estimate (not at full rate) */ + if (gps.fix_type > 2) { + /* x-y-position/velocity estimation in earth frame = gps frame */ + kalman_dlqe3(dT_const_50,K_vicon_50Hz[0],K_vicon_50Hz[1],K_vicon_50Hz[2],x_x_aposteriori_k,posX,gps_updated,0.0f,0.0f,x_x_aposteriori); + memcpy(x_x_aposteriori_k, x_x_aposteriori, sizeof(x_x_aposteriori)); + kalman_dlqe3(dT_const_50,K_vicon_50Hz[0],K_vicon_50Hz[1],K_vicon_50Hz[2],x_y_aposteriori_k,posY,gps_updated,0.0f,0.0f,x_y_aposteriori); + memcpy(x_y_aposteriori_k, x_y_aposteriori, sizeof(x_y_aposteriori)); + /* z-position/velocity estimation in earth frame = vicon frame */ + float z_est = 0.0f; + if (flag_baro_initialized && flag_use_baro) { + z_est = -p0_Pa*logf(p0_Pa/(sensor.baro_pres_mbar*100))/(rho0*const_earth_gravity); + K[0] = K_vicon_50Hz[0]; + K[1] = K_vicon_50Hz[1]; + K[2] = K_vicon_50Hz[2]; + gps_updated = 1.0f; /* always enable the update, cause baro update = 200 Hz */ + } else { + z_est = posZ; + K[0] = K_vicon_50Hz[0]; + K[1] = K_vicon_50Hz[1]; + K[2] = K_vicon_50Hz[2]; + } + + kalman_dlqe3(dT_const_50,K[0],K[1],K[2],x_z_aposteriori_k,z_est,gps_updated,0.0f,0.0f,x_z_aposteriori); + memcpy(x_z_aposteriori_k, x_z_aposteriori, sizeof(x_z_aposteriori)); + local_pos_est.x = x_x_aposteriori_k[0]; + local_pos_est.vx = x_x_aposteriori_k[1]; + local_pos_est.y = x_y_aposteriori_k[0]; + local_pos_est.vy = x_y_aposteriori_k[1]; + local_pos_est.z = x_z_aposteriori_k[0]; + local_pos_est.vz = x_z_aposteriori_k[1]; + local_pos_est.timestamp = hrt_absolute_time(); + if ((isfinite(x_x_aposteriori_k[0])) && (isfinite(x_x_aposteriori_k[1])) && (isfinite(x_y_aposteriori_k[0])) && (isfinite(x_y_aposteriori_k[1])) && (isfinite(x_z_aposteriori_k[0])) && (isfinite(x_z_aposteriori_k[1]))) { + /* publish local position estimate */ + if (local_pos_est_pub > 0) { + orb_publish(ORB_ID(vehicle_local_position), local_pos_est_pub, &local_pos_est); + } else { + local_pos_est_pub = orb_advertise(ORB_ID(vehicle_local_position), &local_pos_est); + } + /* publish on GPS updates */ + if (gps_updated) { + + double lat, lon; + float alt = z_est + gps_origin_altitude; + + map_projection_reproject(local_pos_est.x, local_pos_est.y, &lat, &lon); + + global_pos_est.lat = lat; + global_pos_est.lon = lon; + global_pos_est.alt = alt; + + if (global_pos_est_pub > 0) { + orb_publish(ORB_ID(vehicle_global_position), global_pos_est_pub, &global_pos_est); + } else { + global_pos_est_pub = orb_advertise(ORB_ID(vehicle_global_position), &global_pos_est); + } + } + } + } + } else { + /* x-y-position/velocity estimation in earth frame = vicon frame */ + kalman_dlqe3(dT_const_50,K_vicon_50Hz[0],K_vicon_50Hz[1],K_vicon_50Hz[2],x_x_aposteriori_k,posX,vicon_updated,addNoise,sigma,x_x_aposteriori); + memcpy(x_x_aposteriori_k, x_x_aposteriori, sizeof(x_x_aposteriori)); + kalman_dlqe3(dT_const_50,K_vicon_50Hz[0],K_vicon_50Hz[1],K_vicon_50Hz[2],x_y_aposteriori_k,posY,vicon_updated,addNoise,sigma,x_y_aposteriori); + memcpy(x_y_aposteriori_k, x_y_aposteriori, sizeof(x_y_aposteriori)); + /* z-position/velocity estimation in earth frame = vicon frame */ + float z_est = 0.0f; + float local_sigma = 0.0f; + if (flag_baro_initialized && flag_use_baro) { + z_est = -p0_Pa*logf(p0_Pa/(sensor.baro_pres_mbar*100.0f))/(rho0*const_earth_gravity); + K[0] = K_vicon_50Hz[0]; + K[1] = K_vicon_50Hz[1]; + K[2] = K_vicon_50Hz[2]; + vicon_updated = 1; /* always enable the update, cause baro update = 200 Hz */ + local_sigma = 0.0f; /* don't add noise on barometer in any case */ + } else { + z_est = posZ; + K[0] = K_vicon_50Hz[0]; + K[1] = K_vicon_50Hz[1]; + K[2] = K_vicon_50Hz[2]; + local_sigma = sigma; + } + kalman_dlqe3(dT_const_50,K[0],K[1],K[2],x_z_aposteriori_k,z_est,vicon_updated,addNoise,local_sigma,x_z_aposteriori); + memcpy(x_z_aposteriori_k, x_z_aposteriori, sizeof(x_z_aposteriori)); + local_pos_est.x = x_x_aposteriori_k[0]; + local_pos_est.vx = x_x_aposteriori_k[1]; + local_pos_est.y = x_y_aposteriori_k[0]; + local_pos_est.vy = x_y_aposteriori_k[1]; + local_pos_est.z = x_z_aposteriori_k[0]; + local_pos_est.vz = x_z_aposteriori_k[1]; + local_pos_est.timestamp = hrt_absolute_time(); + if ((isfinite(x_x_aposteriori_k[0])) && (isfinite(x_x_aposteriori_k[1])) && (isfinite(x_y_aposteriori_k[0])) && (isfinite(x_y_aposteriori_k[1])) && (isfinite(x_z_aposteriori_k[0])) && (isfinite(x_z_aposteriori_k[1]))){ + orb_publish(ORB_ID(vehicle_local_position), local_pos_est_pub, &local_pos_est); + } + } + } /* end of poll return value check */ + } + + printf("[pos_est_mc] exiting.\n"); + mavlink_log_info(mavlink_fd, "[pos_est_mc] exiting"); + thread_running = false; + return 0; +} |