问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

无人机 PX4 飞控 | 光流计简介与应用范例

创作时间:
作者:
@小白创作中心

无人机 PX4 飞控 | 光流计简介与应用范例

引用
CSDN
1.
https://blog.csdn.net/qq_32761549/article/details/140983526

光流计是无人机实现自主飞行和定位的重要传感器之一,通过分析摄像头捕捉到的图像序列,计算图像中像素点的运动速度,从而推算出无人机的速度和位置。本文将详细介绍光流计的基本概念、工作原理以及在PX4飞控系统中的具体应用。

前言

光流的概念最早由Gibson在1950年提出,它是空间运动物体在观察成像平面上的像素运动的瞬时速度。光流传感器通过下视相机和向下的距离传感器进行速度估计,主要用于无人机上,可以让飞机保持水平位置,在室内实现定点飞行。

一些常见的光流计实物:

  • px4FLOW(新版本的PX4固件已经不支持)

  • ARK Flow

  • 基于 PMW3901 的 Flow

  • UP-T2

无人机光流计(Drone Optical Flowmeter)是一种用于无人机上的光学测量设备,它利用光流技术来帮助无人机实现自主飞行和定位。光流计在无人机中的应用主要是通过分析摄像头捕捉到的图像序列,计算图像中像素点的运动速度,从而推算出无人机的速度和位置。

基于PMW3901 光流在PX4 1.14.3上的应用

环境说明

  • 光流计模块:MTF-02(微空科技)
  • 无人机飞控:Cuav PX4 X7+
  • 飞控版本:PX4 1.14.3

光流计模块介绍

MTF-02是微空科技研发设计并生产的一款光流测距一体传感器,采用串口输出数据,并内置多种通信协议,可兼容主流开源飞控:Ardupilot、PX4、INAV、FMT。依靠MTF-02传感器,无人机可以实现室内无GPS环境下的自主悬停飞行。

产品参数:

  • 测距范围:0.01-2.5m
  • 测距精度:2%
  • 测距光源:LED
  • 中心波长:940nm
  • 测距视场角:20°
  • 光流视场角:42°
  • 光流最小工作距离:8cm
  • 光流环境光需求:>60Lux
  • 通信接口:UART/IIC
  • 数据频率:50Hz
  • 供电电压:4.0-5.5V
  • 功耗:200mW

产品尺寸:

  • 模块尺寸:25104.5 mm
  • 重量:1.5g

光流计模块使用

安装

将光流计固定于飞机底部,PX4 飞控默认安装方向:光流方向可以用微空助手上位机配置修改,PX4飞控也可以配置光流方向参数。

接线

光流计的接口线序定义:飞控的接口用UART4 , 即下图中的 2号 接口

其引脚定义如下:所以需要 SH1.0-4P转SH1.25-6P 的接头线.

飞控设置

该光流计模块与PX4 飞控估计配合使用,需要将光流计模块的输出协议设置成mav_px4。

MAV_1_CONFIG TELEM n
重启飞控
MAV_1_MODE Normal
SER_TELn_BAUD 115200 8N1
EKF2_OF_CTRL Enabled
EKF2_RNG_CTRL Enabled
EKF2_HGT_REF Range sensor
重启飞控
SENS_FLOW_ROT No rotation
1.14.0版本以后需要设置好所有其它参数,重启飞控后才能在QGC里找到SENS_FLOW_ROT参数
重启飞控,一切正常的话,在QGC的MAVLink Inspector页面中应该能看到DISTANCE_SENSOR和OPTICAL_FLOW_RAD消息。注意,QGC上显示的消息由飞控发出,消息频率并不等同于传感器的数据频率,这个频率由飞控的设置和数据链路的速度决定,主要用于观察,消息频率多少并不重要。
如果地面站是通过某些数传连接飞控时,由于数据链路速率较低,飞控会选择性发出数据,导致在QGC有可能看不到某些消息帧比如OPTICAL_FLOW_RAD,因此这一步建议使用USB连接飞控。

核心代码

满足距离条件,根据光流计数据,计算机体坐标系无人机速度

