/**************************************************************************** * * Copyright (C) 2008-2012 PX4 Development Team. All rights reserved. * Author: Damian Aregger * Tobias Naegeli * Lorenz Meier * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "position_estimator_mc_params.h" //#include #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_cmd("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 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; }