mirror of
https://github.com/ArduPilot/ardupilot
synced 2025-01-03 06:28:27 -04:00
a7b69366a1
The is commit adds a new flight mode called 'Throw' to Copter that enables the copter to be thrown into the air to start motors. This mode can only be netered when the copters EKF has a valid position estimate and goes through the following states Throw_Disarmed - The copter is disarmed and motors are off. Throw_Detecting - The copter is armed, but motors will not spin unless THROW_MOT_START has been set to 1. The copter is waiting to detect the throw. A throw with an upwards velocity of at least 50cm/s is required to trigger the detector. Throw_Uprighting - The throw has been detected and the copter is being uprighted with 50% throttle to maximise control authority. This state transitions when the copter is within 30 degrees of level. Throw_HgtStabilise - The copter is kept level and height is stabilised about the target height which is 3m above the height at which the throw release was detected. This state transitions when the height is no more than 0.5m below the demanded height. Throw_PosHold - The horizontal motion is arrested and the copter is kept at a constant position and height.
456 lines
13 KiB
C++
456 lines
13 KiB
C++
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
|
|
|
#include "Copter.h"
|
|
|
|
/*****************************************************************************
|
|
* The init_ardupilot function processes everything we need for an in - air restart
|
|
* We will determine later if we are actually on the ground and process a
|
|
* ground start in that case.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#if CLI_ENABLED == ENABLED
|
|
|
|
// This is the help function
|
|
int8_t Copter::main_menu_help(uint8_t argc, const Menu::arg *argv)
|
|
{
|
|
cliSerial->printf("Commands:\n"
|
|
" logs\n"
|
|
" setup\n"
|
|
" test\n"
|
|
" reboot\n"
|
|
"\n");
|
|
return(0);
|
|
}
|
|
|
|
// Command/function table for the top-level menu.
|
|
const struct Menu::command main_menu_commands[] = {
|
|
// command function called
|
|
// ======= ===============
|
|
{"logs", MENU_FUNC(process_logs)},
|
|
{"setup", MENU_FUNC(setup_mode)},
|
|
{"test", MENU_FUNC(test_mode)},
|
|
{"reboot", MENU_FUNC(reboot_board)},
|
|
{"help", MENU_FUNC(main_menu_help)},
|
|
};
|
|
|
|
// Create the top-level menu object.
|
|
MENU(main_menu, THISFIRMWARE, main_menu_commands);
|
|
|
|
int8_t Copter::reboot_board(uint8_t argc, const Menu::arg *argv)
|
|
{
|
|
hal.scheduler->reboot(false);
|
|
return 0;
|
|
}
|
|
|
|
// the user wants the CLI. It never exits
|
|
void Copter::run_cli(AP_HAL::UARTDriver *port)
|
|
{
|
|
cliSerial = port;
|
|
Menu::set_port(port);
|
|
port->set_blocking_writes(true);
|
|
|
|
// disable the mavlink delay callback
|
|
hal.scheduler->register_delay_callback(NULL, 5);
|
|
|
|
// disable main_loop failsafe
|
|
failsafe_disable();
|
|
|
|
// cut the engines
|
|
if(motors.armed()) {
|
|
motors.armed(false);
|
|
motors.output();
|
|
}
|
|
|
|
while (1) {
|
|
main_menu.run();
|
|
}
|
|
}
|
|
|
|
#endif // CLI_ENABLED
|
|
|
|
static void mavlink_delay_cb_static()
|
|
{
|
|
copter.mavlink_delay_cb();
|
|
}
|
|
|
|
|
|
static void failsafe_check_static()
|
|
{
|
|
copter.failsafe_check();
|
|
}
|
|
|
|
void Copter::init_ardupilot()
|
|
{
|
|
if (!hal.gpio->usb_connected()) {
|
|
// USB is not connected, this means UART0 may be a Xbee, with
|
|
// its darned bricking problem. We can't write to it for at
|
|
// least one second after powering up. Simplest solution for
|
|
// now is to delay for 1 second. Something more elegant may be
|
|
// added later
|
|
delay(1000);
|
|
}
|
|
|
|
// initialise serial port
|
|
serial_manager.init_console();
|
|
|
|
// init vehicle capabilties
|
|
init_capabilities();
|
|
|
|
cliSerial->printf("\n\nInit " FIRMWARE_STRING
|
|
"\n\nFree RAM: %u\n",
|
|
(unsigned)hal.util->available_memory());
|
|
|
|
//
|
|
// Report firmware version code expect on console (check of actual EEPROM format version is done in load_parameters function)
|
|
//
|
|
report_version();
|
|
|
|
// load parameters from EEPROM
|
|
load_parameters();
|
|
|
|
BoardConfig.init();
|
|
|
|
// initialise serial port
|
|
serial_manager.init();
|
|
|
|
// init EPM cargo gripper
|
|
#if EPM_ENABLED == ENABLED
|
|
epm.init();
|
|
#endif
|
|
|
|
// initialise notify system
|
|
// disable external leds if epm is enabled because of pin conflict on the APM
|
|
notify.init(true);
|
|
|
|
// initialise battery monitor
|
|
battery.init();
|
|
|
|
// Init RSSI
|
|
rssi.init();
|
|
|
|
barometer.init();
|
|
|
|
// Register the mavlink service callback. This will run
|
|
// anytime there are more than 5ms remaining in a call to
|
|
// hal.scheduler->delay.
|
|
hal.scheduler->register_delay_callback(mavlink_delay_cb_static, 5);
|
|
|
|
// we start by assuming USB connected, as we initialed the serial
|
|
// port with SERIAL0_BAUD. check_usb_mux() fixes this if need be.
|
|
ap.usb_connected = true;
|
|
check_usb_mux();
|
|
|
|
// init the GCS connected to the console
|
|
gcs[0].setup_uart(serial_manager, AP_SerialManager::SerialProtocol_Console, 0);
|
|
|
|
// init telemetry port
|
|
gcs[1].setup_uart(serial_manager, AP_SerialManager::SerialProtocol_MAVLink, 0);
|
|
|
|
// setup serial port for telem2
|
|
gcs[2].setup_uart(serial_manager, AP_SerialManager::SerialProtocol_MAVLink, 1);
|
|
|
|
// setup serial port for fourth telemetry port (not used by default)
|
|
gcs[3].setup_uart(serial_manager, AP_SerialManager::SerialProtocol_MAVLink, 2);
|
|
|
|
#if FRSKY_TELEM_ENABLED == ENABLED
|
|
// setup frsky
|
|
frsky_telemetry.init(serial_manager);
|
|
#endif
|
|
|
|
// identify ourselves correctly with the ground station
|
|
mavlink_system.sysid = g.sysid_this_mav;
|
|
|
|
#if LOGGING_ENABLED == ENABLED
|
|
log_init();
|
|
#endif
|
|
|
|
GCS_MAVLINK::set_dataflash(&DataFlash);
|
|
|
|
// update motor interlock state
|
|
update_using_interlock();
|
|
|
|
#if FRAME_CONFIG == HELI_FRAME
|
|
// trad heli specific initialisation
|
|
heli_init();
|
|
#endif
|
|
|
|
init_rc_in(); // sets up rc channels from radio
|
|
init_rc_out(); // sets up motors and output to escs
|
|
|
|
// initialise which outputs Servo and Relay events can use
|
|
ServoRelayEvents.set_channel_mask(~motors.get_motor_mask());
|
|
|
|
relay.init();
|
|
|
|
/*
|
|
* setup the 'main loop is dead' check. Note that this relies on
|
|
* the RC library being initialised.
|
|
*/
|
|
hal.scheduler->register_timer_failsafe(failsafe_check_static, 1000);
|
|
|
|
// Do GPS init
|
|
gps.init(&DataFlash, serial_manager);
|
|
|
|
if(g.compass_enabled)
|
|
init_compass();
|
|
|
|
#if OPTFLOW == ENABLED
|
|
// make optflow available to AHRS
|
|
ahrs.set_optflow(&optflow);
|
|
#endif
|
|
|
|
// initialise attitude and position controllers
|
|
attitude_control.set_dt(MAIN_LOOP_SECONDS);
|
|
pos_control.set_dt(MAIN_LOOP_SECONDS);
|
|
|
|
// init the optical flow sensor
|
|
init_optflow();
|
|
|
|
#if MOUNT == ENABLED
|
|
// initialise camera mount
|
|
camera_mount.init(&DataFlash, serial_manager);
|
|
#endif
|
|
|
|
#if PRECISION_LANDING == ENABLED
|
|
// initialise precision landing
|
|
init_precland();
|
|
#endif
|
|
|
|
#ifdef USERHOOK_INIT
|
|
USERHOOK_INIT
|
|
#endif
|
|
|
|
#if CLI_ENABLED == ENABLED
|
|
if (g.cli_enabled) {
|
|
const char *msg = "\nPress ENTER 3 times to start interactive setup\n";
|
|
cliSerial->println(msg);
|
|
if (gcs[1].initialised && (gcs[1].get_uart() != NULL)) {
|
|
gcs[1].get_uart()->println(msg);
|
|
}
|
|
if (num_gcs > 2 && gcs[2].initialised && (gcs[2].get_uart() != NULL)) {
|
|
gcs[2].get_uart()->println(msg);
|
|
}
|
|
}
|
|
#endif // CLI_ENABLED
|
|
|
|
#if HIL_MODE != HIL_MODE_DISABLED
|
|
while (barometer.get_last_update() == 0) {
|
|
// the barometer begins updating when we get the first
|
|
// HIL_STATE message
|
|
gcs_send_text(MAV_SEVERITY_WARNING, "Waiting for first HIL_STATE message");
|
|
delay(1000);
|
|
}
|
|
|
|
// set INS to HIL mode
|
|
ins.set_hil_mode();
|
|
#endif
|
|
|
|
// read Baro pressure at ground
|
|
//-----------------------------
|
|
init_barometer(true);
|
|
|
|
// initialise sonar
|
|
#if CONFIG_SONAR == ENABLED
|
|
init_sonar();
|
|
#endif
|
|
|
|
// initialise AP_RPM library
|
|
rpm_sensor.init();
|
|
|
|
// initialise mission library
|
|
mission.init();
|
|
|
|
// initialise the flight mode and aux switch
|
|
// ---------------------------
|
|
reset_control_switch();
|
|
init_aux_switches();
|
|
|
|
startup_INS_ground();
|
|
|
|
// set landed flags
|
|
set_land_complete(true);
|
|
set_land_complete_maybe(true);
|
|
|
|
// we don't want writes to the serial port to cause us to pause
|
|
// mid-flight, so set the serial ports non-blocking once we are
|
|
// ready to fly
|
|
serial_manager.set_blocking_writes_all(false);
|
|
|
|
// enable CPU failsafe
|
|
failsafe_enable();
|
|
|
|
ins.set_raw_logging(should_log(MASK_LOG_IMU_RAW));
|
|
ins.set_dataflash(&DataFlash);
|
|
|
|
cliSerial->print("\nReady to FLY ");
|
|
|
|
// flag that initialisation has completed
|
|
ap.initialised = true;
|
|
}
|
|
|
|
|
|
//******************************************************************************
|
|
//This function does all the calibrations, etc. that we need during a ground start
|
|
//******************************************************************************
|
|
void Copter::startup_INS_ground()
|
|
{
|
|
// initialise ahrs (may push imu calibration into the mpu6000 if using that device).
|
|
ahrs.init();
|
|
ahrs.set_vehicle_class(AHRS_VEHICLE_COPTER);
|
|
|
|
// Warm up and calibrate gyro offsets
|
|
ins.init(scheduler.get_loop_rate_hz());
|
|
|
|
// reset ahrs including gyro bias
|
|
ahrs.reset();
|
|
}
|
|
|
|
// calibrate gyros - returns true if succesfully calibrated
|
|
bool Copter::calibrate_gyros()
|
|
{
|
|
// gyro offset calibration
|
|
copter.ins.init_gyro();
|
|
|
|
// reset ahrs gyro bias
|
|
if (copter.ins.gyro_calibrated_ok_all()) {
|
|
copter.ahrs.reset_gyro_drift();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// position_ok - returns true if the horizontal absolute position is ok and home position is set
|
|
bool Copter::position_ok()
|
|
{
|
|
// return false if ekf failsafe has triggered
|
|
if (failsafe.ekf) {
|
|
return false;
|
|
}
|
|
|
|
// check ekf position estimate
|
|
return (ekf_position_ok() || optflow_position_ok());
|
|
}
|
|
|
|
// ekf_position_ok - returns true if the ekf claims it's horizontal absolute position estimate is ok and home position is set
|
|
bool Copter::ekf_position_ok()
|
|
{
|
|
if (!ahrs.have_inertial_nav()) {
|
|
// do not allow navigation with dcm position
|
|
return false;
|
|
}
|
|
|
|
// with EKF use filter status and ekf check
|
|
nav_filter_status filt_status = inertial_nav.get_filter_status();
|
|
|
|
// if disarmed we accept a predicted horizontal position
|
|
if (!motors.armed()) {
|
|
return ((filt_status.flags.horiz_pos_abs || filt_status.flags.pred_horiz_pos_abs));
|
|
} else {
|
|
// once armed we require a good absolute position and EKF must not be in const_pos_mode
|
|
return (filt_status.flags.horiz_pos_abs && !filt_status.flags.const_pos_mode);
|
|
}
|
|
}
|
|
|
|
// optflow_position_ok - returns true if optical flow based position estimate is ok
|
|
bool Copter::optflow_position_ok()
|
|
{
|
|
#if OPTFLOW != ENABLED
|
|
return false;
|
|
#else
|
|
// return immediately if optflow is not enabled or EKF not used
|
|
if (!optflow.enabled() || !ahrs.have_inertial_nav()) {
|
|
return false;
|
|
}
|
|
|
|
// get filter status from EKF
|
|
nav_filter_status filt_status = inertial_nav.get_filter_status();
|
|
|
|
// if disarmed we accept a predicted horizontal relative position
|
|
if (!motors.armed()) {
|
|
return (filt_status.flags.pred_horiz_pos_rel);
|
|
} else {
|
|
return (filt_status.flags.horiz_pos_rel && !filt_status.flags.const_pos_mode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// update_auto_armed - update status of auto_armed flag
|
|
void Copter::update_auto_armed()
|
|
{
|
|
// disarm checks
|
|
if(ap.auto_armed){
|
|
// if motors are disarmed, auto_armed should also be false
|
|
if(!motors.armed()) {
|
|
set_auto_armed(false);
|
|
return;
|
|
}
|
|
// if in stabilize or acro flight mode and throttle is zero, auto-armed should become false
|
|
if(mode_has_manual_throttle(control_mode) && ap.throttle_zero && !failsafe.radio) {
|
|
set_auto_armed(false);
|
|
}
|
|
#if FRAME_CONFIG == HELI_FRAME
|
|
// if helicopters are on the ground, and the motor is switched off, auto-armed should be false
|
|
// so that rotor runup is checked again before attempting to take-off
|
|
if(ap.land_complete && !motors.rotor_runup_complete()) {
|
|
set_auto_armed(false);
|
|
}
|
|
#endif // HELI_FRAME
|
|
}else{
|
|
// arm checks
|
|
|
|
#if FRAME_CONFIG == HELI_FRAME
|
|
// for tradheli if motors are armed and throttle is above zero and the motor is started, auto_armed should be true
|
|
if(motors.armed() && !ap.throttle_zero && motors.rotor_runup_complete()) {
|
|
set_auto_armed(true);
|
|
}
|
|
#else
|
|
// if motors are armed and throttle is above zero auto_armed should be true
|
|
// if motors are armed and we are in throw mode, then auto_ermed should be true
|
|
if(motors.armed() && (!ap.throttle_zero || control_mode == THROW)) {
|
|
set_auto_armed(true);
|
|
}
|
|
#endif // HELI_FRAME
|
|
}
|
|
}
|
|
|
|
void Copter::check_usb_mux(void)
|
|
{
|
|
bool usb_check = hal.gpio->usb_connected();
|
|
if (usb_check == ap.usb_connected) {
|
|
return;
|
|
}
|
|
|
|
// the user has switched to/from the telemetry port
|
|
ap.usb_connected = usb_check;
|
|
}
|
|
|
|
// frsky_telemetry_send - sends telemetry data using frsky telemetry
|
|
// should be called at 5Hz by scheduler
|
|
#if FRSKY_TELEM_ENABLED == ENABLED
|
|
void Copter::frsky_telemetry_send(void)
|
|
{
|
|
frsky_telemetry.send_frames((uint8_t)control_mode);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
should we log a message type now?
|
|
*/
|
|
bool Copter::should_log(uint32_t mask)
|
|
{
|
|
#if LOGGING_ENABLED == ENABLED
|
|
if (!(mask & g.log_bitmask) || in_mavlink_delay) {
|
|
return false;
|
|
}
|
|
bool ret = motors.armed() || (g.log_bitmask & MASK_LOG_WHEN_DISARMED) != 0;
|
|
if (ret && !DataFlash.logging_started() && !in_log_download) {
|
|
start_logging();
|
|
}
|
|
return ret;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|