pixhawk mc_pos_control.cpp源码解读_pixhawk mc_pos-程序员宅基地

好久没跟新blog了,这段时期边调试边看程序,所以有点慢。要开始着手调试了。。

这篇blog是顺着上一篇pixhawk 整体架构的认识写的,接下来看程序的话,打算把各个功能模块理解一遍,先从mc_pos_control.cpp看起。


先提一下pixhawk的整体逻辑:

commander和navigator产生期望位置
position_estimator是当前位置
通过pos_ctrl产生期望姿态
attitude_estimator是当前姿态
通过att_ctrl产生pwm的数值
最后通过mixer和motor_driver控制4个电机


pos_ctrl的总体逻辑是:

(1)copy  commander和navigator产生的期望位置-----_pos_sp_triplet结构体

(2)产生位置/速度设定值(期望值)-----_pos_sp<3>向量和_vel_sp<3>向量

(3)产生可利用的速度设定值(期望值)-----_vel_sp<3>向量

(4)产生可利用的推力定值(期望值)-----thrust_sp<3>向量

(5)根据推力向量计算姿态设定值(期望姿态)-----q_sp四元数矩阵和R_sp旋转矩阵

(6)将之前程序得到的各种信息填充_local_pos_sp结构体,并发布出去-----_local_pos_sp(第2、3步得到的)

(7)根据具体应用更改之前得到的姿态设定值(期望姿态),并发布出去-----_att_sp(第5步得到的)


那么,直接贴代码了,代码中有详细注释(比之前要更详细点,里面细节逻辑太多了,还没完全理清)

先是main代码,再是control_manual(dt);control_offboard(dt);control_auto(dt);函数,再是map_projection_project()函数,最后是常用矩阵和向量函数

task_main()

