Ardupilot — AP_OpticalFlow代码梳理

简介: Ardupilot — AP_OpticalFlow代码梳理

前言

故事的开始,要从参数 FLOW_TYPE 说起。

FLOW_TYPE:光学流量传感器类型


RebootRequired

Values

True

Value

Meaning

0

None

1

PX4Flow

2

Pixart

3

Bebop

4

CXOF

5

MAVLink

6

UAVCAN

显示详细信息

1 Copter.cpp

1.1 void Copter::setup()

此函数仅在启动时调用一次。用于初始化一些必要的任务。此函数由 HAL 中的 main() 函数调用。

void Copter::setup()
{
    // Load the default values of variables listed in var_info[]s
    AP_Param::setup_sketch_defaults();
 
    // setup storage layout for copter
    StorageManager::set_layout_copter();
 
    init_ardupilot();
 
    // initialise the main loop scheduler
    scheduler.init(&scheduler_tasks[0], ARRAY_SIZE(scheduler_tasks), MASK_LOG_PM);
}

2 system.cpp

2.1 void Copter::init_ardupilot()

init_ardupilot() 函数将处理空中重启所需的一切。稍后将确定飞行器是否真的在地面上,并在这种情况下处理地面启动。

void Copter::init_ardupilot()
{
    ...
 
    // init the optical flow sensor
    init_optflow();
 
    ...
}

3 sensors.cpp

3.1 void Copter::init_optflow()

初始化光流传感器。

// initialise optical flow sensor
void Copter::init_optflow()
{
#if OPTFLOW == ENABLED
    // initialise optical flow sensor
    optflow.init(MASK_LOG_OPTFLOW);
#endif      // OPTFLOW == ENABLED
}

3.2 对象optflow说明

Copter.h 文件中,我们用 OpticalFlow 类定义了 optflow 对象。

    // Optical flow sensor
#if OPTFLOW == ENABLED
    OpticalFlow optflow;
#endif

4 OpticalFlow.cpp

4.1 void OpticalFlow::init(uint32_t log_bit)

所以,我们在跳转 init() 这个成员函数的时候,跳转到 OpticalFlow 类的 init() 函数。

根据参数 FLOW_TYPE,选择不同的光流传感器进行检测。

本例以 Pixart(2)作为示例。

void OpticalFlow::init(uint32_t log_bit)
{
     _log_bit = log_bit;
 
    // return immediately if not enabled or backend already created
    if ((_type == (int8_t)OpticalFlowType::NONE) || (backend != nullptr)) {
        return;
    }
 
    switch ((OpticalFlowType)_type.get()) {
    case OpticalFlowType::NONE:
        break;
    case OpticalFlowType::PX4FLOW:
        backend = AP_OpticalFlow_PX4Flow::detect(*this);
        break;
    case OpticalFlowType::PIXART:
        backend = AP_OpticalFlow_Pixart::detect("pixartflow", *this);
        if (backend == nullptr) {
            backend = AP_OpticalFlow_Pixart::detect("pixartPC15", *this);
        }
        break;
    case OpticalFlowType::BEBOP:
#if CONFIG_HAL_BOARD_SUBTYPE == HAL_BOARD_SUBTYPE_LINUX_BEBOP
        backend = new AP_OpticalFlow_Onboard(*this);
#endif
        break;
    case OpticalFlowType::CXOF:
        backend = AP_OpticalFlow_CXOF::detect(*this);
        break;
    case OpticalFlowType::MAVLINK:
        backend = AP_OpticalFlow_MAV::detect(*this);
        break;
    case OpticalFlowType::UAVCAN:
#if HAL_WITH_UAVCAN
        backend = new AP_OpticalFlow_HereFlow(*this);
#endif
        break;
    case OpticalFlowType::SITL:
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
        backend = new AP_OpticalFlow_SITL(*this);
#endif
        break;
    }
 
    if (backend != nullptr) {
        backend->init();
    }
}

5 AP_OpticalFlow_Pixart.cpp

5.1 AP_OpticalFlow_Pixart *AP_OpticalFlow_Pixart::detect(...)

