光流定点程序梳理

2023-05-16

          本文主要分析飞控获取到光流数据之后,如何实现定点,至于光流算法,不在本文讨论范围内。
          官网介绍的PX4 Flow采用STM32F4作为主控,定点效果不错,但价格稍贵,而且体积比较大,对于口袋无人机来说,肯定是不合适的。像安霸、联咏等芯片的某些系列可以接多个摄像头,主摄像头用来航拍,辅摄像头用来做光流,既节省了成本,又减小了体积。
          切换到定点模式下,例如loiter或poshold模式,会初始化Home Position。打开AP_Inertial_Nav.cpp,找到如下程序。

// setup_home_position - reset state for home position change
void AP_InertialNav::setup_home_position(void)
{
    // set longitude to meters scaling to offset the shrinking longitude as we go towards the poles
    _lon_to_cm_scaling = longitude_scale(_ahrs.get_home()) * LATLON_TO_CM;

    // reset corrections to base position to zero
    _position_base.x = 0.0f;
    _position_base.y = 0.0f;
    _position_correction.x = 0.0f;
    _position_correction.y = 0.0f;
    _position.x = 0.0f;   
    _position.y = 0.0f;   

    // clear historic estimates
    _hist_position_estimate_x.clear();
    _hist_position_estimate_y.clear();

    // set xy as enabled
    _xy_enabled = true;
}


将光流数据根据镜头参数和超声高度数据转化为实际的移动距离,减去由于roll/pitch变化造成的偏差,并对其进行累加,得到相对于机体Home位置的坐标。结合磁力计将机体坐标系转化为地理坐标系,X、Y方向与GPS经纬度保持一致。

// updates internal lon and lat with estimation based on optical flow
// reference: http://ardupilot.org/copter/docs/common-mouse-based-optical-flow-sensor-adns3080.html
void AP_InertialNav::update_optflow_position(float roll, float pitch, float sin_yaw, float cos_yaw, float altitude)  
{
    static float _last_roll;    
    static float _last_pitch;
    static float _last_altitude;
    float diff_roll, diff_pitch;
    float compensation_x = 0, compensation_y = 0;   // compensation caused by roll pitch angle    
    float rate_threshold = 0.15f;   
    diff_roll = _last_roll - roll;
    diff_pitch = _last_pitch - pitch;
    // only update position if angle is not over 45 degrees
    if( fabsf(roll) <= FORTYFIVE_DEGREES && fabsf(pitch) <= FORTYFIVE_DEGREES ) {   
        float avg_altitude = (altitude + _last_altitude) * 0.5;
        // change in position is actual change measured by sensor minus expected change due to change in roll, pitch
        if(fabsf(roll) > rate_threshold)
            compensation_x = diff_roll * avg_altitude * OPTFLOW_COEFFICIENT_X;  // right
        if(fabsf(pitch) > rate_threshold)	
            compensation_y = -diff_pitch * avg_altitude * OPTFLOW_COEFFICIENT_Y;  // forward
 	// exchange x and y to adjust for GPS direction.
        optflow_x_cm = _rcin_values_y * avg_altitude / 457.0 - compensation_y;   // translate to real x,y changing distance,unit cm.
	optflow_y_cm = _rcin_values_x * avg_altitude / 457.0 - compensation_x;  
		
	// translate optflow body frame to earth frame
	optflow_ef_x = optflow_x_cm * cos_yaw - optflow_y_cm * sin_yaw;  // north
    	optflow_ef_y = optflow_x_cm * sin_yaw + optflow_y_cm * cos_yaw;  // east
	
	optflow_ef_sum_x += optflow_ef_x;  
    	optflow_ef_sum_y += optflow_ef_y;  		
	//printf("optflow_x is: %.3f,optflow_y is: %.3f,avg_altitude is: %.3f  \n",optflow_ef_sum_x,optflow_ef_sum_y,sonar_altitude);
    }

    _last_altitude = altitude;
    _last_roll = roll;
    _last_pitch = pitch;
}

利用光流数据计算位置误差,对基准位置进行更新。

void AP_InertialNav::correct_with_optflow(uint32_t now)
{
    float dt,x,y;
    float hist_position_base_x, hist_position_base_y;
    // calculate time since last gps reading
    dt = (float)(now - _optflow_last_update) * 0.001f;
    // update last gps update time
    _optflow_last_update = now;

    // discard samples where dt is too large
    if(!using_optflow) {
	    _position_error.x = 0;
        _position_error.y = 0;	
    }
   
    x = position_x_initial + optflow_ef_sum_x;
    y = position_y_initial + optflow_ef_sum_y;
    if( _hist_position_estimate_x.is_full()) {
        hist_position_base_x = _hist_position_estimate_x.front();
        hist_position_base_y = _hist_position_estimate_y.front();
    }
	else{
        hist_position_base_x = _position_base.x;
        hist_position_base_y = _position_base.y;
    }

    // calculate error in position from gps with our historical estimate
    _position_error.x = x - (hist_position_base_x + _position_correction.x);
    _position_error.y = y - (hist_position_base_y + _position_correction.y);

}

