从SOD到OOD(AutoExposureGain模块)

先来看看数据封装。

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_进行限幅保护输出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值