SAC와 연속 제어
학습 목표
연속 입력의 제어 문제에서 탐색과 입력 비용을 함께 고려하고, 단순 제어기와 비교합니다.
실행 조건
- 필요 패키지: gymnasium·stable-baselines3·PyTorch.
- SB3 정책:
MlpPolicy. - Gymnasium
step: 5개 반환값. - 공개 환경 시각화:
gymnasium[classic-control]추가.
필요한 패키지가 없으면 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 , 목표 .
행동: 연속 , 실제 입력 .
Reward·Constraint: . 에서 종료, 100 step에서 시간 제한.
비교 기준: 동일 입력 제한의 P 제어.
확인할 지표: 추종 RMSE, 평균 u², 안전 종료 수. 목표별 분리 평가.
공조·열 공정 제어
온도 편차와 에너지 사용을 함께 줄이는 제어 문제의 설계 사례입니다.
Observation·State: 온도·목표·외기 조건·최근 입력. 지연이 크면 이력 포함.
행동: 제한된 연속 가열·냉각 출력.
Reward·Constraint: 온도 오차와 에너지 비용. 안전 한계는 보상과 별도 지표로도 기록.
비교 기준: 온도 조절기, PI 제어.
확인할 지표: 오차, 에너지, 한계 위반 시간. 외기 조건별 분리 평가.
이산·연속 행동 선택이 알고리즘과 맞는지 먼저 확인합니다. 자신의 도메인에서는 규칙 제어를 같은 정보·입력 제한으로 비교합니다.
규칙 제어와 SAC
시작 조건
합성 Motor, dt=0.1 s, 입력 , 학습 Reference Angular Velocity 1입니다. 목표 0.5·1·1.5에서 평가합니다.
진행 방법
- 기준 정책의 성능을 먼저 기록하세요. 제공된 Wrapper의 관측·행동 범위와 reset/step 반환값을 확인합니다.
- 2048 Transition의 짧은 학습을 실행하고 동일 조건에서 결정적으로 평가하세요.
- 기준보다 개선된 지표와 악화된 지표를 분리하고, 짧은 학습의 한계를 해석하세요.
남길 결과
- 기준·학습 정책의 평가 표. Reference Angular Velocity별 추종 그림.
- 기록 위치:
motor_evaluate의 기존 평가 루프, Step 직후. - 보존할 값:
env.t*0.1,env.omega,env.reference. - 그림: 기존
seed=100평가의 Reference별 추종 응답과 목표선. - 축: 시간 [s]–Angular Velocity [rad/s]. 기존 평가 기록만 사용합니다.
합성 모델에서 인 Action은 인 모델 입력에 대응합니다. 의 물리 단위는 지정하지 않았습니다.
"""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²:
안전 종료:
확인할 점
- 학습 Return과 원래 단위의 평가 지표를 구분했는가?
- 실제 장비 대신 합성 모델에서 먼저 검증했는가?
SAC를 공개 연속 제어 환경으로 옮기기
시작 조건
Pendulum-v1, SAC 2048 Transition입니다. Motor의 Angular Velocity Observation과 달리 cosθ·sinθ·Angular Velocity 관측을 사용합니다.
진행 방법
- 공식 환경 설명에서 관측·토크 범위·보상·시간 제한을 찾아 Motor와 대응시키세요.
- Zero torque·Random·짧게 학습한 SAC를 같은
seed=100…102에서 비교하세요. - Return과 동작을 해석하고, 실제 연구 환경으로 옮길 때 바꿔야 할 관측·보상·입력 제한을 적으세요.
남길 결과
- 환경 대응 표와 정책 평가 표. 선택적으로 Episode 한 개를 시각화합니다.
"""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 평균·표준편차:
종료 유형:
확인할 점
- Pendulum 점수를 Motor 점수와 직접 비교하지 않았는가?
- 짧은 학습을 Swing-up 성공으로 가정하지 않고 실제 결과를 확인했는가?
본인의 연구 문제로 옮기기
시작 조건
앞의 실험을 바탕으로 Queue·Motor 또는 본인의 연구 문제 하나를 선택해, 실제 적용을 위한 실험을 설계합니다.
진행 방법
- State·Observation·Action·Reward·종료·시간 제한을 표 한 장으로 정의하세요. 각 변수의 단위와 실제로 얻을 수 있는 정보를 적습니다.
- 예상 Transition 3개를 수치로 적어, 좋은 행동과 나쁜 행동의 보상이 의도에 맞는지 확인하세요.
- 규칙 기반 비교 기준 한 개, 평가 조건 한 개, 가장 우려되는 실패 사례 한 개를 정하세요. 필요한 실측 자료나 Simulator도 적습니다.
남길 결과
- MDP 설계 표, Transition 예 3개, 비교 실험 계획.
- 실제 연구 자료가 없으면 적용 사례의 합성 조건으로 설계합니다.
앞서 선택한 연구 문제와 설계 표를 이어서 사용해도 됩니다. 이번 활동에서 배운 개념으로 기존 항목과 Transition 예를 다시 검토하고, 변경한 항목이나 그대로 둔 이유를 갱신하세요.
결과 기록·자가 점검
항목:
구체적 정의·단위:
관측 가능 여부·확인 방법:
확인할 점
- 미래 정보나 평가 Label을 관측에 넣지 않았는가?
- 학습 Return만으로 연구 기여를 판단하지 않고, 해당 도메인의 지표를 정했는가?