对当前速度和位置进行估计,将过去的几个位置数据放到队列里以备后用。

// update - updates velocities and positions using latest info from ahrs and barometer if new data is available;
void AP_InertialNav::update(float dt)
{
    // discard samples where dt is too large
    if( dt > 0.1f ) {
        return;
    }

    // decrement ignore error count if required
    if (_flags.ignore_error > 0) {
        _flags.ignore_error--;
    }

    // check if new baro readings have arrived and use them to correct vertical accelerometer offsets.
    check_baro();

    // check if new gps or optflow readings have arrived and use them to correct position estimates
	check_and_correct_with_optflow_and_gps();
	
    Vector3f accel_ef = _ahrs.get_accel_ef();

    // remove influence of gravity
    accel_ef.z += GRAVITY_MSS;
    accel_ef *= 100.0f;
	
    if(Motor_arm==false)
		accel_ef.z=0.0f;
    // remove xy if not enabled
    if( !using_optflow &&  !_xy_enabled) {  
        accel_ef.x = 0.0f;
        accel_ef.y = 0.0f;
    }

    //Convert North-East-Down to North-East-Up
    accel_ef.z = -accel_ef.z;

    // convert ef position error to horizontal body frame
    Vector2f position_error_hbf;
    position_error_hbf.x = _position_error.x * _ahrs.cos_yaw() + _position_error.y * _ahrs.sin_yaw();
    position_error_hbf.y = -_position_error.x * _ahrs.sin_yaw() + _position_error.y * _ahrs.cos_yaw();

    float tmp = _k3_xy * dt;
    accel_correction_hbf.x += position_error_hbf.x * tmp;
    accel_correction_hbf.y += position_error_hbf.y * tmp;
    accel_correction_hbf.z += _position_error.z * _k3_z  * dt;

    tmp = _k2_xy * dt;
    _velocity.x += _position_error.x * tmp;
    _velocity.y += _position_error.y * tmp;
    _velocity.z += _position_error.z * _k2_z  * dt;

    tmp = _k1_xy * dt;
    _position_correction.x += _position_error.x * tmp;
    _position_correction.y += _position_error.y * tmp;
    _position_correction.z += _position_error.z * _k1_z  * dt;

    // convert horizontal body frame accel correction to earth frame
    Vector2f accel_correction_ef;
    accel_correction_ef.x = accel_correction_hbf.x * _ahrs.cos_yaw() - accel_correction_hbf.y * _ahrs.sin_yaw();
    accel_correction_ef.y = accel_correction_hbf.x * _ahrs.sin_yaw() + accel_correction_hbf.y * _ahrs.cos_yaw();

    // calculate velocity increase adding new acceleration from accelerometers
    Vector3f velocity_increase;
    velocity_increase.x = (accel_ef.x + accel_correction_ef.x) * dt;
    velocity_increase.y = (accel_ef.y + accel_correction_ef.y) * dt;
    velocity_increase.z = (accel_ef.z + accel_correction_hbf.z) * dt;

    // calculate new estimate of position
    _position_base += (_velocity + velocity_increase*0.5) * dt;

    // update the corrected position estimate
    _position = _position_base + _position_correction;

    // calculate new velocity
    _velocity += velocity_increase;

    // store 3rd order estimate (i.e. estimated vertical position) for future use
    _hist_position_estimate_z.push_back(_position_base.z);
	
    // store 3rd order estimate (i.e. horizontal position) for future use at 10hz
    _historic_xy_counter++;
    if( _historic_xy_counter >= AP_INTERTIALNAV_SAVE_POS_AFTER_ITERATIONS ) {
        _historic_xy_counter = 0;
        _hist_position_estimate_x.push_back(_position_base.x);
        _hist_position_estimate_y.push_back(_position_base.y);
    }
}

接下来是定点,对位置控制进行更新。打开AC_WPNav.cpp,找到如下程序,

