From 1caddb7bbb53f3017e2ee67742531b2159999658 Mon Sep 17 00:00:00 2001 From: "Hyon Lim (Retina)" Date: Tue, 21 May 2013 16:11:03 +1000 Subject: [PATCH 01/26] Initial work of so3 nonlinear complementary filter --- .../attitude_estimator_so3_comp_main.cpp | 610 ++++++++++++++++++ .../attitude_estimator_so3_comp_params.c | 44 ++ .../attitude_estimator_so3_comp_params.h | 32 + .../attitude_estimator_so3_comp/module.mk | 8 + 4 files changed, 694 insertions(+) create mode 100755 src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp create mode 100755 src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c create mode 100755 src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.h create mode 100644 src/modules/attitude_estimator_so3_comp/module.mk diff --git a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp new file mode 100755 index 0000000000..381b0df75c --- /dev/null +++ b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp @@ -0,0 +1,610 @@ +/* + * @file attitude_estimator_so3_comp_main.c + * + * Nonlinear SO3 filter for Attitude Estimation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif +#include "attitude_estimator_so3_comp_params.h" +#ifdef __cplusplus +} +#endif + +extern "C" __EXPORT int attitude_estimator_so3_comp_main(int argc, char *argv[]); + +static bool thread_should_exit = false; /**< Deamon exit flag */ +static bool thread_running = false; /**< Deamon status flag */ +static int attitude_estimator_so3_comp_task; /**< Handle of deamon task / thread */ +volatile float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; // quaternion of sensor frame relative to auxiliary frame + +/** + * Mainloop of attitude_estimator_so3_comp. + */ +int attitude_estimator_so3_comp_thread_main(int argc, char *argv[]); + +/** + * Print the correct usage. + */ +static void usage(const char *reason); + +static void +usage(const char *reason) +{ + if (reason) + fprintf(stderr, "%s\n", reason); + + fprintf(stderr, "usage: attitude_estimator_so3_comp {start|stop|status} [-p ]\n\n"); + exit(1); +} + +/** + * The attitude_estimator_so3_comp app 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 attitude_estimator_so3_comp_main(int argc, char *argv[]) +{ + if (argc < 1) + usage("missing command"); + + if (!strcmp(argv[1], "start")) { + + if (thread_running) { + printf("attitude_estimator_so3_comp already running\n"); + /* this is not an error */ + exit(0); + } + + thread_should_exit = false; + attitude_estimator_so3_comp_task = task_spawn("attitude_estimator_so3_comp", + SCHED_DEFAULT, + SCHED_PRIORITY_MAX - 5, + 12400, + attitude_estimator_so3_comp_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) { + printf("\tattitude_estimator_so3_comp app is running\n"); + + } else { + printf("\tattitude_estimator_so3_comp app not started\n"); + } + + exit(0); + } + + usage("unrecognized command"); + exit(1); +} + +//--------------------------------------------------------------------------------------------------- +// Fast inverse square-root +// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root +float invSqrt(float x) { + float halfx = 0.5f * x; + float y = x; + long i = *(long*)&y; + i = 0x5f3759df - (i>>1); + y = *(float*)&i; + y = y * (1.5f - (halfx * y * y)); + return y; +} + +void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, float twoKp, float twoKi, float dt) { + float recipNorm; + float halfvx, halfvy, halfvz; + float halfex, halfey, halfez; + float qa, qb, qc; + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Estimated direction of gravity and vector perpendicular to magnetic flux + halfvx = q1 * q3 - q0 * q2; + halfvy = q0 * q1 + q2 * q3; + halfvz = q0 * q0 - 0.5f + q3 * q3; + + // Error is sum of cross product between estimated and measured direction of gravity + halfex = (ay * halfvz - az * halfvy); + halfey = (az * halfvx - ax * halfvz); + halfez = (ax * halfvy - ay * halfvx); + + // Compute and apply integral feedback if enabled + if(twoKi > 0.0f) { + integralFBx += twoKi * halfex * dt; // integral error scaled by Ki + integralFBy += twoKi * halfey * dt; + integralFBz += twoKi * halfez * dt; + gx += integralFBx; // apply integral feedback + gy += integralFBy; + gz += integralFBz; + } + else { + integralFBx = 0.0f; // prevent integral windup + integralFBy = 0.0f; + integralFBz = 0.0f; + } + + // Apply proportional feedback + gx += twoKp * halfex; + gy += twoKp * halfey; + gz += twoKp * halfez; + } + + // Integrate rate of change of quaternion + gx *= (0.5f * dt); // pre-multiply common factors + gy *= (0.5f * dt); + gz *= (0.5f * dt); + qa = q0; + qb = q1; + qc = q2; + q0 += (-qb * gx - qc * gy - q3 * gz); + q1 += (qa * gx + qc * gz - q3 * gy); + q2 += (qa * gy - qb * gz + q3 * gx); + q3 += (qa * gz + qb * gy - qc * gx); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; +} + +void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, float twoKp, float twoKi, float dt) { + float recipNorm; + float q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + float hx, hy, bx, bz; + float halfvx, halfvy, halfvz, halfwx, halfwy, halfwz; + float halfex, halfey, halfez; + float qa, qb, qc; + + // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) + if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { + MahonyAHRSupdateIMU(gx, gy, gz, ax, ay, az, twoKp, twoKi, dt); + return; + } + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Normalise magnetometer measurement + recipNorm = invSqrt(mx * mx + my * my + mz * mz); + mx *= recipNorm; + my *= recipNorm; + mz *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + q0q0 = q0 * q0; + q0q1 = q0 * q1; + q0q2 = q0 * q2; + q0q3 = q0 * q3; + q1q1 = q1 * q1; + q1q2 = q1 * q2; + q1q3 = q1 * q3; + q2q2 = q2 * q2; + q2q3 = q2 * q3; + q3q3 = q3 * q3; + + // Reference direction of Earth's magnetic field + hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2)); + hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1)); + bx = sqrt(hx * hx + hy * hy); + bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2)); + + // Estimated direction of gravity and magnetic field + halfvx = q1q3 - q0q2; + halfvy = q0q1 + q2q3; + halfvz = q0q0 - 0.5f + q3q3; + halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2); + halfwy = bx * (q1q2 - q0q3) + bz * (q0q1 + q2q3); + halfwz = bx * (q0q2 + q1q3) + bz * (0.5f - q1q1 - q2q2); + + // Error is sum of cross product between estimated direction and measured direction of field vectors + halfex = (ay * halfvz - az * halfvy) + (my * halfwz - mz * halfwy); + halfey = (az * halfvx - ax * halfvz) + (mz * halfwx - mx * halfwz); + halfez = (ax * halfvy - ay * halfvx) + (mx * halfwy - my * halfwx); + + // Compute and apply integral feedback if enabled + if(twoKi > 0.0f) { + integralFBx += twoKi * halfex * dt; // integral error scaled by Ki + integralFBy += twoKi * halfey * dt; + integralFBz += twoKi * halfez * dt; + gx += integralFBx; // apply integral feedback + gy += integralFBy; + gz += integralFBz; + } + else { + integralFBx = 0.0f; // prevent integral windup + integralFBy = 0.0f; + integralFBz = 0.0f; + } + + // Apply proportional feedback + gx += twoKp * halfex; + gy += twoKp * halfey; + gz += twoKp * halfez; + } + + // Integrate rate of change of quaternion + gx *= (0.5f * dt); // pre-multiply common factors + gy *= (0.5f * dt); + gz *= (0.5f * dt); + qa = q0; + qb = q1; + qc = q2; + q0 += (-qb * gx - qc * gy - q3 * gz); + q1 += (qa * gx + qc * gz - q3 * gy); + q2 += (qa * gy - qb * gz + q3 * gx); + q3 += (qa * gz + qb * gy - qc * gx); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; +} + +/* + * [Rot_matrix,x_aposteriori,P_aposteriori] = attitudeKalmanfilter(dt,z_k,x_aposteriori_k,P_aposteriori_k,knownConst) + */ + +/* + * EKF Attitude Estimator main function. + * + * Estimates the attitude recursively once started. + * + * @param argc number of commandline arguments (plus command name) + * @param argv strings containing the arguments + */ +int attitude_estimator_so3_comp_thread_main(int argc, char *argv[]) +{ + +const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds + + float dt = 0.005f; + + /* output euler angles */ + float euler[3] = {0.0f, 0.0f, 0.0f}; + + float Rot_matrix[9] = {1.f, 0, 0, + 0, 1.f, 0, + 0, 0, 1.f + }; /**< init: identity matrix */ + + float acc[3] = {0.0f, 0.0f, 0.0f}; + float gyro[3] = {0.0f, 0.0f, 0.0f}; + float mag[3] = {0.0f, 0.0f, 0.0f}; + + // print text + printf("Nonlinear SO3 Attitude Estimator initialized..\n\n"); + fflush(stdout); + + int overloadcounter = 19; + + /* store start time to guard against too slow update rates */ + uint64_t last_run = hrt_absolute_time(); + + struct sensor_combined_s raw; + memset(&raw, 0, sizeof(raw)); + struct vehicle_attitude_s att; + memset(&att, 0, sizeof(att)); + struct vehicle_status_s state; + memset(&state, 0, sizeof(state)); + + uint64_t last_data = 0; + uint64_t last_measurement = 0; + + /* subscribe to raw data */ + int sub_raw = orb_subscribe(ORB_ID(sensor_combined)); + /* rate-limit raw data updates to 200Hz */ + orb_set_interval(sub_raw, 4); + + /* subscribe to param changes */ + int sub_params = orb_subscribe(ORB_ID(parameter_update)); + + /* subscribe to system state*/ + int sub_state = orb_subscribe(ORB_ID(vehicle_status)); + + /* advertise attitude */ + orb_advert_t pub_att = orb_advertise(ORB_ID(vehicle_attitude), &att); + + int loopcounter = 0; + int printcounter = 0; + + thread_running = true; + + /* advertise debug value */ + // struct debug_key_value_s dbg = { .key = "", .value = 0.0f }; + // orb_advert_t pub_dbg = -1; + + float sensor_update_hz[3] = {0.0f, 0.0f, 0.0f}; + // XXX write this out to perf regs + + /* keep track of sensor updates */ + uint32_t sensor_last_count[3] = {0, 0, 0}; + uint64_t sensor_last_timestamp[3] = {0, 0, 0}; + + struct attitude_estimator_so3_comp_params so3_comp_params; + struct attitude_estimator_so3_comp_param_handles so3_comp_param_handles; + + /* initialize parameter handles */ + parameters_init(&so3_comp_param_handles); + + uint64_t start_time = hrt_absolute_time(); + bool initialized = false; + + float gyro_offsets[3] = { 0.0f, 0.0f, 0.0f }; + unsigned offset_count = 0; + + /* register the perf counter */ + perf_counter_t so3_comp_loop_perf = perf_alloc(PC_ELAPSED, "attitude_estimator_so3_comp"); + + /* Main loop*/ + while (!thread_should_exit) { + + struct pollfd fds[2]; + fds[0].fd = sub_raw; + fds[0].events = POLLIN; + fds[1].fd = sub_params; + fds[1].events = POLLIN; + int ret = poll(fds, 2, 1000); + + if (ret < 0) { + /* XXX this is seriously bad - should be an emergency */ + } else if (ret == 0) { + /* check if we're in HIL - not getting sensor data is fine then */ + orb_copy(ORB_ID(vehicle_status), sub_state, &state); + + if (!state.flag_hil_enabled) { + fprintf(stderr, + "[att so3_comp] WARNING: Not getting sensors - sensor app running?\n"); + } + + } else { + + /* only update parameters if they changed */ + if (fds[1].revents & POLLIN) { + /* read from param to clear updated flag */ + struct parameter_update_s update; + orb_copy(ORB_ID(parameter_update), sub_params, &update); + + /* update parameters */ + parameters_update(&so3_comp_param_handles, &so3_comp_params); + } + + /* only run filter if sensor values changed */ + if (fds[0].revents & POLLIN) { + + /* get latest measurements */ + orb_copy(ORB_ID(sensor_combined), sub_raw, &raw); + + if (!initialized) { + + gyro_offsets[0] += raw.gyro_rad_s[0]; + gyro_offsets[1] += raw.gyro_rad_s[1]; + gyro_offsets[2] += raw.gyro_rad_s[2]; + offset_count++; + + if (hrt_absolute_time() - start_time > 3000000LL) { + initialized = true; + gyro_offsets[0] /= offset_count; + gyro_offsets[1] /= offset_count; + gyro_offsets[2] /= offset_count; + } + + } else { + + perf_begin(so3_comp_loop_perf); + + /* Calculate data time difference in seconds */ + dt = (raw.timestamp - last_measurement) / 1000000.0f; + last_measurement = raw.timestamp; + uint8_t update_vect[3] = {0, 0, 0}; + + /* Fill in gyro measurements */ + if (sensor_last_count[0] != raw.gyro_counter) { + update_vect[0] = 1; + sensor_last_count[0] = raw.gyro_counter; + sensor_update_hz[0] = 1e6f / (raw.timestamp - sensor_last_timestamp[0]); + sensor_last_timestamp[0] = raw.timestamp; + } + + gyro[0] = raw.gyro_rad_s[0] - gyro_offsets[0]; + gyro[1] = raw.gyro_rad_s[1] - gyro_offsets[1]; + gyro[2] = raw.gyro_rad_s[2] - gyro_offsets[2]; + + /* update accelerometer measurements */ + if (sensor_last_count[1] != raw.accelerometer_counter) { + update_vect[1] = 1; + sensor_last_count[1] = raw.accelerometer_counter; + sensor_update_hz[1] = 1e6f / (raw.timestamp - sensor_last_timestamp[1]); + sensor_last_timestamp[1] = raw.timestamp; + } + + acc[0] = raw.accelerometer_m_s2[0]; + acc[1] = raw.accelerometer_m_s2[1]; + acc[2] = raw.accelerometer_m_s2[2]; + + /* update magnetometer measurements */ + if (sensor_last_count[2] != raw.magnetometer_counter) { + update_vect[2] = 1; + sensor_last_count[2] = raw.magnetometer_counter; + sensor_update_hz[2] = 1e6f / (raw.timestamp - sensor_last_timestamp[2]); + sensor_last_timestamp[2] = raw.timestamp; + } + + mag[0] = raw.magnetometer_ga[0]; + mag[1] = raw.magnetometer_ga[1]; + mag[2] = raw.magnetometer_ga[2]; + + uint64_t now = hrt_absolute_time(); + unsigned int time_elapsed = now - last_run; + last_run = now; + + if (time_elapsed > loop_interval_alarm) { + //TODO: add warning, cpu overload here + // if (overloadcounter == 20) { + // printf("CPU OVERLOAD DETECTED IN ATTITUDE ESTIMATOR EKF (%lu > %lu)\n", time_elapsed, loop_interval_alarm); + // overloadcounter = 0; + // } + + overloadcounter++; + } + + static bool const_initialized = false; + + /* initialize with good values once we have a reasonable dt estimate */ + if (!const_initialized && dt < 0.05f && dt > 0.005f) { + dt = 0.005f; + parameters_update(&so3_comp_param_handles, &so3_comp_params); + const_initialized = true; + } + + /* do not execute the filter if not initialized */ + if (!const_initialized) { + continue; + } + + uint64_t timing_start = hrt_absolute_time(); + + MahonyAHRSupdate(gyro[0],gyro[1],gyro[2],acc[0],acc[1],acc[2],mag[0],mag[1],mag[2],so3_comp_params.Kp,so3_comp_params.Ki, dt); + + float aSq = q0*q0; + float bSq = q1*q1; + float cSq = q2*q2; + float dSq = q3*q3; + + Rot_matrix[0] = aSq + bSq - cSq - dSq; + Rot_matrix[1] = 2.0 * (b * c - a * d); + Rot_matrix[2] = 2.0 * (a * c + b * d); + Rot_matrix[3] = 2.0 * (b * c + a * d); + Rot_matrix[4] = aSq - bSq + cSq - dSq; + Rot_matrix[5] = 2.0 * (c * d - a * b); + Rot_matrix[6] = 2.0 * (b * d - a * c); + Rot_matrix[7] = 2.0 * (a * b + c * d); + Rot_matrix[8] = aSq - bSq - cSq + dSq; + + /* Compute Euler angle */ + float theta = asinf(-Rot_matrix[6]); + euler[1] = theta; + + if(fabsf(theta - M_PI_2_F) < 1.0e-3f){ + euler[0] = 0.0f; + euler[2] = atan2f(Rot_matrix[5] - Rot_matrix[1], Rot_matrix[2] + Rot_matrix[4] - euler[0]); + } else if (fabsf(theta + M_PI_2_F) < 1.0e-3f) { + euler[0] = 0.0f; + euler[2] = atan2f(Rot_matrix[5] - Rot_matrix[1], Rot_matrix[2] + Rot_matrix[4] - euler[0]); + } else { + euler[0] = atan2f(Rot_matrix[7], Rot_matrix[8]); + euler[2] = atan2f(Rot_matrix[3], Rot_matrix[0]); + } + + /* swap values for next iteration, check for fatal inputs */ + if (isfinite(euler[0]) && isfinite(euler[1]) && isfinite(euler[2])) { + /* Do something */ + } else { + /* due to inputs or numerical failure the output is invalid, skip it */ + continue; + } + + if (last_data > 0 && raw.timestamp - last_data > 12000) printf("[attitude estimator so3_comp] sensor data missed! (%llu)\n", raw.timestamp - last_data); + + last_data = raw.timestamp; + + /* send out */ + att.timestamp = raw.timestamp; + + // XXX Apply the same transformation to the rotation matrix + att.roll = euler[0] - so3_comp_params.roll_off; + att.pitch = euler[1] - so3_comp_params.pitch_off; + att.yaw = euler[2] - so3_comp_params.yaw_off; + + /* FIXME : This can be a problem for rate controller. Rate in body or inertial? + att.rollspeed = x_aposteriori[0]; + att.pitchspeed = x_aposteriori[1]; + att.yawspeed = x_aposteriori[2]; + att.rollacc = x_aposteriori[3]; + att.pitchacc = x_aposteriori[4]; + att.yawacc = x_aposteriori[5]; + */ + + //att.yawspeed =z_k[2] ; + /* copy offsets */ + //memcpy(&att.rate_offsets, &(x_aposteriori[3]), sizeof(att.rate_offsets)); + + /* copy rotation matrix */ + memcpy(&att.R, Rot_matrix, sizeof(Rot_matrix)); + att.R_valid = true; + + if (isfinite(att.roll) && isfinite(att.pitch) && isfinite(att.yaw)) { + // Broadcast + orb_publish(ORB_ID(vehicle_attitude), pub_att, &att); + + } else { + warnx("NaN in roll/pitch/yaw estimate!"); + } + + perf_end(so3_comp_loop_perf); + } + } + } + + loopcounter++; + } + + thread_running = false; + + return 0; +} diff --git a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c new file mode 100755 index 0000000000..bf0f49db84 --- /dev/null +++ b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c @@ -0,0 +1,44 @@ +/* + * @file attitude_estimator_so3_comp_params.c + * + * Parameters for SO3 complementary filter + */ + +#include "attitude_estimator_so3_comp_params.h" + +/* This is filter gain for nonlinear SO3 complementary filter */ +PARAM_DEFINE_FLOAT(SO3_COMP_KP, 1.0f); +PARAM_DEFINE_FLOAT(SO3_COMP_KI, 0.0f); + +/* offsets in roll, pitch and yaw of sensor plane and body */ +PARAM_DEFINE_FLOAT(ATT_ROLL_OFFS, 0.0f); +PARAM_DEFINE_FLOAT(ATT_PITCH_OFFS, 0.0f); +PARAM_DEFINE_FLOAT(ATT_YAW_OFFS, 0.0f); + +int parameters_init(struct attitude_estimator_ekf_param_handles *h) +{ + /* Filter gain parameters */ + h->Kp = param_find("SO3_COMP_KP"); + h->Ki = param_find("SO3_COMP_KI"); + + /* Attitude offset (WARNING: Do not change if you do not know what exactly this variable wil lchange) */ + h->roll_off = param_find("ATT_ROLL_OFFS"); + h->pitch_off = param_find("ATT_PITCH_OFFS"); + h->yaw_off = param_find("ATT_YAW_OFFS"); + + return OK; +} + +int parameters_update(const struct attitude_estimator_ekf_param_handles *h, struct attitude_estimator_ekf_params *p) +{ + /* Update filter gain */ + param_get(h->Kp, &(p->Kp)); + param_get(h->Ki, &(p->Ki)); + + /* Update attitude offset */ + param_get(h->roll_off, &(p->roll_off)); + param_get(h->pitch_off, &(p->pitch_off)); + param_get(h->yaw_off, &(p->yaw_off)); + + return OK; +} diff --git a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.h b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.h new file mode 100755 index 0000000000..2fccec61ce --- /dev/null +++ b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.h @@ -0,0 +1,32 @@ +/* + * @file attitude_estimator_so3_comp_params.h + * + * Parameters for EKF filter + */ + +#include + +struct attitude_estimator_so3_comp_params { + float Kp; + float Ki; + float roll_off; + float pitch_off; + float yaw_off; +}; + +struct attitude_estimator_so3_comp_param_handles { + param_t Kp, Ki; + param_t roll_off, pitch_off, yaw_off; +}; + +/** + * Initialize all parameter handles and values + * + */ +int parameters_init(struct attitude_estimator_so3_comp_param_handles *h); + +/** + * Update all parameters + * + */ +int parameters_update(const struct attitude_estimator_so3_comp_param_handles *h, struct attitude_estimator_so3_comp_params *p); diff --git a/src/modules/attitude_estimator_so3_comp/module.mk b/src/modules/attitude_estimator_so3_comp/module.mk new file mode 100644 index 0000000000..92f43d9202 --- /dev/null +++ b/src/modules/attitude_estimator_so3_comp/module.mk @@ -0,0 +1,8 @@ +# +# Attitude estimator (Nonlinear SO3 complementary Filter) +# + +MODULE_COMMAND = attitude_estimator_so3_comp + +SRCS = attitude_estimator_so3_comp_main.cpp \ + attitude_estimator_so3_comp_params.c From cd7b0f7aab39099353bda46ba9a498c242d75791 Mon Sep 17 00:00:00 2001 From: "Hyon Lim (Retina)" Date: Tue, 21 May 2013 16:12:19 +1000 Subject: [PATCH 02/26] I missed to add build command --- makefiles/config_px4fmu_default.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/makefiles/config_px4fmu_default.mk b/makefiles/config_px4fmu_default.mk index 1e4d592665..4cf650a986 100644 --- a/makefiles/config_px4fmu_default.mk +++ b/makefiles/config_px4fmu_default.mk @@ -61,6 +61,7 @@ MODULES += modules/mavlink_onboard # Estimation modules (EKF / other filters) # MODULES += modules/attitude_estimator_ekf +MODULES += modules/attitude_estimator_so3_comp MODULES += modules/position_estimator_mc MODULES += modules/position_estimator MODULES += modules/att_pos_estimator_ekf From 0c3412223b0961798e0fa9c27042132ebdfc0bdb Mon Sep 17 00:00:00 2001 From: "Hyon Lim (Retina)" Date: Tue, 21 May 2013 16:17:20 +1000 Subject: [PATCH 03/26] Fixed few minor bug --- .../attitude_estimator_so3_comp_main.cpp | 6 ++++++ .../attitude_estimator_so3_comp_params.c | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp index 381b0df75c..81a5e5b07f 100755 --- a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp +++ b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,7 @@ static bool thread_should_exit = false; /**< Deamon exit flag */ static bool thread_running = false; /**< Deamon status flag */ static int attitude_estimator_so3_comp_task; /**< Handle of deamon task / thread */ volatile float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; // quaternion of sensor frame relative to auxiliary frame +volatile float integralFBx = 0.0f, integralFBy = 0.0f, integralFBz = 0.0f; // integral error terms scaled by Ki /** * Mainloop of attitude_estimator_so3_comp. @@ -525,6 +527,10 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds float bSq = q1*q1; float cSq = q2*q2; float dSq = q3*q3; + float a = q0; + float b = q1; + float c = q2; + float d = q3; Rot_matrix[0] = aSq + bSq - cSq - dSq; Rot_matrix[1] = 2.0 * (b * c - a * d); diff --git a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c index bf0f49db84..158eb19725 100755 --- a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c +++ b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c @@ -15,7 +15,7 @@ PARAM_DEFINE_FLOAT(ATT_ROLL_OFFS, 0.0f); PARAM_DEFINE_FLOAT(ATT_PITCH_OFFS, 0.0f); PARAM_DEFINE_FLOAT(ATT_YAW_OFFS, 0.0f); -int parameters_init(struct attitude_estimator_ekf_param_handles *h) +int parameters_init(struct attitude_estimator_so3_comp_param_handles *h) { /* Filter gain parameters */ h->Kp = param_find("SO3_COMP_KP"); @@ -29,7 +29,7 @@ int parameters_init(struct attitude_estimator_ekf_param_handles *h) return OK; } -int parameters_update(const struct attitude_estimator_ekf_param_handles *h, struct attitude_estimator_ekf_params *p) +int parameters_update(const struct attitude_estimator_so3_comp_param_handles *h, struct attitude_estimator_so3_comp_params *p) { /* Update filter gain */ param_get(h->Kp, &(p->Kp)); From 32bace0824ca37c424cc98ecca6ced86cfe10149 Mon Sep 17 00:00:00 2001 From: "Hyon Lim (Retina)" Date: Tue, 21 May 2013 17:52:12 +1000 Subject: [PATCH 04/26] I do not know why roll angle is not correct. But system looks okay --- .../attitude_estimator_so3_comp_main.cpp | 72 ++++++++++--------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp index 81a5e5b07f..a8561a0780 100755 --- a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp +++ b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp @@ -44,8 +44,8 @@ extern "C" __EXPORT int attitude_estimator_so3_comp_main(int argc, char *argv[]) static bool thread_should_exit = false; /**< Deamon exit flag */ static bool thread_running = false; /**< Deamon status flag */ static int attitude_estimator_so3_comp_task; /**< Handle of deamon task / thread */ -volatile float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; // quaternion of sensor frame relative to auxiliary frame -volatile float integralFBx = 0.0f, integralFBy = 0.0f, integralFBz = 0.0f; // integral error terms scaled by Ki +static float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; // quaternion of sensor frame relative to auxiliary frame +static float integralFBx = 0.0f, integralFBy = 0.0f, integralFBz = 0.0f; // integral error terms scaled by Ki /** * Mainloop of attitude_estimator_so3_comp. @@ -135,7 +135,6 @@ void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float float recipNorm; float halfvx, halfvy, halfvz; float halfex, halfey, halfez; - float qa, qb, qc; // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { @@ -181,13 +180,10 @@ void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float gx *= (0.5f * dt); // pre-multiply common factors gy *= (0.5f * dt); gz *= (0.5f * dt); - qa = q0; - qb = q1; - qc = q2; - q0 += (-qb * gx - qc * gy - q3 * gz); - q1 += (qa * gx + qc * gz - q3 * gy); - q2 += (qa * gy - qb * gz + q3 * gx); - q3 += (qa * gz + qb * gy - qc * gx); + q0 += (-q1 * gx - q2 * gy - q3 * gz); + q1 += (q0 * gx + q2 * gz - q3 * gy); + q2 += (q0 * gy - q1 * gz + q3 * gx); + q3 += (q0 * gz + q1 * gy - q2 * gx); // Normalise quaternion recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); @@ -203,7 +199,6 @@ void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az float hx, hy, bx, bz; float halfvx, halfvy, halfvz, halfwx, halfwy, halfwz; float halfex, halfey, halfez; - float qa, qb, qc; // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { @@ -282,13 +277,10 @@ void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az gx *= (0.5f * dt); // pre-multiply common factors gy *= (0.5f * dt); gz *= (0.5f * dt); - qa = q0; - qb = q1; - qc = q2; - q0 += (-qb * gx - qc * gy - q3 * gz); - q1 += (qa * gx + qc * gz - q3 * gy); - q2 += (qa * gy - qb * gz + q3 * gx); - q3 += (qa * gz + qb * gy - qc * gx); + q0 += (-q1 * gx - q2 * gy - q3 * gz); + q1 += (q0 * gx + q2 * gz - q3 * gy); + q2 += (q0 * gy - q1 * gz + q3 * gx); + q3 += (q0 * gz + q1 * gy - q2 * gx); // Normalise quaternion recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); @@ -532,19 +524,19 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds float c = q2; float d = q3; - Rot_matrix[0] = aSq + bSq - cSq - dSq; - Rot_matrix[1] = 2.0 * (b * c - a * d); - Rot_matrix[2] = 2.0 * (a * c + b * d); - Rot_matrix[3] = 2.0 * (b * c + a * d); - Rot_matrix[4] = aSq - bSq + cSq - dSq; - Rot_matrix[5] = 2.0 * (c * d - a * b); - Rot_matrix[6] = 2.0 * (b * d - a * c); - Rot_matrix[7] = 2.0 * (a * b + c * d); - Rot_matrix[8] = aSq - bSq - cSq + dSq; + Rot_matrix[0] = aSq + bSq - cSq - dSq; // 11 + Rot_matrix[1] = 2.0 * (b * c - a * d); // 12 + Rot_matrix[2] = 2.0 * (a * c + b * d); // 13 + Rot_matrix[3] = 2.0 * (b * c + a * d); // 21 + Rot_matrix[4] = aSq - bSq + cSq - dSq; // 22 + Rot_matrix[5] = 2.0 * (c * d - a * b); // 23 + Rot_matrix[6] = 2.0 * (b * d - a * c); // 31 + Rot_matrix[7] = 2.0 * (a * b + c * d); // 32 + Rot_matrix[8] = aSq - bSq - cSq + dSq; // 33 - /* Compute Euler angle */ - float theta = asinf(-Rot_matrix[6]); - euler[1] = theta; + /* FIXME : Work around this later... + float theta = asinf(-Rot_matrix[6]); // -r_{31} + euler[1] = theta; // pitch angle if(fabsf(theta - M_PI_2_F) < 1.0e-3f){ euler[0] = 0.0f; @@ -556,6 +548,16 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds euler[0] = atan2f(Rot_matrix[7], Rot_matrix[8]); euler[2] = atan2f(Rot_matrix[3], Rot_matrix[0]); } + */ + + float q1q1 = q1*q1; + float q2q2 = q2*q2; + float q3q3 = q3*q3; + + euler[0] = atan2f(2*(q0*q1 + q2*q3),1-2*(q1q1+q2q2)); // roll + euler[1] = asinf(2*(q0*q2 - q3*q1)); // pitch + euler[2] = atan2f(2*(q0*q3 + q1*q2),1-2*(q2q2 + q3q3)); // yaw + /* swap values for next iteration, check for fatal inputs */ if (isfinite(euler[0]) && isfinite(euler[1]) && isfinite(euler[2])) { @@ -577,10 +579,12 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds att.pitch = euler[1] - so3_comp_params.pitch_off; att.yaw = euler[2] - so3_comp_params.yaw_off; - /* FIXME : This can be a problem for rate controller. Rate in body or inertial? - att.rollspeed = x_aposteriori[0]; - att.pitchspeed = x_aposteriori[1]; - att.yawspeed = x_aposteriori[2]; + /* FIXME : This can be a problem for rate controller. Rate in body or inertial? */ + att.rollspeed = q1; + att.pitchspeed = q2; + att.yawspeed = q3; + + /* att.rollacc = x_aposteriori[3]; att.pitchacc = x_aposteriori[4]; att.yawacc = x_aposteriori[5]; From f547044203f81061a9302f1e5c4fcdf2ef73cac2 Mon Sep 17 00:00:00 2001 From: "Hyon Lim (Retina)" Date: Wed, 22 May 2013 00:09:25 +1000 Subject: [PATCH 05/26] Roll pitch yaw should be verified again --- .../attitude_estimator_so3_comp_main.cpp | 180 +++++++++--------- .../attitude_estimator_so3_comp_params.c | 2 +- 2 files changed, 91 insertions(+), 91 deletions(-) diff --git a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp index a8561a0780..ff63640ef9 100755 --- a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp +++ b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp @@ -139,52 +139,52 @@ void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { - // Normalise accelerometer measurement - recipNorm = invSqrt(ax * ax + ay * ay + az * az); - ax *= recipNorm; - ay *= recipNorm; - az *= recipNorm; + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; - // Estimated direction of gravity and vector perpendicular to magnetic flux - halfvx = q1 * q3 - q0 * q2; - halfvy = q0 * q1 + q2 * q3; - halfvz = q0 * q0 - 0.5f + q3 * q3; + // Estimated direction of gravity and vector perpendicular to magnetic flux + halfvx = q1 * q3 - q0 * q2; + halfvy = q0 * q1 + q2 * q3; + halfvz = q0 * q0 - 0.5f + q3 * q3; - // Error is sum of cross product between estimated and measured direction of gravity - halfex = (ay * halfvz - az * halfvy); - halfey = (az * halfvx - ax * halfvz); - halfez = (ax * halfvy - ay * halfvx); + // Error is sum of cross product between estimated and measured direction of gravity + halfex = (ay * halfvz - az * halfvy); + halfey = (az * halfvx - ax * halfvz); + halfez = (ax * halfvy - ay * halfvx); - // Compute and apply integral feedback if enabled - if(twoKi > 0.0f) { - integralFBx += twoKi * halfex * dt; // integral error scaled by Ki - integralFBy += twoKi * halfey * dt; - integralFBz += twoKi * halfez * dt; - gx += integralFBx; // apply integral feedback - gy += integralFBy; - gz += integralFBz; - } - else { - integralFBx = 0.0f; // prevent integral windup - integralFBy = 0.0f; - integralFBz = 0.0f; - } + // Compute and apply integral feedback if enabled + if(twoKi > 0.0f) { + integralFBx += twoKi * halfex * dt; // integral error scaled by Ki + integralFBy += twoKi * halfey * dt; + integralFBz += twoKi * halfez * dt; + gx += integralFBx; // apply integral feedback + gy += integralFBy; + gz += integralFBz; + } + else { + integralFBx = 0.0f; // prevent integral windup + integralFBy = 0.0f; + integralFBz = 0.0f; + } - // Apply proportional feedback - gx += twoKp * halfex; - gy += twoKp * halfey; - gz += twoKp * halfez; + // Apply proportional feedback + gx += twoKp * halfex; + gy += twoKp * halfey; + gz += twoKp * halfez; } // Integrate rate of change of quaternion gx *= (0.5f * dt); // pre-multiply common factors gy *= (0.5f * dt); gz *= (0.5f * dt); - q0 += (-q1 * gx - q2 * gy - q3 * gz); + q0 +=(-q1 * gx - q2 * gy - q3 * gz); q1 += (q0 * gx + q2 * gz - q3 * gy); q2 += (q0 * gy - q1 * gz + q3 * gx); q3 += (q0 * gz + q1 * gy - q2 * gx); - + // Normalise quaternion recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); q0 *= recipNorm; @@ -209,17 +209,17 @@ void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { - // Normalise accelerometer measurement - recipNorm = invSqrt(ax * ax + ay * ay + az * az); - ax *= recipNorm; - ay *= recipNorm; - az *= recipNorm; + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; - // Normalise magnetometer measurement - recipNorm = invSqrt(mx * mx + my * my + mz * mz); - mx *= recipNorm; - my *= recipNorm; - mz *= recipNorm; + // Normalise magnetometer measurement + recipNorm = invSqrt(mx * mx + my * my + mz * mz); + mx *= recipNorm; + my *= recipNorm; + mz *= recipNorm; // Auxiliary variables to avoid repeated arithmetic q0q0 = q0 * q0; @@ -239,45 +239,45 @@ void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az bx = sqrt(hx * hx + hy * hy); bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2)); - // Estimated direction of gravity and magnetic field - halfvx = q1q3 - q0q2; - halfvy = q0q1 + q2q3; - halfvz = q0q0 - 0.5f + q3q3; + // Estimated direction of gravity and magnetic field + halfvx = q1q3 - q0q2; + halfvy = q0q1 + q2q3; + halfvz = q0q0 - 0.5f + q3q3; halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2); halfwy = bx * (q1q2 - q0q3) + bz * (q0q1 + q2q3); halfwz = bx * (q0q2 + q1q3) + bz * (0.5f - q1q1 - q2q2); - // Error is sum of cross product between estimated direction and measured direction of field vectors - halfex = (ay * halfvz - az * halfvy) + (my * halfwz - mz * halfwy); - halfey = (az * halfvx - ax * halfvz) + (mz * halfwx - mx * halfwz); - halfez = (ax * halfvy - ay * halfvx) + (mx * halfwy - my * halfwx); + // Error is sum of cross product between estimated direction and measured direction of field vectors + halfex = (ay * halfvz - az * halfvy) + (my * halfwz - mz * halfwy); + halfey = (az * halfvx - ax * halfvz) + (mz * halfwx - mx * halfwz); + halfez = (ax * halfvy - ay * halfvx) + (mx * halfwy - my * halfwx); - // Compute and apply integral feedback if enabled - if(twoKi > 0.0f) { - integralFBx += twoKi * halfex * dt; // integral error scaled by Ki - integralFBy += twoKi * halfey * dt; - integralFBz += twoKi * halfez * dt; - gx += integralFBx; // apply integral feedback - gy += integralFBy; - gz += integralFBz; - } - else { - integralFBx = 0.0f; // prevent integral windup - integralFBy = 0.0f; - integralFBz = 0.0f; - } + // Compute and apply integral feedback if enabled + if(twoKi > 0.0f) { + integralFBx += twoKi * halfex * dt; // integral error scaled by Ki + integralFBy += twoKi * halfey * dt; + integralFBz += twoKi * halfez * dt; + gx += integralFBx; // apply integral feedback + gy += integralFBy; + gz += integralFBz; + } + else { + integralFBx = 0.0f; // prevent integral windup + integralFBy = 0.0f; + integralFBz = 0.0f; + } - // Apply proportional feedback - gx += twoKp * halfex; - gy += twoKp * halfey; - gz += twoKp * halfez; + // Apply proportional feedback + gx += twoKp * halfex; + gy += twoKp * halfey; + gz += twoKp * halfez; } // Integrate rate of change of quaternion gx *= (0.5f * dt); // pre-multiply common factors gy *= (0.5f * dt); gz *= (0.5f * dt); - q0 += (-q1 * gx - q2 * gy - q3 * gz); + q0 +=(-q1 * gx - q2 * gy - q3 * gz); q1 += (q0 * gx + q2 * gz - q3 * gy); q2 += (q0 * gy - q1 * gz + q3 * gx); q3 += (q0 * gz + q1 * gy - q2 * gx); @@ -515,24 +515,28 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds MahonyAHRSupdate(gyro[0],gyro[1],gyro[2],acc[0],acc[1],acc[2],mag[0],mag[1],mag[2],so3_comp_params.Kp,so3_comp_params.Ki, dt); - float aSq = q0*q0; - float bSq = q1*q1; - float cSq = q2*q2; - float dSq = q3*q3; + float aSq = q0*q0; // 1 + float bSq = q1*q1; // 2 + float cSq = q2*q2; // 3 + float dSq = q3*q3; // 4 float a = q0; float b = q1; float c = q2; float d = q3; - Rot_matrix[0] = aSq + bSq - cSq - dSq; // 11 - Rot_matrix[1] = 2.0 * (b * c - a * d); // 12 - Rot_matrix[2] = 2.0 * (a * c + b * d); // 13 - Rot_matrix[3] = 2.0 * (b * c + a * d); // 21 - Rot_matrix[4] = aSq - bSq + cSq - dSq; // 22 - Rot_matrix[5] = 2.0 * (c * d - a * b); // 23 - Rot_matrix[6] = 2.0 * (b * d - a * c); // 31 - Rot_matrix[7] = 2.0 * (a * b + c * d); // 32 - Rot_matrix[8] = aSq - bSq - cSq + dSq; // 33 + Rot_matrix[0] = 2*aSq - 1 + 2*bSq; // 11 + //Rot_matrix[1] = 2.0 * (b * c - a * d); // 12 + //Rot_matrix[2] = 2.0 * (a * c + b * d); // 13 + Rot_matrix[3] = 2.0 * (b * c - a * d); // 21 + //Rot_matrix[4] = aSq - bSq + cSq - dSq; // 22 + //Rot_matrix[5] = 2.0 * (c * d - a * b); // 23 + Rot_matrix[6] = 2.0 * (b * d + a * c); // 31 + Rot_matrix[7] = 2.0 * (c * d - a * b); // 32 + Rot_matrix[8] = 2*aSq - 1 + 2*dSq; // 33 + + //euler[0] = atan2f(Rot_matrix[7], Rot_matrix[8]); + //euler[1] = asinf(-Rot_matrix[6]); + //euler[2] = atan2f(Rot_matrix[3],Rot_matrix[0]); /* FIXME : Work around this later... float theta = asinf(-Rot_matrix[6]); // -r_{31} @@ -550,13 +554,9 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds } */ - float q1q1 = q1*q1; - float q2q2 = q2*q2; - float q3q3 = q3*q3; - - euler[0] = atan2f(2*(q0*q1 + q2*q3),1-2*(q1q1+q2q2)); // roll - euler[1] = asinf(2*(q0*q2 - q3*q1)); // pitch - euler[2] = atan2f(2*(q0*q3 + q1*q2),1-2*(q2q2 + q3q3)); // yaw + euler[0] = atan2f(2*(q0*q1+q2*q3),1-2*(q1*q1+q2*q2)); + euler[1] = asinf(2*(q0*q2-q3*q1)); + euler[2] = atan2f(2*(q0*q3+q1*q2),1-2*(q2*q2+q3*q3)); /* swap values for next iteration, check for fatal inputs */ diff --git a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c index 158eb19725..068e4340a6 100755 --- a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c +++ b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c @@ -7,7 +7,7 @@ #include "attitude_estimator_so3_comp_params.h" /* This is filter gain for nonlinear SO3 complementary filter */ -PARAM_DEFINE_FLOAT(SO3_COMP_KP, 1.0f); +PARAM_DEFINE_FLOAT(SO3_COMP_KP, 0.5f); PARAM_DEFINE_FLOAT(SO3_COMP_KI, 0.0f); /* offsets in roll, pitch and yaw of sensor plane and body */ From 364d1a06e308334915c5e7e54e1c6f15b11e5b2e Mon Sep 17 00:00:00 2001 From: "Hyon Lim (Retina)" Date: Wed, 22 May 2013 13:03:14 +1000 Subject: [PATCH 06/26] To use freeIMU processing visualization tool, I have implemented float number transmission over uart (default /dev/ttyS2, 115200) But this not tested yet. I should. --- .../attitude_estimator_so3_comp_main.cpp | 271 +++++++++++++----- 1 file changed, 195 insertions(+), 76 deletions(-) diff --git a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp index ff63640ef9..ac898eefc1 100755 --- a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp +++ b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp @@ -47,6 +47,10 @@ static int attitude_estimator_so3_comp_task; /**< Handle of deamon task / thr static float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; // quaternion of sensor frame relative to auxiliary frame static float integralFBx = 0.0f, integralFBy = 0.0f, integralFBz = 0.0f; // integral error terms scaled by Ki +//! Serial packet related +static int uart; +static int baudrate; + /** * Mainloop of attitude_estimator_so3_comp. */ @@ -63,7 +67,9 @@ usage(const char *reason) if (reason) fprintf(stderr, "%s\n", reason); - fprintf(stderr, "usage: attitude_estimator_so3_comp {start|stop|status} [-p ]\n\n"); + fprintf(stderr, "usage: attitude_estimator_so3_comp {start|stop|status} [-d ] [-b ]\n" + "-d and -b options are for separate visualization with raw data (quaternion packet) transfer\n" + "ex) attitude_estimator_so3_comp start -d /dev/ttyS1 -b 115200\n"); exit(1); } @@ -80,6 +86,8 @@ int attitude_estimator_so3_comp_main(int argc, char *argv[]) if (argc < 1) usage("missing command"); + + if (!strcmp(argv[1], "start")) { if (thread_running) { @@ -94,12 +102,18 @@ int attitude_estimator_so3_comp_main(int argc, char *argv[]) SCHED_PRIORITY_MAX - 5, 12400, attitude_estimator_so3_comp_thread_main, - (argv) ? (const char **)&argv[2] : (const char **)NULL); + (const char **)argv); exit(0); } if (!strcmp(argv[1], "stop")) { thread_should_exit = true; + + while(thread_running){ + usleep(200000); + printf("."); + } + printf("terminated."); exit(0); } @@ -121,76 +135,18 @@ int attitude_estimator_so3_comp_main(int argc, char *argv[]) //--------------------------------------------------------------------------------------------------- // Fast inverse square-root // See: http://en.wikipedia.org/wiki/Fast_inverse_square_root -float invSqrt(float x) { - float halfx = 0.5f * x; - float y = x; - long i = *(long*)&y; - i = 0x5f3759df - (i>>1); - y = *(float*)&i; - y = y * (1.5f - (halfx * y * y)); - return y; -} +float invSqrt(float number) { + volatile long i; + volatile float x, y; + volatile const float f = 1.5F; -void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, float twoKp, float twoKi, float dt) { - float recipNorm; - float halfvx, halfvy, halfvz; - float halfex, halfey, halfez; - - // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) - if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { - - // Normalise accelerometer measurement - recipNorm = invSqrt(ax * ax + ay * ay + az * az); - ax *= recipNorm; - ay *= recipNorm; - az *= recipNorm; - - // Estimated direction of gravity and vector perpendicular to magnetic flux - halfvx = q1 * q3 - q0 * q2; - halfvy = q0 * q1 + q2 * q3; - halfvz = q0 * q0 - 0.5f + q3 * q3; - - // Error is sum of cross product between estimated and measured direction of gravity - halfex = (ay * halfvz - az * halfvy); - halfey = (az * halfvx - ax * halfvz); - halfez = (ax * halfvy - ay * halfvx); - - // Compute and apply integral feedback if enabled - if(twoKi > 0.0f) { - integralFBx += twoKi * halfex * dt; // integral error scaled by Ki - integralFBy += twoKi * halfey * dt; - integralFBz += twoKi * halfez * dt; - gx += integralFBx; // apply integral feedback - gy += integralFBy; - gz += integralFBz; - } - else { - integralFBx = 0.0f; // prevent integral windup - integralFBy = 0.0f; - integralFBz = 0.0f; - } - - // Apply proportional feedback - gx += twoKp * halfex; - gy += twoKp * halfey; - gz += twoKp * halfez; - } - - // Integrate rate of change of quaternion - gx *= (0.5f * dt); // pre-multiply common factors - gy *= (0.5f * dt); - gz *= (0.5f * dt); - q0 +=(-q1 * gx - q2 * gy - q3 * gz); - q1 += (q0 * gx + q2 * gz - q3 * gy); - q2 += (q0 * gy - q1 * gz + q3 * gx); - q3 += (q0 * gz + q1 * gy - q2 * gx); - - // Normalise quaternion - recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); - q0 *= recipNorm; - q1 *= recipNorm; - q2 *= recipNorm; - q3 *= recipNorm; + x = number * 0.5F; + y = number; + i = * (( long * ) &y); + i = 0x5f375a86 - ( i >> 1 ); + y = * (( float * ) &i); + y = y * ( f - ( x * y * y ) ); + return y; } void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, float twoKp, float twoKi, float dt) { @@ -202,7 +158,7 @@ void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { - MahonyAHRSupdateIMU(gx, gy, gz, ax, ay, az, twoKp, twoKi, dt); + //MahonyAHRSupdateIMU(gx, gy, gz, ax, ay, az, twoKp, twoKi, dt); return; } @@ -290,6 +246,117 @@ void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az q3 *= recipNorm; } +void send_uart_byte(char c) +{ + write(uart,&c,1); +} + +void send_uart_bytes(uint8_t *data, int length) +{ + write(uart,data,(size_t)(sizeof(uint8_t)*length)); +} + +void send_uart_float(float f) { + uint8_t * b = (uint8_t *) &f; + + //! Assume float is 4-bytes + for(int i=0; i<4; i++) { + + uint8_t b1 = (b[i] >> 4) & 0x0f; + uint8_t b2 = (b[i] & 0x0f); + + uint8_t c1 = (b1 < 10) ? ('0' + b1) : 'A' + b1 - 10; + uint8_t c2 = (b2 < 10) ? ('0' + b2) : 'A' + b2 - 10; + + send_uart_bytes(&c1,1); + send_uart_bytes(&c2,1); + } +} + +void send_uart_float_arr(float *arr, int length) +{ + for(int i=0;i, default : /dev/ttyS2 + //! -b , default : 115200 + while ((ch = getopt(argc,argv,"d:b:")) != EOF){ + switch(ch){ + case 'b': + baudrate = strtoul(optarg, NULL, 10); + if(baudrate == 0) + printf("invalid baud rate '%s'",optarg); + break; + case 'd': + device_name = optarg; + debug_mode = true; + break; + default: + usage("invalid argument"); + } + } + + if(debug_mode){ + uart = open_uart(baudrate, device_name, &uart_config_original, &usb_uart); + if (uart < 0) + printf("could not open %s", device_name); + } + // print text printf("Nonlinear SO3 Attitude Estimator initialized..\n\n"); fflush(stdout); @@ -554,9 +658,9 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds } */ - euler[0] = atan2f(2*(q0*q1+q2*q3),1-2*(q1*q1+q2*q2)); - euler[1] = asinf(2*(q0*q2-q3*q1)); - euler[2] = atan2f(2*(q0*q3+q1*q2),1-2*(q2*q2+q3*q3)); + euler[2] = atan2f(2*(q1*q2-q0*q3),2*(q0*q0+q1*q1)-1); + euler[1]= -asinf(2*(q1*q3+q0*q2)); + euler[0] = atan2f(2*(q2*q3-q0*q1),2*(q0*q0+q3*q3)-1); /* swap values for next iteration, check for fatal inputs */ @@ -607,7 +711,18 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds } perf_end(so3_comp_loop_perf); - } + + if(debug_mode) + { + float quat[4]; + quat[0] = q0; + quat[1] = q1; + quat[2] = q2; + quat[3] = q3; + send_uart_float_arr(quat,4); + send_uart_byte('\n'); + } + }// else } } @@ -616,5 +731,9 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds thread_running = false; + /* Reset the UART flags to original state */ + if (!usb_uart) + tcsetattr(uart, TCSANOW, &uart_config_original); + return 0; } From 4bf05054218efab3b3dc182939f32a96f5ed1673 Mon Sep 17 00:00:00 2001 From: "Hyon Lim (Retina)" Date: Thu, 23 May 2013 16:12:29 +1000 Subject: [PATCH 07/26] Test flight has been performed with nonlinear SO(3) attitude estimator. Here are few observations: - When the system initialized, roll angle is initially reversed. As filter converged, it becomes normal. - I put a negative sign on roll, yaw. It should naturally has right sign, but I do not know why for now. Let me investigate again. - Gain : I do not know what gain is good for quadrotor flight. Let me take a look Ardupilot gain in the later. Anyway, you can fly with this attitude estimator. --- nuttx/configs/px4fmu/nsh/defconfig | 6 +- .../attitude_estimator_so3_comp_main.cpp | 220 +++++++++--------- 2 files changed, 114 insertions(+), 112 deletions(-) diff --git a/nuttx/configs/px4fmu/nsh/defconfig b/nuttx/configs/px4fmu/nsh/defconfig index 02e2243020..94d99112e2 100755 --- a/nuttx/configs/px4fmu/nsh/defconfig +++ b/nuttx/configs/px4fmu/nsh/defconfig @@ -248,7 +248,7 @@ CONFIG_SERIAL_TERMIOS=y CONFIG_SERIAL_CONSOLE_REINIT=y CONFIG_STANDARD_SERIAL=y -CONFIG_USART1_SERIAL_CONSOLE=y +CONFIG_USART1_SERIAL_CONSOLE=n CONFIG_USART2_SERIAL_CONSOLE=n CONFIG_USART3_SERIAL_CONSOLE=n CONFIG_UART4_SERIAL_CONSOLE=n @@ -561,7 +561,7 @@ CONFIG_START_MONTH=1 CONFIG_START_DAY=1 CONFIG_GREGORIAN_TIME=n CONFIG_JULIAN_TIME=n -CONFIG_DEV_CONSOLE=y +CONFIG_DEV_CONSOLE=n CONFIG_DEV_LOWCONSOLE=n CONFIG_MUTEX_TYPES=n CONFIG_PRIORITY_INHERITANCE=y @@ -925,7 +925,7 @@ CONFIG_USBDEV_TRACE_NRECORDS=512 # Size of the serial receive/transmit buffers. Default 256. # CONFIG_CDCACM=y -CONFIG_CDCACM_CONSOLE=n +CONFIG_CDCACM_CONSOLE=y #CONFIG_CDCACM_EP0MAXPACKET CONFIG_CDCACM_EPINTIN=1 #CONFIG_CDCACM_EPINTIN_FSSIZE diff --git a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp index ac898eefc1..28fcf03692 100755 --- a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp +++ b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp @@ -44,8 +44,9 @@ extern "C" __EXPORT int attitude_estimator_so3_comp_main(int argc, char *argv[]) static bool thread_should_exit = false; /**< Deamon exit flag */ static bool thread_running = false; /**< Deamon status flag */ static int attitude_estimator_so3_comp_task; /**< Handle of deamon task / thread */ -static float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; // quaternion of sensor frame relative to auxiliary frame -static float integralFBx = 0.0f, integralFBy = 0.0f, integralFBz = 0.0f; // integral error terms scaled by Ki +static float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; /** quaternion of sensor frame relative to auxiliary frame */ +static float gyro_bias[3] = {0.0f, 0.0f, 0.0f}; /** bias estimation */ +static float gravity_vector[3] = {0.0f,0.0f,0.0f}; /** estimated gravity vector */ //! Serial packet related static int uart; @@ -151,82 +152,91 @@ float invSqrt(float number) { void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, float twoKp, float twoKi, float dt) { float recipNorm; - float q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; - float hx, hy, bx, bz; - float halfvx, halfvy, halfvz, halfwx, halfwy, halfwz; - float halfex, halfey, halfez; + float q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + float halfex = 0.0f, halfey = 0.0f, halfez = 0.0f; + + // Auxiliary variables to avoid repeated arithmetic + q0q0 = q0 * q0; + q0q1 = q0 * q1; + q0q2 = q0 * q2; + q0q3 = q0 * q3; + q1q1 = q1 * q1; + q1q2 = q1 * q2; + q1q3 = q1 * q3; + q2q2 = q2 * q2; + q2q3 = q2 * q3; + q3q3 = q3 * q3; - // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) + //! If magnetometer measurement is available, use it. if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { - //MahonyAHRSupdateIMU(gx, gy, gz, ax, ay, az, twoKp, twoKi, dt); - return; + float hx, hy, bx, bz; + float halfwx, halfwy, halfwz; + + // Normalise magnetometer measurement + recipNorm = invSqrt(mx * mx + my * my + mz * mz); + mx *= recipNorm; + my *= recipNorm; + mz *= recipNorm; + + // Reference direction of Earth's magnetic field + hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2)); + hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1)); + bx = sqrt(hx * hx + hy * hy); + bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2)); + + // Estimated direction of magnetic field + halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2); + halfwy = bx * (q1q2 - q0q3) + bz * (q0q1 + q2q3); + halfwz = bx * (q0q2 + q1q3) + bz * (0.5f - q1q1 - q2q2); + + // Error is sum of cross product between estimated direction and measured direction of field vectors + halfex += (my * halfwz - mz * halfwy); + halfey += (mz * halfwx - mx * halfwz); + halfez += (mx * halfwy - my * halfwx); } // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { - - // Normalise accelerometer measurement - recipNorm = invSqrt(ax * ax + ay * ay + az * az); - ax *= recipNorm; - ay *= recipNorm; - az *= recipNorm; - - // Normalise magnetometer measurement - recipNorm = invSqrt(mx * mx + my * my + mz * mz); - mx *= recipNorm; - my *= recipNorm; - mz *= recipNorm; - - // Auxiliary variables to avoid repeated arithmetic - q0q0 = q0 * q0; - q0q1 = q0 * q1; - q0q2 = q0 * q2; - q0q3 = q0 * q3; - q1q1 = q1 * q1; - q1q2 = q1 * q2; - q1q3 = q1 * q3; - q2q2 = q2 * q2; - q2q3 = q2 * q3; - q3q3 = q3 * q3; - - // Reference direction of Earth's magnetic field - hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2)); - hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1)); - bx = sqrt(hx * hx + hy * hy); - bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2)); - - // Estimated direction of gravity and magnetic field - halfvx = q1q3 - q0q2; - halfvy = q0q1 + q2q3; - halfvz = q0q0 - 0.5f + q3q3; - halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2); - halfwy = bx * (q1q2 - q0q3) + bz * (q0q1 + q2q3); - halfwz = bx * (q0q2 + q1q3) + bz * (0.5f - q1q1 - q2q2); + float halfvx, halfvy, halfvz; - // Error is sum of cross product between estimated direction and measured direction of field vectors - halfex = (ay * halfvz - az * halfvy) + (my * halfwz - mz * halfwy); - halfey = (az * halfvx - ax * halfvz) + (mz * halfwx - mx * halfwz); - halfez = (ax * halfvy - ay * halfvx) + (mx * halfwy - my * halfwx); + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; - // Compute and apply integral feedback if enabled - if(twoKi > 0.0f) { - integralFBx += twoKi * halfex * dt; // integral error scaled by Ki - integralFBy += twoKi * halfey * dt; - integralFBz += twoKi * halfez * dt; - gx += integralFBx; // apply integral feedback - gy += integralFBy; - gz += integralFBz; - } - else { - integralFBx = 0.0f; // prevent integral windup - integralFBy = 0.0f; - integralFBz = 0.0f; + // Estimated direction of gravity and magnetic field + halfvx = q1q3 - q0q2; + halfvy = q0q1 + q2q3; + halfvz = q0q0 - 0.5f + q3q3; + + // Error is sum of cross product between estimated direction and measured direction of field vectors + halfex += ay * halfvz - az * halfvy; + halfey += az * halfvx - ax * halfvz; + halfez += ax * halfvy - ay * halfvx; } - // Apply proportional feedback - gx += twoKp * halfex; - gy += twoKp * halfey; - gz += twoKp * halfez; + // Apply feedback only when valid data has been gathered from the accelerometer or magnetometer + if(halfex != 0.0f && halfey != 0.0f && halfez != 0.0f) { + // Compute and apply integral feedback if enabled + if(twoKi > 0.0f) { + gyro_bias[0] += twoKi * halfex * dt; // integral error scaled by Ki + gyro_bias[1] += twoKi * halfey * dt; + gyro_bias[2] += twoKi * halfez * dt; + gx += gyro_bias[0]; // apply integral feedback + gy += gyro_bias[1]; + gz += gyro_bias[2]; + } + else { + gyro_bias[0] = 0.0f; // prevent integral windup + gyro_bias[1] = 0.0f; + gyro_bias[2] = 0.0f; + } + + // Apply proportional feedback + gx += twoKp * halfex; + gy += twoKp * halfey; + gz += twoKp * halfez; } // Integrate rate of change of quaternion @@ -309,11 +319,11 @@ int open_uart(int baud, const char *uart_name, struct termios *uart_config_origi case 460800: speed = B460800; break; case 921600: speed = B921600; break; default: - fprintf(stderr, "ERROR: Unsupported baudrate: %d\n\tsupported examples:\n\n\t9600\n19200\n38400\n57600\n115200\n230400\n460800\n921600\n\n", baud); + printf("ERROR: Unsupported baudrate: %d\n\tsupported examples:\n\n\t9600\n19200\n38400\n57600\n115200\n230400\n460800\n921600\n\n", baud); return -EINVAL; } - printf("[mavlink] UART is %s, baudrate is %d\n", uart_name, baud); + printf("[so3_comp_filt] UART is %s, baudrate is %d\n", uart_name, baud); uart = open(uart_name, O_RDWR | O_NOCTTY); /* Try to set baud rate */ @@ -321,11 +331,11 @@ int open_uart(int baud, const char *uart_name, struct termios *uart_config_origi int termios_state; *is_usb = false; - /* make some wild guesses including that USB serial is indicated by either /dev/ttyACM0 or /dev/console */ + /* make some wild guesses including that USB serial is indicated by either /dev/ttyACM0 or /dev/console */ if (strcmp(uart_name, "/dev/ttyACM0") != OK && strcmp(uart_name, "/dev/console") != OK) { /* Back up the original uart configuration to restore it after exit */ if ((termios_state = tcgetattr(uart, uart_config_original)) < 0) { - fprintf(stderr, "ERROR getting baudrate / termios config for %s: %d\n", uart_name, termios_state); + printf("ERROR getting baudrate / termios config for %s: %d\n", uart_name, termios_state); close(uart); return -1; } @@ -338,14 +348,14 @@ int open_uart(int baud, const char *uart_name, struct termios *uart_config_origi /* Set baud rate */ if (cfsetispeed(&uart_config, speed) < 0 || cfsetospeed(&uart_config, speed) < 0) { - fprintf(stderr, "ERROR setting baudrate / termios config for %s: %d (cfsetispeed, cfsetospeed)\n", uart_name, termios_state); + printf("ERROR setting baudrate / termios config for %s: %d (cfsetispeed, cfsetospeed)\n", uart_name, termios_state); close(uart); return -1; } if ((termios_state = tcsetattr(uart, TCSANOW, &uart_config)) < 0) { - fprintf(stderr, "ERROR setting baudrate / termios config for %s (tcsetattr)\n", uart_name); + printf("ERROR setting baudrate / termios config for %s (tcsetattr)\n", uart_name); close(uart); return -1; } @@ -420,9 +430,12 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds } if(debug_mode){ + printf("Opening debugging port for 3D visualization\n"); uart = open_uart(baudrate, device_name, &uart_config_original, &usb_uart); if (uart < 0) printf("could not open %s", device_name); + else + printf("Open port success\n"); } // print text @@ -638,30 +651,22 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds Rot_matrix[7] = 2.0 * (c * d - a * b); // 32 Rot_matrix[8] = 2*aSq - 1 + 2*dSq; // 33 + gravity_vector[0] = 2*(q1*q3-q0*q2); + gravity_vector[1] = 2*(q0*q1+q2*q3); + gravity_vector[2] = aSq - bSq - cSq + dSq; + //euler[0] = atan2f(Rot_matrix[7], Rot_matrix[8]); - //euler[1] = asinf(-Rot_matrix[6]); + //euler[1] = -asinf(Rot_matrix[6]); //euler[2] = atan2f(Rot_matrix[3],Rot_matrix[0]); - /* FIXME : Work around this later... - float theta = asinf(-Rot_matrix[6]); // -r_{31} - euler[1] = theta; // pitch angle - - if(fabsf(theta - M_PI_2_F) < 1.0e-3f){ - euler[0] = 0.0f; - euler[2] = atan2f(Rot_matrix[5] - Rot_matrix[1], Rot_matrix[2] + Rot_matrix[4] - euler[0]); - } else if (fabsf(theta + M_PI_2_F) < 1.0e-3f) { - euler[0] = 0.0f; - euler[2] = atan2f(Rot_matrix[5] - Rot_matrix[1], Rot_matrix[2] + Rot_matrix[4] - euler[0]); - } else { - euler[0] = atan2f(Rot_matrix[7], Rot_matrix[8]); - euler[2] = atan2f(Rot_matrix[3], Rot_matrix[0]); - } - */ - - euler[2] = atan2f(2*(q1*q2-q0*q3),2*(q0*q0+q1*q1)-1); - euler[1]= -asinf(2*(q1*q3+q0*q2)); - euler[0] = atan2f(2*(q2*q3-q0*q1),2*(q0*q0+q3*q3)-1); - + // Euler angle directly from quaternion. + euler[0] = -atan2f(gravity_vector[1], sqrtf(gravity_vector[0]*gravity_vector[0] + gravity_vector[2]*gravity_vector[2])); // roll + euler[1] = atan2f(gravity_vector[0], sqrtf(gravity_vector[1]*gravity_vector[1] + gravity_vector[2]*gravity_vector[2])); // pitch + euler[2] = -atan2f(2*(q1*q2-q0*q3),2*(q0*q0+q1*q1)-1); // yaw + + //euler[2] = atan2(2 * q1*q2 - 2 * q0*q3, 2 * q0*q0 + 2 * q1*q1 - 1); // psi + //euler[1] = -asin(2 * q1*q3 + 2 * q0*q2); // theta + //euler[0] = atan2(2 * q2*q3 - 2 * q0*q1, 2 * q0*q0 + 2 * q3*q3 - 1); // phi /* swap values for next iteration, check for fatal inputs */ if (isfinite(euler[0]) && isfinite(euler[1]) && isfinite(euler[2])) { @@ -684,19 +689,15 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds att.yaw = euler[2] - so3_comp_params.yaw_off; /* FIXME : This can be a problem for rate controller. Rate in body or inertial? */ - att.rollspeed = q1; - att.pitchspeed = q2; - att.yawspeed = q3; + att.rollspeed = gyro[0]; + att.pitchspeed = gyro[1]; + att.yawspeed = gyro[2]; + att.rollacc = 0; + att.pitchacc = 0; + att.yawacc = 0; - /* - att.rollacc = x_aposteriori[3]; - att.pitchacc = x_aposteriori[4]; - att.yawacc = x_aposteriori[5]; - */ - - //att.yawspeed =z_k[2] ; - /* copy offsets */ - //memcpy(&att.rate_offsets, &(x_aposteriori[3]), sizeof(att.rate_offsets)); + /* TODO: Bias estimation required */ + memcpy(&att.rate_offsets, &(gyro_bias), sizeof(att.rate_offsets)); /* copy rotation matrix */ memcpy(&att.R, Rot_matrix, sizeof(Rot_matrix)); @@ -712,6 +713,7 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds perf_end(so3_comp_loop_perf); + //! This will print out debug packet to visualization software if(debug_mode) { float quat[4]; @@ -722,12 +724,12 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds send_uart_float_arr(quat,4); send_uart_byte('\n'); } - }// else + } } } loopcounter++; - } + }// while thread_running = false; From f336a86baa6e0a9ef0b7bbb82e7f3ea4847d15dc Mon Sep 17 00:00:00 2001 From: "Hyon Lim (Retina)" Date: Wed, 29 May 2013 00:29:31 +1000 Subject: [PATCH 08/26] I finished to implement nonlinear complementary filter on the SO(3). The previous problem was roll,pitch and yaw angle from quaternion. Now it is fixed. 1-2-3 Euler representation is used. Also accelerometer sign change has been applied. --- .../attitude_estimator_so3_comp_main.cpp | 172 ++++++++++++------ 1 file changed, 118 insertions(+), 54 deletions(-) diff --git a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp index 28fcf03692..9bb749cafb 100755 --- a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp +++ b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp @@ -1,7 +1,19 @@ /* + * Author: Hyon Lim + * * @file attitude_estimator_so3_comp_main.c * - * Nonlinear SO3 filter for Attitude Estimation. + * Implementation of nonlinear complementary filters on the SO(3). + * This code performs attitude estimation by using accelerometer, gyroscopes and magnetometer. + * Result is provided as quaternion, 1-2-3 Euler angle and rotation matrix. + * + * Theory of nonlinear complementary filters on the SO(3) is based on [1]. + * Quaternion realization of [1] is based on [2]. + * Optmized quaternion update code is based on Sebastian Madgwick's implementation. + * + * References + * [1] Mahony, R.; Hamel, T.; Pflimlin, Jean-Michel, "Nonlinear Complementary Filters on the Special Orthogonal Group," Automatic Control, IEEE Transactions on , vol.53, no.5, pp.1203,1218, June 2008 + * [2] Euston, M.; Coote, P.; Mahony, R.; Jonghyuk Kim; Hamel, T., "A complementary filter for attitude estimation of a fixed-wing UAV," Intelligent Robots and Systems, 2008. IROS 2008. IEEE/RSJ International Conference on , vol., no., pp.340,345, 22-26 Sept. 2008 */ #include @@ -46,7 +58,13 @@ static bool thread_running = false; /**< Deamon status flag */ static int attitude_estimator_so3_comp_task; /**< Handle of deamon task / thread */ static float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; /** quaternion of sensor frame relative to auxiliary frame */ static float gyro_bias[3] = {0.0f, 0.0f, 0.0f}; /** bias estimation */ -static float gravity_vector[3] = {0.0f,0.0f,0.0f}; /** estimated gravity vector */ +static bool bFilterInit = false; + +//! Auxiliary variables to reduce number of repeated operations +static float q0q0, q0q1, q0q2, q0q3; +static float q1q1, q1q2, q1q3; +static float q2q2, q2q3; +static float q3q3; //! Serial packet related static int uart; @@ -150,29 +168,77 @@ float invSqrt(float number) { return y; } +//! Using accelerometer, sense the gravity vector. +//! Using magnetometer, sense yaw. +void MahonyAHRSinit(float ax, float ay, float az, float mx, float my, float mz) +{ + float initialRoll, initialPitch; + float cosRoll, sinRoll, cosPitch, sinPitch; + float magX, magY; + float initialHdg, cosHeading, sinHeading; + + initialRoll = atan2(-ay, -az); + initialPitch = atan2(ax, -az); + + cosRoll = cosf(initialRoll); + sinRoll = sinf(initialRoll); + cosPitch = cosf(initialPitch); + sinPitch = sinf(initialPitch); + + magX = mx * cosPitch + my * sinRoll * sinPitch + mz * cosRoll * sinPitch; + + magY = my * cosRoll - mz * sinRoll; + + initialHdg = atan2f(-magY, magX); + + cosRoll = cosf(initialRoll * 0.5f); + sinRoll = sinf(initialRoll * 0.5f); + + cosPitch = cosf(initialPitch * 0.5f); + sinPitch = sinf(initialPitch * 0.5f); + + cosHeading = cosf(initialHdg * 0.5f); + sinHeading = sinf(initialHdg * 0.5f); + + q0 = cosRoll * cosPitch * cosHeading + sinRoll * sinPitch * sinHeading; + q1 = sinRoll * cosPitch * cosHeading - cosRoll * sinPitch * sinHeading; + q2 = cosRoll * sinPitch * cosHeading + sinRoll * cosPitch * sinHeading; + q3 = cosRoll * cosPitch * sinHeading - sinRoll * sinPitch * cosHeading; + + // auxillary variables to reduce number of repeated operations, for 1st pass + q0q0 = q0 * q0; + q0q1 = q0 * q1; + q0q2 = q0 * q2; + q0q3 = q0 * q3; + q1q1 = q1 * q1; + q1q2 = q1 * q2; + q1q3 = q1 * q3; + q2q2 = q2 * q2; + q2q3 = q2 * q3; + q3q3 = q3 * q3; +} + void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, float twoKp, float twoKi, float dt) { float recipNorm; - float q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; float halfex = 0.0f, halfey = 0.0f, halfez = 0.0f; - - // Auxiliary variables to avoid repeated arithmetic - q0q0 = q0 * q0; - q0q1 = q0 * q1; - q0q2 = q0 * q2; - q0q3 = q0 * q3; - q1q1 = q1 * q1; - q1q2 = q1 * q2; - q1q3 = q1 * q3; - q2q2 = q2 * q2; - q2q3 = q2 * q3; - q3q3 = q3 * q3; + //! Make filter converge to initial solution faster + //! This function assumes you are in static position. + //! WARNING : in case air reboot, this can cause problem. But this is very + //! unlikely happen. + if(bFilterInit == false) + { + MahonyAHRSinit(ax,ay,az,mx,my,mz); + bFilterInit = true; + } + //! If magnetometer measurement is available, use it. if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { - float hx, hy, bx, bz; + float hx, hy, hz, bx, bz; float halfwx, halfwy, halfwz; // Normalise magnetometer measurement + // Will sqrt work better? PX4 system is powerful enough? recipNorm = invSqrt(mx * mx + my * my + mz * mz); mx *= recipNorm; my *= recipNorm; @@ -181,8 +247,9 @@ void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az // Reference direction of Earth's magnetic field hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2)); hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1)); + hz = 2 * mx * (q1q3 - q0q2) + 2 * my * (q2q3 + q0q1) + 2 * mz * (0.5 - q1q1 - q2q2); bx = sqrt(hx * hx + hy * hy); - bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2)); + bz = hz; // Estimated direction of magnetic field halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2); @@ -203,7 +270,7 @@ void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az recipNorm = invSqrt(ax * ax + ay * ay + az * az); ax *= recipNorm; ay *= recipNorm; - az *= recipNorm; + az *= recipNorm; // Estimated direction of gravity and magnetic field halfvx = q1q3 - q0q2; @@ -254,6 +321,18 @@ void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az q1 *= recipNorm; q2 *= recipNorm; q3 *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + q0q0 = q0 * q0; + q0q1 = q0 * q1; + q0q2 = q0 * q2; + q0q3 = q0 * q3; + q1q1 = q1 * q1; + q1q2 = q1 * q2; + q1q3 = q1 * q3; + q2q2 = q2 * q2; + q2q3 = q2 * q3; + q3q3 = q3 * q3; } void send_uart_byte(char c) @@ -630,44 +709,29 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds uint64_t timing_start = hrt_absolute_time(); - MahonyAHRSupdate(gyro[0],gyro[1],gyro[2],acc[0],acc[1],acc[2],mag[0],mag[1],mag[2],so3_comp_params.Kp,so3_comp_params.Ki, dt); + // NOTE : Accelerometer is reversed. + // Because proper mount of PX4 will give you a reversed accelerometer readings. + MahonyAHRSupdate(gyro[0],gyro[1],gyro[2],-acc[0],-acc[1],-acc[2],mag[0],mag[1],mag[2],so3_comp_params.Kp,so3_comp_params.Ki, dt); - float aSq = q0*q0; // 1 - float bSq = q1*q1; // 2 - float cSq = q2*q2; // 3 - float dSq = q3*q3; // 4 - float a = q0; - float b = q1; - float c = q2; - float d = q3; - - Rot_matrix[0] = 2*aSq - 1 + 2*bSq; // 11 - //Rot_matrix[1] = 2.0 * (b * c - a * d); // 12 - //Rot_matrix[2] = 2.0 * (a * c + b * d); // 13 - Rot_matrix[3] = 2.0 * (b * c - a * d); // 21 - //Rot_matrix[4] = aSq - bSq + cSq - dSq; // 22 - //Rot_matrix[5] = 2.0 * (c * d - a * b); // 23 - Rot_matrix[6] = 2.0 * (b * d + a * c); // 31 - Rot_matrix[7] = 2.0 * (c * d - a * b); // 32 - Rot_matrix[8] = 2*aSq - 1 + 2*dSq; // 33 - - gravity_vector[0] = 2*(q1*q3-q0*q2); - gravity_vector[1] = 2*(q0*q1+q2*q3); - gravity_vector[2] = aSq - bSq - cSq + dSq; - - //euler[0] = atan2f(Rot_matrix[7], Rot_matrix[8]); - //euler[1] = -asinf(Rot_matrix[6]); - //euler[2] = atan2f(Rot_matrix[3],Rot_matrix[0]); - - // Euler angle directly from quaternion. - euler[0] = -atan2f(gravity_vector[1], sqrtf(gravity_vector[0]*gravity_vector[0] + gravity_vector[2]*gravity_vector[2])); // roll - euler[1] = atan2f(gravity_vector[0], sqrtf(gravity_vector[1]*gravity_vector[1] + gravity_vector[2]*gravity_vector[2])); // pitch - euler[2] = -atan2f(2*(q1*q2-q0*q3),2*(q0*q0+q1*q1)-1); // yaw - - //euler[2] = atan2(2 * q1*q2 - 2 * q0*q3, 2 * q0*q0 + 2 * q1*q1 - 1); // psi - //euler[1] = -asin(2 * q1*q3 + 2 * q0*q2); // theta - //euler[0] = atan2(2 * q2*q3 - 2 * q0*q1, 2 * q0*q0 + 2 * q3*q3 - 1); // phi + // Convert q->R. + Rot_matrix[0] = q0q0 + q1q1 - q2q2 - q3q3;// 11 + Rot_matrix[1] = 2.0 * (q1*q2 + q0*q3); // 12 + Rot_matrix[2] = 2.0 * (q1*q3 - q0*q2); // 13 + Rot_matrix[3] = 2.0 * (q1*q2 - q0*q3); // 21 + Rot_matrix[4] = q0q0 - q1q1 + q2q2 - q3q3;// 22 + Rot_matrix[5] = 2.0 * (q2*q3 + q0*q1); // 23 + Rot_matrix[6] = 2.0 * (q1*q3 + q0*q2); // 31 + Rot_matrix[7] = 2.0 * (q2*q3 - q0*q1); // 32 + Rot_matrix[8] = q0q0 - q1q1 - q2q2 + q3q3;// 33 + //1-2-3 Representation. + //Equation (290) + //Representing Attitude: Euler Angles, Unit Quaternions, and Rotation Vectors, James Diebel. + // Existing PX4 EKF code was generated by MATLAB which uses coloum major order matrix. + euler[0] = atan2f(Rot_matrix[5], Rot_matrix[8]); //! Roll + euler[1] = -asinf(Rot_matrix[2]); //! Pitch + euler[2] = atan2f(Rot_matrix[1],Rot_matrix[0]); //! Yaw + /* swap values for next iteration, check for fatal inputs */ if (isfinite(euler[0]) && isfinite(euler[1]) && isfinite(euler[2])) { /* Do something */ From cc6c590af062d80681430fa5139837c87fbd72f0 Mon Sep 17 00:00:00 2001 From: "Hyon Lim (Retina)" Date: Wed, 29 May 2013 00:29:31 +1000 Subject: [PATCH 09/26] I finished to implement nonlinear complementary filter on the SO(3). The previous problem was roll,pitch and yaw angle from quaternion. Now it is fixed. 1-2-3 Euler representation is used. Also accelerometer sign change has been applied. --- .../attitude_estimator_so3_comp_main.cpp | 172 ++++++++++++------ 1 file changed, 118 insertions(+), 54 deletions(-) diff --git a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp index 28fcf03692..9bb749cafb 100755 --- a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp +++ b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp @@ -1,7 +1,19 @@ /* + * Author: Hyon Lim + * * @file attitude_estimator_so3_comp_main.c * - * Nonlinear SO3 filter for Attitude Estimation. + * Implementation of nonlinear complementary filters on the SO(3). + * This code performs attitude estimation by using accelerometer, gyroscopes and magnetometer. + * Result is provided as quaternion, 1-2-3 Euler angle and rotation matrix. + * + * Theory of nonlinear complementary filters on the SO(3) is based on [1]. + * Quaternion realization of [1] is based on [2]. + * Optmized quaternion update code is based on Sebastian Madgwick's implementation. + * + * References + * [1] Mahony, R.; Hamel, T.; Pflimlin, Jean-Michel, "Nonlinear Complementary Filters on the Special Orthogonal Group," Automatic Control, IEEE Transactions on , vol.53, no.5, pp.1203,1218, June 2008 + * [2] Euston, M.; Coote, P.; Mahony, R.; Jonghyuk Kim; Hamel, T., "A complementary filter for attitude estimation of a fixed-wing UAV," Intelligent Robots and Systems, 2008. IROS 2008. IEEE/RSJ International Conference on , vol., no., pp.340,345, 22-26 Sept. 2008 */ #include @@ -46,7 +58,13 @@ static bool thread_running = false; /**< Deamon status flag */ static int attitude_estimator_so3_comp_task; /**< Handle of deamon task / thread */ static float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; /** quaternion of sensor frame relative to auxiliary frame */ static float gyro_bias[3] = {0.0f, 0.0f, 0.0f}; /** bias estimation */ -static float gravity_vector[3] = {0.0f,0.0f,0.0f}; /** estimated gravity vector */ +static bool bFilterInit = false; + +//! Auxiliary variables to reduce number of repeated operations +static float q0q0, q0q1, q0q2, q0q3; +static float q1q1, q1q2, q1q3; +static float q2q2, q2q3; +static float q3q3; //! Serial packet related static int uart; @@ -150,29 +168,77 @@ float invSqrt(float number) { return y; } +//! Using accelerometer, sense the gravity vector. +//! Using magnetometer, sense yaw. +void MahonyAHRSinit(float ax, float ay, float az, float mx, float my, float mz) +{ + float initialRoll, initialPitch; + float cosRoll, sinRoll, cosPitch, sinPitch; + float magX, magY; + float initialHdg, cosHeading, sinHeading; + + initialRoll = atan2(-ay, -az); + initialPitch = atan2(ax, -az); + + cosRoll = cosf(initialRoll); + sinRoll = sinf(initialRoll); + cosPitch = cosf(initialPitch); + sinPitch = sinf(initialPitch); + + magX = mx * cosPitch + my * sinRoll * sinPitch + mz * cosRoll * sinPitch; + + magY = my * cosRoll - mz * sinRoll; + + initialHdg = atan2f(-magY, magX); + + cosRoll = cosf(initialRoll * 0.5f); + sinRoll = sinf(initialRoll * 0.5f); + + cosPitch = cosf(initialPitch * 0.5f); + sinPitch = sinf(initialPitch * 0.5f); + + cosHeading = cosf(initialHdg * 0.5f); + sinHeading = sinf(initialHdg * 0.5f); + + q0 = cosRoll * cosPitch * cosHeading + sinRoll * sinPitch * sinHeading; + q1 = sinRoll * cosPitch * cosHeading - cosRoll * sinPitch * sinHeading; + q2 = cosRoll * sinPitch * cosHeading + sinRoll * cosPitch * sinHeading; + q3 = cosRoll * cosPitch * sinHeading - sinRoll * sinPitch * cosHeading; + + // auxillary variables to reduce number of repeated operations, for 1st pass + q0q0 = q0 * q0; + q0q1 = q0 * q1; + q0q2 = q0 * q2; + q0q3 = q0 * q3; + q1q1 = q1 * q1; + q1q2 = q1 * q2; + q1q3 = q1 * q3; + q2q2 = q2 * q2; + q2q3 = q2 * q3; + q3q3 = q3 * q3; +} + void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, float twoKp, float twoKi, float dt) { float recipNorm; - float q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; float halfex = 0.0f, halfey = 0.0f, halfez = 0.0f; - - // Auxiliary variables to avoid repeated arithmetic - q0q0 = q0 * q0; - q0q1 = q0 * q1; - q0q2 = q0 * q2; - q0q3 = q0 * q3; - q1q1 = q1 * q1; - q1q2 = q1 * q2; - q1q3 = q1 * q3; - q2q2 = q2 * q2; - q2q3 = q2 * q3; - q3q3 = q3 * q3; + //! Make filter converge to initial solution faster + //! This function assumes you are in static position. + //! WARNING : in case air reboot, this can cause problem. But this is very + //! unlikely happen. + if(bFilterInit == false) + { + MahonyAHRSinit(ax,ay,az,mx,my,mz); + bFilterInit = true; + } + //! If magnetometer measurement is available, use it. if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { - float hx, hy, bx, bz; + float hx, hy, hz, bx, bz; float halfwx, halfwy, halfwz; // Normalise magnetometer measurement + // Will sqrt work better? PX4 system is powerful enough? recipNorm = invSqrt(mx * mx + my * my + mz * mz); mx *= recipNorm; my *= recipNorm; @@ -181,8 +247,9 @@ void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az // Reference direction of Earth's magnetic field hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2)); hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1)); + hz = 2 * mx * (q1q3 - q0q2) + 2 * my * (q2q3 + q0q1) + 2 * mz * (0.5 - q1q1 - q2q2); bx = sqrt(hx * hx + hy * hy); - bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2)); + bz = hz; // Estimated direction of magnetic field halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2); @@ -203,7 +270,7 @@ void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az recipNorm = invSqrt(ax * ax + ay * ay + az * az); ax *= recipNorm; ay *= recipNorm; - az *= recipNorm; + az *= recipNorm; // Estimated direction of gravity and magnetic field halfvx = q1q3 - q0q2; @@ -254,6 +321,18 @@ void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az q1 *= recipNorm; q2 *= recipNorm; q3 *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + q0q0 = q0 * q0; + q0q1 = q0 * q1; + q0q2 = q0 * q2; + q0q3 = q0 * q3; + q1q1 = q1 * q1; + q1q2 = q1 * q2; + q1q3 = q1 * q3; + q2q2 = q2 * q2; + q2q3 = q2 * q3; + q3q3 = q3 * q3; } void send_uart_byte(char c) @@ -630,44 +709,29 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds uint64_t timing_start = hrt_absolute_time(); - MahonyAHRSupdate(gyro[0],gyro[1],gyro[2],acc[0],acc[1],acc[2],mag[0],mag[1],mag[2],so3_comp_params.Kp,so3_comp_params.Ki, dt); + // NOTE : Accelerometer is reversed. + // Because proper mount of PX4 will give you a reversed accelerometer readings. + MahonyAHRSupdate(gyro[0],gyro[1],gyro[2],-acc[0],-acc[1],-acc[2],mag[0],mag[1],mag[2],so3_comp_params.Kp,so3_comp_params.Ki, dt); - float aSq = q0*q0; // 1 - float bSq = q1*q1; // 2 - float cSq = q2*q2; // 3 - float dSq = q3*q3; // 4 - float a = q0; - float b = q1; - float c = q2; - float d = q3; - - Rot_matrix[0] = 2*aSq - 1 + 2*bSq; // 11 - //Rot_matrix[1] = 2.0 * (b * c - a * d); // 12 - //Rot_matrix[2] = 2.0 * (a * c + b * d); // 13 - Rot_matrix[3] = 2.0 * (b * c - a * d); // 21 - //Rot_matrix[4] = aSq - bSq + cSq - dSq; // 22 - //Rot_matrix[5] = 2.0 * (c * d - a * b); // 23 - Rot_matrix[6] = 2.0 * (b * d + a * c); // 31 - Rot_matrix[7] = 2.0 * (c * d - a * b); // 32 - Rot_matrix[8] = 2*aSq - 1 + 2*dSq; // 33 - - gravity_vector[0] = 2*(q1*q3-q0*q2); - gravity_vector[1] = 2*(q0*q1+q2*q3); - gravity_vector[2] = aSq - bSq - cSq + dSq; - - //euler[0] = atan2f(Rot_matrix[7], Rot_matrix[8]); - //euler[1] = -asinf(Rot_matrix[6]); - //euler[2] = atan2f(Rot_matrix[3],Rot_matrix[0]); - - // Euler angle directly from quaternion. - euler[0] = -atan2f(gravity_vector[1], sqrtf(gravity_vector[0]*gravity_vector[0] + gravity_vector[2]*gravity_vector[2])); // roll - euler[1] = atan2f(gravity_vector[0], sqrtf(gravity_vector[1]*gravity_vector[1] + gravity_vector[2]*gravity_vector[2])); // pitch - euler[2] = -atan2f(2*(q1*q2-q0*q3),2*(q0*q0+q1*q1)-1); // yaw - - //euler[2] = atan2(2 * q1*q2 - 2 * q0*q3, 2 * q0*q0 + 2 * q1*q1 - 1); // psi - //euler[1] = -asin(2 * q1*q3 + 2 * q0*q2); // theta - //euler[0] = atan2(2 * q2*q3 - 2 * q0*q1, 2 * q0*q0 + 2 * q3*q3 - 1); // phi + // Convert q->R. + Rot_matrix[0] = q0q0 + q1q1 - q2q2 - q3q3;// 11 + Rot_matrix[1] = 2.0 * (q1*q2 + q0*q3); // 12 + Rot_matrix[2] = 2.0 * (q1*q3 - q0*q2); // 13 + Rot_matrix[3] = 2.0 * (q1*q2 - q0*q3); // 21 + Rot_matrix[4] = q0q0 - q1q1 + q2q2 - q3q3;// 22 + Rot_matrix[5] = 2.0 * (q2*q3 + q0*q1); // 23 + Rot_matrix[6] = 2.0 * (q1*q3 + q0*q2); // 31 + Rot_matrix[7] = 2.0 * (q2*q3 - q0*q1); // 32 + Rot_matrix[8] = q0q0 - q1q1 - q2q2 + q3q3;// 33 + //1-2-3 Representation. + //Equation (290) + //Representing Attitude: Euler Angles, Unit Quaternions, and Rotation Vectors, James Diebel. + // Existing PX4 EKF code was generated by MATLAB which uses coloum major order matrix. + euler[0] = atan2f(Rot_matrix[5], Rot_matrix[8]); //! Roll + euler[1] = -asinf(Rot_matrix[2]); //! Pitch + euler[2] = atan2f(Rot_matrix[1],Rot_matrix[0]); //! Yaw + /* swap values for next iteration, check for fatal inputs */ if (isfinite(euler[0]) && isfinite(euler[1]) && isfinite(euler[2])) { /* Do something */ From 7a2adb22eb3ebec5fd90d1d4fbce3f5dbd61bb3c Mon Sep 17 00:00:00 2001 From: "Hyon Lim (Retina)" Date: Wed, 29 May 2013 00:45:02 +1000 Subject: [PATCH 10/26] Visualization code has been added. --- .../attitude_estimator_so3_comp/README | 5 + .../attitude_estimator_so3_comp_params.c | 14 +- .../attitude_estimator_so3_comp_params.h | 14 +- .../FreeIMU_cube/FreeIMU_cube.pde | 265 +++++++ .../FreeIMU_cube/LICENSE.txt | 674 ++++++++++++++++++ .../FreeIMU_cube/data/CourierNew36.vlw | Bin 0 -> 114920 bytes .../FreeIMU_yaw_pitch_roll.pde | 229 ++++++ .../FreeIMU_yaw_pitch_roll/LICENSE.txt | 674 ++++++++++++++++++ .../data/CourierNew36.vlw | Bin 0 -> 114920 bytes .../visualization_code/README | 9 + 10 files changed, 1882 insertions(+), 2 deletions(-) create mode 100644 src/modules/attitude_estimator_so3_comp/README create mode 100644 src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/FreeIMU_cube.pde create mode 100644 src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/LICENSE.txt create mode 100644 src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/data/CourierNew36.vlw create mode 100644 src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_yaw_pitch_roll/FreeIMU_yaw_pitch_roll.pde create mode 100644 src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_yaw_pitch_roll/LICENSE.txt create mode 100644 src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_yaw_pitch_roll/data/CourierNew36.vlw create mode 100644 src/modules/attitude_estimator_so3_comp/visualization_code/README diff --git a/src/modules/attitude_estimator_so3_comp/README b/src/modules/attitude_estimator_so3_comp/README new file mode 100644 index 0000000000..26b270d37c --- /dev/null +++ b/src/modules/attitude_estimator_so3_comp/README @@ -0,0 +1,5 @@ +Synopsis + + nsh> attitude_estimator_so3_comp start -d /dev/ttyS1 -b 115200 + +Option -d is for debugging packet. See visualization_code folder. diff --git a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c index 068e4340a6..1d5e0670a0 100755 --- a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c +++ b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c @@ -1,7 +1,19 @@ /* + * Author: Hyon Lim + * * @file attitude_estimator_so3_comp_params.c * - * Parameters for SO3 complementary filter + * Implementation of nonlinear complementary filters on the SO(3). + * This code performs attitude estimation by using accelerometer, gyroscopes and magnetometer. + * Result is provided as quaternion, 1-2-3 Euler angle and rotation matrix. + * + * Theory of nonlinear complementary filters on the SO(3) is based on [1]. + * Quaternion realization of [1] is based on [2]. + * Optmized quaternion update code is based on Sebastian Madgwick's implementation. + * + * References + * [1] Mahony, R.; Hamel, T.; Pflimlin, Jean-Michel, "Nonlinear Complementary Filters on the Special Orthogonal Group," Automatic Control, IEEE Transactions on , vol.53, no.5, pp.1203,1218, June 2008 + * [2] Euston, M.; Coote, P.; Mahony, R.; Jonghyuk Kim; Hamel, T., "A complementary filter for attitude estimation of a fixed-wing UAV," Intelligent Robots and Systems, 2008. IROS 2008. IEEE/RSJ International Conference on , vol., no., pp.340,345, 22-26 Sept. 2008 */ #include "attitude_estimator_so3_comp_params.h" diff --git a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.h b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.h index 2fccec61ce..f006956308 100755 --- a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.h +++ b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.h @@ -1,7 +1,19 @@ /* + * Author: Hyon Lim + * * @file attitude_estimator_so3_comp_params.h * - * Parameters for EKF filter + * Implementation of nonlinear complementary filters on the SO(3). + * This code performs attitude estimation by using accelerometer, gyroscopes and magnetometer. + * Result is provided as quaternion, 1-2-3 Euler angle and rotation matrix. + * + * Theory of nonlinear complementary filters on the SO(3) is based on [1]. + * Quaternion realization of [1] is based on [2]. + * Optmized quaternion update code is based on Sebastian Madgwick's implementation. + * + * References + * [1] Mahony, R.; Hamel, T.; Pflimlin, Jean-Michel, "Nonlinear Complementary Filters on the Special Orthogonal Group," Automatic Control, IEEE Transactions on , vol.53, no.5, pp.1203,1218, June 2008 + * [2] Euston, M.; Coote, P.; Mahony, R.; Jonghyuk Kim; Hamel, T., "A complementary filter for attitude estimation of a fixed-wing UAV," Intelligent Robots and Systems, 2008. IROS 2008. IEEE/RSJ International Conference on , vol., no., pp.340,345, 22-26 Sept. 2008 */ #include diff --git a/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/FreeIMU_cube.pde b/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/FreeIMU_cube.pde new file mode 100644 index 0000000000..3706437d33 --- /dev/null +++ b/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/FreeIMU_cube.pde @@ -0,0 +1,265 @@ +/** +Visualize a cube which will assumes the orientation described +in a quaternion coming from the serial port. + +INSTRUCTIONS: +This program has to be run when you have the FreeIMU_serial +program running on your Arduino and the Arduino connected to your PC. +Remember to set the serialPort variable below to point to the name the +Arduino serial port has in your system. You can get the port using the +Arduino IDE from Tools->Serial Port: the selected entry is what you have +to use as serialPort variable. + + +Copyright (C) 2011-2012 Fabio Varesano - http://www.varesano.net/ + +This program is free software: you can redistribute it and/or modify +it under the terms of the version 3 GNU General Public License as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +import processing.serial.*; +import processing.opengl.*; + +Serial myPort; // Create object from Serial class + +final String serialPort = "/dev/ttyUSB9"; // replace this with your serial port. On windows you will need something like "COM1". + +float [] q = new float [4]; +float [] hq = null; +float [] Euler = new float [3]; // psi, theta, phi + +int lf = 10; // 10 is '\n' in ASCII +byte[] inBuffer = new byte[22]; // this is the number of chars on each line from the Arduino (including /r/n) + +PFont font; +final int VIEW_SIZE_X = 800, VIEW_SIZE_Y = 600; + +final int burst = 32; +int count = 0; + +void myDelay(int time) { + try { + Thread.sleep(time); + } catch (InterruptedException e) { } +} + +void setup() +{ + size(VIEW_SIZE_X, VIEW_SIZE_Y, OPENGL); + myPort = new Serial(this, serialPort, 115200); + + // The font must be located in the sketch's "data" directory to load successfully + font = loadFont("CourierNew36.vlw"); + + println("Waiting IMU.."); + + myPort.clear(); + + while (myPort.available() == 0) { + myPort.write("v"); + myDelay(1000); + } + println(myPort.readStringUntil('\n')); + myPort.write("q" + char(burst)); + myPort.bufferUntil('\n'); +} + + +float decodeFloat(String inString) { + byte [] inData = new byte[4]; + + if(inString.length() == 8) { + inData[0] = (byte) unhex(inString.substring(0, 2)); + inData[1] = (byte) unhex(inString.substring(2, 4)); + inData[2] = (byte) unhex(inString.substring(4, 6)); + inData[3] = (byte) unhex(inString.substring(6, 8)); + } + + int intbits = (inData[3] << 24) | ((inData[2] & 0xff) << 16) | ((inData[1] & 0xff) << 8) | (inData[0] & 0xff); + return Float.intBitsToFloat(intbits); +} + +void serialEvent(Serial p) { + if(p.available() >= 18) { + String inputString = p.readStringUntil('\n'); + //print(inputString); + if (inputString != null && inputString.length() > 0) { + String [] inputStringArr = split(inputString, ","); + if(inputStringArr.length >= 5) { // q1,q2,q3,q4,\r\n so we have 5 elements + q[0] = decodeFloat(inputStringArr[0]); + q[1] = decodeFloat(inputStringArr[1]); + q[2] = decodeFloat(inputStringArr[2]); + q[3] = decodeFloat(inputStringArr[3]); + } + } + count = count + 1; + if(burst == count) { // ask more data when burst completed + p.write("q" + char(burst)); + count = 0; + } + } +} + + + +void buildBoxShape() { + //box(60, 10, 40); + noStroke(); + beginShape(QUADS); + + //Z+ (to the drawing area) + fill(#00ff00); + vertex(-30, -5, 20); + vertex(30, -5, 20); + vertex(30, 5, 20); + vertex(-30, 5, 20); + + //Z- + fill(#0000ff); + vertex(-30, -5, -20); + vertex(30, -5, -20); + vertex(30, 5, -20); + vertex(-30, 5, -20); + + //X- + fill(#ff0000); + vertex(-30, -5, -20); + vertex(-30, -5, 20); + vertex(-30, 5, 20); + vertex(-30, 5, -20); + + //X+ + fill(#ffff00); + vertex(30, -5, -20); + vertex(30, -5, 20); + vertex(30, 5, 20); + vertex(30, 5, -20); + + //Y- + fill(#ff00ff); + vertex(-30, -5, -20); + vertex(30, -5, -20); + vertex(30, -5, 20); + vertex(-30, -5, 20); + + //Y+ + fill(#00ffff); + vertex(-30, 5, -20); + vertex(30, 5, -20); + vertex(30, 5, 20); + vertex(-30, 5, 20); + + endShape(); +} + + +void drawCube() { + pushMatrix(); + translate(VIEW_SIZE_X/2, VIEW_SIZE_Y/2 + 50, 0); + scale(5,5,5); + + // a demonstration of the following is at + // http://www.varesano.net/blog/fabio/ahrs-sensor-fusion-orientation-filter-3d-graphical-rotating-cube + rotateZ(-Euler[2]); + rotateX(-Euler[1]); + rotateY(-Euler[0]); + + buildBoxShape(); + + popMatrix(); +} + + +void draw() { + background(#000000); + fill(#ffffff); + + if(hq != null) { // use home quaternion + quaternionToEuler(quatProd(hq, q), Euler); + text("Disable home position by pressing \"n\"", 20, VIEW_SIZE_Y - 30); + } + else { + quaternionToEuler(q, Euler); + text("Point FreeIMU's X axis to your monitor then press \"h\"", 20, VIEW_SIZE_Y - 30); + } + + textFont(font, 20); + textAlign(LEFT, TOP); + text("Q:\n" + q[0] + "\n" + q[1] + "\n" + q[2] + "\n" + q[3], 20, 20); + text("Euler Angles:\nYaw (psi) : " + degrees(Euler[0]) + "\nPitch (theta): " + degrees(Euler[1]) + "\nRoll (phi) : " + degrees(Euler[2]), 200, 20); + + drawCube(); + //myPort.write("q" + 1); +} + + +void keyPressed() { + if(key == 'h') { + println("pressed h"); + + // set hq the home quaternion as the quatnion conjugate coming from the sensor fusion + hq = quatConjugate(q); + + } + else if(key == 'n') { + println("pressed n"); + hq = null; + } +} + +// See Sebastian O.H. Madwick report +// "An efficient orientation filter for inertial and intertial/magnetic sensor arrays" Chapter 2 Quaternion representation + +void quaternionToEuler(float [] q, float [] euler) { + euler[0] = atan2(2 * q[1] * q[2] - 2 * q[0] * q[3], 2 * q[0]*q[0] + 2 * q[1] * q[1] - 1); // psi + euler[1] = -asin(2 * q[1] * q[3] + 2 * q[0] * q[2]); // theta + euler[2] = atan2(2 * q[2] * q[3] - 2 * q[0] * q[1], 2 * q[0] * q[0] + 2 * q[3] * q[3] - 1); // phi +} + +float [] quatProd(float [] a, float [] b) { + float [] q = new float[4]; + + q[0] = a[0] * b[0] - a[1] * b[1] - a[2] * b[2] - a[3] * b[3]; + q[1] = a[0] * b[1] + a[1] * b[0] + a[2] * b[3] - a[3] * b[2]; + q[2] = a[0] * b[2] - a[1] * b[3] + a[2] * b[0] + a[3] * b[1]; + q[3] = a[0] * b[3] + a[1] * b[2] - a[2] * b[1] + a[3] * b[0]; + + return q; +} + +// returns a quaternion from an axis angle representation +float [] quatAxisAngle(float [] axis, float angle) { + float [] q = new float[4]; + + float halfAngle = angle / 2.0; + float sinHalfAngle = sin(halfAngle); + q[0] = cos(halfAngle); + q[1] = -axis[0] * sinHalfAngle; + q[2] = -axis[1] * sinHalfAngle; + q[3] = -axis[2] * sinHalfAngle; + + return q; +} + +// return the quaternion conjugate of quat +float [] quatConjugate(float [] quat) { + float [] conj = new float[4]; + + conj[0] = quat[0]; + conj[1] = -quat[1]; + conj[2] = -quat[2]; + conj[3] = -quat[3]; + + return conj; +} + diff --git a/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/LICENSE.txt b/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/LICENSE.txt new file mode 100644 index 0000000000..94a9ed024d --- /dev/null +++ b/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/LICENSE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/data/CourierNew36.vlw b/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/data/CourierNew36.vlw new file mode 100644 index 0000000000000000000000000000000000000000..904771486a1a6d241fb5184282eb99780f730615 GIT binary patch literal 114920 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%0AM;pp>Tg3o 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 Port: the selected entry is what you have +to use as serialPort variable. + +Copyright (C) 2011-2012 Fabio Varesano - http://www.varesano.net/ + +This program is free software: you can redistribute it and/or modify +it under the terms of the version 3 GNU General Public License as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +import processing.serial.*; + +Serial myPort; // Create object from Serial class + + +float [] q = new float [4]; +float [] Euler = new float [3]; // psi, theta, phi + +int lf = 10; // 10 is '\n' in ASCII +byte[] inBuffer = new byte[22]; // this is the number of chars on each line from the Arduino (including /r/n) + +PFont font; +final int VIEW_SIZE_X = 800, VIEW_SIZE_Y = 600; + +int burst = 32, count = 0; + +void myDelay(int time) { + try { + Thread.sleep(time); + } catch (InterruptedException e) { } +} + +void setup() +{ + size(VIEW_SIZE_X, VIEW_SIZE_Y, P3D); + myPort = new Serial(this, "/dev/ttyUSB9", 115200); + + // The font must be located in the sketch's "data" directory to load successfully + font = loadFont("CourierNew36.vlw"); + + println("Waiting IMU.."); + + myPort.clear(); + + while (myPort.available() == 0) { + myPort.write("v"); + myDelay(1000); + } + println(myPort.readStringUntil('\n')); + myPort.write("q" + char(burst)); + myPort.bufferUntil('\n'); +} + + +float decodeFloat(String inString) { + byte [] inData = new byte[4]; + + if(inString.length() == 8) { + inData[0] = (byte) unhex(inString.substring(0, 2)); + inData[1] = (byte) unhex(inString.substring(2, 4)); + inData[2] = (byte) unhex(inString.substring(4, 6)); + inData[3] = (byte) unhex(inString.substring(6, 8)); + } + + int intbits = (inData[3] << 24) | ((inData[2] & 0xff) << 16) | ((inData[1] & 0xff) << 8) | (inData[0] & 0xff); + return Float.intBitsToFloat(intbits); +} + + +void serialEvent(Serial p) { + if(p.available() >= 18) { + String inputString = p.readStringUntil('\n'); + //print(inputString); + if (inputString != null && inputString.length() > 0) { + String [] inputStringArr = split(inputString, ","); + if(inputStringArr.length >= 5) { // q1,q2,q3,q4,\r\n so we have 5 elements + q[0] = decodeFloat(inputStringArr[0]); + q[1] = decodeFloat(inputStringArr[1]); + q[2] = decodeFloat(inputStringArr[2]); + q[3] = decodeFloat(inputStringArr[3]); + } + } + count = count + 1; + if(burst == count) { // ask more data when burst completed + p.write("q" + char(burst)); + count = 0; + } + } +} + + + +void buildBoxShape() { + //box(60, 10, 40); + noStroke(); + beginShape(QUADS); + + //Z+ (to the drawing area) + fill(#00ff00); + vertex(-30, -5, 20); + vertex(30, -5, 20); + vertex(30, 5, 20); + vertex(-30, 5, 20); + + //Z- + fill(#0000ff); + vertex(-30, -5, -20); + vertex(30, -5, -20); + vertex(30, 5, -20); + vertex(-30, 5, -20); + + //X- + fill(#ff0000); + vertex(-30, -5, -20); + vertex(-30, -5, 20); + vertex(-30, 5, 20); + vertex(-30, 5, -20); + + //X+ + fill(#ffff00); + vertex(30, -5, -20); + vertex(30, -5, 20); + vertex(30, 5, 20); + vertex(30, 5, -20); + + //Y- + fill(#ff00ff); + vertex(-30, -5, -20); + vertex(30, -5, -20); + vertex(30, -5, 20); + vertex(-30, -5, 20); + + //Y+ + fill(#00ffff); + vertex(-30, 5, -20); + vertex(30, 5, -20); + vertex(30, 5, 20); + vertex(-30, 5, 20); + + endShape(); +} + + +void drawcompass(float heading, int circlex, int circley, int circlediameter) { + noStroke(); + ellipse(circlex, circley, circlediameter, circlediameter); + fill(#ff0000); + ellipse(circlex, circley, circlediameter/20, circlediameter/20); + stroke(#ff0000); + strokeWeight(4); + line(circlex, circley, circlex - circlediameter/2 * sin(-heading), circley - circlediameter/2 * cos(-heading)); + noStroke(); + fill(#ffffff); + textAlign(CENTER, BOTTOM); + text("N", circlex, circley - circlediameter/2 - 10); + textAlign(CENTER, TOP); + text("S", circlex, circley + circlediameter/2 + 10); + textAlign(RIGHT, CENTER); + text("W", circlex - circlediameter/2 - 10, circley); + textAlign(LEFT, CENTER); + text("E", circlex + circlediameter/2 + 10, circley); +} + + +void drawAngle(float angle, int circlex, int circley, int circlediameter, String title) { + angle = angle + PI/2; + + noStroke(); + ellipse(circlex, circley, circlediameter, circlediameter); + fill(#ff0000); + strokeWeight(4); + stroke(#ff0000); + line(circlex - circlediameter/2 * sin(angle), circley - circlediameter/2 * cos(angle), circlex + circlediameter/2 * sin(angle), circley + circlediameter/2 * cos(angle)); + noStroke(); + fill(#ffffff); + textAlign(CENTER, BOTTOM); + text(title, circlex, circley - circlediameter/2 - 30); +} + +void draw() { + + quaternionToYawPitchRoll(q, Euler); + + background(#000000); + fill(#ffffff); + + textFont(font, 20); + //float temp_decoded = 35.0 + ((float) (temp + 13200)) / 280; + //text("temp:\n" + temp_decoded + " C", 350, 250); + textAlign(LEFT, TOP); + text("Q:\n" + q[0] + "\n" + q[1] + "\n" + q[2] + "\n" + q[3], 20, 10); + text("Euler Angles:\nYaw (psi) : " + degrees(Euler[0]) + "\nPitch (theta): " + degrees(Euler[1]) + "\nRoll (phi) : " + degrees(Euler[2]), 200, 10); + + drawcompass(Euler[0], VIEW_SIZE_X/2 - 250, VIEW_SIZE_Y/2, 200); + drawAngle(Euler[1], VIEW_SIZE_X/2, VIEW_SIZE_Y/2, 200, "Pitch:"); + drawAngle(Euler[2], VIEW_SIZE_X/2 + 250, VIEW_SIZE_Y/2, 200, "Roll:"); +} + + +void quaternionToYawPitchRoll(float [] q, float [] ypr) { + float gx, gy, gz; // estimated gravity direction + + gx = 2 * (q[1]*q[3] - q[0]*q[2]); + gy = 2 * (q[0]*q[1] + q[2]*q[3]); + gz = q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3]; + + ypr[0] = atan2(2 * q[1] * q[2] - 2 * q[0] * q[3], 2 * q[0]*q[0] + 2 * q[1] * q[1] - 1); + ypr[1] = atan2(gx, sqrt(gy*gy + gz*gz)); + ypr[2] = atan2(gy, sqrt(gx*gx + gz*gz)); +} + + diff --git a/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_yaw_pitch_roll/LICENSE.txt b/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_yaw_pitch_roll/LICENSE.txt new file mode 100644 index 0000000000..94a9ed024d --- /dev/null +++ b/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_yaw_pitch_roll/LICENSE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_yaw_pitch_roll/data/CourierNew36.vlw b/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_yaw_pitch_roll/data/CourierNew36.vlw new file mode 100644 index 0000000000000000000000000000000000000000..904771486a1a6d241fb5184282eb99780f730615 GIT binary patch literal 114920 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%0AM;pp>Tg3o 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 attitude_estimator_so3_comp start -d /dev/ttyS1 -b 115200 + +This visualization code can be executed by Processing. + Download : http://www.processing.org/download/ + +The visualization code works with Processing version 1.5.1. From 90fdf35ae549b4a5ef3b0a8f47a9c23d76e9e485 Mon Sep 17 00:00:00 2001 From: "Hyon Lim (Retina)" Date: Wed, 29 May 2013 00:59:20 +1000 Subject: [PATCH 11/26] GPL Licensed code has been removed --- .../attitude_estimator_so3_comp/README | 2 +- .../FreeIMU_cube/FreeIMU_cube.pde | 265 ------- .../FreeIMU_cube/LICENSE.txt | 674 ------------------ .../FreeIMU_cube/data/CourierNew36.vlw | Bin 114920 -> 0 bytes .../FreeIMU_yaw_pitch_roll.pde | 229 ------ .../FreeIMU_yaw_pitch_roll/LICENSE.txt | 674 ------------------ .../data/CourierNew36.vlw | Bin 114920 -> 0 bytes .../visualization_code/README | 9 - 8 files changed, 1 insertion(+), 1852 deletions(-) delete mode 100644 src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/FreeIMU_cube.pde delete mode 100644 src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/LICENSE.txt delete mode 100644 src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/data/CourierNew36.vlw delete mode 100644 src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_yaw_pitch_roll/FreeIMU_yaw_pitch_roll.pde delete mode 100644 src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_yaw_pitch_roll/LICENSE.txt delete mode 100644 src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_yaw_pitch_roll/data/CourierNew36.vlw delete mode 100644 src/modules/attitude_estimator_so3_comp/visualization_code/README diff --git a/src/modules/attitude_estimator_so3_comp/README b/src/modules/attitude_estimator_so3_comp/README index 26b270d37c..79c50a5318 100644 --- a/src/modules/attitude_estimator_so3_comp/README +++ b/src/modules/attitude_estimator_so3_comp/README @@ -2,4 +2,4 @@ Synopsis nsh> attitude_estimator_so3_comp start -d /dev/ttyS1 -b 115200 -Option -d is for debugging packet. See visualization_code folder. +Option -d is for debugging packet. See code for detailed packet structure. diff --git a/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/FreeIMU_cube.pde b/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/FreeIMU_cube.pde deleted file mode 100644 index 3706437d33..0000000000 --- a/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/FreeIMU_cube.pde +++ /dev/null @@ -1,265 +0,0 @@ -/** -Visualize a cube which will assumes the orientation described -in a quaternion coming from the serial port. - -INSTRUCTIONS: -This program has to be run when you have the FreeIMU_serial -program running on your Arduino and the Arduino connected to your PC. -Remember to set the serialPort variable below to point to the name the -Arduino serial port has in your system. You can get the port using the -Arduino IDE from Tools->Serial Port: the selected entry is what you have -to use as serialPort variable. - - -Copyright (C) 2011-2012 Fabio Varesano - http://www.varesano.net/ - -This program is free software: you can redistribute it and/or modify -it under the terms of the version 3 GNU General Public License as -published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -*/ - -import processing.serial.*; -import processing.opengl.*; - -Serial myPort; // Create object from Serial class - -final String serialPort = "/dev/ttyUSB9"; // replace this with your serial port. On windows you will need something like "COM1". - -float [] q = new float [4]; -float [] hq = null; -float [] Euler = new float [3]; // psi, theta, phi - -int lf = 10; // 10 is '\n' in ASCII -byte[] inBuffer = new byte[22]; // this is the number of chars on each line from the Arduino (including /r/n) - -PFont font; -final int VIEW_SIZE_X = 800, VIEW_SIZE_Y = 600; - -final int burst = 32; -int count = 0; - -void myDelay(int time) { - try { - Thread.sleep(time); - } catch (InterruptedException e) { } -} - -void setup() -{ - size(VIEW_SIZE_X, VIEW_SIZE_Y, OPENGL); - myPort = new Serial(this, serialPort, 115200); - - // The font must be located in the sketch's "data" directory to load successfully - font = loadFont("CourierNew36.vlw"); - - println("Waiting IMU.."); - - myPort.clear(); - - while (myPort.available() == 0) { - myPort.write("v"); - myDelay(1000); - } - println(myPort.readStringUntil('\n')); - myPort.write("q" + char(burst)); - myPort.bufferUntil('\n'); -} - - -float decodeFloat(String inString) { - byte [] inData = new byte[4]; - - if(inString.length() == 8) { - inData[0] = (byte) unhex(inString.substring(0, 2)); - inData[1] = (byte) unhex(inString.substring(2, 4)); - inData[2] = (byte) unhex(inString.substring(4, 6)); - inData[3] = (byte) unhex(inString.substring(6, 8)); - } - - int intbits = (inData[3] << 24) | ((inData[2] & 0xff) << 16) | ((inData[1] & 0xff) << 8) | (inData[0] & 0xff); - return Float.intBitsToFloat(intbits); -} - -void serialEvent(Serial p) { - if(p.available() >= 18) { - String inputString = p.readStringUntil('\n'); - //print(inputString); - if (inputString != null && inputString.length() > 0) { - String [] inputStringArr = split(inputString, ","); - if(inputStringArr.length >= 5) { // q1,q2,q3,q4,\r\n so we have 5 elements - q[0] = decodeFloat(inputStringArr[0]); - q[1] = decodeFloat(inputStringArr[1]); - q[2] = decodeFloat(inputStringArr[2]); - q[3] = decodeFloat(inputStringArr[3]); - } - } - count = count + 1; - if(burst == count) { // ask more data when burst completed - p.write("q" + char(burst)); - count = 0; - } - } -} - - - -void buildBoxShape() { - //box(60, 10, 40); - noStroke(); - beginShape(QUADS); - - //Z+ (to the drawing area) - fill(#00ff00); - vertex(-30, -5, 20); - vertex(30, -5, 20); - vertex(30, 5, 20); - vertex(-30, 5, 20); - - //Z- - fill(#0000ff); - vertex(-30, -5, -20); - vertex(30, -5, -20); - vertex(30, 5, -20); - vertex(-30, 5, -20); - - //X- - fill(#ff0000); - vertex(-30, -5, -20); - vertex(-30, -5, 20); - vertex(-30, 5, 20); - vertex(-30, 5, -20); - - //X+ - fill(#ffff00); - vertex(30, -5, -20); - vertex(30, -5, 20); - vertex(30, 5, 20); - vertex(30, 5, -20); - - //Y- - fill(#ff00ff); - vertex(-30, -5, -20); - vertex(30, -5, -20); - vertex(30, -5, 20); - vertex(-30, -5, 20); - - //Y+ - fill(#00ffff); - vertex(-30, 5, -20); - vertex(30, 5, -20); - vertex(30, 5, 20); - vertex(-30, 5, 20); - - endShape(); -} - - -void drawCube() { - pushMatrix(); - translate(VIEW_SIZE_X/2, VIEW_SIZE_Y/2 + 50, 0); - scale(5,5,5); - - // a demonstration of the following is at - // http://www.varesano.net/blog/fabio/ahrs-sensor-fusion-orientation-filter-3d-graphical-rotating-cube - rotateZ(-Euler[2]); - rotateX(-Euler[1]); - rotateY(-Euler[0]); - - buildBoxShape(); - - popMatrix(); -} - - -void draw() { - background(#000000); - fill(#ffffff); - - if(hq != null) { // use home quaternion - quaternionToEuler(quatProd(hq, q), Euler); - text("Disable home position by pressing \"n\"", 20, VIEW_SIZE_Y - 30); - } - else { - quaternionToEuler(q, Euler); - text("Point FreeIMU's X axis to your monitor then press \"h\"", 20, VIEW_SIZE_Y - 30); - } - - textFont(font, 20); - textAlign(LEFT, TOP); - text("Q:\n" + q[0] + "\n" + q[1] + "\n" + q[2] + "\n" + q[3], 20, 20); - text("Euler Angles:\nYaw (psi) : " + degrees(Euler[0]) + "\nPitch (theta): " + degrees(Euler[1]) + "\nRoll (phi) : " + degrees(Euler[2]), 200, 20); - - drawCube(); - //myPort.write("q" + 1); -} - - -void keyPressed() { - if(key == 'h') { - println("pressed h"); - - // set hq the home quaternion as the quatnion conjugate coming from the sensor fusion - hq = quatConjugate(q); - - } - else if(key == 'n') { - println("pressed n"); - hq = null; - } -} - -// See Sebastian O.H. Madwick report -// "An efficient orientation filter for inertial and intertial/magnetic sensor arrays" Chapter 2 Quaternion representation - -void quaternionToEuler(float [] q, float [] euler) { - euler[0] = atan2(2 * q[1] * q[2] - 2 * q[0] * q[3], 2 * q[0]*q[0] + 2 * q[1] * q[1] - 1); // psi - euler[1] = -asin(2 * q[1] * q[3] + 2 * q[0] * q[2]); // theta - euler[2] = atan2(2 * q[2] * q[3] - 2 * q[0] * q[1], 2 * q[0] * q[0] + 2 * q[3] * q[3] - 1); // phi -} - -float [] quatProd(float [] a, float [] b) { - float [] q = new float[4]; - - q[0] = a[0] * b[0] - a[1] * b[1] - a[2] * b[2] - a[3] * b[3]; - q[1] = a[0] * b[1] + a[1] * b[0] + a[2] * b[3] - a[3] * b[2]; - q[2] = a[0] * b[2] - a[1] * b[3] + a[2] * b[0] + a[3] * b[1]; - q[3] = a[0] * b[3] + a[1] * b[2] - a[2] * b[1] + a[3] * b[0]; - - return q; -} - -// returns a quaternion from an axis angle representation -float [] quatAxisAngle(float [] axis, float angle) { - float [] q = new float[4]; - - float halfAngle = angle / 2.0; - float sinHalfAngle = sin(halfAngle); - q[0] = cos(halfAngle); - q[1] = -axis[0] * sinHalfAngle; - q[2] = -axis[1] * sinHalfAngle; - q[3] = -axis[2] * sinHalfAngle; - - return q; -} - -// return the quaternion conjugate of quat -float [] quatConjugate(float [] quat) { - float [] conj = new float[4]; - - conj[0] = quat[0]; - conj[1] = -quat[1]; - conj[2] = -quat[2]; - conj[3] = -quat[3]; - - return conj; -} - diff --git a/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/LICENSE.txt b/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/LICENSE.txt deleted file mode 100644 index 94a9ed024d..0000000000 --- a/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/LICENSE.txt +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/data/CourierNew36.vlw b/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_cube/data/CourierNew36.vlw deleted file mode 100644 index 904771486a1a6d241fb5184282eb99780f730615..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114920 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%0AM;pp>Tg3o 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 Port: the selected entry is what you have -to use as serialPort variable. - -Copyright (C) 2011-2012 Fabio Varesano - http://www.varesano.net/ - -This program is free software: you can redistribute it and/or modify -it under the terms of the version 3 GNU General Public License as -published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -*/ - -import processing.serial.*; - -Serial myPort; // Create object from Serial class - - -float [] q = new float [4]; -float [] Euler = new float [3]; // psi, theta, phi - -int lf = 10; // 10 is '\n' in ASCII -byte[] inBuffer = new byte[22]; // this is the number of chars on each line from the Arduino (including /r/n) - -PFont font; -final int VIEW_SIZE_X = 800, VIEW_SIZE_Y = 600; - -int burst = 32, count = 0; - -void myDelay(int time) { - try { - Thread.sleep(time); - } catch (InterruptedException e) { } -} - -void setup() -{ - size(VIEW_SIZE_X, VIEW_SIZE_Y, P3D); - myPort = new Serial(this, "/dev/ttyUSB9", 115200); - - // The font must be located in the sketch's "data" directory to load successfully - font = loadFont("CourierNew36.vlw"); - - println("Waiting IMU.."); - - myPort.clear(); - - while (myPort.available() == 0) { - myPort.write("v"); - myDelay(1000); - } - println(myPort.readStringUntil('\n')); - myPort.write("q" + char(burst)); - myPort.bufferUntil('\n'); -} - - -float decodeFloat(String inString) { - byte [] inData = new byte[4]; - - if(inString.length() == 8) { - inData[0] = (byte) unhex(inString.substring(0, 2)); - inData[1] = (byte) unhex(inString.substring(2, 4)); - inData[2] = (byte) unhex(inString.substring(4, 6)); - inData[3] = (byte) unhex(inString.substring(6, 8)); - } - - int intbits = (inData[3] << 24) | ((inData[2] & 0xff) << 16) | ((inData[1] & 0xff) << 8) | (inData[0] & 0xff); - return Float.intBitsToFloat(intbits); -} - - -void serialEvent(Serial p) { - if(p.available() >= 18) { - String inputString = p.readStringUntil('\n'); - //print(inputString); - if (inputString != null && inputString.length() > 0) { - String [] inputStringArr = split(inputString, ","); - if(inputStringArr.length >= 5) { // q1,q2,q3,q4,\r\n so we have 5 elements - q[0] = decodeFloat(inputStringArr[0]); - q[1] = decodeFloat(inputStringArr[1]); - q[2] = decodeFloat(inputStringArr[2]); - q[3] = decodeFloat(inputStringArr[3]); - } - } - count = count + 1; - if(burst == count) { // ask more data when burst completed - p.write("q" + char(burst)); - count = 0; - } - } -} - - - -void buildBoxShape() { - //box(60, 10, 40); - noStroke(); - beginShape(QUADS); - - //Z+ (to the drawing area) - fill(#00ff00); - vertex(-30, -5, 20); - vertex(30, -5, 20); - vertex(30, 5, 20); - vertex(-30, 5, 20); - - //Z- - fill(#0000ff); - vertex(-30, -5, -20); - vertex(30, -5, -20); - vertex(30, 5, -20); - vertex(-30, 5, -20); - - //X- - fill(#ff0000); - vertex(-30, -5, -20); - vertex(-30, -5, 20); - vertex(-30, 5, 20); - vertex(-30, 5, -20); - - //X+ - fill(#ffff00); - vertex(30, -5, -20); - vertex(30, -5, 20); - vertex(30, 5, 20); - vertex(30, 5, -20); - - //Y- - fill(#ff00ff); - vertex(-30, -5, -20); - vertex(30, -5, -20); - vertex(30, -5, 20); - vertex(-30, -5, 20); - - //Y+ - fill(#00ffff); - vertex(-30, 5, -20); - vertex(30, 5, -20); - vertex(30, 5, 20); - vertex(-30, 5, 20); - - endShape(); -} - - -void drawcompass(float heading, int circlex, int circley, int circlediameter) { - noStroke(); - ellipse(circlex, circley, circlediameter, circlediameter); - fill(#ff0000); - ellipse(circlex, circley, circlediameter/20, circlediameter/20); - stroke(#ff0000); - strokeWeight(4); - line(circlex, circley, circlex - circlediameter/2 * sin(-heading), circley - circlediameter/2 * cos(-heading)); - noStroke(); - fill(#ffffff); - textAlign(CENTER, BOTTOM); - text("N", circlex, circley - circlediameter/2 - 10); - textAlign(CENTER, TOP); - text("S", circlex, circley + circlediameter/2 + 10); - textAlign(RIGHT, CENTER); - text("W", circlex - circlediameter/2 - 10, circley); - textAlign(LEFT, CENTER); - text("E", circlex + circlediameter/2 + 10, circley); -} - - -void drawAngle(float angle, int circlex, int circley, int circlediameter, String title) { - angle = angle + PI/2; - - noStroke(); - ellipse(circlex, circley, circlediameter, circlediameter); - fill(#ff0000); - strokeWeight(4); - stroke(#ff0000); - line(circlex - circlediameter/2 * sin(angle), circley - circlediameter/2 * cos(angle), circlex + circlediameter/2 * sin(angle), circley + circlediameter/2 * cos(angle)); - noStroke(); - fill(#ffffff); - textAlign(CENTER, BOTTOM); - text(title, circlex, circley - circlediameter/2 - 30); -} - -void draw() { - - quaternionToYawPitchRoll(q, Euler); - - background(#000000); - fill(#ffffff); - - textFont(font, 20); - //float temp_decoded = 35.0 + ((float) (temp + 13200)) / 280; - //text("temp:\n" + temp_decoded + " C", 350, 250); - textAlign(LEFT, TOP); - text("Q:\n" + q[0] + "\n" + q[1] + "\n" + q[2] + "\n" + q[3], 20, 10); - text("Euler Angles:\nYaw (psi) : " + degrees(Euler[0]) + "\nPitch (theta): " + degrees(Euler[1]) + "\nRoll (phi) : " + degrees(Euler[2]), 200, 10); - - drawcompass(Euler[0], VIEW_SIZE_X/2 - 250, VIEW_SIZE_Y/2, 200); - drawAngle(Euler[1], VIEW_SIZE_X/2, VIEW_SIZE_Y/2, 200, "Pitch:"); - drawAngle(Euler[2], VIEW_SIZE_X/2 + 250, VIEW_SIZE_Y/2, 200, "Roll:"); -} - - -void quaternionToYawPitchRoll(float [] q, float [] ypr) { - float gx, gy, gz; // estimated gravity direction - - gx = 2 * (q[1]*q[3] - q[0]*q[2]); - gy = 2 * (q[0]*q[1] + q[2]*q[3]); - gz = q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3]; - - ypr[0] = atan2(2 * q[1] * q[2] - 2 * q[0] * q[3], 2 * q[0]*q[0] + 2 * q[1] * q[1] - 1); - ypr[1] = atan2(gx, sqrt(gy*gy + gz*gz)); - ypr[2] = atan2(gy, sqrt(gx*gx + gz*gz)); -} - - diff --git a/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_yaw_pitch_roll/LICENSE.txt b/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_yaw_pitch_roll/LICENSE.txt deleted file mode 100644 index 94a9ed024d..0000000000 --- a/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_yaw_pitch_roll/LICENSE.txt +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_yaw_pitch_roll/data/CourierNew36.vlw b/src/modules/attitude_estimator_so3_comp/visualization_code/FreeIMU_yaw_pitch_roll/data/CourierNew36.vlw deleted file mode 100644 index 904771486a1a6d241fb5184282eb99780f730615..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114920 zcmeFa4{TLS7caE#d9_wvMG-6Vu#fK{uVSr;A|fL4P(-{UB8rHJh$tdrtrf*9VnZ4d zLI@#*G$Eux#9GCQNDzsHV69kDthLr!t4*!7)?>BS<8iy)Z|gF@HM3@B?{nJAzk4ry zUw=t^_MX{$_WWJ5X3d(lh<;c{M0G^8nTY-r5&Z>3^k)*$pRm`p_7~xJ4%kFQf0T&+ zEF0HAM1O{j`-_QaJrS+3ak%~?HV)rt%*7RK+@G}1>wEZ~#x)VqpC7!3YfbYnv3bEg z-a%e8pIeA%gS|%_{#M!V{iRjnYHVCH5&gMD^yk=j@Sc9}FSGg3eExYsK3i;F@O`|4 zIze3mf4NNq=_0N^NOP-w7SHQ@T86*E)|tK+*5@|+yvBw3{3~r-wKDuwHcrcc_q49J z+c=a9;bGigZSTW0^}QW7u3DOZjg1TQsdc@xN}Sg9U%O1)t}5TtH2*r=ep;XSuGZ&n z+n2QOA`X2V`0H)|K_5c7@vGQl`@6=W4itaGGVkrRaaw11U(1K__7~c|pmnWr`|Nv& zm%K1D+YkPY%Y5%|vTXp9OK4-Qv(xrk z%FfI&vn%^QXveEyF)#^R9J`awyI&^WHyPUa;-(XWM7OxPN?^G%qfb=09P}r{fm+ zRJ2wpyVlD;Y2&o)VfosslwI*pRf*Fy+n4#?KW*di9h4itTGy9snDz<8YnuOzeKu?d zt?Pf*#<{WwwEVJ-({U8m%Rg5oPSgC!makfx|Ga&#S{bg`aRfdH`NyA*qkqB1p}ZQW zWAo}V@BNE*e$_Tw9n{OeWal5`8)+V7=Ksz%(j!|2d+qo2d&rNr!@p+xhw>AMQ(pLAx8Fk;{*X__ z6U!@t4@4Z=QQ!MFh@=j~`qc0B+4r=+{L!E<{hKx~+U_Bb`E;4Q{9E=togX#?X+E<& zv-TIXiRSa)wrN(Q^Dma6t^ZDyv8m;IY3o|cfV$E4`*(AEGWwbJmsgf{D=kNQO3VMf zW$OBMmAcmNy|ww&zJxp}E%^6|=#N|e4SD?IL7nw46Zao16E|S{pzs#J#ih zTHk97^70?qHqy9|w;8l)YQILG(suZdZ9cURqO2PCewn!cWSO}C)P7IX1ka&qez5Op zUE_Po5B_J%#0@PI_n+JEX`1++rukpkIJ6!9!nz)@X(}xb%l=<36F0g{+<#@ir)h>| zA6q8wzqb7oeI5D4Skpc@p5rk=L$p5sn`QDcu}s{5yG$JDHu7Dq43qXfrAw$Y#gxsv zwmZt9`TXy5v=uZ+$KUiaasR_IaWnRN+OP3FP4j|CVdD4fIR za%qB2=zITjm3(U1i_66QFU!Qu=e`%#=l^Qo(>@>0oB!J~aSO}D{qOdBnkK%deenON zk{2y|$)>689=7fOxlCMnnYjPUeoxa3+csJz?*Ar|cGbBGZLIAO+c-@V&uAI`ADb8L z^C*M9mn;+arG+1`kkG35WYU3O{YXXKcIwfIQ+}%ZG1h z9Nt0Rv@hXW<3Ky}<yVC?1=n_r{Qy}P($VkXTFVDn7w+{vTJc?kNNzK3?#ILJfryuKIqGsr`h`X1yVOMMUWkc~nA4Eq`6 zA&ApPpfyF*@pw$nIVYaHYS_@2h$TH_!u zK>0Kd*BXa)IO-GSLjG}W@dMW3`8v~nhIRN-b%u2~@~v%zYkd#vaO7Lt2-jM6tizZ3 z9@gPYeeZ6rPoNz3dj+f!K+kn8itpJqUIA-_rQ*PU;yHZ}ZKUsk|HL)^@Gbn=wORrE zXBdY(+qGH&{3oteCV)23IPjmS2mKz}L%#?96YpSO3f~IK@JDnVu5F9=Y}@`3tm9D6 z+6QrM>*aR{!}l}}Y1{XHhqMrf@8EmEI{bGC!?P$a;_y8yNBJGfnvb((_#NhsFiz_T zEWux76`94txgkQ)LeL9rpj0iqri+q>J+5yIKb9 z)gfP+Ca$&4biWFD$9wp*_#Ngr%<(E`!26me=D2*E?GwMlyn=k_IKr>CA7mx?R@m+q ze*$GdKT=+5otF!5!u!aZwmYtE88)Gh<TF=u4(=;&tq-^O-7vN z1^Kpl`IA`pBToAuu5JJP(|88o&@^#v@u#(}HBA>+-yN5`>L23sk07+?IKlsD4*w@! z;{Wp}`2Y9;{&%(E|LNoSfAkRkA85w^^|kn4poF6V}|tdF+aM^cZldV-#N|Lqd3i{jxZRLBYb2(qj&D- z{q>CIuje%du>OSqZ{+KcFcDE}jOpNF+-Yu!7Lf&-V|g5JNS@4pK|S*VQpA73Zsq~9 z{{@GbpOXDAIKjxW`7b!n$h!GY)LKkZ=BQLkP}OtiQ4b*!x+qnzn1i&R-ZAf%|JcU` z7MbhxzaWu9nE!NfE=60GWBgAR+bchEelFE7Fmnl_dOE`Vq=5er!~*9e=VzI3+kgj* z$~XT7PnZvp{Vy0`ZYFxe45a{|gkE9)3u4ClF=j{s1V1HU7}^qShd5kLAh8c%`bYq` zCKGJyMy(Ye*!rpM_N7|{?>Q4d-W>QFR}f!LMTYBodJ5nosI|7X6d4 ziwEqSnLmo|$^7CZv@I-lnh%tpUOQlEz&ZUSmu`a;5bb*Bf)=o8O+;JTo=lm*63CN5 z?ZGP_nE8R;3p$yLXQB@vUk(tN!kwP!?eI!1A{Xi`hnl6qrmt@iE z5gnv?f5$wso#=dgv#GsrfthC?V_#a4t#zW02&ibe%W=@Qhx? z=WA-tMXb-fW$7sA7~)?bW|?Sfq+eAHu&y&LQTzu*daS`*P8X2yoVRc5|ML{C{rFHJKuC<{6!N`-vl zSMN1mpU3#Z;s&*amMviDgPXj`%(t*wO3e2Om5MocUCHE+ezF5W^G_Q`OJ~oPN3re% zbsWJse8bEgAZjUdj1?TxMT@LKF;VddxQXQ|2sU0tJAf3TXn6HUmN`}A1&}cpz>nCn zVZPS;wV+PH^Qccu?E}ob&!j(Ja0Od~Vr(?GT_%6g29saZN5njej(xT??1?a4h_MKE zn8noLA@hVsjyAg6Q_;w^Epof1W2AISIv2Y2Fxsj#?s^t5(e%_a1nJYwM5|AuA2au# z1#fPEB8`gDb_DgY898`H)DA9r zBY^;-zphD{r|O7KG0S}Nj2XMKH3=u+4UU*jN!>gvlyZ*2j}Y}^3)T+UPeJogGPLDA zGk-u;C14VUS`cQEvuG)zQ+vF2v!h_EBkKk)nL}26S1x)`*iVNanXwP-Fv1MoO2r9g zY`ZH8lbi-j5?x@vuW^tj%yJS)dO&5|Iw0M`upQZ$r#9_vfR?T{G~Vm5=N zq@tS{6j;&33?YfbQ)UPl72DX18^s&ukQds=j-e2|XUCocN0^_R2!r=#zI2GBn|NF; zLLoMbZ4aiCWcq>D?se@bX415CJOG`Uz2HZ877E+3UfYIs6hAb{J;Zu!pXsJR-7NFo zZY3H-beXw(%*+FVX6CUtn7s?k7=|ViFd6xz`+R!ievyD_f3!Pjx>n|gdzoh}Z&5pm znjdoI>1G}wVYmBs6bd_E7=kYohy`Xc$JO~Bwe8VVnoPR-B-(uP5w=Z%=5xJZoo%6H z&FgO+5i-xV#*;l~wz-E5exGh-vG0(*vnvXRE-C)rhtqK~*LTWthfRYTKMq}I+7713T*wEN#^2e>R?`wNsFkyAA@{`8B%RQ6GmdpWt+J001>q&e&KV38T*S4 z8|Sj4vl;6=T|S}U7&#p>SyLB5HA?)nq!{z+%ug|neTc+ zRNu=yYD)t&jdH2oWuE9nnV&&)WAJzm>RK5H+Z!T3FG@cx9PS=0#YySqWwW@X3n-;Q zj8(NTl^z%s5D5&Q*;Js~ePEIg5uKYaIVSwNVG#n~!5Aq^)&rlFq_pk8?N5cQVn@wm_Y?83rg z*N@U{tM<2{&cQ%saFNbsSdsPS!V4hKxLZFwqz3$?ha$M>YDcl5eo05e0jj}rPC~h)M5qEB;^_R=cV+CYw)^JJN zO3deHm=|>!*UJ3*DKmCt1*@4$*O@)}WlnZkCLP#}b{XebrY)1VCm3a=pIQo3dl+2* z9*Cqpgq^+X?(GU{>z$z}jo#nF;@ZW1JR~mwc=h{k*bsFE^CJ2{%!?t5+h$hn4k4Rm zm_+D5I)pWYy~|YA%nluyW~9PQz$6l(S~7&cKg&RGVV(;JI^AKu?vf^2Rc7{GMr7of zb;VF-#zrqFg{e_n60FWwxArZ@@nT<_Stg&s;1n!^i?CuvgX_h`m!*MLgPKf-;p+}Z64)d#|!+Z3I zG$y{@TqrbmVshO3gn7~SKQKdun7Zf_u!*AU%40 zq>gCSVNi#CBHVF+l&9UzawSu#Yulcc(saJB)mFsXtAkNmdUj4*V4uzq6DFfObXI4M zKm=|=7mOS-J)r22&JEigc0fvK;0zsb}-qFD4a)&q<*>t*D;5E1k33d zZol@aS^5CGLFCpPV=mbYiBNkL#6jiD4Yc8!WH2RK=Y@HY=0XIb+XHi#j0BKqZ*MV) z`Y)}z;o$M&t--=B)JJg39@8ChXF+0w+dd&-EK@M_Cp# zPj19Vqi!4(Rn&K)q>nt^C!!0OWcmX}ooFA}z{yYyKwHo~N|)_^zF_U0lsUZ{N+xP9 zgXFxo*WBS6oSGfEK{;+Vh0;v=ljx)0yJU5E?o|113*eN zWu#64&Y|q@b(T}q3jyj4$uiK0wL;Q^I=k&2lMr{wHsumqP{jfLVa~QqHM7ZvTk8nGcesGvyuP>y^Jru z%nJ61*4{zZ4~XflkVJ8C3{UzEB29DQQkZ5#S4GFK zm3%(WRw(#NtxFE%yO-b!Zr`45E>P_OOp03rasET*N0Hm~$4*4?vS&6hDBMQlOHn6$w17B#b%iQ3uQ@mrt$9RXk{)xw$5FT@ZYRr!M+i9={ z^><5g96#@aT*Km$Q(quz<{96a-&p~~s}}@`_fM;7$4hBRTXrLU+W#twXM5bRx2ss3 zUCF>ym)Xb)Vb`^8za{{7q|BCB7M;v5vy8G}O^L^Aiaej6IXbb@?8q((%zoPh(YiD* z8j;6k{gNT(Ys*FyzCFXv2;*#4PJ^5{){ETZqUjQBH8=@1;On}S-yGgHW@|o;hP$DYFDM*&G!`YL7j5?FH^-SMdO5J4e)r){m4xpW1c7RVCBx*qEJ!ExLd(DsVwA(VKbtijJI5=UQX~G8@(*qbU>CRFa7$4BI)&j6=09to{&4OR>=rI>k(jJQbn`G|bm?viS14 z=`u3nQqxKTq@P9bo&~DkWy4*5Vu+P+nYX%>XuITs)NpMe4R4?Mm=Kqmfk`tIt%YAP zc?d(y_0NV>p;{I2b+0gQg;g)gn-!>hQ)DDzuCxh0FU-`3zFXN6n!2k)g$)VXCy?Ig zL%A?{r&`!jt)d9uQG8Tb&^o#k5oXSnBB2K{Odte&N(r$`Qx585o7^EoM*6VCIT`Vg0$Dp>mp(-`zhXlZP#bG`L>dmn3n$ zwxy5;_t(BYv^2Sjvk&|N&t+ggpTRO=h%)%)@~2<06{2;EV7=DtB22G+-MVQg!)BYU z(Gb3W0d_=YDm>RF-}$YpHbM5Ty8eZ$ar1_48FOc0!@|1E(zaZM3V!3TOQGb$=B4B% zGp?Bf;Y|>xMV7FN#ncv2BXW-eYd_eXK#0#f53MEIdUc+8MkF&@kAkAvZrplD_bHiQ zda2dUt50W>Wc98NRPU~Clgm@d0~Fw_9OwvKz6Fr$=IG8zOQwyMEE5KL3dWi+i0Fgt8S zjUyyNCRciHLI;oz9wUGXD432ud`n@HaP-boF@rEy-*=E@8N&kE>(2_(rdb}^Bmp3Z zg*tfv>;jAYCw1cz!0xD(|4bWmobdtOgKb=Y=nf_aBLo9%Q*i$t(uxK)t7so>a$eJMtevA z+ABQqq%=|)KYiRh=Hzuc2pyF~I&U3C%B125QZ@}e-WJeVJmBTxnqhg(?T}_P^i*w*J!p}S$I5IQMMCKNJJDNQz14(;4_?V_>u5ZJWoo%?M7 zQ9qJPfoN0Fm~WK&)>xulW&=AHDvCa)w+7G3`8o;J>bH#tvaJI|Un41vr;M&a>6Q5! z4rBGs#K}yrt9qi{*sT(b!PYFGMOiI_8cu+ZZQN^8KJRC{h%w2M4;GmmuV zbPzh3yZ778b&G}th?Pe9pPuQ*Ug)LwT7BApL`T!T$1z=NG8W^z-1u~egiCStvH|B_7qG?Pf`}R>jN~#1 z0=32|;V`I*SQ%P6xu>oeA$#!R2{zj%8q{zE8-b!9<+2G3=(tqG*9=c$?~~H2tGYR& zX$APaprBTD_~_>Wn|?6y`GEuMe>5G()4c(I*Mj3ff>E9Ra8yW<8f|M3gxrwX95D}A zy$KyMufwiphRoK=A+!HR<&e4mx^l=2m#PdIFLJP8PLe9m%~u&RPb-Ja!%LMz<^WFH z`Js;?GbZvD9L$cQrY&Wz>)&R{@)&{Z=JD`_G+0Vc8Bf7^!; z?Rv#Lvu|B}^Pa-C=W@_!2hOeqfL@MOWK(U56q^_w(ahs>`)aB7NY7$Cac6VTS<+~> z?}BB4gOq@>Vc>%Tpzdy!23)9|ztrM(d!ce$ncLWSv&Mq-WPL;rgf~T3*RgVvao&*dK z06{1^gI!|g|zQD(n_UiojsP8V#^thEh0o-r|)wP-VM|HT5&*G1XscS-4EJ10NXa zKDo;+!E|pteSg!|;sL)M^xW3q=F^V{4P|-fULYwTydafxYg&4r?QaTwD!{iG=)NE!(1nhr@4W2(UR1?%#mG%>?!G4#0St7YoWq6s0>c#=%sw?o}MV%pWUk6WYpm4JmRRagS&+PsT!F4Ue{ zHn`{xW1bIHN962)CGR*!Aba?N3(QkWUM`rOUO{0>Ia+Mj%=nwLnRHuOwMGgPVX`=p zYIR;tG0(V_Bvj5ci{nA{1GPnS+~WlET!!_>$x3jmV_r|$V%0qCi5z3AMHS!*`1X-N zzNM&NVD9GZfmZ{wwh!cL3MeV?qSq_)=*SA;Pc$G@3=zPW>H*iljjXjUE)t6xrQ2n3{kx z45UBMdE0LdApo)2!-AG1OgEef&cMBv?fGrYi%?m|-kh;9WYjDMMBVwYs1x+NT!@xb zj8lw#m}8rezT5L_Vdl;Ekl1$S`SyalZzR(!?$AT1)BEz85?mf7j~s( zPQ;0g0mocOK9sQ&d6inUF&ZJ#RWWEF8Bj(5_X)cNVKx^;{a($hwwOH)^1#z5x~jP_ zM8i%D_QrM&e994eLn>_6-OVkI9kZ})(H=2+&I|Ui&xSc=9qNZHOSxvuOM}+W@w+<0 za%D_;J)-Rqju<}UIlmK0bd>q_eJ(jhQE&-Da3@>0c%AREMEeuwQbxOuww8Ek3@4X7 z*3a=&!}=M=`9k72D!JHHAYgu^AG{*pQ^^Fy-sv6UK9 zYOUwYp!9R7_8cq8j;GCmO-;x)k{lJt&c^AiIWpptsu|&;D%v*|jb#v`mXzO{xutH1 zQ&m#xP~mD%urp8?HuAT2=3%ArM9ndz9Q%xL)8R7n!k+m&y?oDDGRes`k0x-XF-~NK zfZ)i@rVcupW$>#6YE>&`6(|AC}4X96S zEZW{{+153qybxAy;SDD$7Zp6tX~SI&njARcj`$nwP54bX0XHr1xE;B&&CoE#MIE-t zRNWw#-M~Y@Q==7T0G0PlOKUZK4tyg4m<;ieZ|o=7#4`F^UF}Bf<`C(E-eS-x+Zr4) zYGxjT;Xa*_r-o`N1cDd{okY_xt1bN`*far}Ql9W*-0a4AF3(u;n9Xoiw{nAhZJ8G` zJWfY&o)f5_=d#KY1nu6aouGD_lgycyb52fSP{^I51ZM#og&xDg4|p8N%*nhvfa?q13dnX^~790t#+YcRi93Ff?l1%1rSR6jhMaI znn<-4*B5!Fd6tVtR97>|@lM*A@SsqP`$n{T&M=mzwK*SxQQsEvdqY<0hB(?f6iikN zJQ52#XmE(*8zI~I^j5n&Sen5i42@Mq(mHQSo5OtTGqd|XWlWxJ8e@K1PgMVuc?^ug z6{8nT2WFqq>Z6^#{flv;@^xz%Okt%qx57L)!e~%?a5OmNYO&Fp;0ob2DSo^^P>PeN zh+}2)#Qq8WAb_CEo{I#*de_wDtpzHq+5_&}n4x;;sJICV^p=~Uer>kydv+9H_|TXR zu|~?Wct8jB+$oQX{ZYJI-}bVUMj!6)mu{~NjAYQmB;@Y_GhVV`e(YpmGMU9vVf(sA zqNMa(#7qE$Xj+b0=>(Nv7RDT?dyBxegSf8oA!V~5a$QVp`XoZ4P!orWe zc}gKpFl5KUfm*7o+!4VOILXA!I*W=c1Sr>KcF(W6-Nsn?-vfzRJ|TbUy*)Klvj<1R zUIg5W#rrk#5VJpb+qFvTzHp>xGEPfxuQ?M@g`+(aQ4&o(Io~uWwmaz{mLm=@!3It| zKq|)#c>MfEqBXLI!%Ph&=3bpPu&a@x7(;%h8HZZsK(G=cIy$#_5+`-_HAO}&W3ZS# zyAC`truh`>N9GP4U{3wvyT%E85;}{oPHZU<)gHJGYBEjr5P=6sek3Y%1V@JXy-_z> zeXe&nO4DfS>D54cA20+~zinVr%!YlnE?Ik;5Zw4UQdW@PKHQ6N@b10JKSxHeY*FN~3`*b|jcp?cT28D2ayd z`US0*q<=8-ZkQGFfN%qn?()suwN$hFCM+uMq{fJOST)bqe}KN!E#_&}f+AWo#e8Pe z9y_ww%luppwqyXK#5u$nk}pW^y4??lrZhM@VgY-RuTDUZw{Gw8TQ5q{PXx{_15lr1 zvJ;{ximb124qo-VOP4`whqhVXPF8fs6|%t>p5Ys~1Bfh|atHU@$2r8LGedbP>uD zr)qqdM}?s(nSe#cAL6dBBZERSB>VQvb!%YFTTN>Cp}gRBOk0b>gGTSJ+C}uQK52NM zGS+sCB=NL2FwM*-wHv*uwjun%a6RZbt8<1RDMQ4hpz%sO?qvHpdm8hbta3I#72|(L?}_Puh~rKt&)Q<(cFiA^II)}LH2Gb z-T6$Wm%jNq|F)vk?6h8O3CcKjYoUulBi!;U;A1Pt6)7q~+Y3DoWsgzwyMt7^>vEL1{|!pZA# zhT(F*s$2+8F%M*x^`c@Jcw{l$eln$6E`puRQ};MfIV%h>cQvKV&#ZvC{+aO1I0iD^ zwwY*iD-P~Ly-pk;Da=`qvrf=Br`mypHOQK~^E=O(e|0te#-;O=Z!u zUf@X^Z+?i9`M$F%ybL?O;Q4Z7rX+32ZNRDaqG4lI!8nO~TBzFxGgNG#jC<3+2Hu-OyR&Siw@Mxf<4 zMBp?tFVKPm3?iBhdp`~cnUfUi7sog#u!OzX4mm-m-Oa1rbL_&Seth`B*{EMR=DHiqr8!;l8{@1(XI{fZO`+w& z7~_ath~g$Elm=cm(wX?i&Ks{vXrV z<@?W>`x>dR^OD&(l(4OtacBqmVKuxod_by1p4@O}EJ?;5>@`&*Mh2IuvV)oai1ym)Eg9ukXPJlv5~#{;RhS`ARiJ4mcG~V6PgJej7?_ z81J^y{k#AE^n83r0C!TtNs_Vr%PLnP3;e6@SJBx zE4&v4&2?5ybAzZ^;Y8`U9K|%7yF=VdYlNm8qGx822;p{AxXXN(d7viI%Gtr(Zk3Qb zy*05p%#5eOoF@!FzycI&x*(d__E0d6Yg(lZtPPyZz9uoM&UFi+8SiJ}%?!;GoUlFv z2WU9^5!-b~+aj!vE`mm1FqdtPWI`d)5|D)G@~~WkGZo{4(Xi2CTt98Jj!rU9>SSfk zx*o>n@Qj(H`;S}mq=b6VfpDEVCCz zKgnEKzHIj>vmMma16vH+MO9@x+evY|YR(&5AfgdvJ;ipn;+*dSV_vDae%5P^9dd`t zurl+atbR5B-WZ2QEVruOevif*JTgn8?Q-hXqcN6De#j}E;ZygqR)uzc04!FwPiI!6 zf_BC+q>Q&H07nPBZnOuKt6_RB2dE)bFd9bFN0&qa!b2VbCb_6Y*g#2(>k$Upet-=E z-0~+_0ELf!Bs9gJ#Sb!{`N#r* zMBm+MjAM!_h#6QiUWH`9a$j-T}(N|67Rf}SI%S@i^j*^ zMvLbUm12sF;GE1v=DbFIn;*1(L-b3^S!BLu^eV(J zhd4FG=B}ZRGS7-Xl11iLL*{)QefoE`ZC2ScOy|V)094AQyz-^HxXH+tNelhwx>Tz)W&B?`KnMrgBG@*ym zsm26-&?yCIn@Gp63MFYPE*3M`J2SIK_>4DlETsf1*qj!`DdJl3Uv)sd zf*%n9$!VcU_h5yeAB%T|=dui&k{Jj`yO|SNdXGp)JV%#{MXiy-A!D{p@tEx?2a7m3 zjsqYujr-aAy30bcr|uXYT#Ti{aSp1~xqSP5Uw>%MG zm?WY$r?Fvtpey_n4wXUcD}5iFWV@#IcpoUMw?%D4n|h2VT7w0S;I1f(#UQH|H*(^D zWW0^8M1D2XnOsenFCOeIC8gd2ZOjo&;(FZo8=1y86AYm|+(a9}KJFG(IKvNiiLR$p zZ)}u8`W__Ju*htv=4Od~qDOch%DYgodAx}>VkX-oD=&wRoXPJSF?0K#-bG$~viA~$ z?S^w>gUu~#VqHC>VP?H8@fqyCRMOMYzvD6pANEt=iuu; zKt50o`dPTwzc5W#<+{=k=vOj1&??u}|FxxA2*VXzOuU!Zj-+ELe#5a4=G)DWbkkl5 z+K*m#WP{e17LMDN(x4mO9e@UnYXU^EB?c*+rW!o4M?2bPLfJEMxW zeTaGFv|UOZEt)0zp#kRUcEc?+sho1#gM{a=HBw>ACGV%YK@1;(;(6T!s)y3R#J!NH zW6=(0+CruO6-2!s(KZi=eZpLlr#+5xudZ0poM} zTGE-w=Mqjjq;qs6)>&A?#lE>XDfV3qxR=$p9EnVBsVIKO3?~ndt|4kT_B7>LBi!9p z;{L#@}o?l1wNSsY>?wU?YJv-jK27lf|n`QlYT-%w6)|I*G8XRHl zV!q!@H7Dn!*Q>^aQpkYKY~7ath;lgm=J5ZtiCNmz$R2JII(c)Sj**p zoE&mlaOW};Q6#&HbmHeYX&{9aHAbC1cdE!&o^@g!a7gIMJm&e^Q1 zsD}lx4ZEYD&NkaRBdOm z+F0X@4>OmK)84mn{Hz6g^2&j)eQge{t$k$=uWg++0tx-4lMzpWeb!%0AM;pp>Tg3o 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 attitude_estimator_so3_comp start -d /dev/ttyS1 -b 115200 - -This visualization code can be executed by Processing. - Download : http://www.processing.org/download/ - -The visualization code works with Processing version 1.5.1. From 032f7d0b0e0bfb0cb5b77ebb8553cf3dc7ddda9b Mon Sep 17 00:00:00 2001 From: Simon Wilks Date: Tue, 4 Jun 2013 23:24:30 +0200 Subject: [PATCH 12/26] Fix syncing issue with receiver on startup. --- src/drivers/hott_telemetry/hott_telemetry_main.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/drivers/hott_telemetry/hott_telemetry_main.c b/src/drivers/hott_telemetry/hott_telemetry_main.c index 4318244f81..1d2bdd92ee 100644 --- a/src/drivers/hott_telemetry/hott_telemetry_main.c +++ b/src/drivers/hott_telemetry/hott_telemetry_main.c @@ -133,15 +133,14 @@ recv_req_id(int uart, uint8_t *id) if (poll(fds, 1, timeout_ms) > 0) { /* Get the mode: binary or text */ read(uart, &mode, sizeof(mode)); - /* Read the device ID being polled */ - read(uart, id, sizeof(*id)); /* if we have a binary mode request */ if (mode != BINARY_MODE_REQUEST_ID) { - warnx("Non binary request ID detected: %d", mode); return ERROR; } + /* Read the device ID being polled */ + read(uart, id, sizeof(*id)); } else { warnx("UART timeout on TX/RX port"); return ERROR; @@ -216,9 +215,15 @@ hott_telemetry_thread_main(int argc, char *argv[]) uint8_t buffer[MESSAGE_BUFFER_SIZE]; size_t size = 0; uint8_t id = 0; + bool connected = true; while (!thread_should_exit) { if (recv_req_id(uart, &id) == OK) { + if (!connected) { + connected = true; + warnx("OK"); + } + switch (id) { case EAM_SENSOR_ID: build_eam_response(buffer, &size); @@ -234,7 +239,8 @@ hott_telemetry_thread_main(int argc, char *argv[]) send_data(uart, buffer, size); } else { - warnx("NOK"); + connected = false; + warnx("syncing"); } } From 68931f38d56c82c67d7d01e4db3157fac5815258 Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Wed, 5 Jun 2013 15:04:49 +0200 Subject: [PATCH 13/26] HOTFIX: Added start / stop syntax to GPIO led command --- src/modules/gpio_led/gpio_led.c | 72 ++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 23 deletions(-) diff --git a/src/modules/gpio_led/gpio_led.c b/src/modules/gpio_led/gpio_led.c index a80bf9cb83..43cbe74c74 100644 --- a/src/modules/gpio_led/gpio_led.c +++ b/src/modules/gpio_led/gpio_led.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,7 @@ struct gpio_led_s { }; static struct gpio_led_s gpio_led_data; +static bool gpio_led_started = false; __EXPORT int gpio_led_main(int argc, char *argv[]); @@ -75,31 +77,54 @@ int gpio_led_main(int argc, char *argv[]) { int pin = GPIO_EXT_1; - if (argc > 1) { - if (!strcmp(argv[1], "-p")) { - if (!strcmp(argv[2], "1")) { - pin = GPIO_EXT_1; + if (argc < 2) { + errx(1, "no argument provided. Try 'start' or 'stop' [-p 1/2]"); - } else if (!strcmp(argv[2], "2")) { - pin = GPIO_EXT_2; + } else { + + /* START COMMAND HANDLING */ + if (!strcmp(argv[1], "start")) { + + if (argc > 2) { + if (!strcmp(argv[1], "-p")) { + if (!strcmp(argv[2], "1")) { + pin = GPIO_EXT_1; + + } else if (!strcmp(argv[2], "2")) { + pin = GPIO_EXT_2; + + } else { + warnx("[gpio_led] Unsupported pin: %s\n", argv[2]); + exit(1); + } + } + } + + memset(&gpio_led_data, 0, sizeof(gpio_led_data)); + gpio_led_data.pin = pin; + int ret = work_queue(LPWORK, &gpio_led_data.work, gpio_led_start, &gpio_led_data, 0); + + if (ret != 0) { + warnx("[gpio_led] Failed to queue work: %d\n", ret); + exit(1); } else { - printf("[gpio_led] Unsupported pin: %s\n", argv[2]); - exit(1); + gpio_led_started = true; } + + exit(0); + + /* STOP COMMAND HANDLING */ + + } else if (!strcmp(argv[1], "stop")) { + gpio_led_started = false; + + /* INVALID COMMAND */ + + } else { + errx(1, "unrecognized command '%s', only supporting 'start' or 'stop'", argv[1]); } } - - memset(&gpio_led_data, 0, sizeof(gpio_led_data)); - gpio_led_data.pin = pin; - int ret = work_queue(LPWORK, &gpio_led_data.work, gpio_led_start, &gpio_led_data, 0); - - if (ret != 0) { - printf("[gpio_led] Failed to queue work: %d\n", ret); - exit(1); - } - - exit(0); } void gpio_led_start(FAR void *arg) @@ -110,7 +135,7 @@ void gpio_led_start(FAR void *arg) priv->gpio_fd = open(GPIO_DEVICE_PATH, 0); if (priv->gpio_fd < 0) { - printf("[gpio_led] GPIO: open fail\n"); + warnx("[gpio_led] GPIO: open fail\n"); return; } @@ -125,11 +150,11 @@ void gpio_led_start(FAR void *arg) int ret = work_queue(LPWORK, &priv->work, gpio_led_cycle, priv, 0); if (ret != 0) { - printf("[gpio_led] Failed to queue work: %d\n", ret); + warnx("[gpio_led] Failed to queue work: %d\n", ret); return; } - printf("[gpio_led] Started, using pin GPIO_EXT%i\n", priv->pin); + warnx("[gpio_led] Started, using pin GPIO_EXT%i\n", priv->pin); } void gpio_led_cycle(FAR void *arg) @@ -187,5 +212,6 @@ void gpio_led_cycle(FAR void *arg) priv->counter = 0; /* repeat cycle at 5 Hz*/ - work_queue(LPWORK, &priv->work, gpio_led_cycle, priv, USEC2TICK(200000)); + if (gpio_led_started) + work_queue(LPWORK, &priv->work, gpio_led_cycle, priv, USEC2TICK(200000)); } From 6537759dfc58117258610bb64d621da646d7d4ea Mon Sep 17 00:00:00 2001 From: "Hyon Lim (Retina)" Date: Thu, 6 Jun 2013 21:28:40 +1000 Subject: [PATCH 14/26] Add detailed documentation for SO3 gains tuning. USB nsh has been removed. --- nuttx/configs/px4fmu/nsh/defconfig | 6 +-- .../attitude_estimator_so3_comp_main.cpp | 48 +++++++++++++++---- .../attitude_estimator_so3_comp_params.c | 11 ++++- 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/nuttx/configs/px4fmu/nsh/defconfig b/nuttx/configs/px4fmu/nsh/defconfig index 94d99112e2..02e2243020 100755 --- a/nuttx/configs/px4fmu/nsh/defconfig +++ b/nuttx/configs/px4fmu/nsh/defconfig @@ -248,7 +248,7 @@ CONFIG_SERIAL_TERMIOS=y CONFIG_SERIAL_CONSOLE_REINIT=y CONFIG_STANDARD_SERIAL=y -CONFIG_USART1_SERIAL_CONSOLE=n +CONFIG_USART1_SERIAL_CONSOLE=y CONFIG_USART2_SERIAL_CONSOLE=n CONFIG_USART3_SERIAL_CONSOLE=n CONFIG_UART4_SERIAL_CONSOLE=n @@ -561,7 +561,7 @@ CONFIG_START_MONTH=1 CONFIG_START_DAY=1 CONFIG_GREGORIAN_TIME=n CONFIG_JULIAN_TIME=n -CONFIG_DEV_CONSOLE=n +CONFIG_DEV_CONSOLE=y CONFIG_DEV_LOWCONSOLE=n CONFIG_MUTEX_TYPES=n CONFIG_PRIORITY_INHERITANCE=y @@ -925,7 +925,7 @@ CONFIG_USBDEV_TRACE_NRECORDS=512 # Size of the serial receive/transmit buffers. Default 256. # CONFIG_CDCACM=y -CONFIG_CDCACM_CONSOLE=y +CONFIG_CDCACM_CONSOLE=n #CONFIG_CDCACM_EP0MAXPACKET CONFIG_CDCACM_EPINTIN=1 #CONFIG_CDCACM_EPINTIN_FSSIZE diff --git a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp index 9bb749cafb..3cbc62ea1d 100755 --- a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp +++ b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_main.cpp @@ -57,6 +57,7 @@ static bool thread_should_exit = false; /**< Deamon exit flag */ static bool thread_running = false; /**< Deamon status flag */ static int attitude_estimator_so3_comp_task; /**< Handle of deamon task / thread */ static float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; /** quaternion of sensor frame relative to auxiliary frame */ +static float dq0 = 0.0f, dq1 = 0.0f, dq2 = 0.0f, dq3 = 0.0f; /** quaternion of sensor frame relative to auxiliary frame */ static float gyro_bias[3] = {0.0f, 0.0f, 0.0f}; /** bias estimation */ static bool bFilterInit = false; @@ -170,7 +171,7 @@ float invSqrt(float number) { //! Using accelerometer, sense the gravity vector. //! Using magnetometer, sense yaw. -void MahonyAHRSinit(float ax, float ay, float az, float mx, float my, float mz) +void NonlinearSO3AHRSinit(float ax, float ay, float az, float mx, float my, float mz) { float initialRoll, initialPitch; float cosRoll, sinRoll, cosPitch, sinPitch; @@ -218,7 +219,7 @@ void MahonyAHRSinit(float ax, float ay, float az, float mx, float my, float mz) q3q3 = q3 * q3; } -void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, float twoKp, float twoKi, float dt) { +void NonlinearSO3AHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, float twoKp, float twoKi, float dt) { float recipNorm; float halfex = 0.0f, halfey = 0.0f, halfez = 0.0f; @@ -228,7 +229,7 @@ void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az //! unlikely happen. if(bFilterInit == false) { - MahonyAHRSinit(ax,ay,az,mx,my,mz); + NonlinearSO3AHRSinit(ax,ay,az,mx,my,mz); bFilterInit = true; } @@ -306,14 +307,25 @@ void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az gz += twoKp * halfez; } - // Integrate rate of change of quaternion + //! Integrate rate of change of quaternion +#if 0 gx *= (0.5f * dt); // pre-multiply common factors gy *= (0.5f * dt); gz *= (0.5f * dt); - q0 +=(-q1 * gx - q2 * gy - q3 * gz); - q1 += (q0 * gx + q2 * gz - q3 * gy); - q2 += (q0 * gy - q1 * gz + q3 * gx); - q3 += (q0 * gz + q1 * gy - q2 * gx); +#endif + + // Time derivative of quaternion. q_dot = 0.5*q\otimes omega. + //! q_k = q_{k-1} + dt*\dot{q} + //! \dot{q} = 0.5*q \otimes P(\omega) + dq0 = 0.5f*(-q1 * gx - q2 * gy - q3 * gz); + dq1 = 0.5f*(q0 * gx + q2 * gz - q3 * gy); + dq2 = 0.5f*(q0 * gy - q1 * gz + q3 * gx); + dq3 = 0.5f*(q0 * gz + q1 * gy - q2 * gx); + + q0 += dt*dq0; + q1 += dt*dq1; + q2 += dt*dq2; + q3 += dt*dq3; // Normalise quaternion recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); @@ -528,8 +540,11 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds struct sensor_combined_s raw; memset(&raw, 0, sizeof(raw)); + + //! Initialize attitude vehicle uORB message. struct vehicle_attitude_s att; memset(&att, 0, sizeof(att)); + struct vehicle_status_s state; memset(&state, 0, sizeof(state)); @@ -711,7 +726,7 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds // NOTE : Accelerometer is reversed. // Because proper mount of PX4 will give you a reversed accelerometer readings. - MahonyAHRSupdate(gyro[0],gyro[1],gyro[2],-acc[0],-acc[1],-acc[2],mag[0],mag[1],mag[2],so3_comp_params.Kp,so3_comp_params.Ki, dt); + NonlinearSO3AHRSupdate(gyro[0],gyro[1],gyro[2],-acc[0],-acc[1],-acc[2],mag[0],mag[1],mag[2],so3_comp_params.Kp,so3_comp_params.Ki, dt); // Convert q->R. Rot_matrix[0] = q0q0 + q1q1 - q2q2 - q3q3;// 11 @@ -752,14 +767,27 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds att.pitch = euler[1] - so3_comp_params.pitch_off; att.yaw = euler[2] - so3_comp_params.yaw_off; - /* FIXME : This can be a problem for rate controller. Rate in body or inertial? */ + //! Euler angle rate. But it needs to be investigated again. + /* + att.rollspeed = 2.0f*(-q1*dq0 + q0*dq1 - q3*dq2 + q2*dq3); + att.pitchspeed = 2.0f*(-q2*dq0 + q3*dq1 + q0*dq2 - q1*dq3); + att.yawspeed = 2.0f*(-q3*dq0 -q2*dq1 + q1*dq2 + q0*dq3); + */ att.rollspeed = gyro[0]; att.pitchspeed = gyro[1]; att.yawspeed = gyro[2]; + att.rollacc = 0; att.pitchacc = 0; att.yawacc = 0; + //! Quaternion + att.q[0] = q0; + att.q[1] = q1; + att.q[2] = q2; + att.q[3] = q3; + att.q_valid = true; + /* TODO: Bias estimation required */ memcpy(&att.rate_offsets, &(gyro_bias), sizeof(att.rate_offsets)); diff --git a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c index 1d5e0670a0..f962515dfb 100755 --- a/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c +++ b/src/modules/attitude_estimator_so3_comp/attitude_estimator_so3_comp_params.c @@ -19,8 +19,15 @@ #include "attitude_estimator_so3_comp_params.h" /* This is filter gain for nonlinear SO3 complementary filter */ -PARAM_DEFINE_FLOAT(SO3_COMP_KP, 0.5f); -PARAM_DEFINE_FLOAT(SO3_COMP_KI, 0.0f); +/* NOTE : How to tune the gain? First of all, stick with this default gain. And let the quad in stable place. + Log the steady state reponse of filter. If it is too slow, increase SO3_COMP_KP. + If you are flying from ground to high altitude in short amount of time, please increase SO3_COMP_KI which + will compensate gyro bias which depends on temperature and vibration of your vehicle */ +PARAM_DEFINE_FLOAT(SO3_COMP_KP, 1.0f); //! This parameter will give you about 15 seconds convergence time. + //! You can set this gain higher if you want more fast response. + //! But note that higher gain will give you also higher overshoot. +PARAM_DEFINE_FLOAT(SO3_COMP_KI, 0.05f); //! This gain will incorporate slow time-varying bias (e.g., temperature change) + //! This gain is depend on your vehicle status. /* offsets in roll, pitch and yaw of sensor plane and body */ PARAM_DEFINE_FLOAT(ATT_ROLL_OFFS, 0.0f); From 382c9a69e44a6fa5dfa5abec12257ea621018148 Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Thu, 6 Jun 2013 17:13:10 +0200 Subject: [PATCH 15/26] Removed big RAM consumer (inactive filter) --- makefiles/config_px4fmu_default.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/config_px4fmu_default.mk b/makefiles/config_px4fmu_default.mk index 230a527fec..d50eb1e500 100644 --- a/makefiles/config_px4fmu_default.mk +++ b/makefiles/config_px4fmu_default.mk @@ -63,7 +63,7 @@ MODULES += modules/gpio_led # MODULES += modules/attitude_estimator_ekf MODULES += modules/attitude_estimator_so3_comp -MODULES += modules/position_estimator_mc +#MODULES += modules/position_estimator_mc MODULES += modules/position_estimator MODULES += modules/att_pos_estimator_ekf From b09fc1468c8db65ea99dd94f59f5c075dfce5c7e Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Thu, 6 Jun 2013 17:25:47 +0200 Subject: [PATCH 16/26] Hotfix: Fix typos in tutorial code --- src/examples/px4_daemon_app/px4_daemon_app.c | 53 +++++++++++--------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/src/examples/px4_daemon_app/px4_daemon_app.c b/src/examples/px4_daemon_app/px4_daemon_app.c index a5d847777e..26f31b9e6c 100644 --- a/src/examples/px4_daemon_app/px4_daemon_app.c +++ b/src/examples/px4_daemon_app/px4_daemon_app.c @@ -1,7 +1,6 @@ /**************************************************************************** * - * Copyright (C) 2012 PX4 Development Team. All rights reserved. - * Author: @author Example User + * Copyright (c) 2012, 2013 PX4 Development Team. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,27 +32,32 @@ ****************************************************************************/ /** - * @file px4_deamon_app.c - * Deamon application example for PX4 autopilot + * @file px4_daemon_app.c + * daemon application example for PX4 autopilot + * + * @author Example User */ #include +#include #include #include -static bool thread_should_exit = false; /**< Deamon exit flag */ -static bool thread_running = false; /**< Deamon status flag */ -static int deamon_task; /**< Handle of deamon task / thread */ +#include + +static bool thread_should_exit = false; /**< daemon exit flag */ +static bool thread_running = false; /**< daemon status flag */ +static int daemon_task; /**< Handle of daemon task / thread */ /** - * Deamon management function. + * daemon management function. */ -__EXPORT int px4_deamon_app_main(int argc, char *argv[]); +__EXPORT int px4_daemon_app_main(int argc, char *argv[]); /** - * Mainloop of deamon. + * Mainloop of daemon. */ -int px4_deamon_thread_main(int argc, char *argv[]); +int px4_daemon_thread_main(int argc, char *argv[]); /** * Print the correct usage. @@ -64,20 +68,19 @@ static void usage(const char *reason) { if (reason) - fprintf(stderr, "%s\n", reason); - fprintf(stderr, "usage: deamon {start|stop|status} [-p ]\n\n"); - exit(1); + warnx("%s\n", reason); + errx(1, "usage: daemon {start|stop|status} [-p ]\n\n"); } /** - * The deamon app only briefly exists to start + * The daemon app 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 px4_deamon_app_main(int argc, char *argv[]) +int px4_daemon_app_main(int argc, char *argv[]) { if (argc < 1) usage("missing command"); @@ -85,17 +88,17 @@ int px4_deamon_app_main(int argc, char *argv[]) if (!strcmp(argv[1], "start")) { if (thread_running) { - printf("deamon already running\n"); + warnx("daemon already running\n"); /* this is not an error */ exit(0); } thread_should_exit = false; - deamon_task = task_spawn("deamon", + daemon_task = task_spawn("daemon", SCHED_DEFAULT, SCHED_PRIORITY_DEFAULT, 4096, - px4_deamon_thread_main, + px4_daemon_thread_main, (argv) ? (const char **)&argv[2] : (const char **)NULL); exit(0); } @@ -107,9 +110,9 @@ int px4_deamon_app_main(int argc, char *argv[]) if (!strcmp(argv[1], "status")) { if (thread_running) { - printf("\tdeamon app is running\n"); + printf("\tdaemon app is running\n"); } else { - printf("\tdeamon app not started\n"); + printf("\tdaemon app not started\n"); } exit(0); } @@ -118,18 +121,18 @@ int px4_deamon_app_main(int argc, char *argv[]) exit(1); } -int px4_deamon_thread_main(int argc, char *argv[]) { +int px4_daemon_thread_main(int argc, char *argv[]) { - printf("[deamon] starting\n"); + printf("[daemon] starting\n"); thread_running = true; while (!thread_should_exit) { - printf("Hello Deamon!\n"); + printf("Hello daemon!\n"); sleep(10); } - printf("[deamon] exiting.\n"); + printf("[daemon] exiting.\n"); thread_running = false; From fa1b057bb158ab62babef625c57956b2b63707e0 Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Thu, 6 Jun 2013 17:27:01 +0200 Subject: [PATCH 17/26] Minor cleanup --- src/examples/px4_daemon_app/px4_daemon_app.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/examples/px4_daemon_app/px4_daemon_app.c b/src/examples/px4_daemon_app/px4_daemon_app.c index 26f31b9e6c..4dd5165c8c 100644 --- a/src/examples/px4_daemon_app/px4_daemon_app.c +++ b/src/examples/px4_daemon_app/px4_daemon_app.c @@ -110,9 +110,9 @@ int px4_daemon_app_main(int argc, char *argv[]) if (!strcmp(argv[1], "status")) { if (thread_running) { - printf("\tdaemon app is running\n"); + warnx("\trunning\n"); } else { - printf("\tdaemon app not started\n"); + warnx("\tnot started\n"); } exit(0); } @@ -123,16 +123,16 @@ int px4_daemon_app_main(int argc, char *argv[]) int px4_daemon_thread_main(int argc, char *argv[]) { - printf("[daemon] starting\n"); + warnx("[daemon] starting\n"); thread_running = true; while (!thread_should_exit) { - printf("Hello daemon!\n"); + warnx("Hello daemon!\n"); sleep(10); } - printf("[daemon] exiting.\n"); + warnx("[daemon] exiting.\n"); thread_running = false; From 026cad832ad4717b762041a78261f7d6faeef894 Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Thu, 6 Jun 2013 18:53:33 +0200 Subject: [PATCH 18/26] Hotfix: Added missing header --- src/examples/px4_daemon_app/px4_daemon_app.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/examples/px4_daemon_app/px4_daemon_app.c b/src/examples/px4_daemon_app/px4_daemon_app.c index 4dd5165c8c..c568aaadcf 100644 --- a/src/examples/px4_daemon_app/px4_daemon_app.c +++ b/src/examples/px4_daemon_app/px4_daemon_app.c @@ -43,6 +43,7 @@ #include #include +#include #include static bool thread_should_exit = false; /**< daemon exit flag */ From 2aa16dc44764485639921eb4adbbca429c3a4773 Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Thu, 6 Jun 2013 19:12:10 +0200 Subject: [PATCH 19/26] Hotfix: Disable instrumentation on IO --- makefiles/toolchain_gnu-arm-eabi.mk | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/makefiles/toolchain_gnu-arm-eabi.mk b/makefiles/toolchain_gnu-arm-eabi.mk index c75a08bd16..99c2776fdf 100644 --- a/makefiles/toolchain_gnu-arm-eabi.mk +++ b/makefiles/toolchain_gnu-arm-eabi.mk @@ -70,6 +70,14 @@ ARCHCPUFLAGS_CORTEXM3 = -mcpu=cortex-m3 \ -march=armv7-m \ -mfloat-abi=soft +ARCHINSTRUMENTATIONDEFINES_CORTEXM4F = -finstrument-functions \ + -ffixed-r10 + +ARCHINSTRUMENTATIONDEFINES_CORTEXM4 = -finstrument-functions \ + -ffixed-r10 + +ARCHINSTRUMENTATIONDEFINES_CORTEXM3 = + # Pick the right set of flags for the architecture. # ARCHCPUFLAGS = $(ARCHCPUFLAGS_$(CONFIG_ARCH)) @@ -91,8 +99,8 @@ ARCHOPTIMIZATION = $(MAXOPTIMIZATION) \ # enable precise stack overflow tracking # note - requires corresponding support in NuttX -INSTRUMENTATIONDEFINES = -finstrument-functions \ - -ffixed-r10 +INSTRUMENTATIONDEFINES = $(ARCHINSTRUMENTATIONDEFINES_$(CONFIG_ARCH)) + # Language-specific flags # ARCHCFLAGS = -std=gnu99 From b3c5bd5d3a3cc4b480c40b524484aca2b9a66422 Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Thu, 6 Jun 2013 22:14:11 +0200 Subject: [PATCH 20/26] Saved a few string bytes, cleaned up task names and output --- .../att_pos_estimator_ekf/kalman_main.cpp | 15 ++++++++------- .../fixedwing_backside_main.cpp | 17 +++++++++-------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/modules/att_pos_estimator_ekf/kalman_main.cpp b/src/modules/att_pos_estimator_ekf/kalman_main.cpp index aebe3d1feb..10592ec7c7 100644 --- a/src/modules/att_pos_estimator_ekf/kalman_main.cpp +++ b/src/modules/att_pos_estimator_ekf/kalman_main.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include "KalmanNav.hpp" @@ -73,7 +74,7 @@ usage(const char *reason) if (reason) fprintf(stderr, "%s\n", reason); - fprintf(stderr, "usage: kalman_demo {start|stop|status} [-p ]\n\n"); + warnx("usage: att_pos_estimator_ekf {start|stop|status} [-p ]"); exit(1); } @@ -94,13 +95,13 @@ int att_pos_estimator_ekf_main(int argc, char *argv[]) if (!strcmp(argv[1], "start")) { if (thread_running) { - printf("kalman_demo already running\n"); + warnx("already running"); /* this is not an error */ exit(0); } thread_should_exit = false; - deamon_task = task_spawn("kalman_demo", + deamon_task = task_spawn("att_pos_estimator_ekf", SCHED_DEFAULT, SCHED_PRIORITY_MAX - 5, 4096, @@ -116,10 +117,10 @@ int att_pos_estimator_ekf_main(int argc, char *argv[]) if (!strcmp(argv[1], "status")) { if (thread_running) { - printf("\tkalman_demo app is running\n"); + warnx("is running\n"); } else { - printf("\tkalman_demo app not started\n"); + warnx("not started\n"); } exit(0); @@ -132,7 +133,7 @@ int att_pos_estimator_ekf_main(int argc, char *argv[]) int kalman_demo_thread_main(int argc, char *argv[]) { - printf("[kalman_demo] starting\n"); + warnx("starting\n"); using namespace math; @@ -144,7 +145,7 @@ int kalman_demo_thread_main(int argc, char *argv[]) nav.update(); } - printf("[kalman_demo] exiting.\n"); + printf("exiting.\n"); thread_running = false; diff --git a/src/modules/fixedwing_backside/fixedwing_backside_main.cpp b/src/modules/fixedwing_backside/fixedwing_backside_main.cpp index e21990c929..c3d57a85ae 100644 --- a/src/modules/fixedwing_backside/fixedwing_backside_main.cpp +++ b/src/modules/fixedwing_backside/fixedwing_backside_main.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -80,7 +81,7 @@ usage(const char *reason) if (reason) fprintf(stderr, "%s\n", reason); - fprintf(stderr, "usage: control_demo {start|stop|status} [-p ]\n\n"); + fprintf(stderr, "usage: fixedwing_backside {start|stop|status} [-p ]\n\n"); exit(1); } @@ -101,13 +102,13 @@ int fixedwing_backside_main(int argc, char *argv[]) if (!strcmp(argv[1], "start")) { if (thread_running) { - printf("control_demo already running\n"); + warnx("already running"); /* this is not an error */ exit(0); } thread_should_exit = false; - deamon_task = task_spawn("control_demo", + deamon_task = task_spawn("fixedwing_backside", SCHED_DEFAULT, SCHED_PRIORITY_MAX - 10, 5120, @@ -128,10 +129,10 @@ int fixedwing_backside_main(int argc, char *argv[]) if (!strcmp(argv[1], "status")) { if (thread_running) { - printf("\tcontrol_demo app is running\n"); + warnx("is running"); } else { - printf("\tcontrol_demo app not started\n"); + warnx("not started"); } exit(0); @@ -144,7 +145,7 @@ int fixedwing_backside_main(int argc, char *argv[]) int control_demo_thread_main(int argc, char *argv[]) { - printf("[control_Demo] starting\n"); + warnx("starting"); using namespace control; @@ -156,7 +157,7 @@ int control_demo_thread_main(int argc, char *argv[]) autopilot.update(); } - printf("[control_demo] exiting.\n"); + warnx("exiting."); thread_running = false; @@ -165,6 +166,6 @@ int control_demo_thread_main(int argc, char *argv[]) void test() { - printf("beginning control lib test\n"); + warnx("beginning control lib test"); control::basicBlocksTest(); } From 4302f7640216b2bef88d270a268dbea2f712119e Mon Sep 17 00:00:00 2001 From: px4dev Date: Thu, 6 Jun 2013 22:49:49 -0700 Subject: [PATCH 21/26] Hotfix: fix building firmware parallel --- makefiles/firmware.mk | 12 ++++++------ makefiles/nuttx.mk | 6 +++++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/makefiles/firmware.mk b/makefiles/firmware.mk index 6b09e6ec32..f1c1b496a0 100644 --- a/makefiles/firmware.mk +++ b/makefiles/firmware.mk @@ -176,6 +176,12 @@ GLOBAL_DEPS += $(MAKEFILE_LIST) # EXTRA_CLEANS = +################################################################################ +# NuttX libraries and paths +################################################################################ + +include $(PX4_MK_DIR)/nuttx.mk + ################################################################################ # Modules ################################################################################ @@ -296,12 +302,6 @@ $(LIBRARY_CLEANS): LIBRARY_MK=$(mkfile) \ clean -################################################################################ -# NuttX libraries and paths -################################################################################ - -include $(PX4_MK_DIR)/nuttx.mk - ################################################################################ # ROMFS generation ################################################################################ diff --git a/makefiles/nuttx.mk b/makefiles/nuttx.mk index 346735a02a..d283096b25 100644 --- a/makefiles/nuttx.mk +++ b/makefiles/nuttx.mk @@ -69,10 +69,14 @@ INCLUDE_DIRS += $(NUTTX_EXPORT_DIR)include \ LIB_DIRS += $(NUTTX_EXPORT_DIR)libs LIBS += -lapps -lnuttx -LINK_DEPS += $(NUTTX_EXPORT_DIR)libs/libapps.a \ +NUTTX_LIBS = $(NUTTX_EXPORT_DIR)libs/libapps.a \ $(NUTTX_EXPORT_DIR)libs/libnuttx.a +LINK_DEPS += $(NUTTX_LIBS) $(NUTTX_CONFIG_HEADER): $(NUTTX_ARCHIVE) @$(ECHO) %% Unpacking $(NUTTX_ARCHIVE) $(Q) $(UNZIP_CMD) -q -o -d $(WORK_DIR) $(NUTTX_ARCHIVE) $(Q) $(TOUCH) $@ + + $(LDSCRIPT): $(NUTTX_CONFIG_HEADER) + $(NUTTX_LIBS): $(NUTTX_CONFIG_HEADER) From 6c7c130de72b3323211c1ac2e08c8ccf1630c865 Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Fri, 7 Jun 2013 10:34:55 +0200 Subject: [PATCH 22/26] Hotfix: Make IOs mixer loading pedantic to make sure the full mixer loads --- src/modules/px4iofirmware/mixer.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/modules/px4iofirmware/mixer.cpp b/src/modules/px4iofirmware/mixer.cpp index 0b8ed6dc59..a2193b526b 100644 --- a/src/modules/px4iofirmware/mixer.cpp +++ b/src/modules/px4iofirmware/mixer.cpp @@ -294,8 +294,7 @@ mixer_handle_text(const void *buffer, size_t length) case F2I_MIXER_ACTION_APPEND: isr_debug(2, "append %d", length); - /* check for overflow - this is really fatal */ - /* XXX could add just what will fit & try to parse, then repeat... */ + /* check for overflow - this would be really fatal */ if ((mixer_text_length + text_length + 1) > sizeof(mixer_text)) { r_status_flags &= ~PX4IO_P_STATUS_FLAGS_MIXER_OK; return; @@ -314,8 +313,13 @@ mixer_handle_text(const void *buffer, size_t length) /* if anything was parsed */ if (resid != mixer_text_length) { - /* ideally, this should test resid == 0 ? */ - r_status_flags |= PX4IO_P_STATUS_FLAGS_MIXER_OK; + /* only set mixer ok if no residual is left over */ + if (resid == 0) { + r_status_flags |= PX4IO_P_STATUS_FLAGS_MIXER_OK; + } else { + /* not yet reached the end of the mixer, set as not ok */ + r_status_flags &= ~PX4IO_P_STATUS_FLAGS_MIXER_OK; + } isr_debug(2, "used %u", mixer_text_length - resid); @@ -338,11 +342,13 @@ mixer_set_failsafe() { /* * Check if a custom failsafe value has been written, - * else use the opportunity to set decent defaults. + * or if the mixer is not ok and bail out. */ - if (r_setup_arming & PX4IO_P_SETUP_ARMING_FAILSAFE_CUSTOM) + if ((r_setup_arming & PX4IO_P_SETUP_ARMING_FAILSAFE_CUSTOM) || + !(r_status_flags & PX4IO_P_STATUS_FLAGS_MIXER_OK)) return; + /* set failsafe defaults to the values for all inputs = 0 */ float outputs[IO_SERVO_COUNT]; unsigned mixed; From 11544d27b7629078b6a7a2247f159b535816e019 Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Fri, 7 Jun 2013 10:35:37 +0200 Subject: [PATCH 23/26] Hotfix: Enlarge the buffer size for mixers, ensure that reasonable setups with 16 outputs can work --- src/systemcmds/mixer/mixer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/systemcmds/mixer/mixer.c b/src/systemcmds/mixer/mixer.c index 55c4f08362..e642ed0676 100644 --- a/src/systemcmds/mixer/mixer.c +++ b/src/systemcmds/mixer/mixer.c @@ -88,8 +88,8 @@ load(const char *devname, const char *fname) { int dev; FILE *fp; - char line[80]; - char buf[512]; + char line[120]; + char buf[2048]; /* open the device */ if ((dev = open(devname, 0)) < 0) From 4e3f4b57e3e603aaea665758ea0240c48ea9e54f Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Fri, 7 Jun 2013 10:36:56 +0200 Subject: [PATCH 24/26] Hotfix: Allow the IO mixer loading to load larger mixers, fix up the px4io test command to allow a clean exit --- src/drivers/px4io/px4io.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/drivers/px4io/px4io.cpp b/src/drivers/px4io/px4io.cpp index 0934e614b3..19163cebe3 100644 --- a/src/drivers/px4io/px4io.cpp +++ b/src/drivers/px4io/px4io.cpp @@ -1302,7 +1302,7 @@ PX4IO::print_status() io_reg_get(PX4IO_PAGE_STATUS, PX4IO_P_STATUS_VBATT), io_reg_get(PX4IO_PAGE_STATUS, PX4IO_P_STATUS_IBATT), io_reg_get(PX4IO_PAGE_SETUP, PX4IO_P_SETUP_VBATT_SCALE)); - printf("amp_per_volt %.3f amp_offset %.3f mAhDischarged %.3f\n", + printf("amp_per_volt %.3f amp_offset %.3f mAh discharged %.3f\n", (double)_battery_amp_per_volt, (double)_battery_amp_bias, (double)_battery_mamphour_total); @@ -1496,7 +1496,7 @@ PX4IO::ioctl(file *filep, int cmd, unsigned long arg) case MIXERIOCLOADBUF: { const char *buf = (const char *)arg; - ret = mixer_send(buf, strnlen(buf, 1024)); + ret = mixer_send(buf, strnlen(buf, 2048)); break; } @@ -1637,6 +1637,13 @@ test(void) if (ioctl(fd, PWM_SERVO_ARM, 0)) err(1, "failed to arm servos"); + /* Open console directly to grab CTRL-C signal */ + int console = open("/dev/console", O_NONBLOCK | O_RDONLY | O_NOCTTY); + if (!console) + err(1, "failed opening console"); + + warnx("Press CTRL-C or 'c' to abort."); + for (;;) { /* sweep all servos between 1000..2000 */ @@ -1671,6 +1678,16 @@ test(void) if (value != servos[i]) errx(1, "servo %d readback error, got %u expected %u", i, value, servos[i]); } + + /* Check if user wants to quit */ + char c; + if (read(console, &c, 1) == 1) { + if (c == 0x03 || c == 0x63) { + warnx("User abort\n"); + close(console); + exit(0); + } + } } } From 5b5d20bb638c884f943612da43a5ce30c1742eb8 Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Fri, 7 Jun 2013 10:37:31 +0200 Subject: [PATCH 25/26] Hotfix: Add an IO pass mixer with 8 outputs --- ROMFS/px4fmu_common/mixers/IO_pass.mix | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 ROMFS/px4fmu_common/mixers/IO_pass.mix diff --git a/ROMFS/px4fmu_common/mixers/IO_pass.mix b/ROMFS/px4fmu_common/mixers/IO_pass.mix new file mode 100644 index 0000000000..39f875ddb9 --- /dev/null +++ b/ROMFS/px4fmu_common/mixers/IO_pass.mix @@ -0,0 +1,38 @@ +Passthrough mixer for PX4IO +============================ + +This file defines passthrough mixers suitable for testing. + +Channel group 0, channels 0-7 are passed directly through to the outputs. + +M: 1 +O: 10000 10000 0 -10000 10000 +S: 0 0 10000 10000 0 -10000 10000 + +M: 1 +O: 10000 10000 0 -10000 10000 +S: 0 1 10000 10000 0 -10000 10000 + +M: 1 +O: 10000 10000 0 -10000 10000 +S: 0 2 10000 10000 0 -10000 10000 + +M: 1 +O: 10000 10000 0 -10000 10000 +S: 0 3 10000 10000 0 -10000 10000 + +M: 1 +O: 10000 10000 0 -10000 10000 +S: 0 4 10000 10000 0 -10000 10000 + +M: 1 +O: 10000 10000 0 -10000 10000 +S: 0 5 10000 10000 0 -10000 10000 + +M: 1 +O: 10000 10000 0 -10000 10000 +S: 0 6 10000 10000 0 -10000 10000 + +M: 1 +O: 10000 10000 0 -10000 10000 +S: 0 7 10000 10000 0 -10000 10000 From 5c74809dac57f58f92ad92433496731481703982 Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Fri, 7 Jun 2013 10:38:09 +0200 Subject: [PATCH 26/26] Config change: Set USB console as default. --- nuttx/configs/px4fmu/nsh/defconfig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nuttx/configs/px4fmu/nsh/defconfig b/nuttx/configs/px4fmu/nsh/defconfig index 02e2243020..0662f7fbe3 100755 --- a/nuttx/configs/px4fmu/nsh/defconfig +++ b/nuttx/configs/px4fmu/nsh/defconfig @@ -248,7 +248,7 @@ CONFIG_SERIAL_TERMIOS=y CONFIG_SERIAL_CONSOLE_REINIT=y CONFIG_STANDARD_SERIAL=y -CONFIG_USART1_SERIAL_CONSOLE=y +CONFIG_USART1_SERIAL_CONSOLE=n CONFIG_USART2_SERIAL_CONSOLE=n CONFIG_USART3_SERIAL_CONSOLE=n CONFIG_UART4_SERIAL_CONSOLE=n @@ -561,7 +561,7 @@ CONFIG_START_MONTH=1 CONFIG_START_DAY=1 CONFIG_GREGORIAN_TIME=n CONFIG_JULIAN_TIME=n -CONFIG_DEV_CONSOLE=y +CONFIG_DEV_CONSOLE=n CONFIG_DEV_LOWCONSOLE=n CONFIG_MUTEX_TYPES=n CONFIG_PRIORITY_INHERITANCE=y @@ -717,7 +717,7 @@ CONFIG_ARCH_BZERO=n # zero for all dynamic allocations. # CONFIG_MAX_TASKS=32 -CONFIG_MAX_TASK_ARGS=8 +CONFIG_MAX_TASK_ARGS=10 CONFIG_NPTHREAD_KEYS=4 CONFIG_NFILE_DESCRIPTORS=32 CONFIG_NFILE_STREAMS=25 @@ -925,7 +925,7 @@ CONFIG_USBDEV_TRACE_NRECORDS=512 # Size of the serial receive/transmit buffers. Default 256. # CONFIG_CDCACM=y -CONFIG_CDCACM_CONSOLE=n +CONFIG_CDCACM_CONSOLE=y #CONFIG_CDCACM_EP0MAXPACKET CONFIG_CDCACM_EPINTIN=1 #CONFIG_CDCACM_EPINTIN_FSSIZE