강의자료 · 전기공학을 위한 강화학습

SAC와 연속 제어

학습 목표

연속 입력의 제어 문제에서 탐색과 입력 비용을 함께 고려하고, 단순 제어기와 비교합니다.

실행 조건

필요한 패키지가 없으면 Colab·Jupyter 코드 셀에서 %pip install control==0.10.2 gymnasium==1.3.0 stable-baselines3==2.9.2를 실행하세요.

적용 사례

구동계의 추종 성능과 입력 비용

각 실험에서 일정한 Reference Angular Velocity를 추종하는 합성 1차 Motor 모델입니다. 학습은 목표 1, 평가는 0.5·1·1.5에서 구분합니다.

Observation·State: Angular Velocity ω/5\omega/5, 목표 r/2r/2.

행동: 연속 a∈[−1,1]a\in[-1,1], 실제 입력 u=2au=2a.

Reward·Constraint: −((r−ω)2+0.05u2)-((r-\omega)^2+0.05u^2). |ω|>5|\omega|>5에서 종료, 100 step에서 시간 제한.

비교 기준: 동일 입력 제한의 P 제어.

확인할 지표: 추종 RMSE, 평균 u², 안전 종료 수. 목표별 분리 평가.

공조·열 공정 제어

온도 편차와 에너지 사용을 함께 줄이는 제어 문제의 설계 사례입니다.

Observation·State: 온도·목표·외기 조건·최근 입력. 지연이 크면 이력 포함.

행동: 제한된 연속 가열·냉각 출력.

Reward·Constraint: 온도 오차와 에너지 비용. 안전 한계는 보상과 별도 지표로도 기록.

비교 기준: 온도 조절기, PI 제어.

확인할 지표: 오차, 에너지, 한계 위반 시간. 외기 조건별 분리 평가.

이산·연속 행동 선택이 알고리즘과 맞는지 먼저 확인합니다. 자신의 도메인에서는 규칙 제어를 같은 정보·입력 제한으로 비교합니다.

규칙 제어와 SAC

시작 조건

합성 Motor, dt=0.1 s, 입력 u∈[−2,2]u\in[-2,2], 학습 Reference Angular Velocity 1입니다. 목표 0.5·1·1.5에서 평가합니다.

진행 방법

  1. 기준 정책의 성능을 먼저 기록하세요. 제공된 Wrapper의 관측·행동 범위와 reset/step 반환값을 확인합니다.
  2. 2048 Transition의 짧은 학습을 실행하고 동일 조건에서 결정적으로 평가하세요.
  3. 기준보다 개선된 지표와 악화된 지표를 분리하고, 짧은 학습의 한계를 해석하세요.

남길 결과

합성 모델에서 a∈[−1,1]a\in[-1,1]인 Action은 u=2a∈[−2,2]u=2a\in[-2,2]인 모델 입력에 대응합니다. uu의 물리 단위는 지정하지 않았습니다.

"""Synthetic motor; not a hardware interface. dt=0.1 s, scalar speed and bounded input."""
import numpy as np
class Motor:
    def reset(self, seed=0, reference=1., noise=.01):
        self.rng = np.random.default_rng(seed)
        self.omega, self.t = 0., 0
        self.reference, self.noise = reference, noise
        return np.array([self.omega / 5, reference / 2], dtype=np.float32)
    def step(self, action):
        u = 2 * float(np.clip(np.asarray(action).item(), -1, 1))
        self.omega += .1 * (-.4 * self.omega + .8 * u + self.rng.uniform(-self.noise, self.noise))
        self.t += 1; error = self.reference - self.omega
        reward = -(error**2 + .05 * u**2)
        return np.array([self.omega / 5, self.reference / 2], dtype=np.float32), reward, abs(self.omega) > 5, self.t >= 100, {'error': error, 'u': u}