/// update_loiter - run the loiter controller - should be called at 100hz
void AC_WPNav::update_loiter()
{
    // calculate dt
    uint32_t now = hal.scheduler->millis();
    float dt = (now - _loiter_last_update) / 1000.0f;

    // reset step back to 0 if 0.1 seconds has passed and we completed the last full cycle
    if (dt >= WPNAV_LOITER_UPDATE_TIME) {
        // double check dt is reasonable
        if (dt >= 1.0f) {
            dt = 0.0;
        }
        // capture time since last iteration
        _loiter_last_update = now;
        // translate any adjustments from pilot to loiter target
        calc_loiter_desired_velocity(dt);
        // trigger position controller on next update
        _pos_control.trigger_xy();
    }else{
        // run horizontal position controller
        _pos_control.update_xy_controller(true);
    }
}

跳转到函数calc_loiter_desired_velocity(dt),该函数将飞行员的输入转化为期望的加速度,积分后转化为期望的速度。

/// calc_loiter_desired_velocity - updates desired velocity (i.e. feed forward) with pilot requested acceleration and fake wind resistance
///		updated velocity sent directly to position controller
void AC_WPNav::calc_loiter_desired_velocity(float nav_dt)
{
    // range check nav_dt
    if( nav_dt < 0 ) {
        return;
    }

    // check loiter speed and avoid divide by zero
    if( _loiter_speed_cms < WPNAV_LOITER_SPEED_MIN) {
        _loiter_speed_cms = WPNAV_LOITER_SPEED_MIN;
        _loiter_accel_cms = _loiter_speed_cms/2.0f;
    }

    // rotate pilot input to lat/lon frame
    Vector2f desired_accel;
    desired_accel.x = (_pilot_accel_fwd_cms*_ahrs.cos_yaw() - _pilot_accel_rgt_cms*_ahrs.sin_yaw());
    desired_accel.y = (_pilot_accel_fwd_cms*_ahrs.sin_yaw() + _pilot_accel_rgt_cms*_ahrs.cos_yaw());

    // calculate the difference
    Vector2f des_accel_diff = (desired_accel - _loiter_desired_accel);

    // constrain and scale the desired acceleration
    float des_accel_change_total = pythagorous2(des_accel_diff.x, des_accel_diff.y);
    float accel_change_max = _loiter_jerk_max_cmsss * nav_dt;
    if (des_accel_change_total > accel_change_max && des_accel_change_total > 0.0f) {
        des_accel_diff.x = accel_change_max * des_accel_diff.x/des_accel_change_total;
        des_accel_diff.y = accel_change_max * des_accel_diff.y/des_accel_change_total;
    }
    // adjust the desired acceleration
    _loiter_desired_accel += des_accel_diff;

    // get pos_control's feed forward velocity
    Vector3f desired_vel = _pos_control.get_desired_velocity();

    // add pilot commanded acceleration
    desired_vel.x += _loiter_desired_accel.x * nav_dt;
    desired_vel.y += _loiter_desired_accel.y * nav_dt;

    // reduce velocity with fake wind resistance
    if (_pilot_accel_fwd_cms != 0.0f || _pilot_accel_rgt_cms != 0.0f) {
        desired_vel.x -= (_loiter_accel_cms)*nav_dt*desired_vel.x/_loiter_speed_cms;
        desired_vel.y -= (_loiter_accel_cms)*nav_dt*desired_vel.y/_loiter_speed_cms;
    } else {
        desired_vel.x -= (_loiter_accel_cms-WPNAV_LOITER_ACCEL_MIN)*nav_dt*desired_vel.x/_loiter_speed_cms;
        if(desired_vel.x > 0 ) {
            desired_vel.x = max(desired_vel.x - WPNAV_LOITER_ACCEL_MIN*nav_dt, 0);
        }else if(desired_vel.x < 0) {
            desired_vel.x = min(desired_vel.x + WPNAV_LOITER_ACCEL_MIN*nav_dt, 0);
        }
        desired_vel.y -= (_loiter_accel_cms-WPNAV_LOITER_ACCEL_MIN)*nav_dt*desired_vel.y/_loiter_speed_cms;
        if(desired_vel.y > 0 ) {
            desired_vel.y = max(desired_vel.y - WPNAV_LOITER_ACCEL_MIN*nav_dt, 0);
        }else if(desired_vel.y < 0) {
            desired_vel.y = min(desired_vel.y + WPNAV_LOITER_ACCEL_MIN*nav_dt, 0);
        }
    }

    // send adjusted feed forward velocity back to position controller
    _pos_control.set_desired_velocity_xy(desired_vel.x,desired_vel.y);
}

打开AC_PosControl.cpp,找到位置更新函数update_xy_controller(true),分四步处理。

