APM_ArduCopter源码解析学习(四)——IMU
- 前言
- 一、system.cpp
-
- 1.1 无人机内部初始化
- 1.2 Copter::init_ardupilot()
- 1.3 Copter::startup_INS_ground()
- 二、库内传感器配置及前后端
-
- 三、Copter.cpp
前言
感谢博主:https://blog.csdn.net/moumde/article/details/108943516。本文打算来写一下Copter下面IMU的具体驱动,当然对于其他类型的无人机实际上原理也是大同小异的,也可用作参考。在具体开始之前,想先从初始化方式开始,捋清楚了初始化顺序,到后面才不会觉得混乱。如有错误,请务必留言指出。
一、system.cpp
上一篇博文已经说过了,在init_ardupilot()内部无人机将完成大部分设备的初始化工作。ArduCopter的init_ardupilot()这个函数还是很好找的,就实现在ardupilot/ArduCopter/system.cpp文件内部。但是在开始讲解这个函数之前,想先把无人机内部的初始化流程先说一下。
1.1 无人机内部初始化
关于无人机类型及其继承方式,详见我的上一篇博文:APM_ArduCopter源码解析学习(三)——无人机类型
继承关系如下:
AP_HAL::HAL::Callbacks
|---- AP_Vechile
|---- Copter
之前已经说过,AP_Vehicle类在公有部分内部声明有两个函数setup()和loop(),并且在对应的.cpp文件中得到实现;在protected部分提供了一个纯虚的接口函数init_ardupilot(),并且未在AP_Vehicle.cpp中对其进行实现(具体查看我的上一篇博文)。而实际上,在AP_Vehicle.cpp中对setup()进行具体实现的时候调用了init_ardupilot()。
Copter类继承自AP_Vehicle类,并且并未对setup()和loop()进行修改或重写。相对应的,其在内部以private方式继承了fast_loop()和init_ardupilot(),fast_loop()的具体实现在第一篇博文里面已经介绍过了,这边主要来说一下init_ardupilot()。具体路径位于Copter.h。
private:
...
void fast_loop() override;
...
void init_ardupilot() override;
已知Copter类继承了AP_Vehicle类所实现的函数,那么Copter::setup()函数在运行过程中便会调用到Copter::init_ardupilot()函数,而Copter::init_ardupilot则正好实现在system.cpp内部,由此引出我们本次的主要内容。
1.2 Copter::init_ardupilot()
如下内容是针对于Copter的初始化函数。部分内容已备注。
void Copter::init_ardupilot()
{
#if STATS_ENABLED == ENABLED
g2.stats.init();
#endif
BoardConfig.init();
#if HAL_MAX_CAN_PROTOCOL_DRIVERS
can_mgr.init();
#endif
#if GRIPPER_ENABLED == ENABLED
g2.gripper.init();
#endif
#if AC_FENCE == ENABLED
fence.init();
#endif
#if WINCH_ENABLED == ENABLED
g2.winch.init();
#endif
notify.init();
notify_flight_mode();
battery.init();
rssi.init();
barometer.init();
gcs().setup_uarts();
#if GENERATOR_ENABLED
generator.init();
#endif
#if OSD_ENABLED == ENABLED
osd.init();
#endif
#if LOGGING_ENABLED == ENABLED
log_init();
#endif
update_using_interlock();
#if FRAME_CONFIG == HELI_FRAME
heli_init();
#endif
#if FRAME_CONFIG == HELI_FRAME
input_manager.set_loop_rate(scheduler.get_loop_rate_hz());
#endif
init_rc_in();
allocate_motors();
rc().init();
init_rc_out();
esc_calibration_startup_check();
ap.initialised_params = true;
relay.init();
hal.scheduler->register_timer_failsafe(failsafe_check_static, 1000);
gps.set_log_gps_bit(MASK_LOG_GPS);
gps.init(serial_manager);
AP::compass().set_log_bit(MASK_LOG_COMPASS);
AP::compass().init();
#if OPTFLOW == ENABLED
ahrs.set_optflow(&optflow);
#endif
#if AP_TERRAIN_AVAILABLE && AC_TERRAIN
Location::set_terrain(&terrain);
wp_nav->set_terrain(&terrain);
#endif
#if AC_OAPATHPLANNER_ENABLED == ENABLED
g2.oa.init();
#endif
attitude_control->parameter_sanity_check();
pos_control->set_dt(scheduler.get_loop_period_s());
init_optflow();
#if HAL_MOUNT_ENABLED
camera_mount.init();
#endif
#if PRECISION_LANDING == ENABLED
init_precland();
#endif
landinggear.init();
#ifdef USERHOOK_INIT
USERHOOK_INIT
#endif
#if HIL_MODE != HIL_MODE_DISABLED
while (barometer.get_last_update() == 0) {
gcs().send_text(MAV_SEVERITY_WARNING, "Waiting for first HIL_STATE message");
delay(1000);
}
ins.set_hil_mode();
#endif
barometer.set_log_baro_bit(MASK_LOG_IMU);
barometer.calibrate();
init_rangefinder();
init_proximity();
#if BEACON_ENABLED == ENABLED
g2.beacon.init();
#endif
#if RPM_ENABLED == ENABLED
rpm_sensor.init();
#endif
#if MODE_AUTO_ENABLED == ENABLED
mode_auto.mission.init();
#endif
#if MODE_SMARTRTL_ENABLED == ENABLED
g2.smart_rtl.init();
#endif
logger.setVehicle_Startup_Writer(FUNCTOR_BIND(&copter, &Copter::Log_Write_Vehicle_Startup_Messages, void));
startup_INS_ground();
#ifdef ENABLE_SCRIPTING
g2.scripting.init();
#endif
set_land_complete(true);
set_land_complete_maybe(true);
serial_manager.set_blocking_writes_all(false);
failsafe_enable();
ins.set_log_raw_bit(MASK_LOG_IMU_RAW);
if (arming.rc_calibration_checks(true)) {
enable_motor_output();
}
BoardConfig.init_safety();
ap.initialised = true;
}
无论如何请在上述程序段中找到下面这段,该函数可进行地面启动过程中需要的所有惯性传感器校准等工作。
startup_INS_ground();
1.3 Copter::startup_INS_ground()
由此我们跳转到位于同一文件下的该函数内部
void Copter::startup_INS_ground()
{
ahrs.init();
ahrs.set_vehicle_class(AHRS_VEHICLE_COPTER);
ins.init(scheduler.get_loop_rate_hz());
ahrs.reset();
}
注意只有ins.init()这个函数是用于传感器的。
二、库内传感器配置及前后端
跳转到 init() 这个函数里面,其路径是在ardupilot\libraries\AP_InertialSensor\AP_InertialSensor.cpp中。在开始研究之前需要说明一下AP_InertialSensor这个库中的相关结构。
对于这个库,官方的描述是:读取陀螺仪和加速度计数据,执行校准并将数据以标准单位(度/秒,米/秒)提供给主代码和其他库。
其中AP_InertialSensor.cpp/.h文件是用于和APM上层进行连接通讯的前端。而AP_InertialSensor_Backend.cpp/.h则是中间层文件,是IMU驱动程序后端类。 每种支持的陀螺/加速度传感器类型需要具有一个派生自此类的对象,简单来说就是用于派生具体IMU传感器类型的基类。诸如AP_InertialSensor_XXX形式的都是内部提供的派生自AP_InertialSensor_Backend类的各种IMU传感器类型,如ADIS1647、BMI055等等,其中AP_InertialSensor_Invensense用于MPU9250、MPU6000等。
再来回看这个init()函数:
void
AP_InertialSensor::init(uint16_t loop_rate)
{
_loop_rate = loop_rate;
_loop_delta_t = 1.0f / loop_rate;
_loop_delta_t_max = 10 * _loop_delta_t;
if (_gyro_count == 0 && _accel_count == 0) {
_start_backends();
}
for (uint8_t i=0; i<get_accel_count(); i++) {
if (_accel_scale[i].get().is_zero()) {
_accel_scale[i].set(Vector3f(1,1,1));
}
}
if (gyro_calibration_timing() != GYRO_CAL_NEVER) {
init_gyro();
}
_sample_period_usec = 1000*1000UL / _loop_rate;
_delta_time = 0;
_next_sample_usec = 0;
_last_sample_usec = 0;
_have_sample = false;
batchsampler.init();
_calculated_harmonic_notch_freq_hz[0] = _harmonic_notch_filter.center_freq_hz();
_num_calculated_harmonic_notch_frequencies = 1;
for (uint8_t i=0; i<get_gyro_count(); i++) {
_gyro_harmonic_notch_filter[i].allocate_filters(_harmonic_notch_filter.harmonics(), _harmonic_notch_filter.hasOption(HarmonicNotchFilterParams::Options::DoubleNotch));
_gyro_harmonic_notch_filter[i].init(_gyro_raw_sample_rates[i], _calculated_harmonic_notch_freq_hz[0],
_harmonic_notch_filter.bandwidth_hz(), _harmonic_notch_filter.attenuation_dB());
}
}
其他内容大致看看了解一下,这边需要关注的是_start_backends()和init_gyro()这两个函数。_start_backends()用于配置并且开启后端,而init_gyro()完成陀螺仪的校准工作。
backend
这边重点来讲一下IMU传感器在APM后端的配置过程。关于init_gyro()建议阅读源代码学习。
void AP_InertialSensor::_start_backends()
{
detect_backends();
for (uint8_t i = 0; i < _backend_count; i++) {
_backends[i]->start();
}
if (_gyro_count == 0 || _accel_count == 0) {
AP_HAL::panic("INS needs at least 1 gyro and 1 accel");
}
for (uint8_t i=get_accel_count(); i<INS_MAX_INSTANCES; i++) {
_accel_id[i].set(0);
}
for (uint8_t i=get_gyro_count(); i<INS_MAX_INSTANCES; i++) {
_gyro_id[i].set(0);
}
}
在_start_backends()中,首先调用detect_backends()函数,该函数的作用就是检测飞控板上可用的IMU传感器。在detect_backends()函数内部,需要注意的是在检测到功能板之后,该函数会对具体的板子类型进行判断,并且将对应的IMU加入到后端,方便前端调用。原始代码中并未直接定义AP_FEATURE_BOARD_DETECT。具体路径在libraries/AP_BoardConfig/AP_BoardConfig.h中。
AP_BoardConfig.h宏定义部分:
#ifndef AP_FEATURE_BOARD_DETECT
#if defined(HAL_CHIBIOS_ARCH_FMUV3) || defined(HAL_CHIBIOS_ARCH_FMUV4) || defined(HAL_CHIBIOS_ARCH_FMUV5) || defined(HAL_CHIBIOS_ARCH_MINDPXV2) || defined(HAL_CHIBIOS_ARCH_FMUV4PRO) || defined(HAL_CHIBIOS_ARCH_BRAINV51) || defined(HAL_CHIBIOS_ARCH_BRAINV52) || defined(HAL_CHIBIOS_ARCH_UBRAINV51) || defined(HAL_CHIBIOS_ARCH_COREV10) || defined(HAL_CHIBIOS_ARCH_BRAINV54)
#define AP_FEATURE_BOARD_DETECT 1
#else
#define AP_FEATURE_BOARD_DETECT 0
#endif
#endif
detect_backends():
#elif AP_FEATURE_BOARD_DETECT
switch (AP_BoardConfig::get_board_type()) {
case AP_BoardConfig::PX4_BOARD_PX4V1:
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device(HAL_INS_MPU60x0_NAME), ROTATION_NONE));
break;
case AP_BoardConfig::PX4_BOARD_PIXHAWK:
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device(HAL_INS_MPU60x0_NAME), ROTATION_ROLL_180));
ADD_BACKEND(AP_InertialSensor_LSM9DS0::probe(*this,
hal.spi->get_device(HAL_INS_LSM9DS0_G_NAME),
hal.spi->get_device(HAL_INS_LSM9DS0_A_NAME),
ROTATION_ROLL_180,
ROTATION_ROLL_180_YAW_270,
ROTATION_PITCH_180));
break;
case AP_BoardConfig::PX4_BOARD_PIXHAWK2:
_fast_sampling_mask.set_default(1);
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device(HAL_INS_MPU9250_EXT_NAME), ROTATION_PITCH_180));
ADD_BACKEND(AP_InertialSensor_LSM9DS0::probe(*this,
hal.spi->get_device(HAL_INS_LSM9DS0_EXT_G_NAME),
hal.spi->get_device(HAL_INS_LSM9DS0_EXT_A_NAME),
ROTATION_ROLL_180_YAW_270,
ROTATION_ROLL_180_YAW_90,
ROTATION_ROLL_180_YAW_90));
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device(HAL_INS_MPU9250_NAME), ROTATION_YAW_270));
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device("icm20602_ext"), ROTATION_ROLL_180_YAW_270));
ADD_BACKEND(AP_InertialSensor_Invensensev2::probe(*this, hal.spi->get_device("icm20948_ext"), ROTATION_PITCH_180));
ADD_BACKEND(AP_InertialSensor_Invensensev2::probe(*this, hal.spi->get_device("icm20948"), ROTATION_YAW_270));
break;
case AP_BoardConfig::PX4_BOARD_FMUV5:
case AP_BoardConfig::PX4_BOARD_FMUV6:
_fast_sampling_mask.set_default(1);
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device("icm20689"), ROTATION_NONE));
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device("icm20602"), ROTATION_NONE));
ADD_BACKEND(AP_InertialSensor_BMI055::probe(*this,
hal.spi->get_device("bmi055_a"),
hal.spi->get_device("bmi055_g"),
ROTATION_ROLL_180_YAW_90));
ADD_BACKEND(AP_InertialSensor_BMI088::probe(*this,
hal.spi->get_device("bmi055_a"),
hal.spi->get_device("bmi055_g"),
ROTATION_ROLL_180_YAW_90));
break;
case AP_BoardConfig::PX4_BOARD_SP01:
_fast_sampling_mask.set_default(1);
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device(HAL_INS_MPU9250_EXT_NAME), ROTATION_NONE));
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device(HAL_INS_MPU9250_NAME), ROTATION_NONE));
break;
case AP_BoardConfig::PX4_BOARD_PIXHAWK_PRO:
_fast_sampling_mask.set_default(3);
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device(HAL_INS_ICM20608_NAME), ROTATION_ROLL_180_YAW_90));
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device(HAL_INS_MPU9250_NAME), ROTATION_ROLL_180_YAW_90));
break;
case AP_BoardConfig::PX4_BOARD_PHMINI:
_fast_sampling_mask.set_default(3);
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device(HAL_INS_ICM20608_AM_NAME), ROTATION_ROLL_180));
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device(HAL_INS_MPU9250_NAME), ROTATION_ROLL_180));
break;
case AP_BoardConfig::PX4_BOARD_AUAV21:
_fast_sampling_mask.set_default(3);
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device(HAL_INS_ICM20608_AM_NAME), ROTATION_ROLL_180_YAW_90));
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device(HAL_INS_MPU9250_NAME), ROTATION_ROLL_180_YAW_90));
break;
case AP_BoardConfig::PX4_BOARD_PH2SLIM:
_fast_sampling_mask.set_default(1);
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device(HAL_INS_MPU9250_NAME), ROTATION_YAW_270));
break;
case AP_BoardConfig::PX4_BOARD_AEROFC:
_fast_sampling_mask.set_default(1);
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device(HAL_INS_MPU6500_NAME), ROTATION_YAW_270));
break;
case AP_BoardConfig::PX4_BOARD_MINDPXV2:
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device(HAL_INS_MPU6500_NAME), ROTATION_NONE));
ADD_BACKEND(AP_InertialSensor_LSM9DS0::probe(*this,
hal.spi->get_device(HAL_INS_LSM9DS0_G_NAME),
hal.spi->get_device(HAL_INS_LSM9DS0_A_NAME),
ROTATION_YAW_90,
ROTATION_YAW_90,
ROTATION_YAW_90));
break;
case AP_BoardConfig::VRX_BOARD_BRAIN54:
_fast_sampling_mask.set_default(7);
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device(HAL_INS_MPU60x0_NAME), ROTATION_YAW_180));
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device(HAL_INS_MPU60x0_EXT_NAME), ROTATION_YAW_180));
在代码段内,会对检测到的板子类型加入对应的IMU传感器到后端,以方便前端的调用。以PX4_BOARD_PIXHAWK为例,该case语句里面包含了两个ADD_BACKEND()函数,AP_InertialSensor_Invensense::probe()函数如下。函数接收3个参数,在原来的detect_backends()中,this指代的就是传感器,hal.spi->get_device()获取由spi驱动的IMU设备,ROTATION_ROLL_180顾名思义就是设置为将传感器默认为旋转180°。
AP_InertialSensor_Backend *AP_InertialSensor_Invensense::probe(AP_InertialSensor &imu,
AP_HAL::OwnPtr<AP_HAL::SPIDevice> dev,
enum Rotation rotation)
{
if (!dev) {
return nullptr;
}
AP_InertialSensor_Invensense *sensor;
dev->set_read_flag(0x80);
sensor = new AP_InertialSensor_Invensense(imu, std::move(dev), rotation);
if (!sensor || !sensor->_init()) {
delete sensor;
return nullptr;
}
if (sensor->_mpu_type == Invensense_MPU9250) {
sensor->_id = HAL_INS_MPU9250_SPI;
} else if (sensor->_mpu_type == Invensense_MPU6500) {
sensor->_id = HAL_INS_MPU6500;
} else {
sensor->_id = HAL_INS_MPU60XX_SPI;
}
return sensor;
}
AP_InertialSensor_Invensense::probe()函数代码内部,由AP_InertialSensor_Invensense新建了一个sensor对象,并通过sensor->_init()完成对应传感器的初始化工作,sensor->_init()内部会调用_hardware_init()(_hardware_init()建议仔细看一下,这里由于太长就不放进来了)对寄存器进行配置。再对sensor对象的mpu类型进行判断,获取id号,最后返回sensor对象。
20201207增加_hardware_init()的功能介绍
bool AP_InertialSensor_Invensense::_hardware_init(void)
{
_dev->get_semaphore()->take_blocking();
_dev->setup_checked_registers(7, _dev->bus_type() == AP_HAL::Device::BUS_TYPE_I2C?200:20);
_dev->set_speed(AP_HAL::Device::SPEED_LOW);
if (!_check_whoami()) {
_dev->get_semaphore()->give();
return false;
}
uint8_t tries;
for (tries = 0; tries < 5; tries++) {
_last_stat_user_ctrl = _register_read(MPUREG_USER_CTRL);
if (_last_stat_user_ctrl & BIT_USER_CTRL_I2C_MST_EN) {
_last_stat_user_ctrl &= ~BIT_USER_CTRL_I2C_MST_EN;
_register_write(MPUREG_USER_CTRL, _last_stat_user_ctrl);
hal.scheduler->delay(10);
}
_register_write(MPUREG_PWR_MGMT_1, BIT_PWR_MGMT_1_DEVICE_RESET);
hal.scheduler->delay(100);
if (_dev->bus_type() == AP_HAL::Device::BUS_TYPE_SPI) {
_last_stat_user_ctrl |= BIT_USER_CTRL_I2C_IF_DIS;
_register_write(MPUREG_USER_CTRL, _last_stat_user_ctrl);
}
if ((_dev->bus_type() == AP_HAL::Device::BUS_TYPE_I2C) && (_mpu_type == Invensense_MPU9250 || _mpu_type == Invensense_ICM20789)) {
_register_write(MPUREG_INT_PIN_CFG, BIT_BYPASS_EN);
}
_register_write(MPUREG_PWR_MGMT_1, BIT_PWR_MGMT_1_CLK_ZGYRO);
hal.scheduler->delay(5);
if (_register_read(MPUREG_PWR_MGMT_1) == BIT_PWR_MGMT_1_CLK_ZGYRO) {
break;
}
hal.scheduler->delay(10);
if (_data_ready()) {
break;
}
}
_dev->set_speed(AP_HAL::Device::SPEED_HIGH);
if (tries == 5) {
hal.console->printf("Failed to boot Invensense 5 times\n");
_dev->get_semaphore()->give();
return false;
}
if (_mpu_type == Invensense_ICM20608 ||
_mpu_type == Invensense_ICM20602 ||
_mpu_type == Invensense_ICM20601) {
_register_write(MPUREG_ICM_UNDOC1, MPUREG_ICM_UNDOC1_VALUE, true);
}
_dev->get_semaphore()->give();
return true;
}
上述代码中,可以看出,在进行传感器的配置过程中,通过一个for循环语句实现了多次传感器的配置工作,从而避免配置一次而导致配置不成功,异常报错,无法使用IMU的问题。for语句如下面所示:
uint8_t tries;
for (tries = 0; tries < 5; tries++) {
_last_stat_user_ctrl = _register_read(MPUREG_USER_CTRL);
if (_last_stat_user_ctrl & BIT_USER_CTRL_I2C_MST_EN) {
_last_stat_user_ctrl &= ~BIT_USER_CTRL_I2C_MST_EN;
_register_write(MPUREG_USER_CTRL, _last_stat_user_ctrl);
hal.scheduler->delay(10);
}
_register_write(MPUREG_PWR_MGMT_1, BIT_PWR_MGMT_1_DEVICE_RESET);
hal.scheduler->delay(100);
if (_dev->bus_type() == AP_HAL::Device::BUS_TYPE_SPI) {
_last_stat_user_ctrl |= BIT_USER_CTRL_I2C_IF_DIS;
_register_write(MPUREG_USER_CTRL, _last_stat_user_ctrl);
}
if ((_dev->bus_type() == AP_HAL::Device::BUS_TYPE_I2C) && (_mpu_type == Invensense_MPU9250 || _mpu_type == Invensense_ICM20789)) {
_register_write(MPUREG_INT_PIN_CFG, BIT_BYPASS_EN);
}
_register_write(MPUREG_PWR_MGMT_1, BIT_PWR_MGMT_1_CLK_ZGYRO);
hal.scheduler->delay(5);
if (_register_read(MPUREG_PWR_MGMT_1) == BIT_PWR_MGMT_1_CLK_ZGYRO) {
break;
}
hal.scheduler->delay(10);
if (_data_ready()) {
break;
}
}
针对于原来的语句
ADD_BACKEND(AP_InertialSensor_Invensense::probe(*this, hal.spi->get_device(HAL_INS_MPU60x0_NAME), ROTATION_ROLL_180));
我们观察ADD_BACKEND()这个宏,具体路径在AP_InertialSensor.cpp中。
#define ADD_BACKEND(x) do { \
if (((1U<<probe_count)&enable_mask) && _add_backend(x)) { \
found_mask |= (1U<<probe_count); \
} \
probe_count++; \
} while (0)
可知最主要的是其中的_add_backend()
bool AP_InertialSensor::_add_backend(AP_InertialSensor_Backend *backend)
{
if (!backend) {
return false;
}
if (_backend_count == INS_MAX_BACKENDS) {
AP_HAL::panic("Too many INS backends");
}
_backends[_backend_count++] = backend;
return true;
}
_add_backend()接收AP_InertialSensor_Backend类型的指针backend,并且将其放入后端对象的数组_backends[]中,方便后续调用。
然后我们再次回到_start_backends()函数(小节开始的那段代码)中,继续向下。可以很清楚地看出,_backends[]数组对于其中的每一个成员调用了start()方法,用于配置并启动所有传感器。
for (uint8_t i = 0; i < _backend_count; i++) {
_backends[i]->start();
}
对于_backends[]数组中的成员,我们刚刚获取到的是AP_InertialSensor_Invensense中的mpu,因此于对应的.cpp文件中查找到对应的函数。这边由于函数内容过长就不全部放上来了,内部具体的实现内容简单说了就是配置一下芯片的参数及量程什么的(不太准确,建议自己去阅读一下)。具体路径为: AP_InertialSensor_Invensense.cpp中void AP_InertialSensor_Invensense::start()函数,然后这个函数最关键的地方就是在最后面:
_dev->register_periodic_callback(1000000UL / _gyro_backend_rate_hz, FUNCTOR_BIND_MEMBER(&AP_InertialSensor_Invensense::_poll_data, void));
这段程序开启了一个新的读取线程,线程运行时间为1000000UL / _gyro_backend_rate_hz,其中_gyro_backend_rate_hz 表示的是the rate we generating samples into the backend for gyros
_gyro_backend_rate_hz = _accel_backend_rate_hz = 1000;
由此可知该读线程运行时间为1000us,即1ms运行一次,通过_poll_data()函数内部调用的_read_fifo()读取mpu内部的数据,因此我们在Copter.cpp中找不到对于imu读取的相关函数,因为在start()函数内部以及实现。
为了方便理清关系,这边把对应的逻辑表示一下
Copter::init_ardupilot()
|---- startup_INS_ground();
|---- ins.init();
|---- _start_backends();
|----detect_backends(); |----_backends[i]->start();
|----ADD_BACKEND(); |----_dev->register_periodic_callback();
三、Copter.cpp
回到最初的Copter.cpp下面的fast_loop(),里面的ins.update()完成了立即更新INS以获取当前的陀螺仪数据的功能。内部具体代码如下
void AP_InertialSensor::update(void)
{
wait_for_sample();
if (!_hil_mode) {
for (uint8_t i=0; i<INS_MAX_INSTANCES; i++) {
_gyro_healthy[i] = false;
_accel_healthy[i] = false;
_delta_velocity_valid[i] = false;
_delta_angle_valid[i] = false;
}
for (uint8_t i=0; i<_backend_count; i++) {
_backends[i]->update();
}
for (uint8_t i = 0; i < INS_MAX_INSTANCES; i++) {
_delta_velocity_acc[i].zero();
_delta_velocity_acc_dt[i] = 0;
_delta_angle_acc[i].zero();
_delta_angle_acc_dt[i] = 0;
}
if (!_startup_error_counts_set) {
for (uint8_t i=0; i<INS_MAX_INSTANCES; i++) {
_accel_startup_error_count[i] = _accel_error_count[i];
_gyro_startup_error_count[i] = _gyro_error_count[i];
}
if (_startup_ms == 0) {
_startup_ms = AP_HAL::millis();
} else if (AP_HAL::millis()-_startup_ms > 2000) {
_startup_error_counts_set = true;
}
}
for (uint8_t i=0; i<INS_MAX_INSTANCES; i++) {
if (_accel_error_count[i] < _accel_startup_error_count[i]) {
_accel_startup_error_count[i] = _accel_error_count[i];
}
if (_gyro_error_count[i] < _gyro_startup_error_count[i]) {
_gyro_startup_error_count[i] = _gyro_error_count[i];
}
}
bool have_zero_accel_error_count = false;
bool have_zero_gyro_error_count = false;
for (uint8_t i=0; i<INS_MAX_INSTANCES; i++) {
if (_accel_healthy[i] && _accel_error_count[i] <= _accel_startup_error_count[i]) {
have_zero_accel_error_count = true;
}
if (_gyro_healthy[i] && _gyro_error_count[i] <= _gyro_startup_error_count[i]) {
have_zero_gyro_error_count = true;
}
}
for (uint8_t i=0; i<INS_MAX_INSTANCES; i++) {
if (_gyro_healthy[i] && _gyro_error_count[i] > _gyro_startup_error_count[i] && have_zero_gyro_error_count) {
_gyro_healthy[i] = false;
}
if (_accel_healthy[i] && _accel_error_count[i] > _accel_startup_error_count[i] && have_zero_accel_error_count) {
_accel_healthy[i] = false;
}
}
for (uint8_t i=0; i<INS_MAX_INSTANCES; i++) {
if (_gyro_healthy[i] && _use[i]) {
_primary_gyro = i;
break;
}
}
for (uint8_t i=0; i<INS_MAX_INSTANCES; i++) {
if (_accel_healthy[i] && _use[i]) {
_primary_accel = i;
break;
}
}
}
_last_update_usec = AP_HAL::micros();
_have_sample = false;
}
注意到内部的后端更新函数
for (uint8_t i=0; i<_backend_count; i++) {
_backends[i]->update();
}
类似于_backends[i]->start()的调用方式,该函数内部如下。
bool AP_InertialSensor_Invensense::update()
{
update_accel(_accel_instance);
update_gyro(_gyro_instance);
_publish_temperature(_accel_instance, _temp_filtered);
return true;
}
由此可以看到AP_InertialSensor::update()这个函数确实完成了后端和前端的通讯功能,如前所述,AP_InertialSensor.h/.cpp这两份文件主要实现的就是前后端的连接。
目前先写到这,后面有时间再更新
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)