def motor_evaluate(policy, references=(.5, 1., 1.5)):
    rows=[]
    for reference in references:
        env=Motor(); obs=env.reset(100, reference)
        errors=[]; inputs=[]; total=0.
        while True:
            obs,r,terminated,truncated,info=env.step(policy(obs))
            errors.append(info['error']);inputs.append(info['u']);total+=r
            if terminated or truncated:break
        rows.append([reference, np.sqrt(np.mean(np.square(errors))),np.mean(np.square(inputs)),int(terminated),total])
    return np.array(rows)

def proportional(obs):return np.clip((2 * obs[1] - 5 * obs[0]), -1, 1)
print('reference / RMSE / mean u² / safety termination / Return')
print(motor_evaluate(proportional))

# Run the Queue or Motor definition first. Install gymnasium and stable-baselines3 if absent.
import gymnasium as gym
from gymnasium import spaces
from stable_baselines3 import A2C, PPO, SAC
class QueueGym(gym.Env):
    def __init__(self):
        self.model=Queue();self.action_space=spaces.Discrete(3)
        self.observation_space=spaces.Box(0.,1.,(1,),dtype=np.float32)
    def reset(self,seed=None,options=None):
        super().reset(seed=seed)
        return np.array([self.model.reset(seed)/20],dtype=np.float32),{}
    def step(self,a):
        q,r,te,tr,info=self.model.step(a)
        return np.array([q/20],dtype=np.float32),r/20,te,tr,info
class MotorGym(gym.Env):
    def __init__(self):
        self.model=Motor();self.action_space=spaces.Box(-1.,1.,(1,),dtype=np.float32)
        self.observation_space=spaces.Box(-np.inf,np.inf,(2,),dtype=np.float32)
    def reset(self,seed=None,options=None):
        super().reset(seed=seed)
        return self.model.reset(seed),{}
    def step(self,a):return self.model.step(a)

def fit_sb3(algorithm='PPO',seed=0,steps=2048):
    cls={'A2C':A2C,'PPO':PPO,'SAC':SAC}[algorithm]
    env=MotorGym() if algorithm=='SAC' else QueueGym()
    options={'policy_kwargs':dict(net_arch=[32,32]),'seed':seed,'verbose':0,'device':'cpu'}
    if algorithm=='PPO':options.update(n_steps=128,batch_size=64,n_epochs=3)
    if algorithm=='A2C':options.update(n_steps=32)
    if algorithm=='SAC':options.update(learning_starts=100,buffer_size=5000,batch_size=64)
    model=cls('MlpPolicy',env,**options);model.learn(total_timesteps=steps)
    return model

model=fit_sb3('SAC')
print(motor_evaluate(lambda obs:model.predict(obs,deterministic=True)[0]))

결과 기록·자가 점검

정책:

목표 속도:

평가 Return:

추종 RMSE:

평균 u²:

안전 종료:

확인할 점

SAC를 공개 연속 제어 환경으로 옮기기

시작 조건

Pendulum-v1, SAC 2048 Transition입니다. Motor의 Angular Velocity Observation과 달리 cosθ·sinθ·Angular Velocity 관측을 사용합니다.

진행 방법

  1. 공식 환경 설명에서 관측·토크 범위·보상·시간 제한을 찾아 Motor와 대응시키세요.
  2. Zero torque·Random·짧게 학습한 SAC를 같은 seed=100…102에서 비교하세요.
  3. Return과 동작을 해석하고, 실제 연구 환경으로 옮길 때 바꿔야 할 관측·보상·입력 제한을 적으세요.

남길 결과

"""Small public-environment benchmark; evaluation traces are ordinary numeric arrays."""
import json
from importlib.metadata import version
from pathlib import Path
import numpy as np
import gymnasium as gym

def collect(env_id='CartPole-v1', policy=None, seeds=range(100,103)):
    env=gym.make(env_id); transitions=[]; summary=[]
    for seed in seeds:
        obs,info=env.reset(seed=seed);env.action_space.seed(seed);total=0.;length=0
        while True:
            action=env.action_space.sample() if policy is None else policy(obs)
            nxt,reward,terminated,truncated,info=env.step(action)
            transitions.append((obs.copy(),np.asarray(action).copy(),reward,nxt.copy(),terminated,truncated,seed))
            obs=nxt;total+=reward;length+=1
            if terminated or truncated:break
        summary.append([seed,total,length,int(terminated),int(truncated)])
    env.close()
    columns=list(zip(*transitions))
    data={k:np.asarray(v) for k,v in zip(['observations','actions','rewards','next_observations','terminated','truncated','episode_seed'],columns)}
    return np.asarray(summary),data