void
MulticopterPositionControl::task_main()
{

	_mavlink_fd = px4_open(MAVLINK_LOG_DEVICE, 0);

	/*
	 * do subscriptions
	 */
	_vehicle_status_sub = orb_subscribe(ORB_ID(vehicle_status));
	_ctrl_state_sub = orb_subscribe(ORB_ID(control_state));
	_att_sp_sub = orb_subscribe(ORB_ID(vehicle_attitude_setpoint));
	_control_mode_sub = orb_subscribe(ORB_ID(vehicle_control_mode));
	_params_sub = orb_subscribe(ORB_ID(parameter_update));
	_manual_sub = orb_subscribe(ORB_ID(manual_control_setpoint));
	_arming_sub = orb_subscribe(ORB_ID(actuator_armed));
	_local_pos_sub = orb_subscribe(ORB_ID(vehicle_local_position));
	_pos_sp_triplet_sub = orb_subscribe(ORB_ID(position_setpoint_triplet));
	_local_pos_sp_sub = orb_subscribe(ORB_ID(vehicle_local_position_setpoint));
	_global_vel_sp_sub = orb_subscribe(ORB_ID(vehicle_global_velocity_setpoint));


	parameters_update(true);

	/* initialize values of critical structs until first regular update */
	_arming.armed = false;

	/* get an initial update for all sensor and status data */
	poll_subscriptions();

	bool reset_int_z = true;
	bool reset_int_z_manual = false;
	bool reset_int_xy = true;
	bool reset_yaw_sp = true;
	bool was_armed = false;

	hrt_abstime t_prev = 0;

	math::Vector<3> thrust_int;
	thrust_int.zero();


	math::Matrix<3, 3> R;
	R.identity();

	/* wakeup source */
	px4_pollfd_struct_t fds[1];

	fds[0].fd = _local_pos_sub;//主要是判断是否有当地位置跟新
	fds[0].events = POLLIN;//#define POLLIN       (0x01)
///大循环//
	while (!_task_should_exit) {
/*****************第一部分:一系列的准备工作*****************/
		/* wait for up to 500ms for data */
		int pret = px4_poll(&fds[0], (sizeof(fds) / sizeof(fds[0])), 500);

		/* timed out - periodic check for _task_should_exit */
		if (pret == 0) {
			continue;
		}

		/* this is undesirable but not much we can do */
		if (pret < 0) {
			warn("poll error %d, %d", pret, errno);
			continue;
		}
///获取各种信息///
		poll_subscriptions();

		parameters_update(false);

		hrt_abstime t = hrt_absolute_time();//获取绝对时间,类似于时刻
		float dt = t_prev != 0 ? (t - t_prev) * 0.000001f : 0.0f;//相对时间,类似时间段
		t_prev = t;//跟新t_prev

		// set dt for control blocks   给控制块设置dt(这个是操作系统层的)
		setDt(dt);
/对解锁状态进行判断之后复位位置和高度设置/
		if (_control_mode.flag_armed && !was_armed) {
			/* reset setpoints and integrals on arming */
			_reset_pos_sp = true;
			_reset_alt_sp = true;
			_vel_sp_prev.zero();
			reset_int_z = true;
			reset_int_xy = true;
			reset_yaw_sp = true;
		}
固定翼模式为垂直起降控制复位yaw和高度设置/
		/* reset yaw and altitude setpoint for VTOL which are in fw mode */
		if (_vehicle_status.is_vtol) {
			if (!_vehicle_status.is_rotary_wing) {
				reset_yaw_sp = true;
				_reset_alt_sp = true;
			}
		}
///跟新前一时刻的解锁状态/
		//Update previous arming state
		was_armed = _control_mode.flag_armed;

		update_ref();//跟新一些地坐标xyz方向基准值
//将之前获取的值赋给mc_pos_control_main.cpp里的变量///		
//独立于当前模式跟新加速度//
		/* Update velocity derivative,
		 * independent of the current flight mode
		 */
		if (_local_pos.timestamp > 0) {

			if (PX4_ISFINITE(_local_pos.x) &&
					PX4_ISFINITE(_local_pos.y) &&
					PX4_ISFINITE(_local_pos.z)) {
			/*ISFINITE是判断是否为有限数*/
				_pos(0) = _local_pos.x;
				_pos(1) = _local_pos.y;
				_pos(2) = _local_pos.z;
			}

			if (PX4_ISFINITE(_local_pos.vx) &&
					PX4_ISFINITE(_local_pos.vy) &&
					PX4_ISFINITE(_local_pos.vz)) {

				_vel(0) = _local_pos.vx;
				_vel(1) = _local_pos.vy;
				_vel(2) = _local_pos.vz;
			}

			_vel_err_d(0) = _vel_x_deriv.update(-_vel(0));
			_vel_err_d(1) = _vel_y_deriv.update(-_vel(1));
			_vel_err_d(2) = _vel_z_deriv.update(-_vel(2));
			/*	math::Vector<3> _vel_err_d;		//< derivative of current velocity */
			/*  _vel_err_d当前速度的导数*/
		}
非手动模式下或者不需要位置高度控制的情况下,复位水平和垂直位置hold的标志位///
以下的_control_mode.xxxxxx均来自commander.cpp///
		// reset the horizontal and vertical position hold flags for non-manual modes
		// or if position / altitude is not controlled
		if (!_control_mode.flag_control_position_enabled || !_control_mode.flag_control_manual_enabled) {
			_pos_hold_engaged = false;
		}

		if (!_control_mode.flag_control_altitude_enabled || !_control_mode.flag_control_manual_enabled) {
			_alt_hold_engaged = false;
		}
/*****************第二部分:这部分应该就是控制位置控制逻辑的集中体现了*****************/
高度控制、位置控制、爬升速率控制、速度控制任意一个使能则进入以下这段程序//
		if (_control_mode.flag_control_altitude_enabled ||
				_control_mode.flag_control_position_enabled ||
				_control_mode.flag_control_climb_rate_enabled ||
				_control_mode.flag_control_velocity_enabled) {

			_vel_ff.zero();//置零(有何用还没找到)
/默认是位置/高度控制器,也可以直接运行速度控制器/
			/* by default, run position/altitude controller. the control_* functions
			 * can disable this and run velocity controllers directly in this cycle */
			_run_pos_control = true;//标志位
			_run_alt_control = true;//标志位
/*****************第二部分第一步:产生位置/速度设定值(期望值)*****************/
选择控制源是手动、机外(offboard)、还是自动控制,产生位置/速度设定值(期望值)//
这部分的三个函数具体会在下面展开/
			/* select control source */
			if (_control_mode.flag_control_manual_enabled) {
				/* manual control */
				control_manual(dt);
				_mode_auto = false;

			} else if (_control_mode.flag_control_offboard_enabled) {
				/* offboard control */
				control_offboard(dt);
				_mode_auto = false;

			} else {
				/* AUTO */
				control_auto(dt);
			}
/*****************第二部分第二步:产生姿态设定值(期望值)*****************/
vtol不用管,它是用于固定翼的///
			/* weather-vane mode for vtol: disable yaw control */
			if (!_control_mode.flag_control_manual_enabled && _pos_sp_triplet.current.disable_mc_yaw_control == true) {
				_att_sp.disable_mc_yaw_control = true;

			} else {
				/* reset in case of setpoint updates */
				_att_sp.disable_mc_yaw_control = false;
			}
工作在手动控制失能&&当前位置设定值合法&&当前位置设定值的类型是空闲状态下///
那么不运行控制器,并且设置油门为0/
			if (!_control_mode.flag_control_manual_enabled && _pos_sp_triplet.current.valid
					&& _pos_sp_triplet.current.type == position_setpoint_s::SETPOINT_TYPE_IDLE) {
				/* idle state, don't run controller and set zero thrust */
			/*	static const uint8_t SETPOINT_TYPE_POSITION = 0;
			 *	static const uint8_t SETPOINT_TYPE_VELOCITY = 1;
			 *	static const uint8_t SETPOINT_TYPE_LOITER = 2;
			 *	static const uint8_t SETPOINT_TYPE_TAKEOFF = 3;
			 *	static const uint8_t SETPOINT_TYPE_LAND = 4;
			 *	static const uint8_t SETPOINT_TYPE_IDLE = 5;
			 *	static const uint8_t SETPOINT_TYPE_OFFBOARD = 6;
			 */
				R.identity();//R矩阵单位化
			/*	在大循环外定义了R矩阵
			 *	math::Matrix<3, 3> R;
			 *	R.identity();
			 *	
			 * Firmware/src/lib/mathlib/math/Matrix.hpp
			 * set identity matrix单位矩阵
			 *
			 * void identity(void) {
			 *	memset(data, 0, sizeof(data));
			 *	unsigned int n = (M < N) ? M : N;
			 *
			 *	for (unsigned int i = 0; i < n; i++)
			 *		data[i][i] = 1;
			 * }
			 */
				memcpy(&_att_sp.R_body[0], R.data, sizeof(_att_sp.R_body));
				//将姿态设定值的R_body[9]数组复制到R矩阵中
				_att_sp.R_valid = true;

				_att_sp.roll_body = 0.0f;
				_att_sp.pitch_body = 0.0f;
				_att_sp.yaw_body = _yaw;
				_att_sp.thrust = 0.0f;

				_att_sp.timestamp = hrt_absolute_time();
//此处发布的姿态设定值不是正常使用的,都被置为0了//
				/* publish attitude setpoint */
				if (_att_sp_pub != nullptr) {
					orb_publish(_attitude_setpoint_id, _att_sp_pub, &_att_sp);

				} else if (_attitude_setpoint_id) {
					_att_sp_pub = orb_advertise(_attitude_setpoint_id, &_att_sp);
				}

			}
手动控制使能&&着陆使能状态下///
			else if (_control_mode.flag_control_manual_enabled
					&& _vehicle_status.condition_landed) {
				//不运行控制器,当着落的时候(所以位置和高度设定值等复位)//
				/* don't run controller when landed */
				_reset_pos_sp = true;
				_reset_alt_sp = true;
				_mode_auto = false;
				reset_int_z = true;
				reset_int_xy = true;

				R.identity();//R矩阵单位化
				memcpy(&_att_sp.R_body[0], R.data, sizeof(_att_sp.R_body));
				//将姿态设定值的R_body[9]数组复制到R矩阵中
				_att_sp.R_valid = true;

				_att_sp.roll_body = 0.0f;
				_att_sp.pitch_body = 0.0f;
				_att_sp.yaw_body = _yaw;
				_att_sp.thrust = 0.0f;

				_att_sp.timestamp = hrt_absolute_time();
//这里发布的姿态设定值是和着陆模式有关的,并不是飞行的姿态设定值///
				/* publish attitude setpoint */
				if (_att_sp_pub != nullptr) {
					orb_publish(_attitude_setpoint_id, _att_sp_pub, &_att_sp);

				} else if (_attitude_setpoint_id) {
					_att_sp_pub = orb_advertise(_attitude_setpoint_id, &_att_sp);
				}

			}
/*****************第二部分第二步的重点:产生姿态设定值(期望值)*****************/
///这段程序应该才是发布正常飞行状态的姿态设定值//
///运行位置和高度控制器(否则采用已经计算出来的速度设定值)//
			else {
			/****************第二部分第二步的重点(1):产生可利用的速度设定值(期望值)*******************/
				/* run position & altitude controllers, if enabled (otherwise use already computed velocity setpoints) */
				if (_run_pos_control) {
					_vel_sp(0) = (_pos_sp(0) - _pos(0)) * _params.pos_p(0);
					_vel_sp(1) = (_pos_sp(1) - _pos(1)) * _params.pos_p(1);
				}

				if (_run_alt_control) {
					_vel_sp(2) = (_pos_sp(2) - _pos(2)) * _params.pos_p(2);
				}
			/*	刚进入大循环时赋值,进行选择
			 *	_run_pos_control = true;//标志位
			 *	_run_alt_control = true;//标志位
			 *
			 *	_pos(n)就是之前orb_copy(ORB_ID(vehicle_local_position), _local_pos_sub, &_local_pos);
			 *	_pos(0) = _local_pos.x;
			 *	_pos(1) = _local_pos.y;
			 *	_pos(2) = _local_pos.z;
			 *
			 *	_pos_sp就是之前control_manual(dt);control_offboard(dt);control_auto(dt);的输出值
			 */
				/* make sure velocity setpoint is saturated in xy*/
				float vel_norm_xy = sqrtf(_vel_sp(0) * _vel_sp(0) +
							  _vel_sp(1) * _vel_sp(1));

				if (vel_norm_xy > _params.vel_max(0)) {
					/* note assumes vel_max(0) == vel_max(1) */
					_vel_sp(0) = _vel_sp(0) * _params.vel_max(0) / vel_norm_xy;
					_vel_sp(1) = _vel_sp(1) * _params.vel_max(1) / vel_norm_xy;
				}
				//以下是设定垂直速度///
				/* make sure velocity setpoint is saturated in z*/
				float vel_norm_z = sqrtf(_vel_sp(2) * _vel_sp(2));

				if (vel_norm_z > _params.vel_max(2)) {
					_vel_sp(2) = _vel_sp(2) * _params.vel_max(2) / vel_norm_z;
				}

				if (!_control_mode.flag_control_position_enabled) {
					_reset_pos_sp = true;
				}

				if (!_control_mode.flag_control_altitude_enabled) {
					_reset_alt_sp = true;
				}

				if (!_control_mode.flag_control_velocity_enabled) {
					_vel_sp_prev(0) = _vel(0);
					_vel_sp_prev(1) = _vel(1);
					_vel_sp(0) = 0.0f;
					_vel_sp(1) = 0.0f;
					control_vel_enabled_prev = false;
				}

				if (!_control_mode.flag_control_climb_rate_enabled) {
					_vel_sp(2) = 0.0f;
				}
			/以下是起飞的垂直速度设定/
				/* use constant descend rate when landing, ignore altitude setpoint */
				/* 当着陆时,忽略高度设定值,用恒定的速度下降 */
				if (!_control_mode.flag_control_manual_enabled && _pos_sp_triplet.current.valid
						&& _pos_sp_triplet.current.type == position_setpoint_s::SETPOINT_TYPE_LAND) {
					_vel_sp(2) = _params.land_speed;
				}

				/* special thrust setpoint generation for takeoff from ground */
				/* 起飞时产生专用的推力设定值 */
				if (!_control_mode.flag_control_manual_enabled && _pos_sp_triplet.current.valid
						&& _pos_sp_triplet.current.type == position_setpoint_s::SETPOINT_TYPE_TAKEOFF
						&& _control_mode.flag_armed) {

					// check if we are not already in air.
					// if yes then we don't need a jumped takeoff anymore
					// 检查飞机是否在空中					
					if (!_takeoff_jumped && !_vehicle_status.condition_landed && fabsf(_takeoff_thrust_sp) < FLT_EPSILON) {
					// 是否在空中
						_takeoff_jumped = true;
					}

					if (!_takeoff_jumped) {
						// ramp thrust setpoint up
						// 阶梯式的提高推力设定值
						if (_vel(2) > -(_params.tko_speed / 2.0f)) {
							_takeoff_thrust_sp += 0.5f * dt;
							_vel_sp.zero();
							_vel_prev.zero();

						} else {
							// copter has reached our takeoff speed. split the thrust setpoint up
							// into an integral part and into a P part
							// 飞行器已达到起飞速度。将推力设定值分为积分和比例部分
							thrust_int(2) = _takeoff_thrust_sp - _params.vel_p(2) * fabsf(_vel(2));
							thrust_int(2) = -math::constrain(thrust_int(2), _params.thr_min, _params.thr_max);//限幅
							_vel_sp_prev(2) = -_params.tko_speed;
							_takeoff_jumped = true;
							reset_int_z = false;
						}
					}

					if (_takeoff_jumped) {
						_vel_sp(2) = -_params.tko_speed;
					}
				///以上是起飞垂直速度设定/
				} else {
					_takeoff_jumped = false;
					_takeoff_thrust_sp = 0.0f;
				}
			/以上是起飞的垂直速度设定/


				// limit total horizontal acceleration
				// 限制水平加速度
				math::Vector<2> acc_hor;
				acc_hor(0) = (_vel_sp(0) - _vel_sp_prev(0)) / dt;
				acc_hor(1) = (_vel_sp(1) - _vel_sp_prev(1)) / dt;

				if (acc_hor.length() > _params.acc_hor_max) {
					acc_hor.normalize();//标准化
					acc_hor *= _params.acc_hor_max;//限幅完成
					math::Vector<2> vel_sp_hor_prev(_vel_sp_prev(0), _vel_sp_prev(1));
					math::Vector<2> vel_sp_hor = acc_hor * dt + vel_sp_hor_prev;//acc*dt+v_prev_sp
					_vel_sp(0) = vel_sp_hor(0);//修改限幅后的水平速度设定
					_vel_sp(1) = vel_sp_hor(1);//修改限幅后的水平速度设定
				}

				// limit vertical acceleration
				// 限制垂直加速度
				float acc_v = (_vel_sp(2) - _vel_sp_prev(2)) / dt;

				if (fabsf(acc_v) > 2 * _params.acc_hor_max) {
					acc_v /= fabsf(acc_v);//标准化
					_vel_sp(2) = acc_v * 2 * _params.acc_hor_max * dt + _vel_sp_prev(2);//acc*dt+v_prev_sp
				}

				_vel_sp_prev = _vel_sp;

				_global_vel_sp.vx = _vel_sp(0);
				_global_vel_sp.vy = _vel_sp(1);
				_global_vel_sp.vz = _vel_sp(2);
				
				/* publish velocity setpoint */
				if (_global_vel_sp_pub != nullptr) {
					orb_publish(ORB_ID(vehicle_global_velocity_setpoint), _global_vel_sp_pub, &_global_vel_sp);

				} else {
					_global_vel_sp_pub = orb_advertise(ORB_ID(vehicle_global_velocity_setpoint), &_global_vel_sp);
				}
		/************************************************************************************************
		 *orb_copy(ORB_ID(position_setpoint_triplet), _pos_sp_triplet_sub, &_pos_sp_triplet);
		 * 经过control_manual(dt);control_offboard(dt);control_auto(dt);输出pos_sp
		 * 经过上部分输出_vel_sp
		 * 发布_global_vel_sp
		 ************************************************************************************************/
		 
		 /****************第二部分第二步的重点(2):产生可利用的推力定值(期望值)*******************/
爬升速率控制使能||水平速度控制使能///
				if (_control_mode.flag_control_climb_rate_enabled || _control_mode.flag_control_velocity_enabled) {
					/* reset integrals if needed */
					if (_control_mode.flag_control_climb_rate_enabled) {
					//爬升速率控制使能
						if (reset_int_z) {
							reset_int_z = false;
							float i = _params.thr_min;

							if (reset_int_z_manual) {
								i = _params.thr_hover;

								if (i < _params.thr_min) {
									i = _params.thr_min;

								} else if (i > _params.thr_max) {
									i = _params.thr_max;
								}
							}

							thrust_int(2) = -i;
						}

					} else {
						reset_int_z = true;
					}

					if (_control_mode.flag_control_velocity_enabled) {
					//水平速度控制使能
						if (reset_int_xy) {
							reset_int_xy = false;
							thrust_int(0) = 0.0f;
							thrust_int(1) = 0.0f;
						}

					} else {
						reset_int_xy = true;
					}

					/* velocity error */
					math::Vector<3> vel_err = _vel_sp - _vel;
					/* 	_vel是实际飞行器的速度
					 * 	_vel(0) = _local_pos.vx;
					 * 	_vel(1) = _local_pos.vy;
					 *	_vel(2) = _local_pos.vz;
					 *	struct vehicle_local_position_s			_local_pos;	
					 *	orb_copy(ORB_ID(vehicle_local_position), _local_pos_sub, &_local_pos);
					 */
					// check if we have switched from a non-velocity controlled mode into a velocity controlled mode
					// if yes, then correct xy velocity setpoint such that the attitude setpoint is continuous
					// 检查我们是否将非速度控制模式转变成速度控制模式,如果是,那么矫正xy速度设定值,以便姿态设定值是连续的
					if (!control_vel_enabled_prev && _control_mode.flag_control_velocity_enabled) {

						// choose velocity xyz setpoint such that the resulting thrust setpoint has the direction
						// given by the last attitude setpoint
						//矫正xy速度设定值
						_vel_sp(0) = _vel(0) + (-PX4_R(_att_sp.R_body, 0, 2) * _att_sp.thrust - thrust_int(0) - _vel_err_d(0) * _params.vel_d(0)) / _params.vel_p(0);
						_vel_sp(1) = _vel(1) + (-PX4_R(_att_sp.R_body, 1, 2) * _att_sp.thrust - thrust_int(1) - _vel_err_d(1) * _params.vel_d(1)) / _params.vel_p(1);
						_vel_sp(2) = _vel(2) + (-PX4_R(_att_sp.R_body, 2, 2) * _att_sp.thrust - thrust_int(2) - _vel_err_d(2) * _params.vel_d(2)) / _params.vel_p(2);
						_vel_sp_prev(0) = _vel_sp(0);
						_vel_sp_prev(1) = _vel_sp(1);
						_vel_sp_prev(2) = _vel_sp(2);
						control_vel_enabled_prev = true;

						// compute updated velocity error
						//用矫正后的速度设定值-实际速度,跟新速度误差
						vel_err = _vel_sp - _vel;
					}

					/* thrust vector in NED frame */
					math::Vector<3> thrust_sp = vel_err.emult(_params.vel_p) + _vel_err_d.emult(_params.vel_d) + thrust_int;
					//推力设定值(三维)=速度差*P+速度差的差*D+积分
		/*********************************************************
		 ************上部分就将设定速度转变成设定推力*************
		 *********************************************************/			
					if (_pos_sp_triplet.current.type == position_setpoint_s::SETPOINT_TYPE_TAKEOFF
							&& !_takeoff_jumped && !_control_mode.flag_control_manual_enabled) {
						// for jumped takeoffs use special thrust setpoint calculated above
						thrust_sp.zero();
						thrust_sp(2) = -_takeoff_thrust_sp;
					}

					if (!_control_mode.flag_control_velocity_enabled) {
						thrust_sp(0) = 0.0f;
						thrust_arrayssp(1) = 0.0f;
					}

					if (!_control_mode.flag_control_climb_rate_enabled) {
						thrust_sp(2) = 0.0f;
					}

					/* limit thrust vector and check for saturation */
					/* 限制推力大小,检查是否饱和 */
					bool saturation_xy = false;
					bool saturation_z = false;

					/* limit min lift */
					//限制最小升力
					float thr_min = _params.thr_min;

					if (!_control_mode.flag_control_velocity_enabled && thr_min < 0.0f) {
						/* don't allow downside thrust direction in manual attitude mode */
						//不允许下降推力在手动姿态模式
						thr_min = 0.0f;
					}

					float thrust_abs = thrust_sp.length();
					float tilt_max = _params.tilt_max_air;
					float thr_max = _params.thr_max;
					/* filter vel_z over 1/8sec */
					_vel_z_lp = _vel_z_lp * (1.0f - dt * 8.0f) + dt * 8.0f * _vel(2);//垂直速度低通滤波
					/* filter vel_z change over 1/8sec */
					float vel_z_change = (_vel(2) - _vel_prev(2)) / dt;
					_acc_z_lp = _acc_z_lp * (1.0f - dt * 8.0f) + dt * 8.0f * vel_z_change;//垂直加速度低通滤波

					/* adjust limits for landing mode */
					/***********************着陆处理************************/
					if (!_control_mode.flag_control_manual_enabled && _pos_sp_triplet.current.valid &&
							_pos_sp_triplet.current.type == position_setpoint_s::SETPOINT_TYPE_LAND) {
						/* limit max tilt and min lift when landing */
						//降落时限制最大倾斜和最小升力
						tilt_max = _params.tilt_max_land;

						if (thr_min < 0.0f) {
							thr_min = 0.0f;
						}

						/* descend stabilized, we're landing */判断是否正在下降准备着陆
						if (!_in_landing && !_lnd_reached_ground
								&& (float)fabs(_acc_z_lp) < 0.1f
								&& _vel_z_lp > 0.5f * _params.land_speed) {
							_in_landing = true;
						}

						/* assume ground, cut thrust */
						//判断是否已经着陆
						if (_in_landing
								&& _vel_z_lp < 0.1f) {
							thr_max = 0.0f;
							_in_landing = false;
							_lnd_reached_ground = true;
						}

						/* once we assumed to have reached the ground always cut the thrust.
							Only free fall detection below can revoke this
						*/
						//如果已经着陆,那么切断推力。
						if (!_in_landing && _lnd_reached_ground) {
							thr_max = 0.0f;
						}

						/* if we suddenly fall, reset landing logic and remove thrust limit */
						// 如果突然下落,复位着陆的逻辑标志位并移除推力限制
						if (_lnd_reached_ground
								/* XXX: magic value, assuming free fall above 4m/s2 acceleration */
								//假定自由落体加速度大于4米每秒的平方,速度 > 2.0f * _params.land_speed
								&& (_acc_z_lp > 4.0f
								    || _vel_z_lp > 2.0f * _params.land_speed)) {
							thr_max = _params.thr_max;
							_in_landing = false;
							_lnd_reached_ground = false;
						}

					} else {
						_in_landing = false;
						_lnd_reached_ground = false;
					}
					/***********************着陆处理完毕************************/
					/* limit min lift */
					//限制最小升力
					if (-thrust_sp(2) < thr_min) {
						thrust_sp(2) = -thr_min;
						saturation_z = true;
					}
					if (_control_mode.flag_control_velocity_enabled) {
						/* limit max tilt */
						//限制最大斜率(xy方向推力限幅)
						if (thr_min >= 0.0f && tilt_max < M_PI_F / 2 - 0.05f) {
							/* absolute horizontal thrust */
							float thrust_sp_xy_len = math::Vector<2>(thrust_sp(0), thrust_sp(1)).length();

							if (thrust_sp_xy_len > 0.01f) {
								/* max horizontal thrust for given vertical thrust*/
								float thrust_xy_max = -thrust_sp(2) * tanf(tilt_max);

								if (thrust_sp_xy_len > thrust_xy_max) {
									float k = thrust_xy_max / thrust_sp_xy_len;
									thrust_sp(0) *= k;
									thrust_sp(1) *= k;
									saturation_xy = true;
								}
							}
						}
					}

					if (_control_mode.flag_control_altitude_enabled) {
						/* thrust compensation for altitude only control modes */
						//推力补偿,用于高度控制
						float att_comp;

						if (_R(2, 2) > TILT_COS_MAX) {
							att_comp = 1.0f / _R(2, 2);

						} else if (_R(2, 2) > 0.0f) {
							att_comp = ((1.0f / TILT_COS_MAX - 1.0f) / TILT_COS_MAX) * _R(2, 2) + 1.0f;
							saturation_z = true;

						} else {
							att_comp = 1.0f;
							saturation_z = true;
						}

						thrust_sp(2) *= att_comp;
					}

					/* limit max thrust */
					//推力限幅
					thrust_abs = thrust_sp.length(); /* recalculate because it might have changed */

					if (thrust_abs > thr_max) {
						if (thrust_sp(2) < 0.0f) {
							if (-thrust_sp(2) > thr_max) {
								/* thrust Z component is too large, limit it */
								thrust_sp(0) = 0.0f;
								thrust_sp(1) = 0.0f;
								thrust_sp(2) = -thr_max;
								saturation_xy = true;
								saturation_z = true;

							} else {
								/* preserve thrust Z component and lower XY, keeping altitude is more important than position */
								float thrust_xy_max = sqrtf(thr_max * thr_max - thrust_sp(2) * thrust_sp(2));
								float thrust_xy_abs = math::Vector<2>(thrust_sp(0), thrust_sp(1)).length();
								float k = thrust_xy_max / thrust_xy_abs;
								thrust_sp(0) *= k;
								thrust_sp(1) *= k;
								saturation_xy = true;
							}

						} else {
							/* Z component is negative, going down, simply limit thrust vector */
							float k = thr_max / thrust_abs;
							thrust_sp *= k;
							saturation_xy = true;
							saturation_z = true;
						}

						thrust_abs = thr_max;
					}

					/* update integrals */
					//跟新推力积分
					if (_control_mode.flag_control_velocity_enabled && !saturation_xy) {
						thrust_int(0) += vel_err(0) * _params.vel_i(0) * dt;
						thrust_int(1) += vel_err(1) * _params.vel_i(1) * dt;
					}

					if (_control_mode.flag_control_climb_rate_enabled && !saturation_z) {
						thrust_int(2) += vel_err(2) * _params.vel_i(2) * dt;

						/* protection against flipping on ground when landing */
						if (thrust_int(2) > 0.0f) {
							thrust_int(2) = 0.0f;
						}
					}

					/* calculate attitude setpoint from thrust vector */
			/*********************第二部分第二步的重点(3):根据推力向量计算姿态设定值(期望姿态)***********************/
			/*********************最后使用用于控制的四元数表达的旋转矩阵(旋转矩阵就是姿态)***********************/
					if (_control_mode.flag_control_velocity_enabled) {
						/* desired body_z axis = -normalize(thrust_vector) */
						/*************先求出body_x、body_y、body_z*****************/
						///body_x、body_y、body_z应该是方向余弦矩阵的三个列向量//
						math::Vector<3> body_x;
						math::Vector<3> body_y;
						math::Vector<3> body_z;

						if (thrust_abs > SIGMA) {
							body_z = -thrust_sp / thrust_abs;//body_z矩阵是推力设定值矩阵的标准化

						} else {
							/* no thrust, set Z axis to safe value */
							body_z.zero();
							body_z(2) = 1.0f;
						}

						/* vector of desired yaw direction in XY plane, rotated by PI/2 */
						math::Vector<3> y_C(-sinf(_att_sp.yaw_body), cosf(_att_sp.yaw_body), 0.0f);
						//_att_sp.yaw_body = _pos_sp_triplet.current.yaw;(来自control_offboard和control_auto)
						//y_C相当于是矩阵(-sin(偏航角),cos(偏航角),0)
						if (fabsf(body_z(2)) > SIGMA) {
							/* desired body_x axis, orthogonal to body_z */
							body_x = y_C % body_z;//%是求叉积运算

							/* keep nose to front while inverted upside down */
							if (body_z(2) < 0.0f) {
								body_x = -body_x;
							}

							body_x.normalize();

						} else {
							/* desired thrust is in XY plane, set X downside to construct correct matrix,
							 * but yaw component will not be used actually */
							body_x.zero();
							body_x(2) = 1.0f;
						}

						/* desired body_y axis */
						body_y = body_z % body_x;
						/****************再求出R<3,3>矩阵******************/
						/* fill rotation matrix */
						for (int i = 0; i < 3; i++) {
							R(i, 0) = body_x(i);
							R(i, 1) = body_y(i);
							R(i, 2) = body_z(i);
						}

						/* copy rotation matrix to attitude setpoint topic */
						/****************将R<3,3>矩阵copy到_att_sp.R_body[]******************/
						memcpy(&_att_sp.R_body[0], R.data, sizeof(_att_sp.R_body));
						_att_sp.R_valid = true;

						/* copy quaternion setpoint to attitude setpoint topic */
						/****************由方向余弦旋转矩阵R得到四元数,并copy到att_sp.q_d[]/****************
						math::Quaternion q_sp;
						q_sp.from_dcm(R);
						memcpy(&_att_sp.q_d[0], &q_sp.data[0], sizeof(_att_sp.q_d));

						/* calculate euler angles, for logging only, must not be used for control */
						/****************由旋转矩阵R得到姿态设置欧拉角,只是log调试用,不是给控制用的****************/
						math::Vector<3> euler = R.to_euler();
						_att_sp.roll_body = euler(0);
						_att_sp.pitch_body = euler(1);
						/* yaw already used to construct rot matrix, but actual rotation matrix can have different yaw near singularity */
						//yaw虽然用于构建原始矩阵,但实际上旋转矩阵在奇点附近是有区别的
					} else if (!_control_mode.flag_control_manual_enabled) {
						/* autonomous altitude control without position control (failsafe landing),
						 * force level attitude, don't change yaw */
						//没有位置控制的高度控制(故障安全降落),固定水平姿态,不改变yaw角
						R.from_euler(0.0f, 0.0f, _att_sp.yaw_body);

						/* copy rotation matrix to attitude setpoint topic */
						//将旋转矩阵R<3,3>  copy到_att_sp.R_body[]
						memcpy(&_att_sp.R_body[0], R.data, sizeof(_att_sp.R_body));
						_att_sp.R_valid = true;

						/* copy quaternion setpoint to attitude setpoint topic */
						//由方向余弦旋转矩阵R得到四元数,并copy到_att_sp.q_d[]
						math::Quaternion q_sp;
						q_sp.from_dcm(R);
						memcpy(&_att_sp.q_d[0], &q_sp.data[0], sizeof(_att_sp.q_d));

						_att_sp.roll_body = 0.0f;
						_att_sp.pitch_body = 0.0f;
					}

					_att_sp.thrust = thrust_abs;
					//推力向量的长度赋值给姿态推力设定值(att_sp.thrust),这样才够各个方向力度分配

					/* save thrust setpoint for logging */
					//用于log,方便调试
					_local_pos_sp.acc_x = thrust_sp(0) * ONE_G;
					_local_pos_sp.acc_y = thrust_sp(1) * ONE_G;
					_local_pos_sp.acc_z = thrust_sp(2) * ONE_G;

					_att_sp.timestamp = hrt_absolute_time();//测出用的绝对时间


				} else {
					reset_int_z = true;
				}
			}
	/*********************第三部分:将之前程序得到的各种信息填充_local_pos_sp结构体,并发布出去***********************/
			/* fill local position, velocity and thrust setpoint */
			_local_pos_sp.timestamp = hrt_absolute_time();
			_local_pos_sp.x = _pos_sp(0);
			_local_pos_sp.y = _pos_sp(1);
			_local_pos_sp.z = _pos_sp(2);
			//第二部分第一步:产生位置/速度设定值(期望值)
			_local_pos_sp.yaw = _att_sp.yaw_body;
			_local_pos_sp.vx = _vel_sp(0);
			_local_pos_sp.vy = _vel_sp(1);
			_local_pos_sp.vz = _vel_sp(2);
			//第二部分第二步的重点(1):产生可利用的速度设定值(期望值)
			/* publish local position setpoint */
			if (_local_pos_sp_pub != nullptr) {
				orb_publish(ORB_ID(vehicle_local_position_setpoint), _local_pos_sp_pub, &_local_pos_sp);

			} else {
				_local_pos_sp_pub = orb_advertise(ORB_ID(vehicle_local_position_setpoint), &_local_pos_sp);
			}

		}
高度控制、位置控制、爬升速率控制、速度控制的相关程序结束//
//其他情况(位置控制失能)就复位各种设定值
		else {
			/* position controller disabled, reset setpoints */
			_reset_alt_sp = true;
			_reset_pos_sp = true;
			_mode_auto = false;
			reset_int_z = true;
			reset_int_xy = true;
			control_vel_enabled_prev = false;

			/* store last velocity in case a mode switch to position control occurs */
			_vel_sp_prev = _vel;
		}
//以下判断是并列于“高度控制、位置控制、爬升速率控制、速度控制”的判断
//所以会出现混控现象,在执行任务的时候还可以遥控控制/
//手动控制和姿态控制都使能,则运行以下程序产生姿态设定值
		/* generate attitude setpoint from manual controls */
		if (_control_mode.flag_control_manual_enabled && _control_mode.flag_control_attitude_enabled) {

			/* reset yaw setpoint to current position if needed */
			if (reset_yaw_sp) {
				reset_yaw_sp = false;
				_att_sp.yaw_body = _yaw;
			}

			/* do not move yaw while sitting on the ground */
			//当飞行器在地上是,不要移动yaw
			else if (!_vehicle_status.condition_landed &&
					!(!_control_mode.flag_control_altitude_enabled && _manual.z < 0.1f)) {

				/* we want to know the real constraint, and global overrides manual */
				const float yaw_rate_max = (_params.man_yaw_max < _params.global_yaw_max) ? _params.man_yaw_max :
							   _params.global_yaw_max;
				const float yaw_offset_max = yaw_rate_max / _params.mc_att_yaw_p;

				_att_sp.yaw_sp_move_rate = _manual.r * yaw_rate_max;//旋转*旋转比率
				float yaw_target = _wrap_pi(_att_sp.yaw_body + _att_sp.yaw_sp_move_rate * dt);//期望yaw
				float yaw_offs = _wrap_pi(yaw_target - _yaw);//期望yaw-飞机yaw=需要调整的yaw
				//_wrap_pi()函数是将输入角度参数控制到(0,2*pi)
				
				// If the yaw offset became too big for the system to track stop
				// shifting it

				// XXX this needs inspection - probably requires a clamp, not an if
				if (fabsf(yaw_offs) < yaw_offset_max) {
					_att_sp.yaw_body = yaw_target;
				}
			}

			/* control roll and pitch directly if we no aiding velocity controller is active */
			if (!_control_mode.flag_control_velocity_enabled) {
				_att_sp.roll_body = _manual.y * _params.man_roll_max;
				_att_sp.pitch_body = -_manual.x * _params.man_pitch_max;
			}

			/* control throttle directly if no climb rate controller is active */
			//如果没有爬升速率控制器,则直接推力控制
			if (!_control_mode.flag_control_climb_rate_enabled) {
				float thr_val = throttle_curve(_manual.z, _params.thr_hover);//手动推力的转换,以便控制器输出控制推力力度
				_att_sp.thrust = math::min(thr_val, _manual_thr_max.get());

				/* enforce minimum throttle if not landed */
				//如果没有着陆,则需要限制最小推力
				if (!_vehicle_status.condition_landed) {
					_att_sp.thrust = math::max(_att_sp.thrust, _manual_thr_min.get());
				}
			}

			math::Matrix<3, 3> R_sp;

			/* construct attitude setpoint rotation matrix */
			//构建姿态设定值的旋转矩阵并copy到_att_sp.R_body[]
			R_sp.from_euler(_att_sp.roll_body, _att_sp.pitch_body, _att_sp.yaw_body);
			memcpy(&_att_sp.R_body[0], R_sp.data, sizeof(_att_sp.R_body));

			/* reset the acceleration set point for all non-attitude flight modes */
			//非姿态飞行模式,复位加速度设定值
			if (!(_control_mode.flag_control_offboard_enabled &&
					!(_control_mode.flag_control_position_enabled ||
					  _control_mode.flag_control_velocity_enabled))) {

				_thrust_sp_prev = R_sp * math::Vector<3>(0, 0, -_att_sp.thrust);
			}

			/* copy quaternion setpoint to attitude setpoint topic */
			//将姿态设定值的旋转矩阵转换成四元数矩阵并copy到_att_sp.q_d[],以便发布
			math::Quaternion q_sp;
			q_sp.from_dcm(R_sp);
			memcpy(&_att_sp.q_d[0], &q_sp.data[0], sizeof(_att_sp.q_d));
			_att_sp.timestamp = hrt_absolute_time();

		}
///手动控制部分结束///
///手动控制失能///
		else {
			reset_yaw_sp = true;
		}
/跟新前一个时刻的速度用于D部分(D应该是PID的D)
		/* update previous velocity for velocity controller D part */
		_vel_prev = _vel;
/发布姿态设定值///
/如果位置/速度失能而机外(offboard)使能,则不发布姿态设定值//
/因为这种情况姿态设定值是通过mavlink应用发布的//
/飞机工作于垂直起降或者做一个过渡,也不发布,因为此时由垂直起降控制部分发布/
		/* publish attitude setpoint
		 * Do not publish if offboard is enabled but position/velocity control is disabled,
		 * in this case the attitude setpoint is published by the mavlink app. Also do not publish
		 * if the vehicle is a VTOL and it's just doing a transition (the VTOL attitude control module will generate
		 * attitude setpoints for the transition).
		 */
		if (!(_control_mode.flag_control_offboard_enabled &&
				!(_control_mode.flag_control_position_enabled ||
				  _control_mode.flag_control_velocity_enabled))) {

			if (_att_sp_pub != nullptr) {
				orb_publish(_attitude_setpoint_id, _att_sp_pub, &_att_sp);

			} else if (_attitude_setpoint_id) {
				_att_sp_pub = orb_advertise(_attitude_setpoint_id, &_att_sp);
			}
		}
///手动控制后复位高度控制的积分(悬停油门),以便更好的转变为手动模式
		/* reset altitude controller integral (hovering throttle) to manual throttle after manual throttle control */
		reset_int_z_manual = _control_mode.flag_armed && _control_mode.flag_control_manual_enabled
				     && !_control_mode.flag_control_climb_rate_enabled;
	}

	mavlink_log_info(_mavlink_fd, "[mpc] stopped");

	_control_task = -1;
}
control_manual()函数
void
MulticopterPositionControl::control_manual(float dt)
{
	math::Vector<3> req_vel_sp; // X,Y in local frame and Z in global (D), in [-1,1] normalized range
	req_vel_sp.zero();

	if (_control_mode.flag_control_altitude_enabled) {
		/* set vertical velocity setpoint with throttle stick */
		/* 将自稳模式的油门杆转换成控制垂直速度设定值(以中间速度为0,往上拨速度向上,往下拨速度向下,速度大小与拨动幅度成正比) */
		req_vel_sp(2) = -scale_control(_manual.z - 0.5f, 0.5f, _params.alt_ctl_dz, _params.alt_ctl_dy); // D
	}

	if (_control_mode.flag_control_position_enabled) {
		/* set horizontal velocity setpoint with roll/pitch stick */
		/* 水平速度设定值由roll和pitch杆确定 */
		req_vel_sp(0) = _manual.x;
		req_vel_sp(1) = _manual.y;
	}

	if (_control_mode.flag_control_altitude_enabled) {
		/* reset alt setpoint to current altitude if needed */
		reset_alt_sp();//复位高度设定值
	}

	if (_control_mode.flag_control_position_enabled) {
		/* reset position setpoint to current position if needed */
		reset_pos_sp();//复位位置设定值
	}
//以下限制速度设定值///
	/* limit velocity setpoint */
	float req_vel_sp_norm = req_vel_sp.length();

	if (req_vel_sp_norm > 1.0f) {
		req_vel_sp /= req_vel_sp_norm;
	}
//以上限制速度设定值///
	/* _req_vel_sp scaled to 0..1, scale it to max speed and rotate around yaw */
	math::Matrix<3, 3> R_yaw_sp;
	R_yaw_sp.from_euler(0.0f, 0.0f, _att_sp.yaw_body);//由欧拉角得到旋转矩阵
	/**Firmware/src/lib/mathlib/math/Matrix.hpp
	 * create a rotation matrix from given euler angles
	 * based on http://gentlenav.googlecode.com/files/EulerAngles.pdf
	 *
	 *	void from_euler(float roll, float pitch, float yaw) {
	 *		float cp = cosf(pitch);
	 *		float sp = sinf(pitch);
	 *		float sr = sinf(roll);
	 *		float cr = cosf(roll);
	 *		float sy = sinf(yaw);
	 *		float cy = cosf(yaw);
	 *
	 *		data[0][0] = cp * cy;
	 *		data[0][1] = (sr * sp * cy) - (cr * sy);
	 *		data[0][2] = (cr * sp * cy) + (sr * sy);
	 *		data[1][0] = cp * sy;
	 *		data[1][1] = (sr * sp * sy) + (cr * cy);
	 *		data[1][2] = (cr * sp * sy) - (sr * cy);
	 *		data[2][0] = -sp;
	 *		data[2][1] = sr * cp;
	 *		data[2][2] = cr * cp;
	 *	}
	 */
	math::Vector<3> req_vel_sp_scaled = R_yaw_sp * req_vel_sp.emult(
			_params.vel_max); // in NED and scaled to actual velocity
							  // 在NED坐标系下,还原到真实的速度
/以下为水平轴辅助速度模式:用户控制速度,但是如果速度很小,那么相应轴上的位置保持不变/
	/*
	 * assisted velocity mode: user controls velocity, but if	velocity is small enough, position
	 * hold is activated for the corresponding axis
	 */
	/* horizontal axes */
	/* 水平轴 */
	if (_control_mode.flag_control_position_enabled) {
		/* check for pos. hold */
		if (fabsf(req_vel_sp(0)) < _params.hold_xy_dz && fabsf(req_vel_sp(1)) < _params.hold_xy_dz) {
			if (!_pos_hold_engaged) {
				if (_params.hold_max_xy < FLT_EPSILON || (fabsf(_vel(0)) < _params.hold_max_xy
						&& fabsf(_vel(1)) < _params.hold_max_xy)) {
					_pos_hold_engaged = true;

				} else {
					_pos_hold_engaged = false;
				}
			}

		} else {
			_pos_hold_engaged = false;
		}
/以上为辅助速度模式:用户控制速度,但是如果速度很小,那么相应轴上的位置保持不变/
/以下为速度设定值,作为此函数的输出///
		/* set requested velocity setpoint */
		if (!_pos_hold_engaged) {//不需要位置锁定(辅助速度模式)
			_pos_sp(0) = _pos(0);
			_pos_sp(1) = _pos(1);
			_run_pos_control = false; /* request velocity setpoint to be used, instead of position setpoint */
									  /* 用于速度设定而不是位置设定 */
			_vel_sp(0) = req_vel_sp_scaled(0);
			_vel_sp(1) = req_vel_sp_scaled(1);
		}
	}
以下为垂直轴辅助速度模式:用户控制速度,但是如果速度很小,那么相应轴上的位置保持不变/
	/* vertical axis */
	if (_control_mode.flag_control_altitude_enabled) {
		/* check for pos. hold */
		if (fabsf(req_vel_sp(2)) < FLT_EPSILON) {
			if (!_alt_hold_engaged) {
				if (_params.hold_max_z < FLT_EPSILON || fabsf(_vel(2)) < _params.hold_max_z) {
					_alt_hold_engaged = true;

				} else {
					_alt_hold_engaged = false;
				}
			}

		} else {
			_alt_hold_engaged = false;
		}
以上为垂直轴辅助速度模式:用户控制速度,但是如果速度很小,那么相应轴上的位置保持不变/
		/* set requested velocity setpoint */
		if (!_alt_hold_engaged) {
			_run_alt_control = false; /* request velocity setpoint to be used, instead of altitude setpoint */
									  /* 用于速度设定而不是位置设定 */
			_vel_sp(2) = req_vel_sp_scaled(2);
			_pos_sp(2) = _pos(2);
		}
	}
}
control_offboard()函数
void
MulticopterPositionControl::control_offboard(float dt)
{
	bool updated;
	orb_check(_pos_sp_triplet_sub, &updated);

	if (updated) {
		orb_copy(ORB_ID(position_setpoint_triplet), _pos_sp_triplet_sub, &_pos_sp_triplet);
	}
//水平轴设定//
	if (_pos_sp_triplet.current.valid) {
		if (_control_mode.flag_control_position_enabled && _pos_sp_triplet.current.position_valid) {
		//控制模式-位置控制使能&&当前位置设定值合法,那么进行位置控制
			/* control position */
			_pos_sp(0) = _pos_sp_triplet.current.x;
			_pos_sp(1) = _pos_sp_triplet.current.y;

		} else if (_control_mode.flag_control_velocity_enabled && _pos_sp_triplet.current.velocity_valid) {
		//控制模式-速度控制使能&&当前速度设定值合法
			/* control velocity */
			/* reset position setpoint to current position if needed */
			reset_pos_sp();//速度控制时,需要复位位置

			/* set position setpoint move rate */
			_vel_sp(0) = _pos_sp_triplet.current.vx;
			_vel_sp(1) = _pos_sp_triplet.current.vy;

			_run_pos_control = false; /* request velocity setpoint to be used, instead of position setpoint */
		}
yaw姿态设定///
		if (_pos_sp_triplet.current.yaw_valid) {
			_att_sp.yaw_body = _pos_sp_triplet.current.yaw;

		} else if (_pos_sp_triplet.current.yawspeed_valid) {
			_att_sp.yaw_body = _att_sp.yaw_body + _pos_sp_triplet.current.yawspeed * dt;
		}
/垂直轴设定///
		if (_control_mode.flag_control_altitude_enabled && _pos_sp_triplet.current.position_valid) {
		//控制模式:高度控制模式&&当前位置设定是否合法
			/* Control altitude */
			_pos_sp(2) = _pos_sp_triplet.current.z;

		} else if (_control_mode.flag_control_climb_rate_enabled && _pos_sp_triplet.current.velocity_valid) {
		//控制模式:爬升速度控制模式&&当前速度设定是否合法
			/* reset alt setpoint to current altitude if needed */
			reset_alt_sp();

			/* set altitude setpoint move rate */
			_vel_sp(2) = _pos_sp_triplet.current.vz;

			_run_alt_control = false; /* request velocity setpoint to be used, instead of position setpoint */
		}

	} else {
		reset_pos_sp();
		reset_alt_sp();
	}
}
control_auto()函数
void MulticopterPositionControl::control_auto(float dt)
{
///复位位置设定值在自动模式下或者我们没有在动作控制模式(MC mode)下	
	/* reset position setpoint on AUTO mode activation or if we are not in MC mode */
	if (!_mode_auto || !_vehicle_status.is_rotary_wing) {
		if (!_mode_auto) {
			_mode_auto = true;
		}

		_reset_pos_sp = true;
		_reset_alt_sp = true;

		reset_pos_sp();
		reset_alt_sp();
	}
//获取三重位置设定值//
	//Poll position setpoint
	bool updated;
	orb_check(_pos_sp_triplet_sub, &updated);

	if (updated) {
		orb_copy(ORB_ID(position_setpoint_triplet), _pos_sp_triplet_sub, &_pos_sp_triplet);
///当前三重位置设定值是否为有限数,如果是则为有效值//
		//Make sure that the position setpoint is valid
		if (!PX4_ISFINITE(_pos_sp_triplet.current.lat) ||
				!PX4_ISFINITE(_pos_sp_triplet.current.lon) ||
				!PX4_ISFINITE(_pos_sp_triplet.current.alt)) {
			_pos_sp_triplet.current.valid = false;
		}
	}
/初始化标志位///
	bool current_setpoint_valid = false;
	bool previous_setpoint_valid = false;

	math::Vector<3> prev_sp;
	math::Vector<3> curr_sp;
//如果当前三重位置设定值合法,将当前设定值经纬度和高度转换成地坐标系的xyz值
	if (_pos_sp_triplet.current.valid) {

		/* project setpoint to local frame */
		/*这个函数在此cpp里面经常使用,是将将经纬度转换成地坐标系xy值*/
		map_projection_project(&_ref_pos,
				       _pos_sp_triplet.current.lat, _pos_sp_triplet.current.lon,
				       &curr_sp.data[0], &curr_sp.data[1]);
		curr_sp(2) = -(_pos_sp_triplet.current.alt - _ref_alt);

		if (PX4_ISFINITE(curr_sp(0)) &&
				PX4_ISFINITE(curr_sp(1)) &&
				PX4_ISFINITE(curr_sp(2))) {
			current_setpoint_valid = true;
		}
	}
//如果上一时刻三重位置设定值合法,将当前设定值经纬度和高度转换成地坐标系的xyz值
	if (_pos_sp_triplet.previous.valid) {
		map_projection_project(&_ref_pos,
				       _pos_sp_triplet.previous.lat, _pos_sp_triplet.previous.lon,
				       &prev_sp.data[0], &prev_sp.data[1]);
		prev_sp(2) = -(_pos_sp_triplet.previous.alt - _ref_alt);

		if (PX4_ISFINITE(prev_sp(0)) &&
				PX4_ISFINITE(prev_sp(1)) &&
				PX4_ISFINITE(prev_sp(2))) {
			previous_setpoint_valid = true;
		}
	}
///如果当前位置设定值合法/
	if (current_setpoint_valid) {
/*********************	下部分只是将设定值进行比例变换,缩小进一个区间   ******************/
		/范围区间:位置误差导致的最大允许速度 为1,也就是说(0,1)之间///
		/* scaled space: 1 == position error resulting max allowed speed */
		math::Vector<3> scale = _params.pos_p.edivide(_params.vel_max);	// TODO add mult param here
		/*用_params.pos_p的每一个元素除以_params.vel_max对应位置的每一个元素,结果赋值给scale
		 *	const Vector<N> edivide(const Vector<N> &v) const {
		 *		Vector<N> res;
		 *
		 *		for (unsigned int i = 0; i < N; i++)
		 *			res.data[i] = data[i] / v.data[i];
		 *
		 *		return res;
		 *	}
		 */
		将当前设定值转换到范围区间中//
		/* convert current setpoint to scaled space */
		math::Vector<3> curr_sp_s = curr_sp.emult(scale);
		/*	用curr_sp的每一个元素和scale对应位置的每一个元素相乘,结果赋值给curr_sp_s
		 *	Matrix<Type, M, N> emult(const Matrix<Type, M, N> &other) const
		 *	{
		 *		Matrix<Type, M, N> res;
		 *		const Matrix<Type, M, N> &self = *this;
		 *
		 *		for (size_t i = 0; i < M; i++) {
		 *			for (size_t j = 0; j < N; j++) {
		 *				res(i , j) = self(i, j)*other(i, j);
		 *			}
		 *		}
		 *
		 *		return res;
		 *	}
		 */
/*********************	上部分只是将设定值进行比例变换,缩小进一个区间   ******************/
///默认使用的当前设定值///
		/* by default use current setpoint as is */
		math::Vector<3> pos_sp_s = curr_sp_s;
//判断当前类型是否是位置设定类型&&上一次设定值是否合法
		if (_pos_sp_triplet.current.type == position_setpoint_s::SETPOINT_TYPE_POSITION && previous_setpoint_valid) {
			/* follow "previous - current" line */
//遵守"previous - current"主线///
			if ((curr_sp - prev_sp).length() > MIN_DIST) {

				/* find X - cross point of unit sphere and trajectory */
				math::Vector<3> pos_s = _pos.emult(scale);//copy的_local_pos位置信息 * 比例
				math::Vector<3> prev_sp_s = prev_sp.emult(scale);//带了_s的都是乘以了比例的 scale
				math::Vector<3> prev_curr_s = curr_sp_s - prev_sp_s;
				math::Vector<3> curr_pos_s = pos_s - curr_sp_s;
				float curr_pos_s_len = curr_pos_s.length();
/根据飞行器位置距离当前位置设定点的距离分2种情况:小于单位半径和不小于单位半径
小于单位半径
				if (curr_pos_s_len < 1.0f) {
					/* copter is closer to waypoint than unit radius */
					/* check next waypoint and use it to avoid slowing down when passing via waypoint */
					/*飞行器距离航点在单位半径以内*/
					/*在奔向当前航点的时候检测下一个航点,避免通过当前航点时减速*/
					if (_pos_sp_triplet.next.valid) {
						math::Vector<3> next_sp;
						map_projection_project(&_ref_pos,
								       _pos_sp_triplet.next.lat, _pos_sp_triplet.next.lon,
								       &next_sp.data[0], &next_sp.data[1]);
						next_sp(2) = -(_pos_sp_triplet.next.alt - _ref_alt);

						if ((next_sp - curr_sp).length() > MIN_DIST) {
							math::Vector<3> next_sp_s = next_sp.emult(scale);

							/* calculate angle prev - curr - next */
							math::Vector<3> curr_next_s = next_sp_s - curr_sp_s;
							math::Vector<3> prev_curr_s_norm = prev_curr_s.normalized();
								/*标准化prev_curr_s
								 * returns the normalized version of this vector
								 *
								 *	Vector<N> normalized() const {
								 *		return *this / length();
								 *	}
								 */

							/* cos(a) * curr_next, a = angle between current and next trajectory segments */
							/* prev_curr_s_norm是单位向量(当前位置设定-前一次位置设定),curr_next_s是另一个向量(下一次位置设定-当前位置设定)*/
							 * 向量相乘点乘 ab=丨a丨丨b丨cosα,结果是一代数
							 * 向量相乘叉乘|向量c|=|向量a×向量b|=|a||b|sin,结果是一向量
							 */
							float cos_a_curr_next = prev_curr_s_norm * curr_next_s;

							/* cos(b), b = angle pos - curr_sp - prev_sp */
							/* curr_pos_s是向量当前位置指向实际位置(实际位置-当前位置设定)
							 * prev_curr_s_norm是前一次位置设定指向当前位置设定的单位向量(当前位置设定-前一次位置设定)
							 * curr_pos_s_len是当前位置设定与实际位置之间长度
							 * 所以cos_b就是pos - curr_sp - prev_sp三点连线的角度的余弦值
							 */
							float cos_b = -curr_pos_s * prev_curr_s_norm / curr_pos_s_len;

							if (cos_a_curr_next > 0.0f && cos_b > 0.0f) {//a、b为锐角
								float curr_next_s_len = curr_next_s.length();

								/* if curr - next distance is larger than unit radius, limit it */
								/*当前位置设定到下个位置设定的距离超过单位半径*/
								if (curr_next_s_len > 1.0f) {
									cos_a_curr_next /= curr_next_s_len;//cos_a_curr_next=cos(a) * curr_next/||curr_next||
								}

								/* feed forward position setpoint offset */
								/*前馈位置设定值偏移*/
								math::Vector<3> pos_ff = prev_curr_s_norm *
											 cos_a_curr_next * cos_b * cos_b * (1.0f - curr_pos_s_len) *
											 (1.0f - expf(-curr_pos_s_len * curr_pos_s_len * 20.0f));
								pos_sp_s += pos_ff;
							}
						}
					}

				}
不小于单位半径
				else {
					bool near = cross_sphere_line(pos_s, 1.0f, prev_sp_s, curr_sp_s, pos_sp_s);

					if (near) {
						/* unit sphere crosses trajectory */单位球越过轨迹

					} else {
						/* copter is too far from trajectory */
						/* if copter is behind prev waypoint, go directly to prev waypoint */
						/* 	飞行器离设定轨迹很远
						 *	如果飞行器在前一个设定位置后面,则先到前一个设定位置
						 *	角pos_sp - prev_sp - curr_sp大于90°
						 */
						if ((pos_sp_s - prev_sp_s) * prev_curr_s < 0.0f) {
							pos_sp_s = prev_sp_s;
						}

						/* if copter is in front of curr waypoint, go directly to curr waypoint */
						/* 如果飞行器在前一个设定位置前面,则到当前设定位置*/
						if ((pos_sp_s - curr_sp_s) * prev_curr_s > 0.0f) {
							pos_sp_s = curr_sp_s;
						}

						pos_sp_s = pos_s + (pos_sp_s - pos_s).normalized();
					}
				}
			}
		}
/* 由上述程序大概就可以看出控制逻辑
 * 先根据任务设定前一个、当前、下一个位置标定(prev_sp_s、curr_sp_s、next_sp_s),用于控制飞行器按照此轨迹飞行
 * pos_sp_s是实时位置设定值,用于指导飞行器具体如何一个点一个点的靠近轨迹标定值(prev_sp_s、curr_sp_s、next_sp_s)
 * pos_s是飞行器实时位置
 * 带了_s的都是经过大小比例缩放的,不是实际值
 */
以下部分是限速用的//
		/* move setpoint not faster than max allowed speed */
		math::Vector<3> pos_sp_old_s = _pos_sp.emult(scale);

		/* difference between current and desired position setpoints, 1 = max speed */
		math::Vector<3> d_pos_m = (pos_sp_s - pos_sp_old_s).edivide(_params.pos_p);
		float d_pos_m_len = d_pos_m.length();

		if (d_pos_m_len > dt) {
			pos_sp_s = pos_sp_old_s + (d_pos_m / d_pos_m_len * dt).emult(_params.pos_p);
		}

		/* scale result back to normal space */
		_pos_sp = pos_sp_s.edivide(scale);
以上部分是限速用的//
		/* update yaw setpoint if needed */
		/* 跟新yaw的姿态设定值 */
		if (PX4_ISFINITE(_pos_sp_triplet.current.yaw)) {
			_att_sp.yaw_body = _pos_sp_triplet.current.yaw;
		}
/以下部分是用于起飞到位置巡航光滑切换///
		/*
		 * if we're already near the current takeoff setpoint don't reset in case we switch back to posctl.
		 * this makes the takeoff finish smoothly.
		 */
		if ((_pos_sp_triplet.current.type == position_setpoint_s::SETPOINT_TYPE_TAKEOFF
				|| _pos_sp_triplet.current.type == position_setpoint_s::SETPOINT_TYPE_LOITER)
				&& _pos_sp_triplet.current.acceptance_radius > 0.0f
				/* need to detect we're close a bit before the navigator switches from takeoff to next waypoint */
				&& (_pos - _pos_sp).length() < _pos_sp_triplet.current.acceptance_radius * 1.2f) {
			_reset_pos_sp = false;
			_reset_alt_sp = false;

			/* otherwise: in case of interrupted mission don't go to waypoint but stay at current position */
			/* 在中断任务的情况下,不要去航点,但留在当前位置 */
		} else {
			_reset_pos_sp = true;
			_reset_alt_sp = true;
		}
/以上部分是用于起飞到位置巡航光滑切换///
	} else {
		/* no waypoint, do nothing, setpoint was already reset */
	}
}
以上计算都是基于map_projection_project(&_ref_pos,sp.lat, sp.lon,&sp.data[0], &sp.data[1]);函数的计算(将经纬度转换成地坐标系xy值)也就是说是基于GPS的位置自动控制
map_projection_project()函数
map_projection_project(&_ref_pos,
				       _pos_sp_triplet.current.lat, _pos_sp_triplet.current.lon,
				       &curr_sp.data[0], &curr_sp.data[1]);
					   
