2.3[Lib]AP_Motors
来源:互联网 发布:淘宝助理有什么用 编辑:程序博客网 时间:2024/06/07 23:47
前言
之前在分析RCOutput的源码时候,其中的output_ch的成员函数实现过程中,对RC通道的Function进行了分类输出,具体的分成了4类:
- Function manual 下RC通道为通道直通;
- Function rc1~rc16 下RC通道则是映射到实际的ch0~ch15;
Function motor1~motor8 则是通过AP_Motors::rc_write来实现控制;
其他,则是直接输出到当前SRV_Channel绑定的物理通道;
所以这里就对AP_Motors进行分析:
AP_Motors
// offsets for motors in motor_out and _motor_filtered arrays#define AP_MOTORS_MOT_1 0U#define AP_MOTORS_MOT_2 1U#define AP_MOTORS_MOT_3 2U#define AP_MOTORS_MOT_4 3U#define AP_MOTORS_MOT_5 4U#define AP_MOTORS_MOT_6 5U#define AP_MOTORS_MOT_7 6U#define AP_MOTORS_MOT_8 7U#define AP_MOTORS_MAX_NUM_MOTORS 8// motor update rate#define AP_MOTORS_SPEED_DEFAULT 490 // default output rate to the motors/// @class AP_Motorsclass AP_Motors {public: 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, }; 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_Y6B = 10, MOTOR_FRAME_TYPE_Y6F = 11 // for FireFlyY6 }; // Constructor AP_Motors(uint16_t loop_rate, uint16_t speed_hz = AP_MOTORS_SPEED_DEFAULT); // check initialisation succeeded bool initialised_ok() const { return _flags.initialised_ok; } // arm, disarm or check status status of motors,通过AP_Notify传递解锁状态 bool armed() const { return _flags.armed; } void armed(bool arm); // set motor interlock status void set_interlock(bool set) { _flags.interlock = set;} // get motor interlock status. true means motors run, false motors don't run bool get_interlock() const { return _flags.interlock; } // set_roll, set_pitch, set_yaw, set_throttle,设置各个通道输入值范围 void set_roll(float roll_in) { _roll_in = roll_in; }; // range -1 ~ +1 void set_pitch(float pitch_in) { _pitch_in = pitch_in; }; // range -1 ~ +1 void set_yaw(float yaw_in) { _yaw_in = yaw_in; }; // range -1 ~ +1 void set_throttle(float throttle_in) { _throttle_in = throttle_in; }; // range 0 ~ 1 void set_throttle_avg_max(float throttle_avg_max) { _throttle_avg_max = constrain_float(throttle_avg_max,0.0f,1.0f); }; // range 0 ~ 1 void set_throttle_filter_cutoff(float filt_hz) { _throttle_filter.set_cutoff_frequency(filt_hz); } void set_forward(float forward_in) { _forward_in = forward_in; }; // range -1 ~ +1 void set_lateral(float lateral_in) { _lateral_in = lateral_in; }; // range -1 ~ +1 // accessors for roll, pitch, yaw and throttle inputs to motors,获取当前各个通道的输入值 float get_roll() const { return _roll_in; } float get_pitch() const { return _pitch_in; } float get_yaw() const { return _yaw_in; } float get_throttle() const { return constrain_float(_throttle_filter.get(),0.0f,1.0f); } float get_throttle_bidirectional() const { return constrain_float(2*(_throttle_filter.get()-0.5f),-1.0f,1.0f); } float get_forward() const { return _forward_in; } float get_lateral() const { return _lateral_in; } virtual float get_throttle_hover() const = 0; // spool up states enum spool_up_down_desired { DESIRED_SHUT_DOWN = 0, // all motors stop DESIRED_SPIN_WHEN_ARMED = 1, // all motors at spin when armed DESIRED_THROTTLE_UNLIMITED = 2, // motors are no longer constrained by start up procedure }; virtual void set_desired_spool_state(enum spool_up_down_desired spool) { _spool_desired = spool; }; enum spool_up_down_desired get_desired_spool_state(void) const { return _spool_desired; } // // voltage, current and air pressure compensation or limiting features - multicopters only // // set_voltage - set voltage to be used for output scaling void set_voltage(float volts){ _batt_voltage = volts; } // set_current - set current to be used for output scaling void set_current(float current){ _batt_current = current; } // set_density_ratio - sets air density as a proportion of sea level density void set_air_density_ratio(float ratio) { _air_density_ratio = ratio; } // structure for holding motor limit flags struct AP_Motors_limit { uint8_t roll_pitch : 1; // we have reached roll or pitch limit uint8_t yaw : 1; // we have reached yaw limit uint8_t throttle_lower : 1; // we have reached throttle's lower limit uint8_t throttle_upper : 1; // we have reached throttle's upper limit } limit; // // virtual functions that should be implemented by child classes // // set update rate to motors - a value in hertz,设置Motors的更新频率 virtual void set_update_rate( uint16_t speed_hz ) { _speed_hz = speed_hz; } // init,设置机身类型[6轴、8轴等轴数]和机身布局类型[V、H等布局类型] virtual void init(motor_frame_class frame_class, motor_frame_type frame_type) = 0; // set frame class (i.e. quad, hexa, heli) and type (i.e. x, plus),设置机身类型和机身布局类型 virtual void set_frame_class_and_type(motor_frame_class frame_class, motor_frame_type frame_type) = 0; // enable - starts allowing signals to be sent to motors,使能hal对应通道 virtual void enable() = 0; // output - sends commands to the motors virtual void output() = 0; // output_min - sends minimum values out to the motors virtual void output_min() = 0; // output_test - spin a motor at the pwm value specified // motor_seq is the motor's sequence number from 1 to the number of motors on the frame // pwm value is an actual pwm value that will be output, normally in the range of 1000 ~ 2000 // 指定电机以固定的频率旋转 virtual void output_test(uint8_t motor_seq, int16_t pwm) = 0; // get_motor_mask - returns a bitmask of which outputs are being used for motors (1 means being used) // this can be used to ensure other pwm outputs (i.e. for servos) do not conflict // 返回当前正在运行的电机编号 virtual uint16_t get_motor_mask() = 0; // pilot input in the -1 ~ +1 range for roll, pitch and yaw. 0~1 range for throttle // RC通道直通为姿态输入,直接控制电机 void set_radio_passthrough(float roll_input, float pitch_input, float throttle_input, float yaw_input); // set loop rate. Used to support loop rate as a parameter void set_loop_rate(uint16_t loop_rate) { _loop_rate = loop_rate; } enum pwm_type { PWM_TYPE_NORMAL=0, PWM_TYPE_ONESHOT=1, PWM_TYPE_ONESHOT125=2, PWM_TYPE_BRUSHED16kHz=3 }; pwm_type get_pwm_type(void) const { return (pwm_type)_pwm_type.get(); }protected: // output functions that should be overloaded by child classes // 下面这些就是用于派生类 多态实现,具体分析我们参照实际的例子来 virtual void output_armed_stabilizing()=0; virtual void rc_write(uint8_t chan, uint16_t pwm); virtual void rc_set_freq(uint32_t mask, uint16_t freq_hz); virtual void rc_enable_ch(uint8_t chan); virtual uint32_t rc_map_mask(uint32_t mask) const; // add a motor to the motor map,增加一个电机 void add_motor_num(int8_t motor_num); // update the throttle input filter,更新油门的滤波器 virtual void update_throttle_filter() = 0; // save parameters as part of disarming,在上锁的时候保存参数 virtual void save_params_on_disarm() {} // convert input in -1 to +1 range to pwm output,转换输入pwm范围到[-1,1] int16_t calc_pwm_output_1to1(float input, const SRV_Channel *servo); // convert input in 0 to +1 range to pwm output,转换输入pwm到[0,1] int16_t calc_pwm_output_0to1(float input, const SRV_Channel *servo); // flag bitmask struct AP_Motors_flags { uint8_t armed : 1; // 0 if disarmed, 1 if armed uint8_t interlock : 1; // 1 if the motor interlock is enabled (i.e. motors run), 0 if disabled (motors don't run) uint8_t initialised_ok : 1; // 1 if initialisation was successful } _flags; // internal variables 内部变量 uint16_t _loop_rate; // rate in Hz at which output() function is called (normally 400hz)AP_Motor的执行频率 uint16_t _speed_hz; // speed in hz to send updates to motors pwm输出的更新频率 float _roll_in; // desired roll control from attitude controllers, -1 ~ +1 float _pitch_in; // desired pitch control from attitude controller, -1 ~ +1 float _yaw_in; // desired yaw control from attitude controller, -1 ~ +1 float _throttle_in; // last throttle input from set_throttle caller float _forward_in; // last forward input from set_forward caller float _lateral_in; // last lateral input from set_lateral caller float _throttle_avg_max; // last throttle input from set_throttle_avg_max LowPassFilterFloat _throttle_filter; // throttle input filter,油门低通滤波器 spool_up_down_desired _spool_desired; // desired spool state // battery voltage, current and air pressure compensation variables,电池电压,电流和气压补偿变量 float _batt_voltage; // latest battery voltage reading float _batt_current; // latest battery current reading float _air_density_ratio; // air density / sea level density - decreases in altitude // mapping to output channels,电机通道映射 uint8_t _motor_map[AP_MOTORS_MAX_NUM_MOTORS]; uint16_t _motor_map_mask; uint16_t _motor_fast_mask; // pass through variables,这个不明白意思 float _roll_radio_passthrough = 0.0f; // roll input from pilot in -1 ~ +1 range. used for setup and providing servo feedback while landed float _pitch_radio_passthrough = 0.0f; // pitch input from pilot in -1 ~ +1 range. used for setup and providing servo feedback while landed float _throttle_radio_passthrough = 0.0f; // throttle/collective input from pilot in 0 ~ 1 range. used for setup and providing servo feedback while landed float _yaw_radio_passthrough = 0.0f; // yaw input from pilot in -1 ~ +1 range. used for setup and providing servo feedback while landed AP_Int8 _pwm_type; // PWM output type,PWM输出模式};
从上面的数据结构来看,整个AP_Motors是定义了一个面向电机组从输入到输出的框架,从数据结构上来看,分为两个部分:
1.方法,自身实现了的方法包括了对姿态期望自输入的归一化,以及电压、电流、气压计补偿值等参数的设置;虚函数则是主要对实际rc输出的实现,这里不同的机型和布局,是存在区别;
2.变量,变量上主要也是对姿态控制器出来的数值的读入、电流、电压等一些数据的读入、电机通道的映射…
简单来说AP_Motors就是读取并保存姿态控制器计算出来的姿态各个方向的值,并归一化,然后依据motor_map的映射关系对应通道输出;
下面来找一个实际的多旋翼的例子进行说明:
void Copter::init_ardupilot()下面调用Copter::allocate_motors(void),/* allocate the motors class */void Copter::allocate_motors(void){ const struct AP_Param::GroupInfo *var_info; switch ((AP_Motors::motor_frame_class)g2.frame_class.get()) {/*这是针对非直升机的模型,对应的motor的类型都是AP_MotorsMatrix*/#if FRAME_CONFIG != HELI_FRAME case AP_Motors::MOTOR_FRAME_QUAD://四旋翼 case AP_Motors::MOTOR_FRAME_HEXA://六旋翼 case AP_Motors::MOTOR_FRAME_Y6: //三旋翼上下桨 case AP_Motors::MOTOR_FRAME_OCTA://八旋翼 case AP_Motors::MOTOR_FRAME_OCTAQUAD://四旋翼上下桨 default: motors = new AP_MotorsMatrix(MAIN_LOOP_RATE); var_info = AP_MotorsMatrix::var_info; break;/*这是针对三旋翼的模型,对应的motor的类型是AP_MotorsTri*/ case AP_Motors::MOTOR_FRAME_TRI: motors = new AP_MotorsTri(MAIN_LOOP_RATE); var_info = AP_MotorsTri::var_info; AP_Param::set_frame_type_flags(AP_PARAM_FRAME_TRICOPTER); break;/*这是针对单轴的模型,对应AP_MotorsSingle*/ case AP_Motors::MOTOR_FRAME_SINGLE: motors = new AP_MotorsSingle(MAIN_LOOP_RATE); var_info = AP_MotorsSingle::var_info; break;/*这是针对单轴的模型,对应AP_MotorsCoax*/ case AP_Motors::MOTOR_FRAME_COAX: motors = new AP_MotorsCoax(MAIN_LOOP_RATE); var_info = AP_MotorsCoax::var_info; break;/*这是针对单轴的模型,对应AP_MotorsTailsitter*/ case AP_Motors::MOTOR_FRAME_TAILSITTER: motors = new AP_MotorsTailsitter(MAIN_LOOP_RATE); var_info = AP_MotorsTailsitter::var_info; break;#else // FRAME_CONFIG == HELI_FRAME直升机模型 case AP_Motors::MOTOR_FRAME_HELI: default: motors = new AP_MotorsHeli_Single(MAIN_LOOP_RATE); var_info = AP_MotorsHeli::var_info; AP_Param::set_frame_type_flags(AP_PARAM_FRAME_HELI); break;#endif } if (motors == nullptr) { AP_HAL::panic("Unable to allocate FRAME_CLASS=%u", (unsigned)g2.frame_class.get()); } AP_Param::load_object_from_eeprom(motors, var_info); AP_AHRS_View *ahrs_view = ahrs.create_view(ROTATION_NONE); if (ahrs_view == nullptr) { AP_HAL::panic("Unable to allocate AP_AHRS_View"); }/*下面是控制模型*//*非直升机的控制模型均为AC_AttitudeControl_Multi*/#if FRAME_CONFIG != HELI_FRAME attitude_control = new AC_AttitudeControl_Multi(*ahrs_view, aparm, *motors, MAIN_LOOP_SECONDS); var_info = AC_AttitudeControl_Multi::var_info;#else/*直升机的控制模型为AC_AttitudeControl_Heli*/ attitude_control = new AC_AttitudeControl_Heli(*ahrs_view, aparm, *motors, MAIN_LOOP_SECONDS); var_info = AC_AttitudeControl_Heli::var_info;#endif if (attitude_control == nullptr) { AP_HAL::panic("Unable to allocate AttitudeControl"); } AP_Param::load_object_from_eeprom(attitude_control, var_info);/*位置控制模型均为AC_PosControl*/ pos_control = new AC_PosControl(*ahrs_view, inertial_nav, *motors, *attitude_control, g.p_alt_hold, g.p_vel_z, g.pid_accel_z, g.p_pos_xy, g.pi_vel_xy); if (pos_control == nullptr) { AP_HAL::panic("Unable to allocate PosControl"); } AP_Param::load_object_from_eeprom(pos_control, pos_control->var_info);/*航点导航均为AC_WPNav*/ wp_nav = new AC_WPNav(inertial_nav, *ahrs_view, *pos_control, *attitude_control); if (wp_nav == nullptr) { AP_HAL::panic("Unable to allocate WPNav"); } AP_Param::load_object_from_eeprom(wp_nav, wp_nav->var_info);/*绕圈AC_Circle*/ circle_nav = new AC_Circle(inertial_nav, *ahrs_view, *pos_control); if (wp_nav == nullptr) { AP_HAL::panic("Unable to allocate CircleNav"); } AP_Param::load_object_from_eeprom(circle_nav, circle_nav->var_info); // reload lines from the defaults file that may now be accessible AP_Param::reload_defaults_file(); // now setup some frame-class specific defaults根据不同的机架类型,设定不同的PID参数 switch ((AP_Motors::motor_frame_class)g2.frame_class.get()) { case AP_Motors::MOTOR_FRAME_Y6: attitude_control->get_rate_roll_pid().kP().set_default(0.1); attitude_control->get_rate_roll_pid().kD().set_default(0.006); attitude_control->get_rate_pitch_pid().kP().set_default(0.1); attitude_control->get_rate_pitch_pid().kD().set_default(0.006); attitude_control->get_rate_yaw_pid().kP().set_default(0.15); attitude_control->get_rate_yaw_pid().kI().set_default(0.015); break; case AP_Motors::MOTOR_FRAME_TRI: attitude_control->get_rate_yaw_pid().filt_hz().set_default(100); break; default: break; } if (upgrading_frame_params) { // do frame specific upgrade. This is only done the first time we run the new firmware#if FRAME_CONFIG == HELI_FRAME SRV_Channels::upgrade_motors_servo(Parameters::k_param_motors, 12, CH_1); SRV_Channels::upgrade_motors_servo(Parameters::k_param_motors, 13, CH_2); SRV_Channels::upgrade_motors_servo(Parameters::k_param_motors, 14, CH_3); SRV_Channels::upgrade_motors_servo(Parameters::k_param_motors, 15, CH_4);#else if (g2.frame_class == AP_Motors::MOTOR_FRAME_TRI) { const AP_Param::ConversionInfo tri_conversion_info[] = { { Parameters::k_param_motors, 32, AP_PARAM_INT16, "SERVO7_TRIM" }, { Parameters::k_param_motors, 33, AP_PARAM_INT16, "SERVO7_MIN" }, { Parameters::k_param_motors, 34, AP_PARAM_INT16, "SERVO7_MAX" }, { Parameters::k_param_motors, 35, AP_PARAM_FLOAT, "MOT_YAW_SV_ANGLE" }, }; // we need to use CONVERT_FLAG_FORCE as the SERVO7_* parameters will already be set from RC7_* AP_Param::convert_old_parameters(tri_conversion_info, ARRAY_SIZE(tri_conversion_info), AP_Param::CONVERT_FLAG_FORCE); const AP_Param::ConversionInfo tri_conversion_info_rev { Parameters::k_param_motors, 31, AP_PARAM_INT8, "SERVO7_REVERSED" }; AP_Param::convert_old_parameter(&tri_conversion_info_rev, 1, AP_Param::CONVERT_FLAG_REVERSE | AP_Param::CONVERT_FLAG_FORCE); // AP_MotorsTri was converted from having nested var_info to one level AP_Param::convert_parent_class(Parameters::k_param_motors, motors, motors->var_info); }#endif } // upgrade parameters. This must be done after allocating the objects convert_pid_parameters();}从void Copter::allocate_motors中就会首先依据机架类型设定电机控制模型和姿态控制模型;class AP_MotorsMulticopter : public AP_Motorsvoid Copter::loop() | Vvoid Copter::fast_loop() | Vvoid Copter::motors_output()而在Copter这个类中就定义了:#if FRAME_CONFIG == HELI_FRAME #define MOTOR_CLASS AP_MotorsHeli_Single#else #define MOTOR_CLASS AP_MotorsMulticopter#endif MOTOR_CLASS *motors;所以可以明显看到整个AP_Motors的运行就开始了void Copter::motors_output()还是先从void Copter::motors_output()这个函数的开始,上层的调用逻辑暂时先不去管;// motors_output - send output to motors library which will adjust and send to ESCs and servos// 电机输出,其结果是发送给ESCs和伺服接口void Copter::motors_output(){#if ADVANCED_FAILSAFE == ENABLED // this is to allow the failsafe module to deliberately crash // the vehicle. Only used in extreme circumstances to meet the // OBC rules,我靠,这段是用来故意使机器坠毁的代码,感觉好恶意满满 if (g2.afs.should_crash_vehicle()) { g2.afs.terminate_vehicle(); return; }#endif // Update arming delay state,解锁延迟,也就是机器解锁后不立即启动。 if (ap.in_arming_delay && (!motors->armed() || millis()-arm_time_ms > ARMING_DELAY_SEC*1.0e3f || control_mode == THROW)) { ap.in_arming_delay = false; } // check if we are performing the motor test,检测当前是否处于电机测试模式 if (ap.motor_test) { motor_test_output();//执行电机测试输出 } else { bool interlock = motors->armed() && !ap.in_arming_delay && (!ap.using_interlock || ap.motor_interlock_switch) && !ap.motor_emergency_stop; if (!motors->get_interlock() && interlock) { motors->set_interlock(true); Log_Write_Event(DATA_MOTORS_INTERLOCK_ENABLED); } else if (motors->get_interlock() && !interlock) { motors->set_interlock(false); Log_Write_Event(DATA_MOTORS_INTERLOCK_DISABLED); } // send output signals to motors,执行电机输出 motors->output(); }}
void AP_MotorsMulticopter::output()
接下来就来分析output具体的实现过程了,这也是AP_Motors虚函数的第一个实现
// output - sends commands to the motorsvoid AP_MotorsMulticopter::output(){ // update throttle filter,对油门进行一阶低通滤波 update_throttle_filter(); // update battery resistance,更新电池电阻(确保大机动运动的时候,电池电压稳定) update_battery_resistance(); // calc filtered battery voltage and lift_max,计算电池电压的最大升力 update_lift_max_from_batt_voltage(); // run spool logic //将电机输出设定为几个阶段: //SHUT_DOWN :Motors停转无输出,Servos输出保持中位或测试条件值 //SPIN_WHEN_ARMED :Motors停转无输出或者开始输出旋转,Servos开始输出 //SPOOL_UP :Motors最大油门输出,Servos正常输出 //THROTTLE_UNLIMITED :Motors正常输出,Servos正常输出 //SPOOL_DOWN :Motors最小输出,Servos正常输出 output_logic(); // calculate thrust //计算推力 output_armed_stabilizing(); // apply any thrust compensation for the frame // 对机架进行推力补偿 thrust_compensation(); // convert rpy_thrust values to pwm // 根据_spool_mode分别计算出motor_out[AP_MOTORS_MAX_NUM_MOTORS]的PWM值 // 然后通过hal.rcout->write(chan, pwm);进行输出 output_to_motors();};
到这里面向电机的输入输出的代码梳理完成了,接下来分析这这个过程中用到的一些关键算法和逻辑处理代码
关键算法和逻辑处理
这里就从void AP_MotorsMulticopter::output()开始梳理
- 油门低通滤波器(一阶低通滤)
fl = 1/(2πRC),fl为截止频率;-------①w0 = 1/(RC),w0为截止角频率;-------②通过时域、频域的转换,最终得到采样系数 a = t/(RC),t为采样周期a为与RC值有关的一个参数,称为滤波系数,其值决定新采样值在本次滤波结果中所占的权重,其值通常远小于1而在最终得到a的过程中RC还需通过式①转换截止频率间接得到以下:{ float rc = 1.0f/(M_2PI*cutoff_freq); alpha = constrain_float(dt/(dt+rc), 0.0f, 1.0f); _output += (sample - _output) * alpha; return _output;}
-
电池负载更新(计算的电池内阻+外阻)这里计算了PWM到电机升力的近似线性转换:_lift_max = batt_voltage_filt*(1-_thrust_curve_expo) + _thrust_curve_expo*batt_voltage_filt*batt_voltage_filt;做这一步的目的是对后面每个电机的补偿(这个没懂原理)- 电机输出逻辑(分步实现电机运行) //SHUT_DOWN :Motors停转无输出,Servos输出保持中位或测试条件值 //SPIN_WHEN_ARMED :Motors停转无输出或者开始输出旋转,Servos开始输出 //SPOOL_UP :Motors最大油门输出,Servos正常输出 //THROTTLE_UNLIMITED :Motors正常输出,Servos正常输出 //SPOOL_DOWN :Motors最小输出,Servos正常输出这几个模式之间的切换的结果是改变了_spin_up_ratio和_throttle_thrust_max
阅读全文
0 0
- 2.3[Lib]AP_Motors
- Lib
- lib
- lib
- lib
- Gradle 添加依赖 Could not find json-lib.jar (net.sf.json-lib:json-lib:2.3).
- rts6400.lib rts6400e.lib
- d3d9.lib d3dx9.lib
- lib 对 lib*.a ......
- /lib,usr/lib,/usr/local/lib区别
- ,/lib /usr/lib ,/usr/local/lib 区别
- /lib,usr/lib,/usr/local/lib区别
- /lib /usr/lib /usr/local/lib区别
- mfcs80ud.lib mfc80u.lib mfcs80u.lib
- /lib /usr/lib /usr/local/lib区别
- Lib完工
- Boost Lib
- Json-lib
- no modules name gevent
- sql join
- fabric源码解析11——peer的Admin和Endorser服务
- 原生js异步查询(ajax)多层嵌套解决
- EasyUI中通过id获取不知道具体类型表单控件的值,通过自写map实现
- 2.3[Lib]AP_Motors
- Android数据库框架LitePal的使用
- idea控制台乱码与编译出现Error:(1, 12) java: 非法字符: '\u2e73'错误
- VS2015 快捷键
- java ssm框架学习——mybatis--2
- STL的lower_bound和upper_bound源码
- Go 练习
- Android 的adb无法运行
- myelipse NoClassDefFoundError异常