ArduPilot开源代码之AP_RangeFinder

本文结合代码研读Ardupilot中AP_RangeFinder的应用,其主要用于测量对地距离。介绍了框架设计,包括启动代码对传感器检测初始化、任务代码循环遍历更新数据;还提及重要例程,如状态更新和传感设备检测,最后指出传感器因总线和协议有差异,在驱动代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 源由

AP_RangeFinder的应用的主要用途是用于测量对地距离的,这个与大家通常理解的障碍物避障还是有比较大的差异的。

主要的应用有:

其他应用有:

本次结合代码进行研读,其中也请注意两个应用类型的传感器:upward facing rangefinder和downward facing rangefinder。

2. 框架设计

启动代码:

Copter::init_ardupilot
 └──> Copter::init_rangefinder
     └──> rangefinder.init

任务代码:

SCHED_TASK(read_rangefinder,      20,    100,  33),
 └──> Copter::read_rangefinder
     └──> rangefinder.update

2.1 启动代码

主要作用是对传感器设备进行检测和初始化。

RangeFinder::init
 ├──> <num_instances != 0> // don't re-init if we've found some sensors already
 │   └──> return
 ├──> <uint8_t i=0; i<RANGEFINDER_MAX_INSTANCES; i++> // set orientation defaults
 │   └──> params[i].orientation.set_default(orientation_default)
 ├──> <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
 │   ├──> WITH_SEMAPHORE(detect_sem);
 │   ├──> detect_instance(i, serial_instance);
 │   └──> <drivers[i] != nullptr>
 │       │  // we loaded a driver for this instance, so it must be
 │       │  // present (although it may not be healthy). We use MAX()
 │       │  // here as a UAVCAN rangefinder may already have been
 │       │  // found
 │       └──> num_instances = MAX(num_instances, i+1);
 └──> [initialise status]
     ├──> state[i].status = Status::NotConnected;
     └──> state[i].range_valid_count = 0;

2.2 任务代码

以固定的频率进行循环遍历,得到相应的数据更新。

