Chrome OS Power Manager 剩余电量时间估计

Chrome OS Power Manager 剩余电量时间估计

Chrome OS系统调用内核的sysfs接口/sys/class/power_supply/BAT*/*获取到power_manager电池的状态,
之后系统通过Dbus将power_manager状态字发送给浏览器端,浏览器根据不同的lable显示在界面上。

Chrome浏览器代码中有在cros/src/chrome_root/src/ash/ash_strings.grd中定义关于剩余电量时间的lable
IDS_ASH_STATUS_TRAY_BATTERY_TIME_LEFT_SHORT

浏览器端代码

src/chrome_root/src/ash/system/power/power_status.h

// PowerStatus is a singleton that receives updates about the system's
// power status from chromeos::PowerManagerClient and makes the information
// available to interested classes within Ash.
class ASH_EXPORT PowerStatus : public chromeos::PowerManagerClient::Observer {
......
public:
  // Returns the estimated time until the battery is empty (if line power
  // is disconnected) or full (if line power is connected). These estimates
  // should only be used if IsBatteryTimeBeingCalculated() returns false.
  base::TimeDelta GetBatteryTimeToEmpty() const;
  base::TimeDelta GetBatteryTimeToFull() const;
 
 private:
  power_manager::PowerSupplyProperties proto_;
......
};

其中定义了浏览器获取电池预估时间的两个时间值。
power_status.cc

std::pair<base::string16, base::string16> PowerStatus::GetStatusStrings()
    const {
        ......
        status = l10n_util::GetStringFUTF16(
            IsBatteryCharging()
                ? IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL_SHORT
                : IDS_ASH_STATUS_TRAY_BATTERY_TIME_LEFT_SHORT,
            duration);
        ......
}

base::TimeDelta PowerStatus::GetBatteryTimeToEmpty() const {
  base::TimeDelta::FromSeconds(proto_.battery_time_to_empty_sec());
}

base::TimeDelta PowerStatus::GetBatteryTimeToFull() const {
  return base::TimeDelta::FromSeconds(proto_.battery_time_to_full_sec());
}

系统端代码

cros/src/platform2/power_manager/powerd/system目录
power_supply.ccbool PowerSupply::UpdateBatteryTimeEstimates(PowerStatus* status) {
有关于电池剩余电量的计算公式:

status->battery_time_to_empty = base::TimeDelta::FromSeconds(
    roundl(3600 * (status->battery_charge * status->nominal_voltage) /
           (-signed_current * status->battery_voltage)));

其中battery_chargebattery_voltagepower_supply.h中的定义如下,用来表示当前电池状态的:

// Current battery levels.
double battery_voltage = 0.0;  // V.
double battery_current = 0.0;  // A
double battery_charge = 0.0;  // Ah

nominal_voltage是用来计算的基准电压

// The battery voltage used in calculating time remaining.  This may or may
// not be the same as the instantaneous voltage |battery_voltage|, as voltage
// levels vary over the time the battery is charged or discharged.
double nominal_voltage = 0.0;

signed_current参数解释如下:

 // Positive if the battery is charging and negative if it's discharging.
 const double signed_current =
     status->line_power_on ? current_samples_on_line_power_->GetAverage()
                           : current_samples_on_battery_power_->GetAverage();

其中current_samples_on_line_power_current_samples_on_battery_power_如下:

// A collection of recent current readings (in amperes) used to calculate
// time-to-full and time-to-empty estimates collected while on line or
// battery power. Values are positive when the battery is charging and
// negative when it's discharging.
std::unique_ptr<RollingAverage> current_samples_on_line_power_;
std::unique_ptr<RollingAverage> current_samples_on_battery_power_;

问题分析

系统对于电池剩余时间的计算公式为:

    3600 * (status->battery_charge * status->nominal_voltage) / (-signed_current * status->battery_voltage))

其中:

  • signed_current是一个过去时间若干的电流均值,如果为正,代表使用充电线充电的电流,如果为负代表使用电池放电时的电流。
  • battery_charge是电池当前电池剩余容量,单位为安时(Ah)
  • nominal_voltage是一个电压的标称值,在该计算式中基本为常量。
  • battery_voltage是当前的电池电压值
  • 3600也就是1h = 3600s

根据测试机器,发现影响计算时间的主要因素在于这个时间平均数signed_current

在使用电池放电时,电池的实时电流跳变幅度很大,例如使用触摸板时电流会瞬间增加100~200mA,正是由于这个电流的瞬时变化,
导致signed_current这个电流均数的曲线出现尖锐的毛刺,导致取得的整个平均数会增加,进而导致电量预估时间减少。与此相反,
当不使用机器时,电池的放电电流会减小,将signed_current的电流曲线拉低,进而影响电量预估时间增加,这就导致预估时间时而
增加时而减少。

signed_current的值的计算方法如下:

const double signed_current =
      (status.battery_state ==
       PowerSupplyProperties_BatteryState_DISCHARGING)
          ? -status.battery_current
           : status.battery_current;
......
current_samples_on_battery_power_->AddSample(signed_current, now);

current_samples_on_battery_power_是一个用来计算signed_current的样本队列,它通过加入当前的瞬时电流值到样本空间,之后在
更新电流状态时加入更新时候的瞬时电流到样本空间,样本空间的大小通过一个default_prefs/max_current_samples文件定义,chrome
给出的默认值大小为5。状态更新是通过一个定时器来控制的,定时器的定时时间也是通过一个文件定义的battery_poll_interval_ms
chrome给出的定时时间为30s,即每30s更新一次power_manager状态到浏览器。也就是每30s采集一次瞬时电流到样本空间。

之后用来计算剩余电量预估时间的signed_current其实是current_samples_on_battery_power_样本空间的平均值:

bool PowerSupply::UpdateBatteryTimeEstimates(PowerStatus* status) {
......
status->line_power_on ? current_samples_on_line_power_->GetAverage()
                       : current_samples_on_battery_power_->GetAverage();
......
}

这样就导致了一个很严重的问题,每隔30s的瞬时电流并不能反映过去30s的电流使用情况,因此,如果上一个30s的时刻瞬时电流是一个
很大的值,而当前30s时刻的瞬时电流是一个很小的值,这就会导致整个signed_current样本曲线的突然跳变,直接会导致电池剩余电量
时间估算的剧烈跳变和不准确,因此,解决此问题的关键在于电流样本的加入时机以及样本空间的大小。

解决方案

根据上面的描述,current_samples_on_battery_power_这个样本空间的大小为5,并且每30s加入一次瞬时电流的样本值,这会直接影响
整个电流曲线的平滑程度,使得曲线出现剧变,因此,我对此根据大量的实验和研究,对原有算法做出以下的修改:

  • 增加样本空间的容量:

    如果增加了整体样本空间的容量,电流曲线会更多的参考以前各30s的电流值,使得曲线平滑

  • 加入current_samples_on_battery_power_样本空间的不是瞬时电流,而是过去30s内的电流取样平均

    由于瞬时电流并不能良好的反应某一时间段的电流变化情况,因此如果系统在某一时刻进行大电流的操作,则会导致
    剧烈跳变,使用等间隔采样可以很良好的解决这个问题,可以更加准确的描述30s这个时间段内的电流变化。

根据大量数据实验,最终决定的修改如下:

  • 新增一个样本空间队列period_battery_current_samples_,用来采样30s内的等间隔瞬时电流值,样本空间大小为5
  • 新增一个定时器,用来每隔30/5s进行一次瞬时电流的采样
  • 当30s到达,系统需更新状态时,在current_samples_on_battery_power_的样本空间添加的是period_battery_current_samples_的平均值,而不是瞬时电流值

经过以上修改,比较好的解决了在机器开机启动时由于瞬时电流过大导致预估时间过小,之后会剧烈增加的问题,同时也稳定了在
系统运行过程中由于电流跳变而影响的预估时间剧烈跳变的问题。

  • 注:具体修改详见gitlab提交。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值