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

智能推荐

精益DevOps:优化流程,提升效能【文末送书】_devops平台提升效率-程序员宅基地

文章浏览阅读4.2k次,点赞25次,收藏15次。本书为IT服务交付团队及其领导者撰写,从精益思想和精益管理的视角,深入探讨了DevOps方法的核心要素(如任务式指挥、摩擦、风险、态势感知等),并结合实际案例,阐述了如何通过DevOps方法解决IT服务交付中的各种问题,如何在整个组织内改善信息流,从而向客户的目标成果迈进。此外,本书还提供了许多实用的工具和技巧,包括OODA循环、Cynefin框架、服务交付的成熟度模型和服务工程负责人等,以帮助读者更好地应用DevOps方法。_devops平台提升效率

React中,使用codeMirror设置代码高度自适应_react codemirror 自适应高度-程序员宅基地

文章浏览阅读4.2k次。在对应的less文件下,设置样式:global .CodeMirror { /* Set height, width, borders, and global font properties here */ font-family: monospace; height: auto; color: black; direction: ltr;}..._react codemirror 自适应高度

百问网STM32157适配正点原子7寸电容屏(1024X600)_touchscreen-size-x-程序员宅基地

文章浏览阅读1.5k次。百问网STM32157适配正点原子7寸电容屏(1024X600)文章目录1.修改设备树文件2.修改edt-ft5x06.c(触摸驱动)3.修改QT环境变量(重要,踩坑)1.修改设备树文件由于百问网STM32MP157的触摸IC与HDMI驱动IC复用,因此需要完成以下几个步骤(1)在stm32mp157c-100ask-512d-lcd-v1.dts文件中对i2c4节点增加ft5x06触摸的描述信息,具体参考正点原子STM32MP157开发板的配套源码,如下在正点原子出厂linux源码中找到stm3_touchscreen-size-x

pso神经网络用python_基本pso优化神经网络程序-程序员宅基地

文章浏览阅读901次。clcclearall%一、初始化部分%1.1预处理样本数据%选取训练样本(x,y)fori=1:126x=0+0.0251*(i-1);y(i)=(sin(x)+(x.^2/9+x/3)*exp((-0.5)*(x.^2)))/2;%待逼近函数endAllSamIn=0:0.0251:pi;%训练样本输入AllSamOut=y;%训练样本输出%选取测试样本fori=1:125x=0.0125+..._pid神经网络优化 python

MFC slider滑动条OnNMCustomdrawSlider(NMHDR *pNMHDR, LRESULT *pResult)事件_onnmthemechangedtranslatexslider-程序员宅基地

文章浏览阅读6.8k次。鼠标拖动滑动条的时候,触发事件OnNMCustomdrawSlider(NMHDR *pNMHDR, LRESULT *pResult)但是在窗体被其他页面遮挡重新显示在屏幕最前面的时候也会触发这个事件,例如最小化重新最大化的时候。网友对三种消息类型的解释:NM_CUSTOMDRAW,是子控件通知其父控件它已经完成重画功能,对Slider来说也就是我们每次鼠标按住滑块移动一下就_onnmthemechangedtranslatexslider

jquery-ui日期时间控件实现_jquery ui picker-程序员宅基地

文章浏览阅读7.9k次。日期控件和时间控件为独立控件,日期时间控件要同时导入日期控件和时间控件的js,然后在日期控件添加时间控件显示参数,没有导入时间控件js,日期控件函数设置的时间控件参将包错日期官网网址:http://jqueryui.com/日期控件js:jquery-ui.js对应函数及默认属性设置:function Datepicker()时间官网网址:http://plugins._jquery ui picker

随便推点

经典神经网络论文超详细解读(一)——AlexNet学习笔记(翻译+精读)_alexnet论文-程序员宅基地

文章浏览阅读5.5k次,点赞41次,收藏106次。AlexNet(ImageNet Classification with Deep Convolutional Neural Networks)论文超详细解读。翻译+总结_alexnet论文

在Ubuntu上编译安装LLVM_ubuntu hexagon llvm 8.5.09-程序员宅基地

文章浏览阅读1.1w次,点赞12次,收藏36次。Motivation两周前实验室要求我配置一个叫Speedy.js的编译器,配置这个编译器需要先配置好LLVM。根据这个编译器作者的教程,乱七八糟配置了一通,踩过一些坑,碰过一些雷,浪费了很多时间。花了两周时间,终于全套配置完成,因此想写个教程,留点知识给以后的自己和需要的各位。Speedy.js不好说,但是LLVM这东西以后有大概率会用的上。环境这是我这次配置LLVM过程中最大的_ubuntu hexagon llvm 8.5.09

Anaconda+Tensorflow_Gpu+Spyder安装记录(2022年10月14日更新)_anaconda navigator安装spyderd慢-程序员宅基地

文章浏览阅读1.9k次,点赞7次,收藏15次。2022年6月13日-记录Anaconda+tensorflow_gpu-2.9.0+spyder5.3.1+py3.9安装过程,回忆一下踩过的坑_anaconda navigator安装spyderd慢

数据预处理流程图思维导图-程序员宅基地

文章浏览阅读2.6k次。_数据预处理流程图

Abnova LiquidCell-负富集细胞分离和回收系统-程序员宅基地

文章浏览阅读129次。艾美捷Abnova LiquidCell 是一种非侵入性负富集系统,用于消耗白细胞 (WBC) 以及计数和回收循环稀有细胞 (CRC)。_负富集

构建稳固的数据基础,GaussDB与产业紧密合作-程序员宅基地

文章浏览阅读17次。为了有效地管理和利用大规模数据,企业需要一个强大而可靠的数据底座。本文将介绍GaussDB的特点和优势,并提供相应的源代码示例,以展示其在数据管理方面的强大功能。总结而言,GaussDB作为一种高性能、可扩展和高可靠性的分布式数据库管理系统,与产业界紧密合作,为企业构建了稳固的数据基础。通过示例代码,我们展示了GaussDB的基本用法,希望能够帮助读者更好地了解和应用这一强大的数据管理工具。它支持数据备份和恢复,以及故障转移和自动故障转移,确保数据库在出现故障时能够自动切换到备用节点,保持业务的连续性。

推荐文章

热门文章

相关标签