输入参数:	&_ref_pos
				/* lat/lon are in radians */
				struct map_projection_reference_s {//地图投影参考
					double lat_rad;
					double lon_rad;
					double sin_lat;
					double cos_lat;
					bool init_done;
					uint64_t timestamp;
				};
				纬度是地心到某地连线与地心到赤道连线的夹角就是某地纬度,赤道为0度,两极是90度
				经度是英国伦敦附近的格林尼治天文台为0度,向东西两边逐加分别为东经和西经,到背面就是180度
				度数/360=弧度/2π
			_pos_sp_triplet.current.lat, _pos_sp_triplet.current.lon
				position_setpoint_s结构体中double lat;(纬度)double lon;(经度),由navigator发布
			&curr_sp.data[0], &curr_sp.data[1]
				x,y方向位置
__EXPORT int map_projection_project(const struct map_projection_reference_s *ref, double lat, double lon, float *x,
				    float *y)
输入参数:	*ref
				/* lat/lon are in radians */
				struct map_projection_reference_s {//地图投影参考
					double lat_rad;
					double lon_rad;
					double sin_lat;
					double cos_lat;
					bool init_done;
					uint64_t timestamp;
				};
				纬度是地心到某地连线与地心到赤道连线的夹角就是某地纬度,赤道为0度,两极是90度
				经度是英国伦敦附近的格林尼治天文台为0度,向东西两边逐加分别为东经和西经,到背面就是180度
				度数/360=弧度/2π
			double lat, double lon,
				double lat;(纬度)double lon;(经度)
			float *x, float *y
				x,y方向位置			