// return rangefinder altitude in centimeters
Copter::read_rangefinder
 ├──> <RANGEFINDER_ENABLED != ENABLED>
 │   ├──> [downward facing rangefinder]
 │   │   ├──> rangefinder_state.enabled = false;
 │   │   ├──> rangefinder_state.alt_healthy = false;
 │   │   └──> rangefinder_state.alt_cm = 0;
 │   ├──> [upward facing rangefinder]
 │   │   ├──> rangefinder_up_state.enabled = false;
 │   │   ├──> rangefinder_up_state.alt_healthy = false;
 │   │   └──> rangefinder_up_state.alt_cm = 0;
 │   └──> return
 ├──> rangefinder.update();
 ├──> <RANGEFINDER_TILT_CORRECTION == ENABLED>
 │   └──> const float tilt_correction = MAX(0.707f, ahrs.get_rotation_body_to_ned().c.z);
 ├──> <RANGEFINDER_TILT_CORRECTION != ENABLED>
 │   └──> const float tilt_correction = 1.0f;
 │
 │
 │  // iterate through downward and upward facing lidar
 │  struct {
 │      RangeFinderState &state;
 │      enum Rotation orientation;
 │  } rngfnd[2] = {{rangefinder_state, ROTATION_PITCH_270}, {rangefinder_up_state, ROTATION_PITCH_90}};
 │
 └──> <for (uint8_t i=0; i < ARRAY_SIZE(rngfnd); i++)>
     ├──> [local variables to make accessing simpler]
     │   ├──> RangeFinderState &rf_state = rngfnd[i].state;
     │   └──> enum Rotation rf_orient = rngfnd[i].orientation;
     ├──> [update health]
     │   └──> rf_state.alt_healthy = ((rangefinder.status_orient(rf_orient) == RangeFinder::Status::Good) &&
     │                          (rangefinder.range_valid_count_orient(rf_orient) >= RANGEFINDER_HEALTH_MAX));
     ├──> [tilt corrected but unfiltered, not glitch protected alt]
     │   └──> rf_state.alt_cm = tilt_correction * rangefinder.distance_cm_orient(rf_orient);
     ├──> [remember inertial alt to allow us to interpolate rangefinder]
     │   └──> rf_state.inertial_alt_cm = inertial_nav.get_position_z_up_cm();
     ├──> [glitch handling] 
     │   //rangefinder readings more than RANGEFINDER_GLITCH_ALT_CM from the last good reading
     │   // are considered a glitch and glitch_count becomes non-zero
     │   // glitches clear after RANGEFINDER_GLITCH_NUM_SAMPLES samples in a row.
     │   // glitch_cleared_ms is set so surface tracking (or other consumers) can trigger a target reset
     │   ├──> const int32_t glitch_cm = rf_state.alt_cm - rf_state.alt_cm_glitch_protected;
     │   ├──> bool reset_terrain_offset = false;
     │   ├──> <if (glitch_cm >= RANGEFINDER_GLITCH_ALT_CM)>
     │   │   └──> rf_state.glitch_count = MAX(rf_state.glitch_count+1, 1);
     │   ├──> <else if (glitch_cm <= -RANGEFINDER_GLITCH_ALT_CM) >
     │   │   └──> rf_state.glitch_count = MIN(rf_state.glitch_count-1, -1);
     │   ├──> <else>
     │   │   ├──> rf_state.glitch_count = 0;
     │   │   └──> rf_state.alt_cm_glitch_protected = rf_state.alt_cm;
     │   └──> <abs(rf_state.glitch_count) >= RANGEFINDER_GLITCH_NUM_SAMPLES> // clear glitch and record time so consumers (i.e. surface tracking) can reset their target altitudes
     │       ├──> rf_state.glitch_count = 0;
     │       ├──> rf_state.alt_cm_glitch_protected = rf_state.alt_cm;
     │       ├──> rf_state.glitch_cleared_ms = AP_HAL::millis();
     │       └──> reset_terrain_offset = true;
     ├──> [filter rangefinder altitude]
     │   ├──> uint32_t now = AP_HAL::millis();
     │   ├──> const bool timed_out = now - rf_state.last_healthy_ms > RANGEFINDER_TIMEOUT_MS;
     │   ├──> <rf_state.alt_healthy>
     │   │   ├──> <if (timed_out)>
     │   │   │     // reset filter if we haven't used it within the last second
     │   │   │   ├──> rf_state.alt_cm_filt.reset(rf_state.alt_cm);
     │   │   │   └──> reset_terrain_offset = true;
     │   │   └──> <else >
     │   │       └──> rf_state.alt_cm_filt.apply(rf_state.alt_cm, 0.05f);
     │   └──> rf_state.last_healthy_ms = now;
     ├──> <reset_terrain_offset> // handle reset of terrain offset
     │   ├──> <if (rf_orient == ROTATION_PITCH_90)> // upward facing
     │   │   └──> rf_state.terrain_offset_cm = rf_state.inertial_alt_cm + rf_state.alt_cm;
     │   └──> < else > // assume downward facing
     │       └──> rf_state.terrain_offset_cm = rf_state.inertial_alt_cm - rf_state.alt_cm; 
     └──> <HAL_PROXIMITY_ENABLED> <rf_orient == ROTATION_PITCH_270> <rangefinder_state.alt_healthy || timed_out>
	     │  // send downward facing lidar altitude and health to the libraries that require it
         └──> g2.proximity.set_rangefinder_alt(rangefinder_state.enabled, rangefinder_state.alt_healthy, rangefinder_state.alt_cm_filt.get());

3. 重要例程

3.1 状态更新

针对当前传感器数据有效性进行状态更新。

  • NotConnected
  • NoData
  • OutOfRangeLow
  • OutOfRangeHigh
  • Good
RangeFinder::update
 ├──> <uint8_t i=0; i<num_instances; i++>
 │   └──> <drivers[i] != nullptr>
 │       ├──> <(Type)params[i].type.get() == Type::NONE> // allow user to disable a rangefinder at runtime
 │       │   ├──> state[i].status = Status::NotConnected
 │       │   ├──> state[i].range_valid_count = 0
 │       │   └──> continue
 │       └──> drivers[i]->update()
 └──> <HAL_LOGGING_ENABLED>
     └──> Log_RFND()

3.2 传感设备检测

除了AP_RangeFinder里面使用到的传感器外,衍生阅读可看下360度雷达以及视觉传感,详见Rangefinders (landing page)

  • ANALOG
  • MBI2C
  • PLI2C
  • PX4_PWM
  • BBB_PRU
  • LWI2C
  • LWSER
  • BEBOP
  • MAVLink
  • USD1_Serial
  • LEDDARONE
  • MBSER
  • TRI2C
  • PLI2CV3
  • VL53L0X
  • NMEA
  • WASP
  • BenewakeTF02
  • BenewakeTFmini
  • PLI2CV3HP
  • PWM
  • BLPing
  • UAVCAN
  • BenewakeTFminiPlus
  • Lanbao
  • BenewakeTF03
  • VL53L1X_Short
  • LeddarVu8_Serial
  • HC_SR04
  • GYUS42v2
  • MSP
  • USD1_CAN
  • Benewake_CAN
  • TeraRanger_Serial
  • Lua_Scripting
  • NoopLoop_P
  • TOFSenseP_CAN
  • NRA24_CAN
  • SIM