首先会根据传入的参数,new 一个 AP_OpticalFlow_Pixart 类对象。

然后,检测光流传感器的产品 ID,再配置传感器(sensor->setup_sensor())。

// detect the device
AP_OpticalFlow_Pixart *AP_OpticalFlow_Pixart::detect(const char *devname, OpticalFlow &_frontend)
{
    AP_OpticalFlow_Pixart *sensor = new AP_OpticalFlow_Pixart(devname, _frontend);
    if (!sensor) {
        return nullptr;
    }
    if (!sensor->setup_sensor()) {
        delete sensor;
        return nullptr;
    }
    return sensor;
}

5.2 bool AP_OpticalFlow_Pixart::setup_sensor(void)

先读取光流传感器的产品 ID,识别光流传感器类型。再根据类型的不同,写入不同的参数配置光流传感器。

最后,注册光流传感器的周期运行函数 timer()

// setup the device
bool AP_OpticalFlow_Pixart::setup_sensor(void)
{
    ...
    // check product ID
    uint8_t id1 = reg_read(PIXART_REG_PRODUCT_ID);
    uint8_t id2;
    if (id1 == 0x3f) {
        id2 = reg_read(PIXART_REG_INV_PROD_ID);
    } else {
        id2 = reg_read(PIXART_REG_INV_PROD_ID2);
    }
    debug("id1=0x%02x id2=0x%02x ~id1=0x%02x\n", id1, id2, uint8_t(~id1));
    if (id1 == 0x3F && id2 == uint8_t(~id1)) {
        model = PIXART_3900;
    } else if (id1 == 0x49 && id2 == uint8_t(~id1)) {
        model = PIXART_3901;
    } else {
        debug("Not a recognised device\n");
        return false;
    }
 
    if (model == PIXART_3900) {
        srom_download();
 
        id = reg_read(PIXART_REG_SROM_ID);
        if (id != srom_id) {
            debug("Pixart: bad SROM ID: 0x%02x\n", id);
            return false;
        }
 
        reg_write(PIXART_REG_SROM_EN, 0x15);
        hal.scheduler->delay(10);
 
        crc = reg_read16u(PIXART_REG_DOUT_L);
        if (crc != 0xBEEF) {
            debug("Pixart: bad SROM CRC: 0x%04x\n", crc);
            return false;
        }
    }
 
    if (model == PIXART_3900) {
        load_configuration(init_data_3900, ARRAY_SIZE(init_data_3900));
    } else {
        load_configuration(init_data_3901_1, ARRAY_SIZE(init_data_3901_1));
        hal.scheduler->delay(100);
        load_configuration(init_data_3901_2, ARRAY_SIZE(init_data_3901_2));
    }
 
    hal.scheduler->delay(50);
 
    debug("Pixart %s ready\n", model==PIXART_3900?"3900":"3901");
 
    integral.last_frame_us = AP_HAL::micros();
 
    _dev->register_periodic_callback(2000, FUNCTOR_BIND_MEMBER(&AP_OpticalFlow_Pixart::timer, void));
    return true;
}

5.3 void AP_OpticalFlow_Pixart::timer(void)

2ms 调用一次 timer() 函数,读取光流传感器的测量值。

可以将 #if 0 #endif 屏蔽,打开调试信息。

void AP_OpticalFlow_Pixart::timer(void)
{
    if (AP_HAL::micros() - last_burst_us < 500) {
        return;
    }
    motion_burst();
    last_burst_us = AP_HAL::micros();
 
    uint32_t dt_us = last_burst_us - integral.last_frame_us;
    float dt = dt_us * 1.0e-6;
    const Vector3f &gyro = AP::ahrs_navekf().get_gyro();
 
    {
        WITH_SEMAPHORE(_sem);
 
        integral.sum.x += burst.delta_x;
        integral.sum.y += burst.delta_y;
        integral.sum_us += dt_us;
        integral.last_frame_us = last_burst_us;
        integral.gyro += Vector2f(gyro.x, gyro.y) * dt;
    }
 
#if 0
    static uint32_t last_print_ms;
    static int fd = -1;
    if (fd == -1) {
        fd = open("/dev/ttyACM0", O_WRONLY);
    }
    // used for debugging
    static int32_t sum_x;
    static int32_t sum_y;
    sum_x += burst.delta_x;
    sum_y += burst.delta_y;
 
    uint32_t now = AP_HAL::millis();
    if (now - last_print_ms >= 100 && (sum_x != 0 || sum_y != 0)) {
        last_print_ms = now;
        dprintf(fd, "Motion: %d %d obs:0x%02x squal:%u rds:%u maxr:%u minr:%u sup:%u slow:%u\n",
               (int)sum_x, (int)sum_y, (unsigned)burst.squal, (unsigned)burst.rawdata_sum, (unsigned)burst.max_raw,
               (unsigned)burst.max_raw, (unsigned)burst.min_raw, (unsigned)burst.shutter_upper, (unsigned)burst.shutter_lower);
        sum_x = sum_y = 0;
    }
#endif
}

