Example #1
0
void mr_control_run_iteration(float dt) {
	bool lost_signal = true;

	pos_get_pos(&m_pos_last);

	if (mr_control_time_since_input_update() < INPUT_TIMEOUT_MS) {
		lost_signal = false;
	} else {
		memset(&m_rc, 0, sizeof(MR_RC_STATE));
	}

	if (m_power_override_time > 0.0) {
		m_power_override_time -= dt;

		for (int i = 0;i < 4;i++) {
			actuator_set_motor(i, m_power_override[i]);
		}

		if (m_power_override_time <= 0.0) {
			memset(m_power_override, 0, sizeof(m_power_override));
		}

		return;
	}

	memset(&m_output, 0, sizeof(MR_OUTPUT));

	if (!lost_signal) {
		// Set roll, pitch and yaw goals to the current value if the throttle
		// just got above the threshold.
		static int was_below_tres = 0;
		if (m_rc.throttle < MIN_THROTTLE) {
			was_below_tres = 1;
			actuator_set_motor(-1, 0.0);
			return;
		} else {
			if (was_below_tres) {
				m_ctrl.roll_goal = m_pos_last.roll;
				m_ctrl.pitch_goal = m_pos_last.pitch;
				m_ctrl.yaw_goal = m_pos_last.yaw;
				m_ctrl.roll_integrator = 0.0;
				m_ctrl.pitch_integrator = 0.0;
				m_ctrl.yaw_integrator = 0.0;
			}

			was_below_tres = 0;
		}

		if (m_rc.throttle < THROTTLE_OVERRIDE_LIM || 1) {
			// Manual control
			update_rc_control(&m_ctrl, &m_rc, &m_pos_last, dt);
			m_output.throttle = m_rc.throttle;
		} else {
			// TODO: autopilot
		}

		// Run attitude control
		float roll_error = utils_angle_difference(m_ctrl.roll_goal, m_pos_last.roll);
		float pitch_error = utils_angle_difference(m_ctrl.pitch_goal, m_pos_last.pitch);
		float yaw_error = utils_angle_difference(m_ctrl.yaw_goal, m_pos_last.yaw);

		roll_error /= 360.0;
		pitch_error /= 360.0;
		yaw_error /= 360.0;

		// Run integration
		m_ctrl.roll_integrator += roll_error * dt;
		m_ctrl.pitch_integrator += pitch_error * dt;
		m_ctrl.yaw_integrator += yaw_error * dt;
		// Prevent wind-up
		utils_truncate_number_abs(&m_ctrl.roll_integrator, 1.0 / main_config.mr.ctrl_gain_roll_i);
		utils_truncate_number_abs(&m_ctrl.pitch_integrator, 1.0 / main_config.mr.ctrl_gain_pitch_i);
		utils_truncate_number_abs(&m_ctrl.yaw_integrator, 1.0 / main_config.mr.ctrl_gain_yaw_i);

		float d_roll_sample_process = -utils_angle_difference(m_pos_last.roll, m_ctrl.last_roll_process) / dt;
		float d_roll_sample_error = (roll_error - m_ctrl.last_roll_error) / dt;
		float d_pitch_sample_process = -utils_angle_difference(m_pos_last.pitch, m_ctrl.last_pitch_process) / dt;
		float d_pitch_sample_error = (pitch_error - m_ctrl.last_pitch_error) / dt;
		float d_yaw_sample_process = -utils_angle_difference(m_pos_last.yaw, m_ctrl.last_yaw_process) / dt;
		float d_yaw_sample_error = (yaw_error - m_ctrl.last_yaw_error) / dt;

		m_ctrl.last_roll_process = m_pos_last.roll;
		m_ctrl.last_roll_error = roll_error;
		m_ctrl.last_pitch_process = m_pos_last.pitch;
		m_ctrl.last_pitch_error = pitch_error;
		m_ctrl.last_yaw_process = m_pos_last.yaw;
		m_ctrl.last_yaw_error = yaw_error;

		d_roll_sample_process /= 360.0;
		d_pitch_sample_process /= 360.0;
		d_yaw_sample_process /= 360.0;

		m_output.roll = roll_error * main_config.mr.ctrl_gain_roll_p +
				m_ctrl.roll_integrator * main_config.mr.ctrl_gain_roll_i +
				d_roll_sample_process * main_config.mr.ctrl_gain_roll_dp +
				d_roll_sample_error * main_config.mr.ctrl_gain_roll_de;
		m_output.pitch = pitch_error * main_config.mr.ctrl_gain_pitch_p +
				m_ctrl.pitch_integrator * main_config.mr.ctrl_gain_pitch_i +
				d_pitch_sample_process * main_config.mr.ctrl_gain_pitch_dp +
				d_pitch_sample_error * main_config.mr.ctrl_gain_pitch_de;
		m_output.yaw = yaw_error * main_config.mr.ctrl_gain_yaw_p +
				m_ctrl.yaw_integrator * main_config.mr.ctrl_gain_yaw_i +
				d_yaw_sample_process * main_config.mr.ctrl_gain_yaw_dp +
				d_yaw_sample_error * main_config.mr.ctrl_gain_yaw_de;

		utils_truncate_number_abs(&m_output.roll, 1.0);
		utils_truncate_number_abs(&m_output.pitch, 1.0);
		utils_truncate_number_abs(&m_output.yaw, 1.0);

		// Compensate throttle for roll and pitch
		const float tan_roll = tanf(m_pos_last.roll * M_PI / 180.0);
		const float tan_pitch = tanf(m_pos_last.pitch * M_PI / 180.0);
		const float tilt_comp_factor = sqrtf(tan_roll * tan_roll + tan_pitch * tan_pitch + 1);

		m_output.throttle *= tilt_comp_factor;
		utils_truncate_number(&m_output.throttle, 0.0, 1.0);
	}

	actuator_set_output(m_output.throttle, m_output.roll, m_output.pitch, m_output.yaw);
}
Example #2
0
static msg_t adc_thread(void *arg) {
    (void)arg;

    chRegSetThreadName("APP_ADC");

    // Set servo pin as an input with pullup
    palSetPadMode(HW_ICU_GPIO, HW_ICU_PIN, PAL_MODE_INPUT_PULLUP);

    for(;;) {
        // Sleep for a time according to the specified rate
        systime_t sleep_time = CH_FREQUENCY / config.update_rate_hz;

        // At least one tick should be slept to not block the other threads
        if (sleep_time == 0) {
            sleep_time = 1;
        }
        chThdSleep(sleep_time);

        // Read the external ADC pin and convert the value to a voltage.
        float pwr = (float)ADC_Value[ADC_IND_EXT];
        pwr /= 4095;
        pwr *= V_REG;

        read_voltage = pwr;

        // Optionally apply a mean value filter
        if (config.use_filter) {
            static float filter_buffer[FILTER_SAMPLES];
            static int filter_ptr = 0;

            filter_buffer[filter_ptr++] = pwr;
            if (filter_ptr >= FILTER_SAMPLES) {
                filter_ptr = 0;
            }

            pwr = 0.0;
            for (int i = 0; i < FILTER_SAMPLES; i++) {
                pwr += filter_buffer[i];
            }
            pwr /= FILTER_SAMPLES;
        }

        // Map and truncate the read voltage
        pwr = utils_map(pwr, config.voltage_start, config.voltage_end, 0.0, 1.0);
        utils_truncate_number(&pwr, 0.0, 1.0);

        // Optionally invert the read voltage
        if (config.voltage_inverted) {
            pwr = 1.0 - pwr;
        }

        decoded_level = pwr;

        // Read the servo pin and optionally invert it.
        bool button_val = !palReadPad(HW_ICU_GPIO, HW_ICU_PIN);
        if (config.button_inverted) {
            button_val = !button_val;
        }

        switch (config.ctrl_type) {
        case ADC_CTRL_TYPE_CURRENT_REV_CENTER:
        case ADC_CTRL_TYPE_CURRENT_NOREV_BRAKE_CENTER:
        case ADC_CTRL_TYPE_DUTY_REV_CENTER:
            // Scale the voltage and set 0 at the center
            pwr *= 2.0;
            pwr -= 1.0;
            break;

        case ADC_CTRL_TYPE_CURRENT_REV_BUTTON:
        case ADC_CTRL_TYPE_CURRENT_NOREV_BRAKE_BUTTON:
        case ADC_CTRL_TYPE_DUTY_REV_BUTTON:
            // Invert the voltage if the button is pressed
            if (button_val) {
                pwr = -pwr;
            }
            break;

        default:
            break;
        }

        // Apply a deadband
        utils_deadband(&pwr, config.hyst, 1.0);

        float current = 0;
        bool current_mode = false;
        bool current_mode_brake = false;
        const volatile mc_configuration *mcconf = mcpwm_get_configuration();
        bool send_duty = false;

        // Use the filtered and mapped voltage for control according to the configuration.
        switch (config.ctrl_type) {
        case ADC_CTRL_TYPE_CURRENT:
        case ADC_CTRL_TYPE_CURRENT_REV_CENTER:
        case ADC_CTRL_TYPE_CURRENT_REV_BUTTON:
            current_mode = true;
            if (pwr >= 0.0) {
                current = pwr * mcconf->l_current_max;
            } else {
                current = pwr * fabsf(mcconf->l_current_min);
            }

            if (fabsf(pwr) < 0.001) {
                ms_without_power += (1000.0 * (float)sleep_time) / (float)CH_FREQUENCY;
            }
            break;

        case ADC_CTRL_TYPE_CURRENT_NOREV_BRAKE_CENTER:
        case ADC_CTRL_TYPE_CURRENT_NOREV_BRAKE_BUTTON:
            current_mode = true;
            if (pwr >= 0.0) {
                current = pwr * mcconf->l_current_max;
            } else {
                current = fabsf(pwr * mcconf->l_current_min);
                current_mode_brake = true;
            }

            if (pwr < 0.001) {
                ms_without_power += (1000.0 * (float)sleep_time) / (float)CH_FREQUENCY;
            }
            break;

        case ADC_CTRL_TYPE_DUTY:
        case ADC_CTRL_TYPE_DUTY_REV_CENTER:
        case ADC_CTRL_TYPE_DUTY_REV_BUTTON:
            if (fabsf(pwr) < 0.001) {
                ms_without_power += (1000.0 * (float)sleep_time) / (float)CH_FREQUENCY;
            }

            if (!(ms_without_power < MIN_MS_WITHOUT_POWER && config.safe_start)) {
                mcpwm_set_duty(pwr);
                send_duty = true;
            }
            break;

        default:
            continue;
        }

        // If safe start is enabled and the output has not been zero for long enough
        if (ms_without_power < MIN_MS_WITHOUT_POWER && config.safe_start) {
            static int pulses_without_power_before = 0;
            if (ms_without_power == pulses_without_power_before) {
                ms_without_power = 0;
            }
            pulses_without_power_before = ms_without_power;
            mcpwm_set_brake_current(timeout_get_brake_current());
            continue;
        }

        // Reset timeout
        timeout_reset();

        // Find lowest RPM (for traction control)
        float rpm_local = mcpwm_get_rpm();
        float rpm_lowest = rpm_local;
        if (config.multi_esc) {
            for (int i = 0; i < CAN_STATUS_MSGS_TO_STORE; i++) {
                can_status_msg *msg = comm_can_get_status_msg_index(i);

                if (msg->id >= 0 && UTILS_AGE_S(msg->rx_time) < MAX_CAN_AGE) {
                    float rpm_tmp = msg->rpm;

                    if (fabsf(rpm_tmp) < fabsf(rpm_lowest)) {
                        rpm_lowest = rpm_tmp;
                    }
                }
            }
        }

        // Optionally send the duty cycles to the other ESCs seen on the CAN-bus
        if (send_duty && config.multi_esc) {
            float duty = mcpwm_get_duty_cycle_now();

            for (int i = 0; i < CAN_STATUS_MSGS_TO_STORE; i++) {
                can_status_msg *msg = comm_can_get_status_msg_index(i);

                if (msg->id >= 0 && UTILS_AGE_S(msg->rx_time) < MAX_CAN_AGE) {
                    comm_can_set_duty(msg->id, duty);
                }
            }
        }

        if (current_mode) {
            if (current_mode_brake) {
                mcpwm_set_brake_current(current);

                // Send brake command to all ESCs seen recently on the CAN bus
                for (int i = 0; i < CAN_STATUS_MSGS_TO_STORE; i++) {
                    can_status_msg *msg = comm_can_get_status_msg_index(i);

                    if (msg->id >= 0 && UTILS_AGE_S(msg->rx_time) < MAX_CAN_AGE) {
                        comm_can_set_current_brake(msg->id, current);
                    }
                }
            } else {
                // Apply soft RPM limit
                if (rpm_lowest > config.rpm_lim_end && current > 0.0) {
                    current = mcconf->cc_min_current;
                } else if (rpm_lowest > config.rpm_lim_start && current > 0.0) {
                    current = utils_map(rpm_lowest, config.rpm_lim_start, config.rpm_lim_end, current, mcconf->cc_min_current);
                } else if (rpm_lowest < -config.rpm_lim_end && current < 0.0) {
                    current = mcconf->cc_min_current;
                } else if (rpm_lowest < -config.rpm_lim_start && current < 0.0) {
                    rpm_lowest = -rpm_lowest;
                    current = -current;
                    current = utils_map(rpm_lowest, config.rpm_lim_start, config.rpm_lim_end, current, mcconf->cc_min_current);
                    current = -current;
                    rpm_lowest = -rpm_lowest;
                }

                float current_out = current;
                bool is_reverse = false;
                if (current_out < 0.0) {
                    is_reverse = true;
                    current_out = -current_out;
                    current = -current;
                    rpm_local = -rpm_local;
                    rpm_lowest = -rpm_lowest;
                }

                // Traction control
                if (config.multi_esc) {
                    for (int i = 0; i < CAN_STATUS_MSGS_TO_STORE; i++) {
                        can_status_msg *msg = comm_can_get_status_msg_index(i);

                        if (msg->id >= 0 && UTILS_AGE_S(msg->rx_time) < MAX_CAN_AGE) {
                            if (config.tc) {
                                float rpm_tmp = msg->rpm;
                                if (is_reverse) {
                                    rpm_tmp = -rpm_tmp;
                                }

                                float diff = rpm_tmp - rpm_lowest;
                                current_out = utils_map(diff, 0.0, config.tc_max_diff, current, 0.0);
                                if (current_out < mcconf->cc_min_current) {
                                    current_out = 0.0;
                                }
                            }

                            if (is_reverse) {
                                comm_can_set_current(msg->id, -current_out);
                            } else {
                                comm_can_set_current(msg->id, current_out);
                            }
                        }
                    }

                    if (config.tc) {
                        float diff = rpm_local - rpm_lowest;
                        current_out = utils_map(diff, 0.0, config.tc_max_diff, current, 0.0);
                        if (current_out < mcconf->cc_min_current) {
                            current_out = 0.0;
                        }
                    }
                }

                if (is_reverse) {
                    mcpwm_set_current(-current_out);
                } else {
                    mcpwm_set_current(current_out);
                }
            }
        }
    }

    return 0;
}