def cartpole_rule(obs):return int(obs[2]+.25*obs[3]>0)
def zero_torque(obs):return np.zeros(1,dtype=np.float32)

def save_trace(path, env_id, data, policy_name, seeds):
    # Contains numeric arrays and JSON text only; never requires pickle.
    metadata=dict(env_id=env_id,policy=policy_name,seeds=list(seeds),gymnasium=version('gymnasium'))
    np.savez_compressed(path,**data,metadata_json=np.array(json.dumps(metadata)))
    with np.load(path,allow_pickle=False) as saved:
        assert len(saved['observations'])==len(saved['rewards'])
        print(json.loads(str(saved['metadata_json'])))

def public_model(algorithm='DQN',env_id='CartPole-v1',seed=7,steps=2048,**changes):
    from stable_baselines3 import DQN,PPO,SAC
    options=dict(policy_kwargs=dict(net_arch=[32,32]),seed=seed,verbose=0,device='cpu')
    if algorithm=='DQN':options.update(buffer_size=5000,learning_starts=100,target_update_interval=100,batch_size=32)
    if algorithm=='PPO':options.update(n_steps=128,batch_size=64,n_epochs=3)
    if algorithm=='SAC':options.update(buffer_size=5000,learning_starts=100,batch_size=64)
    options.update(changes)
    env=gym.make(env_id)
    model={'DQN':DQN,'PPO':PPO,'SAC':SAC}[algorithm]('MlpPolicy',env,**options)
    model.learn(total_timesteps=steps)
    env.close()
    return model

print('Gymnasium',version('gymnasium'))
env=gym.make('CartPole-v1')
print('Observation:',env.observation_space,'Action:',env.action_space,'Limit:',env.spec.max_episode_steps)
env.close()
# For visual inspection, create a separate env with render_mode='rgb_array'.
# Rendering requires gymnasium[classic-control]; do not render every training step.

model=public_model('SAC','Pendulum-v1')
for name,policy in [('zero',zero_torque),('random',None),('sac',lambda o:model.predict(o,deterministic=True)[0])]:
    rows,data=collect('Pendulum-v1',policy=policy);print(name,rows)
# TODO: Pendulum의 관측·보상과 Motor의 대응 표를 완성하세요.

결과 기록·자가 점검

정책:

환경:

학습량:

Return 평균·표준편차:

종료 유형:

확인할 점

본인의 연구 문제로 옮기기

시작 조건

앞의 실험을 바탕으로 Queue·Motor 또는 본인의 연구 문제 하나를 선택해, 실제 적용을 위한 실험을 설계합니다.

진행 방법

  1. State·Observation·Action·Reward·종료·시간 제한을 표 한 장으로 정의하세요. 각 변수의 단위와 실제로 얻을 수 있는 정보를 적습니다.
  2. 예상 Transition 3개를 수치로 적어, 좋은 행동과 나쁜 행동의 보상이 의도에 맞는지 확인하세요.
  3. 규칙 기반 비교 기준 한 개, 평가 조건 한 개, 가장 우려되는 실패 사례 한 개를 정하세요. 필요한 실측 자료나 Simulator도 적습니다.

남길 결과

앞서 선택한 연구 문제와 설계 표를 이어서 사용해도 됩니다. 이번 활동에서 배운 개념으로 기존 항목과 Transition 예를 다시 검토하고, 변경한 항목이나 그대로 둔 이유를 갱신하세요.

결과 기록·자가 점검

항목:

구체적 정의·단위:

관측 가능 여부·확인 방법:

확인할 점

공식 예제·데이터