5.4 void AP_OpticalFlow_Pixart::motion_burst(void)

通过 SPI 读取光流传感器运动值。

void AP_OpticalFlow_Pixart::motion_burst(void)
{
    uint8_t *b = (uint8_t *)&burst;
 
    burst.delta_x = 0;
    burst.delta_y = 0;
 
    _dev->set_chip_select(true);
    uint8_t reg = model==PIXART_3900?PIXART_REG_MOT_BURST:PIXART_REG_MOT_BURST2;
 
    _dev->transfer(&reg, 1, nullptr, 0);
    hal.scheduler->delay_microseconds(150);
 
    for (uint8_t i=0; i<sizeof(burst); i++) {
        _dev->transfer(nullptr, 0, &b[i], 1);
        if (i == 0 && (burst.motion & 0x80) == 0) {
            // no motion, save some bus bandwidth
            _dev->set_chip_select(false);
            return;
        }
    }
    _dev->set_chip_select(false);
}

5.5 SCHED_TASK_CLASS(OpticalFlow,          &copter.optflow,             update,         200, 160),

Copter.cpp 文件的周期任务列表中,注册了调用频率为 200Hz 的光流传感器 update() 函数。

const AP_Scheduler::Task Copter::scheduler_tasks[] = {
...
 
#if OPTFLOW == ENABLED
    SCHED_TASK_CLASS(OpticalFlow,          &copter.optflow,             update,         200, 160),
#endif
...
}

5.6 void AP_OpticalFlow_Pixart::update(void)

根据 5.1 中的返回的 AP_OpticalFlow_Pixart 对象,调用 AP_OpticalFlow_Pixart 类中的 update() 函数。

更新 - 从传感器读取最新数值,并填入 xy 和总数。

// update - read latest values from sensor and fill in x,y and totals.
void AP_OpticalFlow_Pixart::update(void)
{
    uint32_t now = AP_HAL::millis();
    if (now - last_update_ms < 100) {
        return;
    }
    last_update_ms = now;
 
    struct OpticalFlow::OpticalFlow_state state;
    state.surface_quality = burst.squal;
 
    if (integral.sum_us > 0) {
        WITH_SEMAPHORE(_sem);
 
        const Vector2f flowScaler = _flowScaler();
        float flowScaleFactorX = 1.0f + 0.001f * flowScaler.x;
        float flowScaleFactorY = 1.0f + 0.001f * flowScaler.y;
        float dt = integral.sum_us * 1.0e-6;
 
        state.flowRate = Vector2f(integral.sum.x * flowScaleFactorX,
                                  integral.sum.y * flowScaleFactorY);
        state.flowRate *= flow_pixel_scaling / dt;
 
        // we only apply yaw to flowRate as body rate comes from AHRS
        _applyYaw(state.flowRate);
 
        state.bodyRate = integral.gyro / dt;
 
        integral.sum.zero();
        integral.sum_us = 0;
        integral.gyro.zero();
    } else {
        state.flowRate.zero();
        state.bodyRate.zero();
    }
 
    // copy results to front end
    _update_frontend(state);
}