/// update_xy_controller - run the horizontal position controller - should be called at 100hz or higher
void AC_PosControl::update_xy_controller(bool use_desired_velocity)
{
    // catch if we've just been started
    uint32_t now = hal.scheduler->millis();
    if ((now - _last_update_xy_ms) >= POSCONTROL_ACTIVE_TIMEOUT_MS) {
        init_xy_controller();
        now = _last_update_xy_ms;
    }

    // check if xy leash needs to be recalculated
    calc_leash_length_xy();

    // reset step back to 0 if loiter or waypoint parents have triggered an update and we completed the last full cycle
    if (_flags.force_recalc_xy && _xy_step > 3) {
        _flags.force_recalc_xy = false;
        _xy_step = 0;
    }

    // run loiter steps
    switch (_xy_step) {
        case 0:
            // capture time since last iteration
            _dt_xy = (now - _last_update_xy_ms) / 1000.0f;
            _last_update_xy_ms = now;

            // translate any adjustments from pilot to loiter target
            desired_vel_to_pos(_dt_xy);
            _xy_step++;
            break;
        case 1:
            // run position controller's position error to desired velocity step
            pos_to_rate_xy(use_desired_velocity,_dt_xy);
            _xy_step++;
            break;
        case 2:
            // run position controller's velocity to acceleration step
            rate_to_accel_xy(_dt_xy);
            _xy_step++;
            break;
        case 3:
            // run position controller's acceleration to lean angle step
            accel_to_lean_angles();
            _xy_step++;
            break;
    }
}

第一步,将期望的速度转化为期望的位置。

/// desired_vel_to_pos - move position target using desired velocities
void AC_PosControl::desired_vel_to_pos(float nav_dt)
{
    // range check nav_dt
    if( nav_dt < 0 ) {
        return;
    }

    // update target position
    if (_flags.reset_desired_vel_to_pos) {
        _flags.reset_desired_vel_to_pos = false;
    } else {
        _pos_target.x += _vel_desired.x * nav_dt;
        _pos_target.y += _vel_desired.y * nav_dt;
    }
}

第二步,根据期望位置和当前位置计算出位置误差_pos_error,注意此处的位置误差不同于光流位置估计的_position_error。_position_error是用来估计当前位置的,而_pos_error是目标位置和当前位置的误差。再根据_pos_error计算出目标速度_vel_target。

/// pos_to_rate_xy - horizontal position error to velocity controller
///     converts position (_pos_target) to target velocity (_vel_target)
///     when use_desired_rate is set to true:
///         desired velocity (_vel_desired) is combined into final target velocity and
///         velocity due to position error is reduce to a maximum of 1m/s
void AC_PosControl::pos_to_rate_xy(bool use_desired_rate, float dt)
{
    Vector3f curr_pos = _inav.get_position();
    float linear_distance;      // the distance we swap between linear and sqrt velocity response
    float kP = _p_pos_xy.kP();

    // avoid divide by zero
    if (kP <= 0.0f) {
        _vel_target.x = 0.0f;
        _vel_target.y = 0.0f;
    }else{
        // calculate distance error
        _pos_error.x = _pos_target.x - curr_pos.x;
        _pos_error.y = _pos_target.y - curr_pos.y;

        // constrain target position to within reasonable distance of current location
        _distance_to_target = pythagorous2(_pos_error.x, _pos_error.y);
        if (_distance_to_target > _leash && _distance_to_target > 0.0f) {
            _pos_target.x = curr_pos.x + _leash * _pos_error.x/_distance_to_target;
            _pos_target.y = curr_pos.y + _leash * _pos_error.y/_distance_to_target;
            // re-calculate distance error
            _pos_error.x = _pos_target.x - curr_pos.x;
            _pos_error.y = _pos_target.y - curr_pos.y;
            _distance_to_target = _leash;
        }

        // calculate the distance at which we swap between linear and sqrt velocity response
        linear_distance = _accel_cms/(2.0f*kP*kP);

        if (_distance_to_target > 2.0f*linear_distance) {
            // velocity response grows with the square root of the distance
            float vel_sqrt = safe_sqrt(2.0f*_accel_cms*(_distance_to_target-linear_distance));
            _vel_target.x = vel_sqrt * _pos_error.x/_distance_to_target;
            _vel_target.y = vel_sqrt * _pos_error.y/_distance_to_target;
        }else{
            // velocity response grows linearly with the distance
            _vel_target.x = _p_pos_xy.kP() * _pos_error.x;
            _vel_target.y = _p_pos_xy.kP() * _pos_error.y;
        }

        // decide velocity limit due to position error
        float vel_max_from_pos_error;
        if (use_desired_rate) {
            // if desired velocity (i.e. velocity feed forward) is being used we limit the maximum velocity correction due to position error to 2m/s
            vel_max_from_pos_error = POSCONTROL_VEL_XY_MAX_FROM_POS_ERR;
        }else{
            // if desired velocity is not used, we allow position error to increase speed up to maximum speed
            vel_max_from_pos_error = _speed_cms;
        }

        // scale velocity to stays within limits
        float vel_total = pythagorous2(_vel_target.x, _vel_target.y);
        if (vel_total > vel_max_from_pos_error) {
            _vel_target.x = vel_max_from_pos_error * _vel_target.x/vel_total;
            _vel_target.y = vel_max_from_pos_error * _vel_target.y/vel_total;
        }

        // add desired velocity (i.e. feed forward).
        if (use_desired_rate) {
            _vel_target.x += _vel_desired.x;
            _vel_target.y += _vel_desired.y;
        }
    }
}