if (_distance_sum_count > 0 && PX4_ISFINITE(_distance_sum)) {
    const float range = _distance_sum / _distance_sum_count;
    vehicle_optical_flow_vel_s flow_vel{};
    flow_vel.timestamp_sample = vehicle_optical_flow.timestamp_sample;
    // NOTE: the EKF uses the reverse sign convention to the flow sensor. EKF assumes positive LOS rate
    // is produced by a RH rotation of the image about the sensor axis.
    const Vector2f flow_xy_rad{-vehicle_optical_flow.pixel_flow[0], -vehicle_optical_flow.pixel_flow[1]};
    const Vector3f gyro_xyz{-vehicle_optical_flow.delta_angle[0], -vehicle_optical_flow.delta_angle[1], -vehicle_optical_flow.delta_angle[2]};
    const float flow_dt = 1e-6f * vehicle_optical_flow.integration_timespan_us;
    // compensate for body motion to give a LOS rate
    const Vector2f flow_compensated_XY_rad = flow_xy_rad - gyro_xyz.xy();
    Vector3f vel_optflow_body;
    vel_optflow_body(0) = - range * flow_compensated_XY_rad(1) / flow_dt;
    vel_optflow_body(1) =   range * flow_compensated_XY_rad(0) / flow_dt;
    vel_optflow_body(2) = 0.f;
    // vel_body
    flow_vel.vel_body[0] = vel_optflow_body(0);
    flow_vel.vel_body[1] = vel_optflow_body(1);
    // vel_ne
    flow_vel.vel_ne[0] = NAN;
    flow_vel.vel_ne[1] = NAN;
    vehicle_attitude_s vehicle_attitude{};
    if (_vehicle_attitude_sub.copy(&vehicle_attitude)) {
        const matrix::Dcmf R_to_earth = matrix::Quatf(vehicle_attitude.q);
        const Vector3f flow_vel_ne = R_to_earth * vel_optflow_body;
        flow_vel.vel_ne[0] = flow_vel_ne(0);
        flow_vel.vel_ne[1] = flow_vel_ne(1);
    }
    // flow_uncompensated_integral
    flow_xy_rad.copyTo(flow_vel.flow_uncompensated_integral);
    // flow_compensated_integral
    flow_compensated_XY_rad.copyTo(flow_vel.flow_compensated_integral);
    const Vector3f measured_body_rate(gyro_xyz * (1.f / flow_dt));
    // gyro_rate
    flow_vel.gyro_rate[0] = measured_body_rate(0);
    flow_vel.gyro_rate[1] = measured_body_rate(1);
    // gyro_rate_integral
    flow_vel.gyro_rate_integral[0] = gyro_xyz(0);
    flow_vel.gyro_rate_integral[1] = gyro_xyz(1);
    flow_vel.timestamp = hrt_absolute_time();
    _vehicle_optical_flow_vel_pub.publish(flow_vel);
}

用光流计采集到的数据,进行更新EKF
主要是水平速度

void Ekf::updateOptFlow(estimator_aid_source2d_s &aid_src)
{
    resetEstimatorAidStatus(aid_src);
    aid_src.timestamp_sample = _flow_sample_delayed.time_us;
    const Vector2f vel_body = predictFlowVelBody();
    const float range = predictFlowRange();
    // calculate optical LOS rates using optical flow rates that have had the body angular rate contribution removed
    // correct for gyro bias errors in the data used to do the motion compensation
    // Note the sign convention used: A positive LOS rate is a RH rotation of the scene about that axis.
    const Vector2f opt_flow_rate = _flow_compensated_XY_rad / _flow_sample_delayed.dt;
    // compute the velocities in body and local frames from corrected optical flow measurement for logging only
    _flow_vel_body(0) = -opt_flow_rate(1) * range;
    _flow_vel_body(1) =  opt_flow_rate(0) * range;
    _flow_vel_ne = Vector2f(_R_to_earth * Vector3f(_flow_vel_body(0), _flow_vel_body(1), 0.f));
    aid_src.observation[0] = opt_flow_rate(0); // flow around the X axis
    aid_src.observation[1] = opt_flow_rate(1); // flow around the Y axis
    aid_src.innovation[0] =  (vel_body(1) / range) - aid_src.observation[0];
    aid_src.innovation[1] = (-vel_body(0) / range) - aid_src.observation[1];
    // calculate the optical flow observation variance
    const float R_LOS = calcOptFlowMeasVar(_flow_sample_delayed);
    aid_src.observation_variance[0] = R_LOS;
    aid_src.observation_variance[1] = R_LOS;
    const Vector24f state_vector = getStateAtFusionHorizonAsVector();
    Vector2f innov_var;
    Vector24f H;
    sym::ComputeFlowXyInnovVarAndHx(state_vector, P, range, R_LOS, FLT_EPSILON, &innov_var, &H);
    innov_var.copyTo(aid_src.innovation_variance);
    // run the innovation consistency check and record result
    setEstimatorAidStatusTestRatio(aid_src, math::max(_params.flow_innov_gate, 1.f));
}
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号