实现的功能:将经纬度转换成地坐标系xy值				
{
	if (!map_projection_initialized(ref)) {
		return -1;
	}

	double lat_rad = lat * M_DEG_TO_RAD;//#define M_DEG_TO_RAD 	0.01745329251994角度转弧度
	double lon_rad = lon * M_DEG_TO_RAD;//#define M_RAD_TO_DEG 	57.2957795130823弧度转角度

	double sin_lat = sin(lat_rad);
	double cos_lat = cos(lat_rad);
	double cos_d_lon = cos(lon_rad - ref->lon_rad);
	//ref->lon_rad是copy ORB_ID(vehicle_local_position)主题,经过update_ref()里面的
	//map_projection_init(&_ref_pos, _local_pos.ref_lat, _local_pos.ref_lon);
	//ref->lon_rad = lon_0 * M_DEG_TO_RAD;

	double c = acos(ref->sin_lat * sin_lat + ref->cos_lat * cos_lat * cos_d_lon);
	double k = (fabs(c) < DBL_EPSILON) ? 1.0 : (c / sin(c));

	*x = k * (ref->cos_lat * sin_lat - ref->sin_lat * cos_lat * cos_d_lon) * CONSTANTS_RADIUS_OF_EARTH;
	*y = k * cos_lat * sin(lon_rad - ref->lon_rad) * CONSTANTS_RADIUS_OF_EARTH;
	//#define CONSTANTS_RADIUS_OF_EARTH(地球半径)			6371000			/* meters (m)		*/
	return 0;
}
常用矩阵向量函数

	/**Firmware/src/lib/mathlib/math/Quaternion.hpp
	 * create rotation matrix for the quaternion
	 */由四元数得到方向余弦旋转矩阵
	Matrix<3, 3> to_dcm(void) const {
		Matrix<3, 3> R;
		float aSq = data[0] * data[0];
		float bSq = data[1] * data[1];
		float cSq = data[2] * data[2];
		float dSq = data[3] * data[3];
		R.data[0][0] = aSq + bSq - cSq - dSq;
		R.data[0][1] = 2.0f * (data[1] * data[2] - data[0] * data[3]);
		R.data[0][2] = 2.0f * (data[0] * data[2] + data[1] * data[3]);
		R.data[1][0] = 2.0f * (data[1] * data[2] + data[0] * data[3]);
		R.data[1][1] = aSq - bSq + cSq - dSq;
		R.data[1][2] = 2.0f * (data[2] * data[3] - data[0] * data[1]);
		R.data[2][0] = 2.0f * (data[1] * data[3] - data[0] * data[2]);
		R.data[2][1] = 2.0f * (data[0] * data[1] + data[2] * data[3]);
		R.data[2][2] = aSq - bSq - cSq + dSq;
		return R;
	}

	/**Firmware/src/lib/mathlib/math/Quaternion.hpp
	 * set quaternion to rotation by DCM
	 * Reference: Shoemake, Quaternions, http://www.cs.ucr.edu/~vbz/resources/quatut.pdf
	 */由方向余弦旋转矩阵得到四元数
	void from_dcm(const Matrix<3, 3> &dcm) {
		float tr = dcm.data[0][0] + dcm.data[1][1] + dcm.data[2][2];
		if (tr > 0.0f) {
			float s = sqrtf(tr + 1.0f);
			data[0] = s * 0.5f;
			s = 0.5f / s;
			data[1] = (dcm.data[2][1] - dcm.data[1][2]) * s;
			data[2] = (dcm.data[0][2] - dcm.data[2][0]) * s;
			data[3] = (dcm.data[1][0] - dcm.data[0][1]) * s;
		} else {
			/* Find maximum diagonal element in dcm
			* store index in dcm_i */
			int dcm_i = 0;
			for (int i = 1; i < 3; i++) {
				if (dcm.data[i][i] > dcm.data[dcm_i][dcm_i]) {
					dcm_i = i;
				}
			}
			int dcm_j = (dcm_i + 1) % 3;
			int dcm_k = (dcm_i + 2) % 3;
			float s = sqrtf((dcm.data[dcm_i][dcm_i] - dcm.data[dcm_j][dcm_j] -
			dcm.data[dcm_k][dcm_k]) + 1.0f);
			data[dcm_i + 1] = s * 0.5f;
			s = 0.5f / s;
			data[dcm_j + 1] = (dcm.data[dcm_i][dcm_j] + dcm.data[dcm_j][dcm_i]) * s;
			data[dcm_k + 1] = (dcm.data[dcm_k][dcm_i] + dcm.data[dcm_i][dcm_k]) * s;
			data[0] = (dcm.data[dcm_k][dcm_j] - dcm.data[dcm_j][dcm_k]) * s;
		}
	}

	/**Firmware/src/lib/mathlib/math/Matrix.hpp
	 * get euler angles from rotation matrix
	 */由旋转矩阵得到欧拉角
	Vector<3> to_euler(void) const {
		Vector<3> euler;
		euler.data[1] = asinf(-data[2][0]);

		if (fabsf(euler.data[1] - M_PI_2_F) < 1.0e-3f) {
			euler.data[0] = 0.0f;
			euler.data[2] = atan2f(data[1][2] - data[0][1], data[0][2] + data[1][1]) + euler.data[0];

		} else if (fabsf(euler.data[1] + M_PI_2_F) < 1.0e-3f) {
			euler.data[0] = 0.0f;
			euler.data[2] = atan2f(data[1][2] - data[0][1], data[0][2] + data[1][1]) - euler.data[0];

		} else {
			euler.data[0] = atan2f(data[2][1], data[2][2]);
			euler.data[2] = atan2f(data[1][0], data[0][0]);
		}

		return euler;
	}
	
	/**Firmware/src/lib/mathlib/math/Matrix.hpp
	 * set zero matrix  零矩阵
	 */
	void zero(void) {
		memset(data, 0, sizeof(data));
	}
	
	/**Firmware/src/lib/mathlib/math/Matrix.hpp
	 * set identity matrix单位矩阵
	 */
	void identity(void) {
		memset(data, 0, sizeof(data));
		unsigned int n = (M < N) ? M : N;

		for (unsigned int i = 0; i < n; i++)
			data[i][i] = 1;
	}

