先来看看数据封装。
class AutoExpGain
{
...
public:
SystemManager* sys_;
const u32 kw_ = 4;
const u32 kh_ = 4;
const u32 scale_ = 1024;
const u32 level_range_ = 10;
WinSum win_sum_;
WinSum win_coe_;
int target_level_;
int MH_;
int ML_;
int min_exp_time_;
int max_exp_time_;
int min_gain_;
int max_gain_;
u32 win_cnt_;
u32 lum_level_;
PID epid_;
PID gpid_;
u32 exp_time_;
u32 gain_;
u32 fresh_;
};
首先定义索引类的成员变量,定义一个system manager对象的索引,用来实现源端引用。
然后定义常量类成员变量,
然后定义复杂结构的成员对象(结构体对象,class对象),
然后定义特定用途的基本类型的成员变量(非结构体变量,非类对象变量)。
再来看看操作集的封装。
class AutoExpGain
{
public:
AutoExpGain(SystemManager* sys);
u32 calc_lum_level();
int compute();
void set_aec_kp(int value)
void set_aec_ki(int value)
void set_aec_kd(int value)
void set_target_level(int value)
int set_exposure_time(int value);
void set_exp_time_inc(int value)
void set_exp_time_dec(int value)
void set_gain(int value)
void set_gain_inc(int value)
void set_gain_dec(int value)
int calc_level_state(u32 value)
u32 limit_exp_time(int value)
u32 limit_gain(int value)
};
定义如下:
1)构建函数
AutoExpGain::AutoExpGain(SystemManager* sys) :
sys_(sys),
target_level_(100),
min_exp_time_(1280),
max_exp_time_(32000),
min_gain_(1024),
max_gain_(15360),//8192
epid_(PID(5120, 10240, 0, 15000)),
gpid_(PID(1024, 2048, 0, 1024))
{
exp_time_ = (max_exp_time_ - min_exp_time_) / 2 + min_exp_time_;
gain_ = min_gain_;
set_target_level(target_level_);
//1024
win_coe_.data_[0][0] = 16;
win_coe_.data_[0][1] = 16;
win_coe_.data_[0][2] = 16;
win_coe_.data_[0][3] = 16;
win_coe_.data_[1][0] = 16;
win_coe_.data_[1][1] = 208;
win_coe_.data_[1][2] = 208;
win_coe_.data_[1][3] = 16;
win_coe_.data_[2][0] = 16;
win_coe_.data_[2][1] = 208;
win_coe_.data_[2][2] = 208;
win_coe_.data_[2][3] = 16;
win_coe_.data_[3][0] = 16;
win_coe_.data_[3][1] = 16;
win_coe_.data_[3][2] = 16;
win_coe_.data_[3][3] = 16;
epid_.set_target_level(target_level_);
epid_.set_ML(ML_);
epid_.set_MH(MH_);
epid_.set_min_val(min_exp_time_);
epid_.set_max_val(max_exp_time_);
epid_.set_inc_dx(256);
gpid_.set_target_level(target_level_);
gpid_.set_ML(ML_);
gpid_.set_MH(MH_);
gpid_.set_min_val(min_gain_);
gpid_.set_max_val(max_gain_);
lum_level_ = target_level_;
exp_time_ = (max_exp_time_ - min_exp_time_) / 2 + min_exp_time_;
gain_ = min_gain_;
fresh_ = 0;
}
给成员变量赋值。
这里的成员对象win_coe_,是class WinSum的一个对象。
这里使用的WinSum,是一个用来封装4x4的数据窗口的class。并没有封装方法,也可以用struct来封装。但是用class来封装,可以为以后封装操作集留下余地。
这里的成员对象epid_和gpid_,是class PID的一个对象。
这里使用的PID,后面再讲。
2)SET系列
void AutoExpGain::set_aec_kp(int value) {
epid_.set_kp(value);
}
void AutoExpGain::set_aec_ki(int value) {
epid_.set_ki(value);
}
void AutoExpGain::set_aec_kd(int value) {
epid_.set_kd(value);
}
void AutoExpGain::set_target_level(int value) {
value = (value < 0) ? 0 : ((value > 255) ? 255 : value);
target_level_ = value;
MH_ = target_level_ + level_range_;
MH_ = (MH_ > 255) ? 255 : MH_;
ML_ = target_level_ - level_range_;
ML_ = (ML_ < 0) ? 1 : ML_;
epid_.set_target_level(target_level_);
}
void AutoExpGain::set_exp_time_inc(int value) {
value = exp_time_ + value;
set_exposure_time(value);
}
void AutoExpGain::set_exp_time_dec(int value) {
value = exp_time_ - value;
set_exposure_time(value);
}
int AutoExpGain::set_exposure_time(int value)
{
int ret;
exp_time_ = limit_exp_time(value);
#if SENSOR_TRIGGER_ENABLE
sys_->xout_.set_exposure_time(exp_time_);
#else
ret = sys_->xcmos_.set_exposure_time(exp_time_, 0);
if (ret < 0) {
return -1;
}
#endif
return 0;
}
void AutoExpGain::set_gain(int value) {
gain_ = limit_gain(value);
}
void AutoExpGain::set_gain_inc(int value) {
int gain = gain_ + value;
set_gain(gain);
}
void AutoExpGain::set_gain_dec(int value) {
int gain = gain_ - value;
set_gain(gain);
}
3)limit系列
u32 AutoExpGain::limit_exp_time(int value) {
return (value < min_exp_time_) ? min_exp_time_ : ((value > max_exp_time_) ? max_exp_time_ : value);
}
u32 AutoExpGain::limit_gain(int value) {
return (value < min_gain_) ? min_gain_ : ((value > max_gain_) ? max_gain_ : value);
}
4)calc_level_state,功能级函数
int AutoExpGain::calc_level_state(u32 value) {
if (value < ML_) {
return -1;//欠曝光
} else if (value > MH_) {
return 1;//过曝光
} else {
return 0;//正常曝光
}
}
5)calc_lum_level,功能级函数
u32 AutoExpGain::calc_lum_level()
{
win_cnt_ = sys_->video_align_height_ * sys_->video_align_width_ / kw_ / kh_;
u32 sum = 0;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
u32 aver = win_sum_.data_[i][j] / win_cnt_ * win_coe_.data_[i][j] / 1024;
sum += aver;
}
}
sum /= 3;
return sum;
}
6)compute,业务级函数
int AutoExpGain::compute()
{
static u32 pre_frame_id = 0;
int ret;
u32 lum_level = 0;
int level_state = 0;
u32 diff_level = 0;
u32 cur_exp_time = 0;
u32 cur_gain = 0;
u32 exp_step = 0;
u32 gain_step = 0;
u32 new_exp_time = 0;
u32 new_gain = 0;
//检查自动曝光是否关闭
if (sys_->exposure_mode_ == 0) {
return 0;
}
//检查处理的频率
u32 diff_id = ABS_DEC(pre_frame_id, sys_->sensor_frame_count_);
if (diff_id <= 3)
return 0;
//获取当前亮度均值
lum_level = lum_level_;
//计算曝光状态
level_state = calc_level_state(lum_level);
if (level_state == 0) {
return 0;
}
diff_level = ABS_DEC(lum_level, target_level_);
cur_exp_time = exp_time_;
cur_gain = gain_;
//计算积分时间步进值
exp_step = epid_.compute(lum_level, cur_exp_time);
//计算曝光时间步进值
if (diff_level > 255) {
gain_step = 4000;
}
else if (diff_level > 244) {
gain_step = 3600;
}
else if (diff_level > 192) {
gain_step = 2800;
}
else if (diff_level > 100) {
gain_step = 2400;
}
else if (diff_level > 60) {
gain_step = 1200;
}
else if (diff_level > 40) {
gain_step = 280;
}
else if (diff_level > 20) {
gain_step = 200;
}
else if (diff_level > 10) {
gain_step = 80;
}
else {
gain_step = 40;
}
//计算最新的曝光时间和增益
if (level_state == 1)
{
if (cur_gain > (min_gain_ + 128)) //过曝,增益未达下限,保持积分时间不变,降低增益
{
new_exp_time = cur_exp_time;
new_gain = (cur_gain > (gain_step + min_gain_)) ? (cur_gain - gain_step) : min_gain_;
}
else //过曝,增益已达下限,降低积分时间
{
new_gain = cur_gain;
new_exp_time = exp_step;
}
}
else if (level_state == -1)
{
if (cur_exp_time < (max_exp_time_ - 256)) //欠曝光,积分时间未达上限,增加积分时间,增益不变
{
new_gain = cur_gain;
new_exp_time = exp_step;
}
else //欠曝光,积分时间已达上限,增加增益
{
new_exp_time = cur_exp_time;
new_gain = cur_gain + gain_step;
if (new_gain > max_gain_)
new_gain = max_gain_;
}
}
//设置曝光时间,增益,生效帧序号
set_gain(new_gain);
set_exposure_time(new_exp_time);
fresh_ = 1;
pre_frame_id = sys_->sensor_frame_count_;
return 0;
}
这里,使用了共享变量和其他函数进行同步。即fresh_。
在这里,fresh_做为flag变量来使用。在本函数中进行V操作,在其他的函数里进行P操作。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
补充,
PID类的封装。
先来看看数据的封装。
class PID
{
...
public:
int kp_;
int ki_;
int kd_;
int target_level_;
int ML_;
int MH_;
int min_val_;
int max_val_;
int res_;
int inc_dx_;
int diff0_;
int diff1_;
int diff2_;
};
这个类并不需要关联到其他的类,所以,内部全部都是特定用途的自有变量。
再来看看操作集的封装,
class PID
{
public:
PID(int kp, int ki, int kd, int init_val) : kp_(kp), ki_(ki), kd_(kd), res_(init_val), diff0_(0), diff1_(0), diff2_(0) {
}
void set_kp(int value) {
kp_ = value;
}
void set_ki(int value) {
ki_ = value;
}
void set_kd(int value) {
kd_ = value;
}
void set_target_level(int value) {
target_level_ = value;
}
void set_ML(int value) {
ML_ = value;
}
void set_MH(int value) {
MH_ = value;
}
void set_min_val(int value) {
min_val_ = value;
}
void set_max_val(int value) {
max_val_ = value;
}
void set_inc_dx(int value) {
inc_dx_ = value;
}
u32 compute(int cur_level, int cur_val);
...
};
来重点分析其中的业务级函数compute
u32 PID::compute(int cur_level, int cur_val) {
//死区
if ((cur_level >= ML_) && (cur_level <= MH_)) {
diff0_ = 0;
}
else {
diff0_ = target_level_ - cur_level;
}
//抗积分饱和
if (cur_val >= max_val_) {
if (diff0_ > 0) {
diff0_ = 0;
diff2_ = 0;
diff1_ = 0;
}
}
else if (cur_val <= min_val_) {
if (diff0_ < 0) {
diff0_ = 0;
diff2_ = 0;
diff1_ = 0;
}
}
int ep = diff0_ - diff1_;
int ei = diff0_;
int ed = diff0_ - 2 * diff1_ + diff2_;
//PID计算
int inc = (kp_ * ep + ki_ * ei + kd_ * ed) / 1024;
if (abs(inc) < inc_dx_) {
inc = 0;
}
res_ += inc;
diff2_ = diff1_;
diff1_ = diff0_;
//对输出值进行限制
res_ = (res_ > max_val_) ? max_val_ : ((res_ < min_val_) ? min_val_ : res_);
return res_;
}
整个算法分为几个主要部分,
diff值设置,
首先进行误差计算,求出diff0,
然后进行抗积分饱和,对diff0~2进行限界保护。
然后进行增量计算,求出ep,ei,ed,
然后使用系数kp_,ki_,kd_,求出增量inc。
然后在当前值上加上增量,
然后进行window shift moving,
然后对res_进行限幅保护输出。