APM_ArduCopter源码解析学习(二)——电机库学习
APM_ArduCopter源码解析学习(二)——电机库学习
- 一、RC输入与输出
- 1.1 RC Input
- 1.2 RC Output
- 二、电机库学习
- 2.1 setup_motors()
- 2.2 add_motor_raw_6dof()
- 2.3 output_min()
- 2.4 calc_thrust_to_pwm()
- 2.5 output_to_motors()
- 2.6 get_current_limit_max_throttle()
- 2.7 output_armed_stabilizing()
- 其他函数
- 三、参考资料
一、RC输入与输出
1.1 RC Input
在开始学习ArduCopter的电机库之前,先来看一下它的RC输入和输出。在Copter.h中声明了最基本的用于运动控制的通道,共4个输入通道,分别为:
// primary input control channelsRC_Channel *channel_roll;RC_Channel *channel_pitch;RC_Channel *channel_throttle;RC_Channel *channel_yaw;
这里先说明一下Copter.h中定义了最基本的无人机类型,即Copter类,内部包含用于控制无人机的最基本的变量和函数等。
这几个通道的意思,就是通过其中的通道输入来控制无人机的某一个方向上的运动,它并不是指某一个特定的电机,通常一个通道影响着很多与它控制运动相关的电机。
官方手册中给出RCInput的具体路径为: libraries/AP_HAL/examples/RCInput/RCInput.cpp
1.2 RC Output
官方手册中给出RCOutput的具体路径为: libraries/AP_HAL/examples/RCOutput/RCOutput.cpp。后期需要对这部分代码进行详细梳理。
二、电机库学习
在开始之前先明确一下什么是多旋翼的推力分配(建议仔细阅读,务必了解原理):
[飞控]从零开始建模(三)-控制分配浅析
多旋翼飞行器的控制分配
在Copter.h中定义的Copter类中,指明了其所使用的电机类型
#if FRAME_CONFIG == HELI_FRAME#define MOTOR_CLASS AP_MotorsHeli
#else#define MOTOR_CLASS AP_MotorsMulticopter
#endif// Motor OutputMOTOR_CLASS *motors;
由此可知ArduCopter的电机库配置位于libraries\AP_Motors路径下的AP_MotorsMulticopter.cpp/AP_MotorsMulticopter.h文件中。其内部继承关系如下所示。
AP_Motors
|---- AP_MotorsMulticopter
|---- AP_MotorsMatrix
2.1 setup_motors()
由于内部代码还是有点小长的,这里就不全部放进来了,节选部分讲解一下。
这个函数最主要的内容就是配置电机,包括每个电机对于不同运动的影响程度(推力分配)。
函数刚开始,首先就是把最初的所有电机配置全部移除,方便后续进行更改。这里的AP_MOTORS_MAX_NUM_MOTORS为最大的电机数,于AP_Motors_Class.h中定义为12.
// remove existing motorsfor (int8_t i=0; i<AP_MOTORS_MAX_NUM_MOTORS; i++) {remove_motor(i);}
- 1
- 2
- 3
- 4
然后进入一个switch()函数中进行具体的配置内容,首先判断是属于哪一种frame_class的架构,ArduCopter这边给出了14种配置结构,定义于AP_Motors_Class.h中的枚举类型里,如下所示
enum motor_frame_class {MOTOR_FRAME_UNDEFINED = 0,MOTOR_FRAME_QUAD = 1,MOTOR_FRAME_HEXA = 2,MOTOR_FRAME_OCTA = 3,MOTOR_FRAME_OCTAQUAD = 4,MOTOR_FRAME_Y6 = 5,MOTOR_FRAME_HELI = 6,MOTOR_FRAME_TRI = 7,MOTOR_FRAME_SINGLE = 8,MOTOR_FRAME_COAX = 9,MOTOR_FRAME_TAILSITTER = 10,MOTOR_FRAME_HELI_DUAL = 11,MOTOR_FRAME_DODECAHEXA = 12,MOTOR_FRAME_HELI_QUAD = 13,};
根据不同的机身结构,frame_type定义了不同的机型类别。
enum motor_frame_type {MOTOR_FRAME_TYPE_PLUS = 0,MOTOR_FRAME_TYPE_X = 1,MOTOR_FRAME_TYPE_V = 2,MOTOR_FRAME_TYPE_H = 3,MOTOR_FRAME_TYPE_VTAIL = 4,MOTOR_FRAME_TYPE_ATAIL = 5,MOTOR_FRAME_TYPE_PLUSREV = 6, // plus with reversed motor directionMOTOR_FRAME_TYPE_Y6B = 10,MOTOR_FRAME_TYPE_Y6F = 11, // for FireFlyY6MOTOR_FRAME_TYPE_BF_X = 12, // X frame, betaflight orderingMOTOR_FRAME_TYPE_DJI_X = 13, // X frame, DJI orderingMOTOR_FRAME_TYPE_CW_X = 14, // X frame, clockwise orderingMOTOR_FRAME_TYPE_I = 15, // (sideways H) octo onlyMOTOR_FRAME_TYPE_NYT_PLUS = 16, // plus frame, no differential torque for yawMOTOR_FRAME_TYPE_NYT_X = 17, // X frame, no differential torque for yawMOTOR_FRAME_TYPE_BF_X_REV = 18, // X frame, betaflight ordering, reversed motors};
以X型四旋翼进行说明:
case MOTOR_FRAME_TYPE_X:add_motor(AP_MOTORS_MOT_1, 45, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 1);add_motor(AP_MOTORS_MOT_2, -135, AP_MOTORS_MATRIX_YAW_FACTOR_CCW, 3);add_motor(AP_MOTORS_MOT_3, -45, AP_MOTORS_MATRIX_YAW_FACTOR_CW, 4);add_motor(AP_MOTORS_MOT_4, 135, AP_MOTORS_MATRIX_YAW_FACTOR_CW, 2);break;
add_moto()这个函数的作用就是配置每一个电机在某一运动功能上的影响因子,AP_MOTORS_MOT_1从右上角开始,逆时针进行编号增加
2.2 add_motor
// add_motor using just position and prop direction - assumes that for each motor, roll and pitch factors are equal
void AP_MotorsMatrix::add_motor(int8_t motor_num, float angle_degrees, float yaw_factor, uint8_t testing_order)
{add_motor(motor_num, angle_degrees, angle_degrees, yaw_factor, testing_order);
}
void AP_MotorsMatrix::add_motor(int8_t motor_num, float roll_factor_in_degrees, float pitch_factor_in_degrees, float yaw_factor, uint8_t testing_order)
{add_motor_raw(motor_num,cosf(radians(roll_factor_in_degrees + 90)),cosf(radians(pitch_factor_in_degrees)),yaw_factor,testing_order);
}
代码内容如上,这部分程序时直接调用了AP_MotorsMatrix.cpp中的add_motor_raw()函数,这个函数原本的作用是用来配置空中无人机的RPY方向上各个电机对各方向的影响因子,但是ROV在水下运动时多了沉浮、前后平移和左右平移的功能,因此在后面添加了3个相关的影响因子配置数组。
add_motor_raw()函数原型如下:
// add_motor
void AP_MotorsMatrix::add_motor_raw(int8_t motor_num, float roll_fac, float pitch_fac, float yaw_fac, uint8_t testing_order)
{// ensure valid motor number is providedif (motor_num >= 0 && motor_num < AP_MOTORS_MAX_NUM_MOTORS) {
2.3 normalise_rpy_factors
// normalizes the roll, pitch and yaw factors so maximum magnitude is 0.5
void AP_MotorsMatrix::normalise_rpy_factors()
{float roll_fac = 0.0f;float pitch_fac = 0.0f;float yaw_fac = 0.0f;// find maximum roll, pitch and yaw factorsfor (uint8_t i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {if (motor_enabled[i]) {if (roll_fac < fabsf(_roll_factor[i])) {roll_fac = fabsf(_roll_factor[i]);}if (pitch_fac < fabsf(_pitch_factor[i])) {pitch_fac = fabsf(_pitch_factor[i]);}if (yaw_fac < fabsf(_yaw_factor[i])) {yaw_fac = fabsf(_yaw_factor[i]);}}}// scale factors back to -0.5 to +0.5 for each axisfor (uint8_t i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {if (motor_enabled[i]) {if (!is_zero(roll_fac)) {_roll_factor[i] = 0.5f * _roll_factor[i] / roll_fac;}if (!is_zero(pitch_fac)) {_pitch_factor[i] = 0.5f * _pitch_factor[i] / pitch_fac;}if (!is_zero(yaw_fac)) {_yaw_factor[i] = 0.5f * _yaw_factor[i] / yaw_fac;}}}
}
该函数的作用是将影响因子约束在-0.5~0.5之间。
2.4 output_to_motors()
void AP_MotorsMatrix::output_to_motors()
{int8_t i;switch (_spool_state) {case SpoolState::SHUT_DOWN: {// no outputfor (i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {if (motor_enabled[i]) {_actuator[i] = 0.0f;}}break;}case SpoolState::GROUND_IDLE:// sends output to motors when armed but not flyingfor (i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {if (motor_enabled[i]) {set_actuator_with_slew(_actuator[i], actuator_spin_up_to_ground_idle());}}break;case SpoolState::SPOOLING_UP:case SpoolState::THROTTLE_UNLIMITED:case SpoolState::SPOOLING_DOWN:// set motor output based on thrust requestsfor (i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {if (motor_enabled[i]) {set_actuator_with_slew(_actuator[i], thrust_to_actuator(_thrust_rpyt_out[i]));}}break;}// convert output to PWM and send to each motorfor (i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {if (motor_enabled[i]) {rc_write(i, output_to_pwm(_actuator[i]));}}
}
这个函数也很简单,最开始的 _actuator数组就是用来储存每一个电机最后的pwm值,switch函数判断轴状态,这里只需要知道SHUT_DOWN和GROUND_IDLE状态下,推进器不工作即可。而在SPOOLING_UP、THROTTLE_UNLIMITED和SPOOLING_DOWN状态下,对每一个电机调用set_actuator_with_slew函数计算最后的pwm值并且保存到 _actuator数组中。最后通过rc_write()函数输出到每一个通道。
2.5 output_armed_stabilizing()
推力限幅及分配函数
// output_armed - sends commands to the motors
// includes new scaling stability patch
void AP_MotorsMatrix::output_armed_stabilizing()
{uint8_t i; // general purpose counterfloat roll_thrust; // roll thrust input value, +/- 1.0float pitch_thrust; // pitch thrust input value, +/- 1.0float yaw_thrust; // yaw thrust input value, +/- 1.0float throttle_thrust; // throttle thrust input value, 0.0 - 1.0float throttle_avg_max; // throttle thrust average maximum value, 0.0 - 1.0float throttle_thrust_max; // throttle thrust maximum value, 0.0 - 1.0float throttle_thrust_best_rpy; // throttle providing maximum roll, pitch and yaw range without climbingfloat rpy_scale = 1.0f; // this is used to scale the roll, pitch and yaw to fit within the motor limitsfloat yaw_allowed = 1.0f; // amount of yaw we can fit infloat thr_adj; // the difference between the pilot's desired throttle and throttle_thrust_best_rpy// apply voltage and air pressure compensationconst float compensation_gain = get_compensation_gain(); // compensation for battery voltage and altituderoll_thrust = (_roll_in + _roll_in_ff) * compensation_gain;pitch_thrust = (_pitch_in + _pitch_in_ff) * compensation_gain;yaw_thrust = (_yaw_in + _yaw_in_ff) * compensation_gain;throttle_thrust = get_throttle() * compensation_gain;throttle_avg_max = _throttle_avg_max * compensation_gain;// If thrust boost is active then do not limit maximum thrustthrottle_thrust_max = _thrust_boost_ratio + (1.0f - _thrust_boost_ratio) * _throttle_thrust_max * compensation_gain;
上述代码中运用电压和气压补偿进入通道运算。如果不用这部分做补偿,也是可行的。
// sanity check throttle is above zero and below current limited throttleif (throttle_thrust <= 0.0f) {throttle_thrust = 0.0f;limit.throttle_lower = true;}if (throttle_thrust >= throttle_thrust_max) {throttle_thrust = throttle_thrust_max;limit.throttle_upper = true;}// ensure that throttle_avg_max is between the input throttle and the maximum throttlethrottle_avg_max = constrain_float(throttle_avg_max, throttle_thrust, throttle_thrust_max);// calculate the highest allowed average thrust that will provide maximum control rangethrottle_thrust_best_rpy = MIN(0.5f, throttle_avg_max);// calculate throttle that gives most possible room for yaw which is the lower of:// 1. 0.5f - (rpy_low+rpy_high)/2.0 - this would give the maximum possible margin above the highest motor and below the lowest// 2. the higher of:// a) the pilot's throttle input// b) the point _throttle_rpy_mix between the pilot's input throttle and hover-throttle// Situation #2 ensure we never increase the throttle above hover throttle unless the pilot has commanded this.// Situation #2b allows us to raise the throttle above what the pilot commanded but not so far that it would actually cause the copter to rise.// We will choose #1 (the best throttle for yaw control) if that means reducing throttle to the motors (i.e. we favor reducing throttle *because* it provides better yaw control)// We will choose #2 (a mix of pilot and hover throttle) only when the throttle is quite low. We favor reducing throttle instead of better yaw control because the pilot has commanded it// Under the motor lost condition we remove the highest motor output from our calculations and let that motor go greater than 1.0// To ensure control and maximum righting performance Hex and Octo have some optimal settings that should be used// Y6 : MOT_YAW_HEADROOM = 350, ATC_RAT_RLL_IMAX = 1.0, ATC_RAT_PIT_IMAX = 1.0, ATC_RAT_YAW_IMAX = 0.5// Octo-Quad (x8) x : MOT_YAW_HEADROOM = 300, ATC_RAT_RLL_IMAX = 0.375, ATC_RAT_PIT_IMAX = 0.375, ATC_RAT_YAW_IMAX = 0.375// Octo-Quad (x8) + : MOT_YAW_HEADROOM = 300, ATC_RAT_RLL_IMAX = 0.75, ATC_RAT_PIT_IMAX = 0.75, ATC_RAT_YAW_IMAX = 0.375// Usable minimums below may result in attitude offsets when motors are lost. Hex aircraft are only marginal and must be handles with care// Hex : MOT_YAW_HEADROOM = 0, ATC_RAT_RLL_IMAX = 1.0, ATC_RAT_PIT_IMAX = 1.0, ATC_RAT_YAW_IMAX = 0.5// Octo-Quad (x8) x : MOT_YAW_HEADROOM = 300, ATC_RAT_RLL_IMAX = 0.25, ATC_RAT_PIT_IMAX = 0.25, ATC_RAT_YAW_IMAX = 0.25// Octo-Quad (x8) + : MOT_YAW_HEADROOM = 300, ATC_RAT_RLL_IMAX = 0.5, ATC_RAT_PIT_IMAX = 0.5, ATC_RAT_YAW_IMAX = 0.25// Quads cannot make use of motor loss handling because it doesn't have enough degrees of freedom.// calculate amount of yaw we can fit into the throttle range// this is always equal to or less than the requested yaw from the pilot or rate controllerfloat rp_low = 1.0f; // lowest thrust valuefloat rp_high = -1.0f; // highest thrust valuefor (i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {if (motor_enabled[i]) {// calculate the thrust outputs for roll and pitch_thrust_rpyt_out[i] = roll_thrust * _roll_factor[i] + pitch_thrust * _pitch_factor[i];// record lowest roll + pitch commandif (_thrust_rpyt_out[i] < rp_low) {rp_low = _thrust_rpyt_out[i];}// record highest roll + pitch commandif (_thrust_rpyt_out[i] > rp_high && (!_thrust_boost || i != _motor_lost_index)) {rp_high = _thrust_rpyt_out[i];}// Check the maximum yaw control that can be used on this channel// Exclude any lost motors if thrust boost is enabledif (!is_zero(_yaw_factor[i]) && (!_thrust_boost || i != _motor_lost_index)){if (is_positive(yaw_thrust * _yaw_factor[i])) {yaw_allowed = MIN(yaw_allowed, fabsf(MAX(1.0f - (throttle_thrust_best_rpy + _thrust_rpyt_out[i]), 0.0f)/_yaw_factor[i]));} else {yaw_allowed = MIN(yaw_allowed, fabsf(MAX(throttle_thrust_best_rpy + _thrust_rpyt_out[i], 0.0f)/_yaw_factor[i]));}}}}// calculate the maximum yaw control that can be used// todo: make _yaw_headroom 0 to 1float yaw_allowed_min = (float)_yaw_headroom / 1000.0f;// increase yaw headroom to 50% if thrust boost enabledyaw_allowed_min = _thrust_boost_ratio * 0.5f + (1.0f - _thrust_boost_ratio) * yaw_allowed_min;// Let yaw access minimum amount of head roomyaw_allowed = MAX(yaw_allowed, yaw_allowed_min);// Include the lost motor scaled by _thrust_boost_ratio to smoothly transition this motor in and out of the calculationif (_thrust_boost && motor_enabled[_motor_lost_index]) {// record highest roll + pitch commandif (_thrust_rpyt_out[_motor_lost_index] > rp_high) {rp_high = _thrust_boost_ratio * rp_high + (1.0f - _thrust_boost_ratio) * _thrust_rpyt_out[_motor_lost_index];}// Check the maximum yaw control that can be used on this channel// Exclude any lost motors if thrust boost is enabledif (!is_zero(_yaw_factor[_motor_lost_index])){if (is_positive(yaw_thrust * _yaw_factor[_motor_lost_index])) {yaw_allowed = _thrust_boost_ratio * yaw_allowed + (1.0f - _thrust_boost_ratio) * MIN(yaw_allowed, fabsf(MAX(1.0f - (throttle_thrust_best_rpy + _thrust_rpyt_out[_motor_lost_index]), 0.0f)/_yaw_factor[_motor_lost_index]));} else {yaw_allowed = _thrust_boost_ratio * yaw_allowed + (1.0f - _thrust_boost_ratio) * MIN(yaw_allowed, fabsf(MAX(throttle_thrust_best_rpy + _thrust_rpyt_out[_motor_lost_index], 0.0f)/_yaw_factor[_motor_lost_index]));}}}if (fabsf(yaw_thrust) > yaw_allowed) {// not all commanded yaw can be usedyaw_thrust = constrain_float(yaw_thrust, -yaw_allowed, yaw_allowed);limit.yaw = true;}// add yaw control to thrust outputsfloat rpy_low = 1.0f; // lowest thrust valuefloat rpy_high = -1.0f; // highest thrust valuefor (i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {if (motor_enabled[i]) {_thrust_rpyt_out[i] = _thrust_rpyt_out[i] + yaw_thrust * _yaw_factor[i];// record lowest roll + pitch + yaw commandif (_thrust_rpyt_out[i] < rpy_low) {rpy_low = _thrust_rpyt_out[i];}// record highest roll + pitch + yaw command// Exclude any lost motors if thrust boost is enabledif (_thrust_rpyt_out[i] > rpy_high && (!_thrust_boost || i != _motor_lost_index)) {rpy_high = _thrust_rpyt_out[i];}}}// Include the lost motor scaled by _thrust_boost_ratio to smoothly transition this motor in and out of the calculationif (_thrust_boost) {// record highest roll + pitch + yaw commandif (_thrust_rpyt_out[_motor_lost_index] > rpy_high && motor_enabled[_motor_lost_index]) {rpy_high = _thrust_boost_ratio * rpy_high + (1.0f - _thrust_boost_ratio) * _thrust_rpyt_out[_motor_lost_index];}}// calculate any scaling needed to make the combined thrust outputs fit within the output rangeif (rpy_high - rpy_low > 1.0f) {rpy_scale = 1.0f / (rpy_high - rpy_low);}if (throttle_avg_max + rpy_low < 0) {rpy_scale = MIN(rpy_scale, -throttle_avg_max / rpy_low);}// calculate how close the motors can come to the desired throttlerpy_high *= rpy_scale;rpy_low *= rpy_scale;throttle_thrust_best_rpy = -rpy_low;thr_adj = throttle_thrust - throttle_thrust_best_rpy;if (rpy_scale < 1.0f) {// Full range is being used by roll, pitch, and yaw.limit.roll = true;limit.pitch = true;limit.yaw = true;if (thr_adj > 0.0f) {limit.throttle_upper = true;}thr_adj = 0.0f;} else {if (thr_adj < 0.0f) {// Throttle can't be reduced to desired value// todo: add lower limit flag and ensure it is handled correctly in altitude controllerthr_adj = 0.0f;} else if (thr_adj > 1.0f - (throttle_thrust_best_rpy + rpy_high)) {// Throttle can't be increased to desired valuethr_adj = 1.0f - (throttle_thrust_best_rpy + rpy_high);limit.throttle_upper = true;}}// add scaled roll, pitch, constrained yaw and throttle for each motorfor (i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; i++) {if (motor_enabled[i]) {_thrust_rpyt_out[i] = throttle_thrust_best_rpy + thr_adj + (rpy_scale * _thrust_rpyt_out[i]);}}// determine throttle thrust for harmonic notchconst float throttle_thrust_best_plus_adj = throttle_thrust_best_rpy + thr_adj;// compensation_gain can never be zero_throttle_out = throttle_thrust_best_plus_adj / compensation_gain;// check for failed motorcheck_for_failed_motor(throttle_thrust_best_plus_adj);
这部分后面有时间再进行梳理
其他函数
无。
三、参考资料
Ardusub官方手册
Ardupilot源码
2020/12/2更新:增加了部分解释以及推力分配内容
感谢博主对Ardusub的详细解说,这里主要参考网址为:https://blog.csdn.net/moumde/article/details/108823550
APM_ArduCopter源码解析学习(二)——电机库学习相关推荐
- APM_ArduCopter源码解析学习(三)——无人机类型
APM_ArduCopter源码解析学习(三)--无人机类型 一.前言 二.class AP_HAL::HAL 三.class AP_Vehicle 3.1 .h 3.2 .cpp 四.class C ...
- Tomcat源码解析系列二:Tomcat总体架构
Tomcat即是一个HTTP服务器,也是一个servlet容器,主要目的就是包装servlet,并对请求响应相应的servlet,纯servlet的web应用似乎很好理解Tomcat是如何装载serv ...
- 【SpringBoot】最新版2019Spring Boot配置解析,源码解析(速成SpringBoot)——学习笔记版【2】
SpringBoot配置文件 文章目录 SpringBoot配置文件 四.配置文件 1.简介 2.YAML用法 2.1 简介 2.2语法 3.为属性注入值 3.1使用.yml配置文件 3.1编写.ym ...
- Tightly Coupled LiDAR Inertial Odometry and Mapping源码解析(二)
Tightly Coupled LiDAR Inertial Odometry and Mapping源码解析(二) 3. Joint optimization 3.1 Marginalization ...
- PyCrypto密码学库源码解析(二)RSA参数生成
Python Crypto库源码解析(二) RSA参数生成 * 版权声明 * 引用请注明出处,转载请联系: h0.1c@foxmail.com 本文主要讲解pycrypto库中RSA参数生成的实现方法 ...
- 【vuejs深入三】vue源码解析之二 htmlParse解析器的实现
写在前面 一个好的架构需要经过血与火的历练,一个好的工程师需要经过无数项目的摧残. 昨天博主分析了一下在vue中,最为基础核心的api,parse函数,它的作用是将vue的模板字符串转换成ast,从而 ...
- 【DETR源码解析】二、Backbone模块
目录 前言 一.Backbone整体结构 一.CNN-Backbone 二.Positional Encoding Reference 前言 最近在看DETR的源码,断断续续看了一星期左右,把主要的模 ...
- 谷歌BERT预训练源码解析(二):模型构建
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_39470744/arti ...
- python flask源码解析_用尽洪荒之力学习Flask源码
[TOC] 一直想做源码阅读这件事,总感觉难度太高时间太少,可望不可见.最近正好时间充裕,决定试试做一下,并记录一下学习心得. 首先说明一下,本文研究的Flask版本是0.12. 首先做个小示例,在p ...
- Mybatis源码解析《二》
导语 在前一篇文章Mybatis源码解析<一>中,已经简单了捋了一下mybatis核心文件和mapper配置文件的一个基本的解析流程,这是理解mybatis的基本,和spring中的配置文 ...
最新文章
- 聊一聊:你碰到过哪些操蛋的文档?
- Rider 2021.3 Beta 现已推出
- 增加RIL组件时编辑出现的问题
- 数据科学家为什要用Git?怎么用?
- Eucalyptus常用查询命令
- docker 安全性_未来的Docker安全性
- Linux 内核 5.4 将于 11月24 日 发布,Linux 5.4-rc8 已可用于公测
- python中score_在Python中“得分必须返回一个数字”scikit-learn中的cross_val_score错误...
- [zencart数据采集]第二课 火车头采集简单系统配置
- 可以联机的的单机游戏
- C语言 简单的文件下载器
- 突破https——https抓包
- 加密解密工具 之 恩尼格玛密码机密码
- 哈哈,电驴资源 的下载问题
- 巨星传奇更新招股书:业务绑定歌手周杰伦 上半年营收降24%
- wifi有网可以连接,但打不开网页了,找不到 服务器 dns 地址
- Git 版本控制/项目迭代
- linux无人值守安装实验,无人值守批量安装linux操作系统
- 危险化学品题库及答案
- memory balloon
热门文章
- 蓝牙耳机连接电脑没有声音解决办法
- 火山PC_数据库知识_MySQL操作
- OHEM算法论文理解
- python安卓脚本精灵使用教程_【按键精灵教程】自动安装apk还可以这么用!
- android adb 刷机工具,ADB 工具 ADB 工具刷机-完美教程资讯
- 51单片机模拟交通灯c语言程序,51单片机protues仿真——LED模拟交通灯
- Windows xp 安装的屏幕保护程序
- 关于男生追女生的数学模型【转王博】
- c语言程序设计辅导资料pdf,C语言程序设计辅导资料(修订版).pdf
- 农行笔试编程题(Java)记录