typedef struct pollfd px4_pollfd_struct_t;
	
/* In the standard poll() definition, the size of the event set is 'short'.
 * Here we pick the smallest storage element that will contain all of the
 * poll events.
 */
/* 在标准轮询()的定义,设置事件的大小是“short”。 在这里,最小存储元件将包含所有的轮训事件*/
typedef uint8_t pollevent_t;

/* This is the Nuttx variant of the standard pollfd structure. */
/*这是标准的pollfd结构的Nuttx变量*/
struct pollfd
{
  int         fd;       /* The descriptor being polled */
  sem_t      *sem;      /* Pointer to semaphore used to post output event */
  pollevent_t events;   /* The input event flags */
  pollevent_t revents;  /* The output event flags */
  FAR void   *priv;     /* For use by drivers */
};

	/**Firmware/src/lib/mathlib/math/Vector.hpp
	 * element by element multiplication
	 */
    Matrix<Type, M, N> emult(const Matrix<Type, M, N> &other) const
    {
        Matrix<Type, M, N> res;
        const Matrix<Type, M, N> &self = *this;

        for (size_t i = 0; i < M; i++) {
            for (size_t j = 0; j < N; j++) {
                res(i , j) = self(i, j)*other(i, j);
            }
        }

        return res;
    }
	
	/**Firmware/src/lib/mathlib/math/Vector.hpp
	 * element by element division
	 */
	const Vector<N> edivide(const Vector<N> &v) const {
		Vector<N> res;

		for (unsigned int i = 0; i < N; i++)
			res.data[i] = data[i] / v.data[i];

		return res;
	}
		
	/**Firmware/src/lib/mathlib/math/Matrix.hpp
	 * create a rotation matrix from given euler angles
	 * based on http://gentlenav.googlecode.com/files/EulerAngles.pdf
	 */由欧拉角得到旋转矩阵
	void from_euler(float roll, float pitch, float yaw) {
		float cp = cosf(pitch);
		float sp = sinf(pitch);
		float sr = sinf(roll);
		float cr = cosf(roll);
		float sy = sinf(yaw);
		float cy = cosf(yaw);

		data[0][0] = cp * cy;
		data[0][1] = (sr * sp * cy) - (cr * sy);
		data[0][2] = (cr * sp * cy) + (sr * sy);
		data[1][0] = cp * sy;
		data[1][1] = (sr * sp * sy) + (cr * cy);
		data[1][2] = (cr * sp * sy) - (sr * cy);
		data[2][0] = -sp;
		data[2][1] = sr * cp;
		data[2][2] = cr * cp;
	}

