Ardupilot — AP_RangeFinder代码梳理

简介: Ardupilot — AP_RangeFinder代码梳理

前言

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

RNGFND1_TYPE:测距仪类型

连接的测距仪类型。


Values

Value

Meaning

0

None

1

Analog

2

MaxbotixI2C

3

LidarLite-I2C

5

PWM

6

BBB-PRU

7

LightWareI2C

8

LightWareSerial

9

Bebop

10

MAVLink

11

USD1_Serial

12

LeddarOne

13

MaxbotixSerial

14

TeraRangerI2C

15

LidarLiteV3-I2C

16

VL53L0X or VL53L1X

17

NMEA

18

WASP-LRF

19

BenewakeTF02

20

Benewake-Serial

21

LidarLightV3HP

22

PWM

23

BlueRoboticsPing

24

DroneCAN

25

BenewakeTFminiPlus-I2C

26

LanbaoPSK-CM8JL65-CC5

27

BenewakeTF03

28

VL53L1X-ShortRange

29

LeddarVu8-Serial

30

HC-SR04

31

GYUS42v2

32

MSP

33

USD1_CAN

34

Benewake_CAN

35

TeraRangerSerial

36

Lua_Scripting

37

NoopLoop_TOFSense

100

SITL

显示详细信息

本文主要梳理,Ardupilot 中测距仪的代码运行流程,以及如何选择相应的测距仪类型。

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()
{
    ...
 
    // initialise rangefinder
    init_rangefinder();
 
    ...
}

3 sensors.cpp

3.1 void Copter::init_rangefinder(void)

测距仪初始化。此函数会初始化朝下的测距仪。

void Copter::init_rangefinder(void)
{
#if RANGEFINDER_ENABLED == ENABLED
   rangefinder.set_log_rfnd_bit(MASK_LOG_CTUN);
   rangefinder.init(ROTATION_PITCH_270);
   rangefinder_state.alt_cm_filt.set_cutoff_frequency(RANGEFINDER_WPNAV_FILT_HZ);
   rangefinder_state.enabled = rangefinder.has_orientation(ROTATION_PITCH_270);
 
   // upward facing range finder
   rangefinder_up_state.alt_cm_filt.set_cutoff_frequency(RANGEFINDER_WPNAV_FILT_HZ);
   rangefinder_up_state.enabled = rangefinder.has_orientation(ROTATION_PITCH_90);
#endif
}

3.2 对象rangefinder说明

AP_Vehicle.h 文件中,我们用 RangeFinder 类定义了 rangefinder 对象。

    // sensor drivers
    AP_GPS gps;
    AP_Baro barometer;
    Compass compass;
    AP_InertialSensor ins;
    AP_Button button;
    RangeFinder rangefinder;

4 RangeFinder.cpp

4.1 void RangeFinder::init(enum Rotation orientation_default)

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

初始化 RangeFinder 类。我们将在这里检测已连接的测距仪。目前我们还不允许热插拔测距仪。

/*
  initialise the RangeFinder class. We do detection of attached range
  finders here. For now we won't allow for hot-plugging of
  rangefinders.
 */
void RangeFinder::init(enum Rotation orientation_default)
{
    if (num_instances != 0) {
        // init called a 2nd time?
        return;
    }
 
    convert_params();
 
    // set orientation defaults
    for (uint8_t i=0; i<RANGEFINDER_MAX_INSTANCES; i++) {
        params[i].orientation.set_default(orientation_default);
    }
 
    for (uint8_t i=0, serial_instance = 0; i<RANGEFINDER_MAX_INSTANCES; i++) {
        // serial_instance will be increased inside detect_instance
        // if a serial driver is loaded for this instance
        detect_instance(i, serial_instance);
        if (drivers[i] != nullptr) {
            // we loaded a driver for this instance, so it must be
            // present (although it may not be healthy)
            num_instances = i+1;
        }
 
        // initialise status
        state[i].status = RangeFinder_NotConnected;
        state[i].range_valid_count = 0;
    }
}

4.2 void RangeFinder::detect_instance(uint8_t instance, uint8_t& serial_instance)

检测是否连接了测距仪实例。

根据我们在文章开始介绍的 RNGFND1_TYPE 参数,选择不同传感器进行检测。

此处,我们以 RNGFND1_TYPE(16) AP_RangeFinder_VL53L3CX 为例进行介绍。

/*
  detect if an instance of a rangefinder is connected.
 */