第三步,跟据当前速度_vel _curr计算出速度误差_vel_error,位置误差的P值 + 速度误差的I值 + 速度误差的D值,得到目标加速度_accel_target。

/// rate_to_accel_xy - horizontal desired rate to desired acceleration
///    converts desired velocities in lat/lon directions to accelerations in lat/lon frame
void AC_PosControl::rate_to_accel_xy(float dt)
{
    const Vector3f &vel_curr = _inav.get_velocity();  // current velocity in cm/s
    float accel_total;                          // total acceleration in cm/s/s
    float lat_i, lon_i;

    // reset last velocity target to current target
    if (_flags.reset_rate_to_accel_xy) {
        _vel_last.x = _vel_target.x;
        _vel_last.y = _vel_target.y;
        _flags.reset_rate_to_accel_xy = false;
    }

    // feed forward desired acceleration calculation
    if (dt > 0.0f) {
    	if (!_flags.freeze_ff_xy) {
    		_accel_feedforward.x = (_vel_target.x - _vel_last.x)/dt;
    		_accel_feedforward.y = (_vel_target.y - _vel_last.y)/dt;
        } else {
    		// stop the feed forward being calculated during a known discontinuity
    		_flags.freeze_ff_xy = false;
    	}
    } else {
    	_accel_feedforward.x = 0.0f;
    	_accel_feedforward.y = 0.0f;
    }

    // store this iteration's velocities for the next iteration
    _vel_last.x = _vel_target.x;
    _vel_last.y = _vel_target.y;

    // calculate velocity error
    _vel_error.x = _vel_target.x - vel_curr.x;
    _vel_error.y = _vel_target.y - vel_curr.y;

    // get current i term
    lat_i = _pid_rate_lat.get_integrator();
    lon_i = _pid_rate_lon.get_integrator();

    // update i term if we have not hit the accel or throttle limits OR the i term will reduce
    if ((!_limit.accel_xy && !_motors.limit.throttle_upper) || ((lat_i>0&&_vel_error.x<0)||(lat_i<0&&_vel_error.x>0))) {
        lat_i = _pid_rate_lat.get_i(_vel_error.x, dt);
    }
    if ((!_limit.accel_xy && !_motors.limit.throttle_upper) || ((lon_i>0&&_vel_error.y<0)||(lon_i<0&&_vel_error.y>0))) {
        lon_i = _pid_rate_lon.get_i(_vel_error.y, dt);
    }

    // combine feed forward accel with PID output from velocity error
    _accel_target.x = _accel_feedforward.x + _pid_rate_lat.get_p(_vel_error.x) + lat_i + _pid_rate_lat.get_d(_vel_error.x, dt);
    _accel_target.y = _accel_feedforward.y + _pid_rate_lon.get_p(_vel_error.y) + lon_i + _pid_rate_lon.get_d(_vel_error.y, dt);

    // scale desired acceleration if it's beyond acceptable limit
    // To-Do: move this check down to the accel_to_lean_angle method?
    accel_total = pythagorous2(_accel_target.x, _accel_target.y);
    if (accel_total > POSCONTROL_ACCEL_XY_MAX && accel_total > 0.0f) {
        _accel_target.x = POSCONTROL_ACCEL_XY_MAX * _accel_target.x/accel_total;
        _accel_target.y = POSCONTROL_ACCEL_XY_MAX * _accel_target.y/accel_total;
        _limit.accel_xy = true;     // unused
    } else {
        // reset accel limit flag
        _limit.accel_xy = false;
    }
}

第四步,将目标加速度转化为目标roll/pitch角度,通过改变飞行器姿态角实现位置控制。

