diff --git a/main.go b/main.go index a58e5c8..f2d3ff3 100644 --- a/main.go +++ b/main.go @@ -15,7 +15,8 @@ import ( "sync" "syscall" "time" - + "amuz.es/src/infra/cpu_ctrl/pid" + "github.com/coreos/go-systemd/daemon" "github.com/shirou/gopsutil/cpu" ) @@ -92,10 +93,10 @@ func ReadTempeture(path string, senseChan chan<- float64, errorChan chan<- error } } -func CpuTempetureMonitoring(info *Processor, notifier chan<- TempetureChange, errorChan chan<- error, ctx context.Context, waiter *sync.WaitGroup) { +func CpuTempetureMonitoring(info *Processor,sampleDuration time.Duration, notifier chan<- TempetureChange, errorChan chan<- error, ctx context.Context, waiter *sync.WaitGroup) { defer waiter.Done() tempeturePathGlob := path.Join(info.TempeturePath, "temp?_input") - ticker := time.Tick(3 * time.Second) + ticker := time.Tick(sampleDuration) for { select { case <-ticker: @@ -129,31 +130,47 @@ func CpuTempetureMonitoring(info *Processor, notifier chan<- TempetureChange, er } func CpuTempetureScraper(processorCount int, notifier <-chan TempetureChange, errorChan chan<- error, ctx context.Context, waiter *sync.WaitGroup) { defer waiter.Done() - pastFan := make([]int, processorCount) + var ( + P,I,D=1.5, 0.4, 2.0 + SetPoint=40.0 + SampleTime=time.Second + WindupGuard=96.0 + maxNoob,minNoob=0x64,0x4 + ) + noobs := make([]int,processorCount) + controllers := make([]pid.Controller, 0,processorCount) + for i:=0 ;i 0x50 { - fan = 0x0 + controller:=controllers[change.Id] + resp:=controller.Update(change) + + adj_noob := -resp + minNoob + if adj_noob > maxNoob{ + adj_noob=maxNoob } - if pastFan[change.Id] != fan { - pastFan[change.Id] = fan - } else { + if noobs[change.Id]== adj_noob{ continue } + noobs[change.Id]=adj_noob + fmt.Printf("cpu %d fan 0x%x\n", change.Id, adj_noob) - fmt.Printf("cpu %d fan 0x%x\n", change.Id, fan) args := make([]string, 0) args = append(args, "raw", "0x3a", "0x01", ) - for _, item := range pastFan { + for _, item := range noobs { args = append(args, fmt.Sprintf("0x%x", item)) } args = append(args, @@ -172,12 +189,13 @@ func CpuTempetureScraper(processorCount int, notifier <-chan TempetureChange, er func main() { var ( - processorCount = 0 //getProcessorCount() + processorCount = getProcessorCount() ctx, canceled = context.WithCancel(context.Background()) waiter = &sync.WaitGroup{} exitSignal = make(chan os.Signal, 1) errorChan = make(chan error, 1) tempetureChange = make(chan TempetureChange) + sampleDuration = time.Second ) if processorCount == 0 { @@ -188,7 +206,7 @@ func main() { errorChan <- err } else { waiter.Add(1) - go CpuTempetureMonitoring(info, tempetureChange, errorChan, ctx, waiter) + go CpuTempetureMonitoring(info,sampleDuration, tempetureChange, errorChan, ctx, waiter) } } waiter.Add(1) diff --git a/pid/pid.go b/pid/pid.go new file mode 100644 index 0000000..185e070 --- /dev/null +++ b/pid/pid.go @@ -0,0 +1,126 @@ +package pid + +import ( + "time" +) + +type controller struct { + Kp float64 + Ki float64 + Kd float64 + lastTime time.Time + SetPoint float64 + SampleTime time.Duration + WindupGuard float64 + Output float64 + + pTerm float64 + iTerm float64 + dTerm float64 + lastError float64 +} + +/* +PID Controller +*/ +type Controller interface { + GetKp() float64 + GetKi() float64 + GetKd() float64 + GetSetPoint() float64 + GetSampleTime() time.Duration + GetWindupGuard() float64 + GetOutput() float64 + + SetKp(float64) + SetKi(float64) + SetKd(float64) + SetSetPoint(float64) + SetSampleTime(time.Duration) + SetWindupGuard(float64) + SetOutput(float64) + + Clear() + Update(float64) float64 +} + +/* +create PID Controller +*/ +func New(proportionalGain float64, integralGain float64, derivativeGain float64) Controller { + return &controller{ + Kp: proportionalGain, + Ki: integralGain, + Kd: derivativeGain, + WindupGuard: 20.0, + lastTime: time.Now(), + } +} + +/* +Clears PID computations and coefficients +*/ +func (c *controller) Clear() { + c.SetPoint = 0.0 + c.pTerm = 0.0 + c.iTerm = 0.0 + c.dTerm = 0.0 + c.lastError = 0.0 + c.WindupGuard = 20.0 + c.Output = 0.0 +} + +/* +Calculates PID value for given reference feedback +.. math:: + u(t) = K_p e(t) + K_i \int_{0}^{t} e(t)dt + K_d {de}/{dt} + +.. figure:: images/pid_1.png + :align: center + + Test PID with Kp=1.2, Ki=1, Kd=0.001 (test_pid.py) +*/ +func (c *controller) Update(feedbackValue float64) float64 { + var ( + errorValue = c.SetPoint - feedbackValue + currentTime = time.Now() + deltaTime, deltaError = currentTime.Sub(c.lastTime), errorValue - c.lastError + ) + + if deltaTime >= c.SampleTime { + c.pTerm = c.Kp * errorValue + c.iTerm += errorValue * deltaTime.Seconds() + + if c.iTerm < -c.WindupGuard { + c.iTerm = -c.WindupGuard + } else if c.iTerm > c.WindupGuard { + c.iTerm = c.WindupGuard + } + + c.dTerm = 0.0 + if deltaTime.Seconds() > 0.0 { + c.dTerm = deltaError / deltaTime.Seconds() + } + c.lastTime = currentTime + c.lastError = errorValue + + c.Output = c.pTerm + (c.Ki * c.iTerm) + (c.Kd * c.dTerm) + } + return c.Output +} + +func (c *controller) GetKp() float64 { return c.Kp } +func (c *controller) GetKi() float64 { return c.Ki } +func (c *controller) GetKd() float64 { return c.Kd } +func (c *controller) GetSetPoint() float64 { return c.SetPoint } +func (c *controller) GetSampleTime() time.Duration { return c.SampleTime } +func (c *controller) GetWindupGuard() float64 { return c.WindupGuard } +func (c *controller) GetOutput() float64 { return c.Output } + +func (c *controller) SetKp(v float64) { c.Kp = v } +func (c *controller) SetKi(v float64) { c.Ki = v } +func (c *controller) SetKd(v float64) { c.Kd = v } +func (c *controller) SetSetPoint(v float64) { c.SetPoint = v } +func (c *controller) SetSampleTime(v time.Duration) { c.SampleTime = v } +func (c *controller) SetWindupGuard(v float64) { c.WindupGuard = v } +func (c *controller) SetOutput(v float64) { c.Output = v } diff --git a/test/test.go b/test/test.go new file mode 100644 index 0000000..63880a0 --- /dev/null +++ b/test/test.go @@ -0,0 +1,50 @@ +package main + +import ( + "fmt" + "time" + + "amuz.es/src/infra/cpu_ctrl/pid" +) + +// 실제 CPU온도환경 모델링 +func test_pid(P float64, I float64, D float64, L int) { + pid := pid.New(P, I, D) + pid.SetSetPoint(40.0) + pid.SetSampleTime(time.Second) + pid.SetWindupGuard(96.0) + + END := L + + feedback := 60.0 + + minTemp := 28.0 + + for i := 0; i < END; i++ { + output := pid.Update((feedback * 1000.0) / 1000.0) + adj := output + if output > 0 { + adj = 0.0 + } else if output < -96.0 { + adj = -96.0 + } + + adj_noob := int(-adj + 4) + + adj /= 10.0 + adj += 1.1 + fmt.Printf("feedback: %0.17f output: %0.17f adj: %0.17f noob: 0x%x\n", feedback, output, adj, adj_noob) + + feedback += adj + + if feedback < minTemp { + feedback = minTemp + } + + time.Sleep(time.Second) + + } +} +func main() { + test_pid(1.5, 0.4, 2.0, 500) +}