RangeFinder::detect_instance
 ├──> <!AP_RANGEFINDER_ENABLED>
 │   └──> return
 ├──> AP_RangeFinder_Backend_Serial *(*serial_create_fn)(RangeFinder::RangeFinder_State&, AP_RangeFinder_Params&) = nullptr;
 ├──> const Type _type = (Type)params[instance].type.get();
 ├──> <case PLI2C/PLI2CV3/PLI2CV3HP> <AP_RANGEFINDER_PULSEDLIGHTLRF_ENABLED>
 │   ├──> _add_backend(AP_RangeFinder_PulsedLightLRF::detect
 │   └──> break;
 ├──> <MBI2C> <AP_RANGEFINDER_MAXSONARI2CXL_ENABLED>
 │   ├──> uint8_t addr = AP_RANGE_FINDER_MAXSONARI2CXL_DEFAULT_ADDR;
 │   ├──> <params[instance].address != 0>
 │   │   └──> addr = params[instance].address;
 │   ├──> _add_backend(AP_RangeFinder_MaxsonarI2CXL::detect
 │   └──> break;
 ├──> <LWI2C> <AP_RANGEFINDER_LWI2C_ENABLED>
 │   ├──> <params[instance].address> // the LW20 needs a long time to boot up, so we delay 1.5s here
 │   │   ├──> <HAL_RANGEFINDER_LIGHTWARE_I2C_BUS>
 │   │   │   └──> _add_backend(AP_RangeFinder_LightWareI2C::detect
 │   │   └──> <!HAL_RANGEFINDER_LIGHTWARE_I2C_BUS>
 │   │       └──> _add_backend(AP_RangeFinder_LightWareI2C::detect
 │   └──> break;
 ├──> <TRI2C> <AP_RANGEFINDER_TRI2C_ENABLED>
 │   ├──> <params[instance].address>
 │   │   └──> __add_backend(AP_RangeFinder_TeraRangerI2C::detect
 │   └──> break;
 ├──> <VL53L0X/VL53L1X_Short> 
 │   ├──> <AP_RANGEFINDER_VL53L0X_ENABLED>
 │   │   └──> _add_backend(AP_RangeFinder_VL53L0X::detect
 │   ├──> <AP_RANGEFINDER_VL53L1X_ENABLED>
 │   │   └──> _add_backend(AP_RangeFinder_VL53L1X::detect
 │   └──> break;
 ├──> <BenewakeTFminiPlus> <AP_RANGEFINDER_BENEWAKE_TFMINIPLUS_ENABLED>
 │   ├──> uint8_t addr = TFMINIPLUS_ADDR_DEFAULT;
 │   ├──> <params[instance].address != 0>
 │   │   └──> addr = params[instance].address;
 │   ├──> _add_backend(AP_RangeFinder_Benewake_TFMiniPlus::detect
 │   └──> break;
 ├──> <PX4_PWM> <AP_RANGEFINDER_PWM_ENABLED>
 │   │  // to ease moving from PX4 to ChibiOS we'll lie a little about
 │   │  // the backend driver...
 │   ├──> <AP_RangeFinder_PWM::detect()>
 │   │   └──> _add_backend(new AP_RangeFinder_PWM(state[instance], params[instance], estimated_terrain_height), instance);
 │   └──> break;
 ├──> <BBB_PRU> <AP_RANGEFINDER_BBB_PRU_ENABLED>
 │   ├──> <AP_RangeFinder_BBB_PRU::detect()>
 │   │   └──> _add_backend(new AP_RangeFinder_BBB_PRU(state[instance], params[instance]), instance);
 │   └──> break;
 ├──> <LWSER> <AP_RANGEFINDER_LIGHTWARE_SERIAL_ENABLED>
 │   ├──> serial_create_fn = AP_RangeFinder_LightWareSerial::create;
 │   └──> break;
 ├──> <LEDDARONE> <AP_RANGEFINDER_LEDDARONE_ENABLED>
 │   ├──> serial_create_fn = AP_RangeFinder_LeddarOne::create;
 │   └──> break;
 ├──> <USD1_Serial> <AP_RANGEFINDER_USD1_SERIAL_ENABLED>
 │   ├──> serial_create_fn = AP_RangeFinder_USD1_Serial::create;
 │   └──> break;
 ├──> <BEBOP> <AP_RANGEFINDER_BEBOP_ENABLED>
 │   ├──> <AP_RangeFinder_Bebop::detect()>
 │   │   └──> _add_backend(new AP_RangeFinder_Bebop(state[instance], params[instance]), instance);
 │   └──> break;
 ├──> <MAVLink> <AP_RANGEFINDER_MAVLINK_ENABLED>
 │   ├──> <AP_RangeFinder_MAVLink::detect()>
 │   │   └──> _add_backend(new AP_RangeFinder_MAVLink(state[instance], params[instance]), instance);
 │   └──> break;
 ├──> <MBSER> <AP_RANGEFINDER_MAXBOTIX_SERIAL_ENABLED>
 │   ├──> serial_create_fn = AP_RangeFinder_MaxsonarSerialLV::create;
 │   └──> break;
 ├──> <ANALOG> <AP_RANGEFINDER_ANALOG_ENABLED>
 │   ├──> <AP_RangeFinder_analog::detect(params[instance])> // note that analog will always come back as present if the pin is valid
 │   │   └──> _add_backend(new AP_RangeFinder_analog(state[instance], params[instance]), instance);
 │   └──> break;
 ├──> <HC_SR04> <AP_RANGEFINDER_HC_SR04_ENABLED>
 │   ├──> <AP_RangeFinder_HC_SR04::detect(params[instance])>  // note that this will always come back as present if the pin is valid
 │   │   └──> _add_backend(new AP_RangeFinder_HC_SR04(state[instance], params[instance]), instance);
 │   └──> break;
 ├──> <NMEA> <AP_RANGEFINDER_NMEA_ENABLED>
 │   ├──> serial_create_fn = AP_RangeFinder_NMEA::create;
 │   └──> break;
 ├──> <WASP> <AP_RANGEFINDER_WASP_ENABLED>
 │   ├──> serial_create_fn = AP_RangeFinder_Wasp::create;
 │   └──> break;
 ├──> <BenewakeTF02> <AP_RANGEFINDER_BENEWAKE_TF02_ENABLED>
 │   ├──> serial_create_fn = AP_RangeFinder_Benewake_TF02::create;
 │   └──> break;
 ├──> <BenewakeTFmini> <AP_RANGEFINDER_BENEWAKE_TFMINI_ENABLED>
 │   ├──> serial_create_fn = AP_RangeFinder_Benewake_TFMini::create;
 │   └──> break;
 ├──> <BenewakeTF03> <AP_RANGEFINDER_BENEWAKE_TF03_ENABLED>
 │   ├──> serial_create_fn = AP_RangeFinder_Benewake_TF03::create;
 │   └──> break;
 ├──> <TeraRanger_Serial> <AP_RANGEFINDER_TERARANGER_SERIAL_ENABLED>
 │   ├──> serial_create_fn = AP_RangeFinder_TeraRanger_Serial::create;
 │   └──> break;
 ├──> <PWM> <AP_RANGEFINDER_PWM_ENABLED>
 │   ├──> <AP_RangeFinder_PWM::detect()>
 │   │   └──> _add_backend(new AP_RangeFinder_PWM(state[instance], params[instance], estimated_terrain_height), instance);
 │   └──> break;
 ├──> <BLPing> <AP_RANGEFINDER_BLPING_ENABLED>
 │   ├──> serial_create_fn = AP_RangeFinder_BLPing::create;
 │   └──> break;
 ├──> <Lanbao> <AP_RANGEFINDER_LANBAO_ENABLED>
 │   ├──> serial_create_fn = AP_RangeFinder_Lanbao::create;
 │   └──> break;
 ├──> <LeddarVu8_Serial> <AP_RANGEFINDER_LEDDARVU8_ENABLED>
 │   ├──> serial_create_fn = AP_RangeFinder_LeddarVu8::create;
 │   └──> break;
 ├──> <UAVCAN> <AP_RANGEFINDER_DRONECAN_ENABLED>
 │   │  /*
 │   │    the UAVCAN driver gets created when we first receive a
 │   │    measurement. We take the instance slot now, even if we don't
 │   │    yet have the driver
 │   │   */
 │   ├──> num_instances = MAX(num_instances, instance+1);
 │   └──> break;
 ├──> <GYUS42v2> <AP_RANGEFINDER_GYUS42V2_ENABLED>
 │   ├──> serial_create_fn = AP_RangeFinder_GYUS42v2::create;
 │   └──> break;
 ├──> <SIM> <AP_RANGEFINDER_SIM_ENABLED>
 │   ├──> _add_backend(new AP_RangeFinder_SITL(state[instance], params[instance], instance), instance);
 │   └──> break;
 ├──> <MSP> <HAL_MSP_RANGEFINDER_ENABLED>
 │   ├──> <AP_RangeFinder_MSP::detect()>
 │   │   └──> _add_backend(new AP_RangeFinder_MSP(state[instance], params[instance]), instance);
 │   └──> break;
 ├──> <USD1_CAN> <AP_RANGEFINDER_USD1_CAN_ENABLED>
 │   ├──> _add_backend(new AP_RangeFinder_USD1_CAN(state[instance], params[instance]), instance);
 │   └──> break;
 ├──> <Benewake_CAN> <AP_RANGEFINDER_BENEWAKE_CAN_ENABLED>
 │   ├──> _add_backend(new AP_RangeFinder_Benewake_CAN(state[instance], params[instance]), instance);
 │   └──> break;
 ├──> <Lua_Scripting> <AP_SCRIPTING_ENABLED>
 │   ├──> _add_backend(new AP_RangeFinder_Lua(state[instance], params[instance]), instance);
 │   └──> break;
 ├──> <NoopLoop_P> ,AP_RANGEFINDER_NOOPLOOP_ENABLED>
 │   ├──> serial_create_fn = AP_RangeFinder_NoopLoop::create;
 │   └──> break;
 ├──> <TOFSenseP_CAN> <AP_RANGEFINDER_TOFSENSEP_CAN_ENABLED>
 │   ├──> _add_backend(new AP_RangeFinder_TOFSenseP_CAN(state[instance], params[instance]), instance);
 │   └──> break;
 ├──> <NRA24_CAN> <AP_RANGEFINDER_NRA24_CAN_ENABLED>
 │   ├──> _add_backend(new AP_RangeFinder_NRA24_CAN(state[instance], params[instance]), instance);
 │   └──> break;
 ├──> <serial_create_fn != nullptr> <AP::serialmanager().have_serial(AP_SerialManager::SerialProtocol_Rangefinder, serial_instance)>
 │   ├──> auto *b = serial_create_fn(state[instance], params[instance]);
 │   └──> <b != nullptr>
 │       └──> _add_backend(b, instance, serial_instance++);
 └──> <drivers[instance] && state[instance].var_info>  // if the backend has some local parameters then make those available in the tree
     ├──> backend_var_info[instance] = state[instance].var_info;
     ├──> AP_Param::load_object_from_eeprom(drivers[instance], backend_var_info[instance]);      
     └──> AP_Param::invalidate_count(); // param count could have changed

4. 总结

各传感器设备因设备总线以及协议的差异,存在一定的差异化,在相应的驱动代码中实现,可参考:ArduPilot之开源代码Library&Sketches设计

作为RangeFinder传感器主要的目的是检测飞机与地面之间的距离

    // The RangeFinder_State structure is filled in by the backend driver
    struct RangeFinder_State {
        float distance_m;               // distance in meters
        uint16_t voltage_mv;            // voltage in millivolts, if applicable, otherwise 0
        enum RangeFinder::Status status; // sensor status
        uint8_t  range_valid_count;     // number of consecutive valid readings (maxes out at 10)
        uint32_t last_reading_ms;       // system time of last successful update from sensor

        const struct AP_Param::GroupInfo *var_info;
    };

注:不同设备更新频率方面主要在驱动中进行数据采集更新,详见驱动代码的init方法中如何挂定时回调钩子函数。

5. 参考资料

【1】ArduPilot开源飞控系统之简单介绍
【2】ArduPilot飞控启动&运行过程简介
【3】ArduPilot之开源代码Library&Sketches设计

class RangeFinder { public: friend class AP_RangeFinder_Backend; RangeFinder(void); // RangeFinder driver types enum RangeFinder_Type { RangeFinder_TYPE_NONE = 0, RangeFinder_TYPE_ANALOG = 1, RangeFinder_TYPE_MBI2C = 2, RangeFinder_TYPE_PLI2C = 3, RangeFinder_TYPE_PX4 = 4, RangeFinder_TYPE_PX4_PWM= 5, RangeFinder_TYPE_BBB_PRU= 6 }; enum RangeFinder_Function { FUNCTION_LINEAR = 0, FUNCTION_INVERTED = 1, FUNCTION_HYPERBOLA = 2 }; enum RangeFinder_Status { RangeFinder_NotConnected = 0, RangeFinder_NoData, RangeFinder_OutOfRangeLow, RangeFinder_OutOfRangeHigh, RangeFinder_Good }; // The RangeFinder_State structure is filled in by the backend driver struct RangeFinder_State { uint8_t instance; // the instance number of this RangeFinder uint16_t distance_cm; // distance: in cm uint16_t voltage_mv; // voltage in millivolts, // if applicable, otherwise 0 enum RangeFinder_Status status; // sensor status uint8_t range_valid_count; // number of consecutive valid readings (maxes out at 10) bool pre_arm_check; // true if sensor has passed pre-arm checks uint16_t pre_arm_distance_min; // min distance captured during pre-arm checks uint16_t pre_arm_distance_max; // max distance captured during pre-arm checks }; // parameters for each instance AP_Int8 _type[RANGEFINDER_MAX_INSTANCES]; AP_Int8 _pin[RANGEFINDER_MAX_INSTANCES]; AP_Int8 _ratiometric[RANGEFINDER_MAX_INSTANCES]; AP_Int8 _stop_pin[RANGEFINDER_MAX_INSTANCES]; AP_Int16 _settle_time_ms[RANGEFINDER_MAX_INSTANCES]; AP_Float _scaling[RANGEFINDER_MAX_INSTANCES]; AP_Float _offset[RANGEFINDER_MAX_INSTANCES]; AP_Int8 _function[RANGEFINDER_MAX_INSTANCES]; AP_Int16 _min_distance_cm[RANGEFINDER_MAX_INSTANCES]; AP_Int16 _max_distance_cm[RANGEFINDER_MAX_INSTANCES]; AP_Int8 _ground_clearance_cm[RANGEFINDER_MAX_INSTANCES]; AP_Int16 _powersave_range; static const struct AP_Param::GroupInfo var_info[]; // Return the number of range finder instances uint8_t num_sensors(void) const { return num_instances; } // detect and initialise any available rangefinders void init(void); // update state of all rangefinders. Should be called at around // 10Hz from main loop void update(void); #define _RangeFinder_STATE(instance) state[instance] uint16_t distance_cm(uint8_t instance) const { return _RangeFinder_STATE(instance).distance_cm; } uint16_t distance_cm() const { return distance_cm(primary_instance); } uint16_t voltage_mv(uint8_t instance) const { return _RangeFinder_STATE(instance).voltage_mv; } uint16_t voltage_mv() const { return voltage_mv(primary_instance); } int16_t max_distance_cm(uint8_t instance) const { return _max_distance_cm[instance]; } int16_t max_distance_cm() const { return max_distance_cm(primary_instance); } int16_t min_distance_cm(uint8_t instance) const { return _min_distance_cm[instance]; } int16_t min_distance_cm() const { return min_distance_cm(primary_instance); } int16_t ground_clearance_cm(uint8_t instance) const { return _ground_clearance_cm[instance]; } int16_t ground_clearance_cm() const { return _ground_clearance_cm[primary_instance]; } // query status RangeFinder_Status status(uint8_t instance) const; RangeFinder_Status status(void) const { return status(primary_instance); } // true if sensor is returning data bool has_data(uint8_t instance) const; bool has_data() const { return has_data(primary_instance); } // returns count of consecutive good readings uint8_t range_valid_count() const { return range_valid_count(primary_instance); } uint8_t range_valid_count(uint8_t instance) const { return _RangeFinder_STATE(instance).range_valid_count; } /* set an externally estimated terrain height. Used to enable power saving (where available) at high altitudes. */ void set_estimated_terrain_height(float height) { estimated_terrain_height = height; } /* returns true if pre-arm checks have passed for all range finders these checks involve the user lifting or rotating the vehicle so that sensor readings between the min and 2m can be captured */ bool pre_arm_check() const; private: RangeFinder_State state[RANGEFINDER_MAX_INSTANCES]; AP_RangeFinder_Backend *drivers[RANGEFINDER_MAX_INSTANCES]; uint8_t primary_instance:2; uint8_t num_instances:2; float estimated_terrain_height; void detect_instance(uint8_t instance); void update_instance(uint8_t instance); void update_pre_arm_check(uint8_t instance); }; #endif // __RANGEFINDER_H__ 这个class的定义说了什么?从代码结构角度进行分析
最新发布
07-17
<think>我们正在分析一个C++类`RangeFinder`的代码结构和功能。由于没有提供具体的代码,我将根据常见的传感器驱动类设计模式,结合ArduPilot代码库中类似组件的结构(如引用中提到的AP_SerialManager、传感器采样等)进行典型解析。 ### 一、RangeFinder类设计目的 `RangeFinder`(测距仪)类在无人机/机器人系统中负责: 1. **统一接口**:封装不同型号测距传感器(超声波、激光雷达等)的通信协议 2. **数据预处理**:提供滤波、温度补偿等数据处理功能 3. **状态管理**:监控传感器健康状态,实现故障检测 4. **多传感器支持**:管理多个安装在不同位置的测距传感器实例[^3] ### 二、典型代码结构解析 #### 1. 类定义框架 ```cpp class RangeFinder { public: // 公共接口(方法) // 枚举类型 private: // 私有成员变量 // 内部工具方法 }; ``` #### 2. 重要枚举类型 通常包含传感器类型和状态枚举: ```cpp enum Type { TYPE_NONE = 0, TYPE_PLI2C, // PulsedLight I2C激光雷达 TYPE_MBSER, // MaxBotix串口超声波 TYPE_LWI2C, // LightWare I2C // ... 其他传感器类型 }; enum Status { STATUS_NO_DATA = 0, // 无数据 STATUS_OUT_OF_RANGE, // 超出量程 STATUS_GOOD // 数据有效 }; ``` #### 3. 核心成员变量 ```cpp private: AP_HAL::UARTDriver *_uart; // 串口指针(用于串口传感器)[^4] AP_HAL::I2CDevice *_i2c_dev; // I2C设备指针 Type _type; // 传感器类型标识 uint8_t _instance; // 传感器实例编号(多传感器时) float _distance_cm; // 最新测量距离值(厘米) Status _status; // 当前状态 uint32_t _last_reading_ms; // 最后有效读数时间戳 LowPassFilterFloat _filter; // 低通滤波器实例[^3] ``` #### 4. 关键公有方法 ##### (1) 初始化与更新 ```cpp // 根据参数初始化传感器 bool init(Type type, uint8_t instance); // 周期性更新数据(通常被主循环调用) void update(); ``` ##### (2) 数据获取接口 ```cpp // 获取最新有效距离(厘米) float distance_cm() const; // 获取传感器状态 Status status() const; // 获取最后更新时间 uint32_t last_reading_ms() const; ``` ##### (3) 参数配置 ```cpp // 设置最大有效距离 void set_max_distance(float dist_cm); // 启用/禁用滤波器 void enable_filter(bool enable); ``` ### 三、工作流程详解 1. **初始化阶段** 通过`init()`根据传感器类型配置通信接口: ```mermaid graph TD A[init调用] --> B{类型判断} B -->|I2C传感器| C[配置I2C设备地址] B -->|串口传感器| D[设置串口波特率] ``` 2. **数据更新循环** `update()`方法周期性执行: ```cpp void RangeFinder::update() { if (_type == TYPE_NONE) return; switch(_type) { case TYPE_PLI2C: _read_pli2c(); // 私有方法读取特定传感器 break; case TYPE_MBSER: _read_mbser(); break; // ...其他类型处理 } // 应用滤波器 if (_status == STATUS_GOOD) { _filter.apply(_distance_cm); } } ``` 3. **数据有效性检查** 在`_read_xxx()`方法中实现: ```cpp void RangeFinder::_read_pli2c() { uint8_t buf[2]; if (_i2c_dev->read_registers(0x5F, buf, 2) != 0) { _status = STATUS_NO_DATA; return; } uint16_t dist = (buf[0] << 8) | buf[1]; if (dist > 5000) { // 示例阈值 _status = STATUS_OUT_OF_RANGE; } else { _distance_cm = dist; _status = STATUS_GOOD; _last_reading_ms = AP_HAL::millis(); } } ``` ### 四、设计特点分析 1. **多态实现** 通过`_type`分支选择不同驱动逻辑,避免虚函数开销(适合嵌入式系统)[^3]。 2. **资源优化** - 使用轻量级`LowPassFilterFloat`滤波器 - 通信超时自动标记无效数据 3. **线程安全考虑** 在ArduPilot中通常通过`HAL`(硬件抽象层)保证原子操作,无需额外锁[^4]。 ### 五、典型使用场景 ```cpp // 系统初始化时 RangeFinder rf; rf.init(RangeFinder::TYPE_PLI2C, 0); // 主循环中 rf.update(); // 控制代码中获取距离 if (rf.status() == RangeFinder::STATUS_GOOD) { float dist = rf.distance_cm(); // 避障决策... } ``` > **注意**:实际实现可能包含更复杂的健康监测、自动校准等功能,参考ArduPilot的`AP_RangeFinder`模块[^3]。 §§相关问题§§ 1. 在嵌入式系统中如何优化传感器数据读取的实时性? 2. 多传感器融合时如何解决测距数据冲突问题? 3. 低通滤波器参数(截止频率)应如何根据应用场景调整?</think>### C++ `RangeFinder` 类代码结构解析 `RangeFinder` 类是无人机/机器人系统中用于管理测距传感器(如激光雷达、超声波)的核心组件。以下是其典型代码结构和功能设计解析(基于 ArduPilot开源框架实现): --- #### 一、类定义框架 ```cpp class RangeFinder { public: // 公有接口(方法+枚举) private: // 私有成员变量 // 内部工具方法 }; ``` --- #### 二、核心设计目的 1. **统一传感器接口** 封装不同测距传感器(超声波/LiDAR/TOF)的硬件差异,提供标准化数据访问 2. **多传感器管理** 支持系统中多个测距传感器的协同工作(如避障、定高) 3. **数据预处理** 实现滤波、温度补偿、故障检测等数据处理逻辑 4. **健康状态监控** 实时检测传感器工作状态[^3] --- #### 三、关键成员解析 ##### 1. 枚举类型(定义工作模式与状态) ```cpp enum Type { TYPE_NONE = 0, TYPE_ULTRASOUND, // 超声波传感器 TYPE_LIDAR, // 激光雷达 TYPE_TOF // 飞行时间传感器 }; enum Status { STATUS_NO_DATA = 0, // 无数据 STATUS_OUT_OF_RANGE, // 超出量程 STATUS_GOOD // 数据有效 }; enum Orientation { ORIENTATION_FRONT, // 前向安装 ORIENTATION_DOWN // 下视安装(高度计) }; ``` ##### 2. 核心成员变量 ```cpp private: AP_HAL::UARTDriver* _uart; // 串口通信接口指针[^4] AP_HAL::I2CDevice* _i2c_dev; // I2C设备指针 Type _type; // 传感器类型标识 uint8_t _instance; // 传感器实例编号(多传感器时) float _distance_cm; // 最新测量距离值(厘米) Status _status; // 当前状态 uint32_t _last_reading_ms; // 最后有效读数时间戳 LowPassFilterFloat _filter; // 低通滤波器实例[^3] ``` ##### 3. 重要公有方法 ```cpp public: // 初始化传感器 bool init(Type type, uint8_t instance); // 周期性更新数据(主循环调用) void update(); // 获取最新有效距离(厘米) float distance_cm() const; // 获取传感器状态 Status status() const; // 设置最大有效距离 void set_max_distance(float dist_cm); // 启用/禁用温度补偿 void enable_temp_compensation(bool enable); // 硬件健康检查(预飞检查) bool pre_arm_check(char* failure_msg, uint8_t len) const; ``` --- #### 四、工作流程详解 1. **初始化阶段** ```mermaid graph TD A[init 调用] --> B{类型判断} B -->|I2C传感器| C[配置I2C设备地址] B -->|串口传感器| D[设置串口波特率] C --> E[验证设备ID] D --> E E -->|成功| F[初始化滤波器] E -->|失败| G[标记状态为故障] ``` 2. **数据更新循环** ```cpp void RangeFinder::update() { if (_type == TYPE_NONE) return; switch(_type) { case TYPE_ULTRASOUND: _read_ultrasound(); // 私有方法读取超声波 break; case TYPE_LIDAR: _read_lidar(); // 私有方法读取激光雷达 break; // ...其他类型处理 } // 应用滤波器 if (_status == STATUS_GOOD) { _filter.apply(_distance_cm); } } ``` 3. **数据有效性检查** ```cpp void RangeFinder::_read_ultrasound() { uint8_t buf[2]; if (_i2c_dev->read_registers(0x01, buf, 2) != 0) { _status = STATUS_NO_DATA; return; } uint16_t dist = (buf[0] << 8) | buf[1]; if (dist > MAX_DISTANCE) { _status = STATUS_OUT_OF_RANGE; } else { _distance_cm = dist; _status = STATUS_GOOD; _last_reading_ms = AP_HAL::millis(); } } ``` --- #### 五、设计特点分析 1. **硬件抽象层封装** 通过 `AP_HAL`(硬件抽象层)实现跨平台支持[^4] 2. **资源优化设计** - 使用轻量级 `LowPassFilterFloat` 滤波器 - 通信超时自动标记无效数据 - 按需初始化硬件(延迟加载) 3. **多传感器协同** ```cpp // 系统获取所有有效测距传感器数据 for (uint8_t i=0; i<num_sensors; i++) { if (rangefinders[i].status() == RangeFinder::STATUS_GOOD) { total_distance += rangefinders[i].distance_cm(); } } ``` 4. **安全机制** 预飞检查 (`pre_arm_check`) 确保传感器可用性[^3] --- #### 六、典型使用场景 ```cpp // 系统初始化时 RangeFinder front_rf; front_rf.init(RangeFinder::TYPE_LIDAR, 0); // 主循环中 front_rf.update(); // 避障决策 if (front_rf.status() == RangeFinder::STATUS_GOOD) { if (front_rf.distance_cm() < SAFE_DISTANCE) { trigger_avoidance(); } } ``` > **提示**:实际实现可能包含更复杂的传感器融合逻辑(如与IMU数据融合)[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值