/// accel_to_lean_angles - horizontal desired acceleration to lean angles
///    converts desired accelerations provided in lat/lon frame to roll/pitch angles
void AC_PosControl::accel_to_lean_angles()
{
    float accel_right, accel_forward;
    float lean_angle_max = _attitude_control.lean_angle_max();

    // To-Do: add 1hz filter to accel_lat, accel_lon

    // rotate accelerations into body forward-right frame
    accel_forward = _accel_target.x*_ahrs.cos_yaw() + _accel_target.y*_ahrs.sin_yaw();
    accel_right = -_accel_target.x*_ahrs.sin_yaw() + _accel_target.y*_ahrs.cos_yaw();

    // update angle targets that will be passed to stabilize controller
    _roll_target = constrain_float(fast_atan(accel_right*_ahrs.cos_pitch()/(GRAVITY_MSS * 100))*(18000/M_PI), -lean_angle_max, lean_angle_max);
    _pitch_target = constrain_float(fast_atan(-accel_forward/(GRAVITY_MSS * 100))*(18000/M_PI),-lean_angle_max, lean_angle_max);
}

整个流程大致如此,如有不对之处,欢迎批评指正!




本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

光流定点程序梳理 的相关文章

  • 卖网红饮品喜茶配方,他一年赚50万

    前两天我去了一趟广州 xff0c 参加了一个内部圈子的交流会 xff0c 加过我微信的朋友可能会看到 在交流会上 xff0c 认识了一个年轻小伙子 xff0c 大伙称呼他为豪哥 xff0c 在分享会上他讲述了自己卖喜茶配方年赚50万的创业历
  • 入职新公司被要工资流水!这背后到底有什么猫腻?

    点击上方 小麦大叔 xff0c 选择 置顶 星标公众号 福利干货 xff0c 第一时间送达 关于 什么时候工资流水成了新人入职必须提供的资料项之一 这件事 xff0c 作为一个打工人还是多少了解一下背后的原因的 其实要说以前 xff08 不
  • 俄罗斯自研的CPU,能用吗?

    点击上方 小麦大叔 xff0c 选择 置顶 星标公众号 福利干货 xff0c 第一时间送达 从 2014 年吞并克里米亚到 2022 年的俄乌战争 xff0c 俄罗斯和西方世界的冲突愈发加剧 xff0c 在能源和科技上的相互制裁不断升级 在
  • 爆一下年终奖

    点击上方 小麦大叔 xff0c 选择 置顶 星标公众号 福利干货 xff0c 第一时间送达 各个公司的年终奖大多尘埃落定了 xff0c 大环境说实话不是很好 xff0c 有人欢喜有人愁 xff0c 汇总了一波 xff0c 大概50位小伙伴的
  • ChatGPT火爆,背后的核心到底是什么?

    点击上方 小麦大叔 xff0c 选择 置顶 星标公众号 福利干货 xff0c 第一时间送达 2022年12月份的时候 xff0c ChatGPT还只是个被人各种撩的聊天工具 但进入2023年后 xff0c 已经向着效率工具迈进了 微软宣布正
  • 不小心当上CTO了

    点击上方 小麦大叔 xff0c 选择 置顶 星标公众号 福利干货 xff0c 第一时间送达 大家好 xff0c 我是小麦 在知乎上看到一篇从创业公司的工程师一路成长为CTO的真实故事 看完我获益匪浅 在这里分享给大家 01 创业初期 在创业
  • 盘点一下电子嵌入式相关的公司

    点击上方 小麦大叔 xff0c 选择 置顶 星标公众号 福利干货 xff0c 第一时间送达 大家好 xff0c 我是小麦 最近入坑电子信息工程专业的表弟问我毕业能去干什么 xff1f 于是在这里盘点了一下电子嵌入式相关的公司 在这里和大家分
  • C# 解析ini类型文件详解

    1 什么是ini文件 INI文件是一种配置文件格式 xff0c 通常用于Windows操作系统中的应用程序中 它是一种文本文件 xff0c 由多个节和键值对组成 xff0c 用于存储应用程序的配置信息 INI文件的特点包括 xff1a IN
  • CAN总线显性电平和隐性电平详解

    相关文章 CAN总线简易入门教程 CAN总线显性电平和隐性电平详解 STM32的CAN总线调试经验分享 CAN 信号线 CAN 传输的两条信号线被称为 CAN H 和CAN L 通电状态 xff1a CAN H xff08 2 5V xff
  • STM32的CAN总线调试经验分享

    相关文章 CAN总线简易入门教程 CAN总线显性电平和隐性电平详解 STM32的CAN总线调试经验分享 文章目录 相关文章背景CAN总线CAN控制器CAN收发器 调试过程硬件排查CAN分析仪芯片CAN控制器调试 总结 背景 最近负责的一个项
  • 我的新副业

    大家好 xff0c 我是麦叔 聊聊我的新副业吧 尝试做了一段时间餐饮 xff0c 差不多有半年时间了 感触很深 在这里和大家分享一下 缘起 去年10月份朋友的店铺转让 xff0c 于是我就盘下来了 店面不大 xff0c 投入也不是很大 xf
  • 螺旋桨拉力

    介绍 螺旋桨拉力计算公式 直径 xff08 米 xff09 X 螺距 xff08 米 xff09 X 桨宽度 xff08 米 xff09 X 转速 xff08 转 秒 xff09 X 经验系数 xff08 0 25 xff09 61 拉力
  • 爆炸了!但YYDS

    正文共 xff1a 2962字 预计阅读时间 xff1a 8分钟 成功发射 xff0c 但在空中爆炸了 这一刻 xff0c 我们都仰望星空 北京时间 4 月 20 日晚 9 点半 xff0c 随着倒计时声音的结束 xff0c 在 Space
  • 赞爆了!Tabby 这款开源工具真的好用!

    点击上方 小麦大叔 xff0c 选择 置顶 星标公众号 福利干货 xff0c 第一时间送达
  • 惠普暗影精灵VMware安装CentOS7显示[此主机支持 Intel VT-x,但 Intel VT-x 处于禁用状态]

    进入BIOS页面开启CPU虚拟化处理技术 电脑开机时按 ESC 键进入系统启动菜单 然后按F10进入BIOS页面 xff0c 开启 处理器虚拟化技术
  • Python的main函数

    在 Python 中 xff0c 程序的入口点通常指的是一个特定的函数 xff0c 即 main 函数 这个函数是程序的起点 xff0c 也是程序的入口 xff0c 通过调用 main 函数 xff0c 程序开始执行 在 Python 中
  • CMakeList静态库多层嵌套问题 undefined reference to

    前言 被一个问题缠绕了很长时间 xff0c 这两天花精力好好研究了一下 xff0c 总算解决了 xff0c 翻过来看 xff0c 就是自己不注意造的很多小问题 我的想法是把一些代码封装起来 xff0c 但是有些部分要求能让现场工作同事有一定
  • gazebo仿真环境中 加入传感器

    1 传感器加入自己的模型中需要那些步骤 1 节点说明 xff0c 链接关系 lt robot name 61 test gt lt link name 61 34 link1 34 gt lt link name 61 34 link2 3
  • 以下为Windows NT 下的32 位C++程序,请计算sizeof 的值

    char str 61 Hello char p 61 str int n 61 10 请计算 sizeof str 61 sizeof p 61 sizeof n 61 答案 6 4 4 void Func char str 100 请计
  • vue实现显示10条数据点击查询看更多

    vue实现显示10条数据点击查询看更多 要求如下 当页面的数据超过10条时只显示10条 且显示 点我查看更多喔 每次点击 页面数据增加10条 思路解析 如题 假设有20条数据 只显示10条 点击查看更多按钮 span class token