如果您觉得此文对您的发展有用,请随意打赏。 
您的鼓励将是笔者书写高质量文章的最大动力^_^!!


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/czyv587/article/details/51728079

智能推荐

leetcode 172. 阶乘后的零-程序员宅基地

文章浏览阅读63次。题目给定一个整数 n,返回 n! 结果尾数中零的数量。解题思路每个0都是由2 * 5得来的,相当于要求n!分解成质因子后2 * 5的数目,由于n中2的数目肯定是要大于5的数目,所以我们只需要求出n!中5的数目。C++代码class Solution {public: int trailingZeroes(int n) { ...

Day15-【Java SE进阶】IO流(一):File、IO流概述、File文件对象的创建、字节输入输出流FileInputStream FileoutputStream、释放资源。_outputstream释放-程序员宅基地

文章浏览阅读992次,点赞27次,收藏15次。UTF-8是Unicode字符集的一种编码方案,采取可变长编码方案,共分四个长度区:1个字节,2个字节,3个字节,4个字节。文件字节输入流:每次读取多个字节到字节数组中去,返回读取的字节数量,读取完毕会返回-1。注意1:字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码。定义一个与文件一样大的字节数组,一次性读取完文件的全部字节。UTF-8字符集:汉字占3个字节,英文、数字占1个字节。GBK字符集:汉字占2个字节,英文、数字占1个字节。GBK规定:汉字的第一个字节的第一位必须是1。_outputstream释放

jeecgboot重新登录_jeecg 登录自动退出-程序员宅基地

文章浏览阅读1.8k次,点赞3次,收藏3次。解决jeecgboot每次登录进去都会弹出请重新登录问题,在utils文件下找到request.js文件注释这段代码即可_jeecg 登录自动退出

数据中心供配电系统负荷计算实例分析-程序员宅基地

文章浏览阅读3.4k次。我国目前普遍采用需要系数法和二项式系数法确定用电设备的负荷,其中需要系数法是国际上普遍采用的确定计算负荷的方法,最为简便;而二项式系数法在确定设备台数较少且各台设备容量差..._数据中心用电负荷统计变压器

HTML5期末大作业:网页制作代码 网站设计——人电影网站(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品 dreamweaver作业静态HTML网页设计模板_网页设计成品百度网盘-程序员宅基地

文章浏览阅读7k次,点赞4次,收藏46次。HTML5期末大作业:网页制作代码 网站设计——人电影网站(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品 dreamweaver作业静态HTML网页设计模板常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 明星、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 军事、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他 等网页设计题目, A+水平作业_网页设计成品百度网盘

【Jailhouse 文章】Look Mum, no VM Exits_jailhouse sr-iov-程序员宅基地

文章浏览阅读392次。jailhouse 文章翻译,Look Mum, no VM Exits!_jailhouse sr-iov

随便推点

chatgpt赋能python:Python怎么删除文件中的某一行_python 删除文件特定几行-程序员宅基地

文章浏览阅读751次。本文由chatgpt生成,文章没有在chatgpt生成的基础上进行任何的修改。以上只是chatgpt能力的冰山一角。作为通用的Aigc大模型,只是展现它原本的实力。对于颠覆工作方式的ChatGPT,应该选择拥抱而不是抗拒,未来属于“会用”AI的人。AI职场汇报智能办公文案写作效率提升教程 专注于AI+职场+办公方向。下图是课程的整体大纲下图是AI职场汇报智能办公文案写作效率提升教程中用到的ai工具。_python 删除文件特定几行

Java过滤特殊字符的正则表达式_java正则表达式过滤特殊字符-程序员宅基地

文章浏览阅读2.1k次。【代码】Java过滤特殊字符的正则表达式。_java正则表达式过滤特殊字符

CSS中设置背景的7个属性及简写background注意点_background设置背景图片-程序员宅基地

文章浏览阅读5.7k次,点赞4次,收藏17次。css中背景的设置至关重要,也是一个难点,因为属性众多,对应的属性值也比较多,这里详细的列举了背景相关的7个属性及对应的属性值,并附上演示代码,后期要用的话,可以随时查看,那我们坐稳开车了······1: background-color 设置背景颜色2:background-image来设置背景图片- 语法:background-image:url(相对路径);-可以同时为一个元素指定背景颜色和背景图片,这样背景颜色将会作为背景图片的底色,一般情况下设置背景..._background设置背景图片

Win10 安装系统跳过创建用户,直接启用 Administrator_windows10msoobe进程-程序员宅基地

文章浏览阅读2.6k次,点赞2次,收藏8次。Win10 安装系统跳过创建用户,直接启用 Administrator_windows10msoobe进程

PyCharm2021安装教程-程序员宅基地

文章浏览阅读10w+次,点赞653次,收藏3k次。Windows安装pycharm教程新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入下载安装PyCharm1、进入官网PyCharm的下载地址:http://www.jetbrains.com/pycharm/downl_pycharm2021

《跨境电商——速卖通搜索排名规则解析与SEO技术》一一1.1 初识速卖通的搜索引擎...-程序员宅基地

文章浏览阅读835次。本节书摘来自异步社区出版社《跨境电商——速卖通搜索排名规则解析与SEO技术》一书中的第1章,第1.1节,作者: 冯晓宁,更多章节内容可以访问云栖社区“异步社区”公众号查看。1.1 初识速卖通的搜索引擎1.1.1 初识速卖通搜索作为速卖通卖家都应该知道,速卖通经常被视为“国际版的淘宝”。那么请想一下,普通消费者在淘宝网上购买商品的时候,他的行为应该..._跨境电商 速卖通搜索排名规则解析与seo技术 pdf

推荐文章

热门文章

相关标签