相关文章
|
传感器
STM32:红外传感器代码部分(内含实物图+外部信号流程,编写代码思路+代码+解析代码和扩展应用)
STM32:红外传感器代码部分(内含实物图+外部信号流程,编写代码思路+代码+解析代码和扩展应用)
3125 0
STM32:红外传感器代码部分(内含实物图+外部信号流程,编写代码思路+代码+解析代码和扩展应用)
|
2天前
|
存储 Python
基于树莓派的流星雨监测系统(RMS)的进一步改造(1)
本文介绍了如何搭建和改造流星雨监测系统,主要涉及两个步骤。首先,文章提供了访问[此处链接](https://blog.csdn.net/delacroix_xu/article/details/119813807)来了解如何搭建系统。接着,针对系统输出的.bin文件格式,作者改造了FRbinViewer.py脚本,增加了输出MP4和GIF格式的功能。改造后的脚本可以根据用户选择将检测到的流星雨帧保存为.gif或.mp4格式,并提供了相应的参数设置。此外,文章还包含了代码示例以展示如何实现这一功能。
|
27天前
|
传感器
Ardupilot — AP_RangeFinder代码梳理
Ardupilot — AP_RangeFinder代码梳理
18 0
|
21天前
|
设计模式 安全 Java
老系统重构系列--如何用一套流程接入所有业务线
**摘要:** 本文介绍了老系统改造的过程,作者提出,ToB业务的挑战在于需要支持多种差异化的业务需求,而模板模式在处理这种需求时可能会导致继承关系复杂和粒度过粗。为了解决这些问题,文章提出了以下步骤: 1. **梳理流程差异点**:识别不同业务流程的差异,以便确定扩展点。 2. **领域模型梳理**:区分核心域和支撑域,确保核心域的稳定性。 3. **二次抽象隔离层**:创建隔离层,避免核心域因新业务接入而变得不稳定。 4. **基于SPI的扩展体系建设**:选择了COLA-SPI实现扩展点,允许业务域定义接口并实现差异化的流程逻辑。
|
7月前
|
异构计算
【FPGA】基本实验步骤演示 | Verilog编码 | 运行合成 | 设备/引脚分配 | 综合/实施 | 设备配置 | 以最简单的逻辑非为例
【FPGA】基本实验步骤演示 | Verilog编码 | 运行合成 | 设备/引脚分配 | 综合/实施 | 设备配置 | 以最简单的逻辑非为例
57 0
|
30天前
MSR04X1 串行通信模块程序调入方案
MSR04X1 串行通信模块程序调入方案
|
11月前
|
API 数据处理
2022年十月份电赛OpenMV巡线方案(2)---主控代码详细分析
2022年十月份电赛OpenMV巡线方案(2)---主控代码详细分析
108 0
|
Ubuntu Linux 数据安全/隐私保护
ZYNQ - 嵌入式Linux开发 -06- petalinux设计流程
ZYNQ - 嵌入式Linux开发 -06- petalinux设计流程
542 0
ZYNQ - 嵌入式Linux开发 -06- petalinux设计流程
硬件开发笔记(十): 硬件开发基本流程,制作一个USB转RS232的模块(九):创建CH340G/MAX232封装库sop-16并关联原理图元器件
有了原理图,可以设计硬件PCB,在设计PCB之间还有一个协同优先动作,就是映射封装,原理图库的元器件我们是自己设计的。为了更好的表述封装设计过程,本文描述了CH340G和MAX232芯片封装创建(SOP-16),并将原理图的元器件关联引脚封装。
硬件开发笔记(十): 硬件开发基本流程,制作一个USB转RS232的模块(九):创建CH340G/MAX232封装库sop-16并关联原理图元器件
硬件开发笔记(九): 硬件开发基本流程,制作一个USB转RS232的模块(八):创建asm1117-3.3V封装库并关联原理图元器件
有了原理图,可以设计硬件PCB,在设计PCB之间还有一个协同优先动作,就是映射封装,原理图库的元器件我们是自己设计的。为了更好的表述封装设计过程,本文描述了一个创建asm1117-3.3V封装,将原理图的元器件关联引脚封装。
硬件开发笔记(九): 硬件开发基本流程,制作一个USB转RS232的模块(八):创建asm1117-3.3V封装库并关联原理图元器件