随机推荐

  • 433和2.4G无线通信比较

    DSSS 2 4G无线数据传输系统优势 一 很正常的升级换代 xff1a 系统工作的长期稳定性和可靠性 xff0c 是一个无线通信系统最重要的指标 由于一般433兆及915兆产品使用的是低频窄带通信技术 xff0c 它们的工作频率范围很窄5
  • Android adb dumpsys 命令总结

    adb shell dumpsys 有哪些命令可以用 xff0c 可以使用adb shell service list来查看 查看package这个service的帮助信息 adb shell dumpsys package h 查看act
  • 在STM32中实现ROS节点——Rosserial的用法

    目录 内容介绍前言生成要移植到stm32的自定义消息和服务生成针对stm32的移植库包roslibs在Mdk中实现C和C 43 43 代码混合编译修改mdk配置修改stm32 ROS通讯接口驱动测试补充说明 内容介绍 本文介绍如何将stm3
  • ubuntu下用apt-get install 安装软件时出现 initramfs-tools错误

    错误提示 xff1a gzip stdout No space left on device E mkinitramfs failure cpio 141 gzip 1 update initramfs failed for boot in
  • ubuntu虚拟机下桥接模式,静态ip上网

    近期使用VM安装了ubuntu12 10玩玩 xff0c 途中也遇到了许多问题 xff0c 安装完成后 xff0c 系统默认是选择NAT连接方式的 xff0c 可以直接通过火狐浏览器上网 xff08 不需任何设置 xff0c 系统自己配置好
  • Windows下安装GTK+(整理)

    以下为网上摘的 xff0c 自己把它整理在了一起 WINDOWS下实现GTK 43 GTKMM的编程 xff08 一 xff09 下载软件包 1 下载dev C 43 43 开发软件 xff08 我的版本是4 9 9 2 xff09 2 下
  • windows gtk+开发环境搭建方法详解(图解)

    GTK 43 开发环境搭建 工欲善其事 必先利其器 首先介绍一下GTK 43 开发环境的搭建 网上很多所谓的GTK的开发环境的搭建基本都是抄来抄去 也不知道有没有人使用他们介绍的方法搭建并编译成功 很不幸的是我使用他们写的方法没有搭建成功
  • GTK在Linux下的安装

    linux gtk 是linux平台下的图形界面开发接口 xff0c 它不同于qt xff0c 它是完全免费的 xff0c Qt是基于c 43 43 的商业开发包 xff0c 涉及版权等问题 xff0c gtk则是基于c的全免费的 xff0
  • android SDK下各目录的用途,以及在不同系统下哪些是通用的。

    Android SDK 目录下有很多文件夹 xff0c 主要都是干什么的呢 1 add ons 这里面保存着附加库 xff0c 比如google Maps xff0c 当然你如果安装了OPhone SDK xff0c 这里也会有一些类库在里
  • Linux系统文件系统基础罗列

    Linux 文件系统是linux的一个十分基础的知识 xff0c 同时也是学习linux的必备知识 本文将站在一个较高的视图来了解linux的文件系统 xff0c 主要包括了linux磁盘分区和目录 挂载基本原理 文件存储结构 软链接硬链接
  • 至今互联网上收集最全的精品教育资源【转载+补充】

    海慧为您奉上 xff01 xff01 xff01 操作指引 xff1a 第一步 xff1a 注册一个360云盘账号 xff0c 注册地址 xff1a yunpan 360 cn xff0c 拥有自己的账号和密码 第二步 xff1a 下载36
  • Linux Android 真机调试配置

    1 USB 链接手机 xff0c 打开USB调试 2 在终端中输入lsusb命令 xff0c 查看USB设备信息 xff0c 找到手机对应的USB信息 eg Bus 002 Device 003 ID 12d1 1035 Huawei Te
  • 【ROS2】类ROS1的rate定时器写法(c++&python例子)

    在ROS2官方教程里 xff0c 代码和ROS1例程有很大不同 xff0c 大部分节点都使用了类的写法 xff0c 并且用到了很多C 43 43 的新功能 其中 xff0c 发布消息是采用了一个定时器timer xff0c 并且注册一个回调
  • C++中STL常用容器的优点和缺点

    我们常用到的STL 容器 有vector list deque map multimap set和multiset xff0c 它们究竟有何区别 xff0c 各自的优缺点是什么 xff0c 为了更好的扬长避短 xff0c 提高程序性能 xf
  • APM飞控学习之路:5 串口概述与收发调试

    云中谁寄锦书来 xff0c 雁字回时 xff0c 月满西楼 当无人机在空中飞翔时 xff0c 从APM飞控到飞手之间有几条看不见的 风筝线 xff08 1 xff09 2 4GHz的遥控 xff1b xff08 2 xff09 433 91
  • 【Android车载系列】第3章 车载通讯CAN协议

    1 CAN总线的基本概念以及由来 1 1 简介 CAN 总线即控制器局域网总线 CAN xff0c Controller Area Network 是一种用于实时应用的串行通讯协议总线 xff0c 被公认为最有发展前景的现场总线之一 由 B
  • 经典面试题---linux启动流程

    linux启动流程 xff1a power on gt BIOS加电自检 xff0c 根据引导顺序启动 gt MBR gt grub stage2 gt boot grub grub conf gt 加载内核和ramfs文件系统 gt in
  • APM添加超声模块及定高程序分析

    给飞控添加新的模块 xff0c 通常的做法是写驱动文件 xff0c 然后用uORB订阅消息 xff0c 这种方法已经有文章介绍了 xff0c 下面介绍另一种更加简洁的方法 硬件连接 UARTD xff08 ttyS2 xff09 超声 Bo
  • 光流定点若干问题分析

    一 光流摄像头移动速度快慢对结果的影响 实际测试发现 xff0c 在一定高度水平慢速移动光流摄像头20个单位长度 xff0c 光流累加值为6 9 Pixel xff0c 水平快速移动光流摄像头20个单位长度 xff0c 光流累加值为50 6
  • 光流定点程序梳理

    本文主要分析飞控获取到光流数据之后 xff0c 如何实现定点 xff0c 至于光流算法 xff0c 不在本文讨论范围内 官网介绍的PX4 Flow采用STM32F4作为主控 xff0c 定点效果不错 xff0c 但价格稍贵 xff0c 而且