def test_run(): from pyctrl import Controller from pyctrl.block.clock import Clock, TimerClock from pyctrl.block import Map controller = Controller() clock = Clock() controller.add_source('clock', clock, ['clock']) # start/stop with condition controller.add_filter('condition', Map(function=lambda x: x < 1), ['clock'], ['is_running']) controller.set_source('clock', reset=True) controller.start() is_running = controller.get_signal('is_running') while is_running: is_running = controller.get_signal('is_running') controller.stop() tk = controller.get_signal('clock') assert tk > 1 and tk < 1.01 # run with condition controller.set_source('clock', reset=True) controller.run() controller.stop() tk = controller.get_signal('clock') assert tk > 1 and tk < 1.01 controller.remove_source('clock')
def _test_mip_balance(): import numpy as np from pyctrl import Controller from pyctrl.block.container import Container, Input, Output from pyctrl.block.system import System, Subtract, Differentiator, Sum, Gain from pyctrl.block.nl import ControlledCombination, Product from pyctrl.block import Fade, Printer from pyctrl.system.ss import DTSS from pyctrl.block.logic import CompareAbsWithHysterisis, SetFilter, State GRN_LED = 61 PAUSE_BTN = 62 # create mip mip = Controller() # add signals mip.add_signals('theta', 'theta_dot', 'encoder1', 'encoder2', 'pwm1', 'pwm2') # phi is the average of the encoders mip.add_signal('phi') mip.add_filter('phi', Sum(gain=0.5), ['encoder1', 'encoder2'], ['phi']) # phi dot mip.add_signal('phi_dot') mip.add_filter('phi_dot', Differentiator(), ['clock', 'phi'], ['phi_dot']) # phi dot and steer reference mip.add_signals('phi_dot_reference', 'phi_dot_reference_fade') mip.add_signals('steer_reference', 'steer_reference_fade') # add fade in filter mip.add_filter('fade', Fade(target=[0, 0.5], period=5), ['clock', 'phi_dot_reference', 'steer_reference'], ['phi_dot_reference_fade', 'steer_reference_fade']) # state-space matrices A = np.array([[0.913134, 0.0363383], [-0.0692862, 0.994003]]) B = np.array([[0.00284353, -0.000539063], [0.00162443, -0.00128745]]) C = np.array([[-383.009, 303.07]]) D = np.array([[-1.22015, 0]]) B = 2 * np.pi * (100 / 7.4) * np.hstack((-B, B[:, 1:])) D = 2 * np.pi * (100 / 7.4) * np.hstack((-D, D[:, 1:])) ssctrl = DTSS(A, B, C, D) # state-space controller mip.add_signals('pwm') mip.add_filter('controller', System(model=ssctrl), ['theta_dot', 'phi_dot', 'phi_dot_reference_fade'], ['pwm']) # enable pwm only if about small_angle mip.add_signals('small_angle', 'small_angle_pwm') mip.add_filter('small_angle_pwm', Product(), ['small_angle', 'pwm'], ['small_angle_pwm']) # steering biasing mip.add_filter( 'steer', ControlledCombination(), ['steer_reference_fade', 'small_angle_pwm', 'small_angle_pwm'], ['pwm1', 'pwm2']) # set references mip.set_signal('phi_dot_reference', 0) mip.set_signal('steer_reference', 0.5) # add supervisor actions on a timer # actions are inside a container so that they are executed all at once mip.add_timer('supervisor', Container(), ['theta'], ['small_angle', 'is_running'], period=0.5, repeat=True) mip.add_signals('timer/supervisor/theta', 'timer/supervisor/small_angle', 'timer/supervisor/is_running') mip.add_source('timer/supervisor/theta', Input(), ['theta']) mip.add_sink('timer/supervisor/small_angle', Output(), ['small_angle']) mip.add_sink('timer/supervisor/is_running', Output(), ['is_running']) # add small angle sensor mip.add_filter( 'timer/supervisor/is_angle_small', CompareAbsWithHysterisis(threshold=0.11, hysterisis=0.09, offset=-0.07, state=(State.LOW, )), ['theta'], ['small_angle']) # reset controller and fade mip.add_sink( 'timer/supervisor/reset_controller', SetFilter(label=['/controller', '/fade'], on_rise={'reset': True}), ['small_angle']) # add pause button on a timer mip.add_source('timer/supervisor/pause_button', ('pyctrl.block', 'Constant'), ['is_running'], kwargs={'value': 0}, enable=True) from pyctrl.flask import JSONEncoder, JSONDecoder json1 = JSONEncoder(sort_keys=True, indent=4).encode(mip) obj = JSONDecoder().decode(json1) json2 = JSONEncoder(sort_keys=True, indent=4).encode(obj) assert json1 == json2 print('json = \n{}'.format(json1))
def test1(): if not test_ode: return from pyctrl import Controller controller = Controller() Ts = 0.01 clock = TimerClock(period=Ts) controller.add_source('clock', clock, ['clock']) a = -1 b = 1 def f(t, x, u, a, b): return a * x + b * u t0 = 0 uk = 1 x0 = np.array([0]) sys = ODE((1, 1, 1), f, x0=x0, t0=t0, pars=(a, b)) controller.add_signals('input', 'output') controller.add_filter('condition', Map(function=lambda x: x < 1), ['clock'], ['is_running']) controller.add_filter('ode', TimeVaryingSystem(model=sys), ['clock', 'input'], ['output']) controller.add_sink('logger', Logger(), ['clock', 'output']) print(controller.info('all')) controller.set_filter('ode', reset=True) controller.set_source('clock', reset=True) controller.set_sink('logger', reset=True) controller.set_signal('input', uk) controller.run() xk = sys.state log = controller.get_sink('logger', 'log') t0 = log['clock'][0, 0] tk = log['clock'][-1, 0] yk = log['output'][-1, 0] print('t0 = {}'.format(t0)) print('tk = {}'.format(tk)) print('yk = {}'.format(yk)) yyk = uk * (1 - math.exp(a * (tk - t0))) + x0[0] * math.exp(a * (tk - t0)) print(log) print(t0, x0, tk, xk, yk, yyk) assert np.abs(yk - yyk) < 1e-2 uk = 0 x0 = sys.state controller.add_filter('condition', Map(function=lambda x: x < 2), ['clock'], ['is_running']) print(controller.info('all')) print('clock = {}'.format(controller.get_signal('clock'))) #controller.set_source('clock', reset = True) controller.set_sink('logger', reset=True) controller.set_signal('input', uk) controller.run() xk = sys.state print('clock = {}'.format(controller.get_signal('clock'))) log = controller.get_sink('logger', 'log') print('log = {}'.format(log)) t0 = log['clock'][0, 0] tk = log['clock'][-1, 0] yk = log['output'][-1, 0] print('t0 = {}, x0 = {}, tk = {}, xk = {}, yk = {}'.format( t0, x0, tk, xk, yk)) yyk = uk * (1 - math.exp(a * (tk - t0))) + x0 * math.exp(a * (tk - t0)) print(log) print(t0, x0, tk, xk, yk, yyk) assert np.abs(yk - np.array([yyk])) < 1e-2 uk = -1 x0 = sys.state controller.add_filter('condition', Map(function=lambda x: x < 3), ['clock'], ['is_running']) #controller.set_source('clock', reset = True) controller.set_sink('logger', reset=True) controller.set_signal('input', uk) controller.run() xk = sys.state log = controller.get_sink('logger', 'log') t0 = log['clock'][0, 0] tk = log['clock'][-1, 0] yk = log['output'][-1, 0] yyk = uk * (1 - math.exp(a * (tk - t0))) + x0 * math.exp(a * (tk - t0)) print(t0, x0, tk, xk, yk, yyk) assert np.abs(yk - np.array([yyk])) < 1e-2 clock.set_enabled(False)
def test2(): if not test_ode: return m1 = 30 / 1000 l1 = 7.6 / 100 r1 = (5 - (10 - 7.6) / 2) / 100 w1 = 10 / 100 d1 = 2.4 / 100 J1 = m1 * (w1**2 + d1**2) / 12 m2 = 44 / 1000 w2 = 25.4 / 100 d2 = 2.4 / 100 J2 = m2 * (w2**2 + d2**2) / 12 r2 = (25.4 / 2 - 1.25) / 100 Jm = 0.004106 km = 0.006039 bm = 0.091503 g = 9.8 bPhi = 0 bTheta = 0 def MK(x, u): theta, phi, thetaDot, phiDot = x return ( np.array([[J2 + m2 * r2**2, m2 * r2 * l1 * math.cos(theta - phi)], [ m2 * r2 * l1 * math.cos(theta - phi), J1 + Jm + m1 * r1**2 + m2 * l1**2 ]]), np.array([ bTheta * thetaDot + m2 * r2 * (g * math.sin(theta) + l1 * math.sin(theta - phi) * phiDot**2), g * (m1 * r1 + m2 * l1) * math.sin(phi) - m2 * r2 * l1 * math.sin(theta - phi) * thetaDot**2 + (bm + bPhi) * phiDot - km * u[0] ])) def ff(t, x, u): M, K = MK(x, u) return np.hstack((x[2:4], -la.solve(M, K))) theta0, phi0 = 0 + math.pi / 6, 0 t0, x0, u0 = 0, np.array([theta0, phi0, 0, 0]), [0] M, K = MK(x0, u0) print(M) print(K) print(ff(t0, x0, u0)) sys = ODE(shape=(1, 4, 4), t0=t0, x0=x0, f=ff) tk = 5 uk = [0] yk = sys.update(tk, uk) print('1. [{:3.2f}, {:3.2f}] = {}'.format(t0, tk, yk)) from pyctrl import Controller controller = Controller() Ts = 0.01 controller.add_source('clock', Clock(), ['clock']) condition = Map(function=lambda t: t < T) controller.add_filter('condition', condition, ['clock'], ['is_running']) controller.add_signals('tau', 'x') controller.add_filter( 'ode', TimeVaryingSystem(model=ODE(shape=(1, 4, 4), t0=t0, x0=x0, f=ff)), ['clock', 'tau'], ['x']) controller.add_sink('logger', Logger(), ['clock', 'x']) controller.set_source('clock', reset=True) T = 5 + Ts controller.run() log = controller.get_sink('logger', 'log') t0 = log['clock'][0, 0] tk = log['clock'][0, -1] yk = log['x'][0, -1] # yk = log[-1,1:] print('2. [{:3.2f}, {:3.2f}] = {}'.format(t0, tk, yk)) import control fc = 7 wc = 2 * math.pi * fc lpf = control.tf(wc, [1, wc]) ctr = -2 * 100 def gg(t, x, u): return [x[0]] Ts = 0.01 Ac, Bc, Cc, Dc = map(np.array, control.ssdata(control.ss(lpf * ctr))) nc = Ac.shape[0] def F(t, x, ref): x, xc = x[0:4], x[4:4 + nc] y = ref - gg(t, x, [0]) u = max(-100, min(100, Cc.dot(xc) + Dc.dot(y))) #print(ff(t,x,u)) return np.hstack((ff(t, x, u), Ac.dot(xc) + Bc.dot(y))) eta = 0 kappa = 0 ref = np.array([eta * math.pi]) theta0 = -20 * math.pi / 180 xx0 = [kappa * math.pi - theta0, eta * math.pi, 0, 0] xc0 = np.zeros((nc, )) x0 = np.hstack((xx0, xc0)) t0 = 0 print('F = {}'.format(F(t0, x0, ref))) sys = ODE(shape=(1, 4, 4), t0=t0, x0=x0, f=F) tk = 1 uk = np.array([0]) yk = sys.update(tk, uk) print('1. [{:3.2f}, {:3.2f}] = {}'.format(t0, tk, yk)) controller.reset() Ts = 0.01 controller.add_source('clock', Clock(), ['clock']) condition = Map(function=lambda t: t < T) controller.add_filter('condition', condition, ['clock'], ['is_running']) controller.add_signals('ref', 'x') controller.add_filter( 'ode', TimeVaryingSystem(model=ODE(shape=(1, 4, 4), t0=t0, x0=x0, f=F)), ['clock', 'ref'], ['x']) controller.add_sink('logger', Logger(), ['clock', 'x']) #print(controller.info('all')) controller.set_source('clock', reset=True) controller.set_signal('ref', ref) T = 1 + Ts controller.run() log = controller.get_sink('logger', 'log') t0 = log['clock'][0, 0] tk = log['clock'][0, -1] yk = log['x'][0, -1] #yk = log[-1,1:] print('2. [{:3.2f}, {:3.2f}] = {}'.format(t0, tk, yk))