void RangeFinder::detect_instance(uint8_t instance, uint8_t& serial_instance)
{
    enum RangeFinder_Type _type = (enum RangeFinder_Type)params[instance].type.get();
    switch (_type) {
    case RangeFinder_TYPE_PLI2C:
    case RangeFinder_TYPE_PLI2CV3:
    case RangeFinder_TYPE_PLI2CV3HP:
        FOREACH_I2C(i) {
            if (_add_backend(AP_RangeFinder_PulsedLightLRF::detect(i, state[instance], params[instance], _type))) {
                break;
            }
        }
        break;
    
    ...
 
    case RangeFinder_TYPE_VL53L0X:
            FOREACH_I2C(i) {
                if (_add_backend(AP_RangeFinder_VL53L0X::detect(state[instance], params[instance],
                                                                 hal.i2c_mgr->get_device(i, params[instance].address)))) {
                    break;
                }
 
                if (_add_backend(AP_RangeFinder_VL53L1X::detect(state[instance], params[instance],
                                                                hal.i2c_mgr->get_device(i, params[instance].address)))) {
                    break;
                }
 
                if (_add_backend(AP_RangeFinder_VL53L3CX::detect(state[instance], params[instance],
                                                                hal.i2c_mgr->get_device(i, params[instance].address)))) {
                    break;
                }
            }
        break;
 
    ...
}

5 I2CDevice.cpp

5.1 hal.i2c_mgr->get_device(i, params[instance].address)

如果当前的 bus 没有超过 i2c_bus_desc,会 new 一个新的 I2CDevice 类对象返回。

AP_HAL::OwnPtr<AP_HAL::I2CDevice>
I2CDeviceManager::get_device(uint8_t bus, uint8_t address,
                             uint32_t bus_clock,
                             bool use_smbus,
                             uint32_t timeout_ms)
{
    if (bus >= ARRAY_SIZE(i2c_bus_desc)) {
        return AP_HAL::OwnPtr<AP_HAL::I2CDevice>(nullptr);
    }
    auto dev = AP_HAL::OwnPtr<AP_HAL::I2CDevice>(new I2CDevice(bus, address, bus_clock, use_smbus, timeout_ms));
    return dev;
}

6 AP_RangeFinder_VL53L3CX.cpp

6.1 AP_RangeFinder_Backend *AP_RangeFinder_VL53L3CX::detect(...)

检测是否连接了 VL53L3CX 测距仪。我们将通过 I2C 读取数据来进行检测。如果得到结果,则表示传感器已连接。

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

然后读取测距仪固有的产品 ID 是否正确(sensor->check_id()),再对测距仪进行必要的初始化(sensor->init())。

/*
   detect if a VL53L3CX rangefinder is connected. We'll detect by
   trying to take a reading on I2C. If we get a result the sensor is
   there.
*/
AP_RangeFinder_Backend *AP_RangeFinder_VL53L3CX::detect(RangeFinder::RangeFinder_State &_state, AP_RangeFinder_Params &_params, AP_HAL::OwnPtr<AP_HAL::I2CDevice> dev)
{
    if (!dev) {
        return nullptr;
    }
 
    hal.console->printf("VL53L3CX: detect...\n");
 
    AP_RangeFinder_VL53L3CX *sensor = new AP_RangeFinder_VL53L3CX(_state, _params, std::move(dev));
 
    if (!sensor) {
        delete sensor;
        hal.console->printf("VL53L3CX: delete sensor...\n");
        return nullptr;
    }
 
    sensor->dev->get_semaphore()->take_blocking();
 
    if (!sensor->check_id() || !sensor->init())
    {
        sensor->dev->get_semaphore()->give();
        delete sensor;
        return nullptr;
    }
 
    sensor->dev->get_semaphore()->give();
 
    return sensor;
}

6.2 bool AP_RangeFinder_VL53L3CX::check_id(void)

检查测距仪 ID 寄存器。每种类型测距仪的 ID 寄存器都有唯一值。

// check sensor ID registers
bool AP_RangeFinder_VL53L3CX::check_id(void)
{
    uint8_t v1 = 0;
    uint8_t v2 = 0;
    if (!(read_register(0x010F, v1) && read_register(0x0110, v2))) {
        hal.console->printf("VL53L3CX: v1=0x%02x  v2=0x%02x\n", v1, v2);
        return false;
    }
 
    if ((v1 != 0xEA) || (v2 != 0xAA)) {
        hal.console->printf("VL53L3CX: Not a recognised device.\n");
        return false;
    }
 
    printf("Detected VL53L3CX on bus 0x%x.\n", dev->get_bus_id());
    return true;
}

6.3 bool AP_RangeFinder_VL53L3CX::init()

初始化传感器,并注册测距仪的周期运行函数 timer()

/*
  initialise sensor
 */
bool AP_RangeFinder_VL53L3CX::init()
{
    ...
    通过I2C向传感器寄存器写入初始值。
    ...
 
    // call timer() every 50ms. We expect new data to be available every 50ms
    dev->register_periodic_callback(50000, FUNCTOR_BIND_MEMBER(&AP_RangeFinder_VL53L3CX::timer, void));
 
    return true;
}

6.4 void AP_RangeFinder_VL53L3CX::timer(void)

20Hz 调用一次该函数,读取测距仪的测量值。

/*
  timer called at 20Hz
*/
void AP_RangeFinder_VL53L3CX::timer(void)
{
    uint16_t range_mm;
    if ((get_reading(range_mm)) && (range_mm <= 4000)) {
        WITH_SEMAPHORE(_sem);
        sum_mm += range_mm;
        // printf("Range_mm: %dmm\n", range_mm);
        counter++;
    }
}

6.5 SCHED_TASK(read_rangefinder,      20,    100)

Copter.cpp 文件的周期任务列表中,注册了调用频率为 20Hzread_rangefinder() 函数。

const AP_Scheduler::Task Copter::scheduler_tasks[] = {
...
 
#if RANGEFINDER_ENABLED == ENABLED
    SCHED_TASK(read_rangefinder,      20,    100),
#endif
...
}

6.6 void Copter::read_rangefinder(void)

sensors.cpp 文件中,以厘米为单位返回测距仪高度。

// return rangefinder altitude in centimeters
void Copter::read_rangefinder(void)
{
#if RANGEFINDER_ENABLED == ENABLED
    rangefinder.update();
...
}

6.7 void AP_RangeFinder_VL53L3CX::update(void)

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

/*
   update the state of the sensor
*/
void AP_RangeFinder_VL53L3CX::update(void)
{
    WITH_SEMAPHORE(_sem);
    if (counter > 0) {
        state.distance_cm = sum_mm / (10*counter);
        state.last_reading_ms = AP_HAL::millis();
        update_status();
        sum_mm = 0;
        counter = 0;
        // printf("VL53L3CX: %dcm\n", state.distance_cm);
    } else if (AP_HAL::millis() - state.last_reading_ms > 200) {
        // if no updates for 0.2s set no-data
        set_status(RangeFinder::RangeFinder_NoData);
    }
}


相关文章
|
传感器
STM32:红外传感器代码部分(内含实物图+外部信号流程,编写代码思路+代码+解析代码和扩展应用)
STM32:红外传感器代码部分(内含实物图+外部信号流程,编写代码思路+代码+解析代码和扩展应用)
3858 1
STM32:红外传感器代码部分(内含实物图+外部信号流程,编写代码思路+代码+解析代码和扩展应用)
|
7月前
|
传感器
Ardupilot — AP_OpticalFlow代码梳理
Ardupilot — AP_OpticalFlow代码梳理
56 0
|
异构计算
【FPGA】基本实验步骤演示 | Verilog编码 | 运行合成 | 设备/引脚分配 | 综合/实施 | 设备配置 | 以最简单的逻辑非为例
【FPGA】基本实验步骤演示 | Verilog编码 | 运行合成 | 设备/引脚分配 | 综合/实施 | 设备配置 | 以最简单的逻辑非为例
105 0
|
7月前
|
安全 AndFix
swap/dapp/lp只涨不跌项目系统开发详细程序/案例开发/功能逻辑/需求设计/源码技术
Developing an LP (Liquidity Pool) system that only rises without falling requires the following steps
|
7月前
|
自然语言处理 搜索推荐 算法
Metaforce佛萨奇2.0丨3.0系统开发稳定版/需求设计/功能说明/案例项目/逻辑方案/源码程序
Metaforce佛萨奇系统是一个基于人工智能技术的虚拟助手系统,
|
API 数据处理
2022年十月份电赛OpenMV巡线方案(2)---主控代码详细分析
2022年十月份电赛OpenMV巡线方案(2)---主控代码详细分析
186 0
|
Windows
nxp官方uboot移植到野火开发板PRO(无任何代码逻辑的修改)
nxp官方uboot移植到野火开发板PRO(无任何代码逻辑的修改)
165 0
硬件开发笔记(十): 硬件开发基本流程,制作一个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封装库并关联原理图元器件
硬件开发笔记(八): 硬件开发基本流程,制作一个USB转RS232的模块(七):创建基础DIP元器件(晶振)封装并关联原理图元器件
有了原理图,可以设计硬件PCB,在设计PCB之间还有一个协同优先动作,就是映射封装,原理图库的元器件我们是自己设计的。为了更好的表述封装设计过程,本文描述了创建晶振封装(DIP),将原理图的元器件关联引脚封装。
硬件开发笔记(八): 硬件开发基本流程,制作一个USB转RS232的模块(七):创建基础DIP元器件(晶振)封装并关联原理图元器件