a
    lɀj{x                     @   s   d Z ddlZddlZddlmZ ddlZddlZi Z	dd Z
g dZdd Ze
d	G d
d dZe
dG dd dZG dd dZe
dG dd dZe
dG dd dZG dd dZdS )ab  Johnny 6's firmware: every baked skill as a COMMAND.

The operator's architecture, stated twice and then demanded: "the manual
joystick should send the robot commands to act on our automated learnt
steps" and "are we not just using that same baked firmware in the free
roam?" Until this file, no - free roam was a hand transcription of the
laws that kept missing organs (the spin's CoM hold, recover's foot
shift, the learned leg speed), and every omission was a fall the exams
had already priced away. That is the same disease controller.py was
built to kill, one layer up.

Structure:
  - Each skill is a LAW CLASS holding its baked gains and one tick()
    of pure control logic, registered under a command name. The law
    tick is EXTRACTED from the stage's exam episode and the episode now
    calls it - so the exam, the demo and free roam execute the same
    bytes, and parity is proved by re-scoring every bake (recorded in
    the commit that introduced this file).
  - Firmware is the dispatcher: it loads the bakes, owns the shared
    base layers (bench-interpolated wheel gains, the lean trim, and
    recover's reflex), maps operator input to commands, and runs the
    handover glue between laws - the one part that is firmware-only,
    which is why it lives here where it can be tested headless, not in
    a web server.

Growing the firmware = write one law class with @command, give its exam
episode the same tick, and the remote gets the command for free. Laws
take plain numbers in and give actuator targets out - no MuJoCo, no
files - so this module is the thing that gets transcribed to the real
robot's MCU.
    N)Pathc                    s    fdd}|S )Nc                    s    | _ | t < | S )N)nameCOMMANDS)clsr    ,/home/ghrups/robot-bench/trainer/firmware.pyreg-   s    zcommand.<locals>.regr   )r   r	   r   r   r   command,   s    r
   ))balance     |@)balance_midg      y@)balance_crouch     @u@c                 C   s   | st dt|}|| d d kr0| d d S || d d krL| d d S t| | dd D ]V\\}}\}}||  kr|kr^n q^|| td||  }d| | ||    S q^| d d S )a  Wheel gains at a height, interpolated between the balance bakes.

    Piecewise LINEAR through the baked points, clamped flat at the ends -
    a quadratic through three points can overshoot between them, and an
    overshot clamp or station is a law nobody examined.zno balance bench is bakedr      Ng&.>      ?)RuntimeErrorfloatzipmax)Zpts	stance_mmhZh0Zg0vZh1Zg1vfr   r   r   interp_gains;   s    "r   spinc                   @   s&   e Zd ZdZdZdd Zd	ddZdS )
SpinLawa  The baked spin skill - on-the-spot rotation at any gate height.

    Verbatim from spin_episode (SCORE_VERSION 93, baked 5.79): rate
    ramp (a stepped rate kicks the elliptical topple), yaw-rate servo
    with its clamp, CoM hold walking the feet under the measured mass,
    the bank answering roll with direction-aware feedforward, cent trim
    against the omega-squared pitch shift, and the skater rise. Serves
    both command shapes: a held rate (the stick / throttle) and a
    turn-to-heading (indexed quarters, mission verbs).r   c           
      C   s   t |dgd  d d \| _}}}}	| _| _| _| _| _| _t|| _	t
dtdt|| _t|| _t
dtdt|	| _dd |D \| _| _| _| _t|| _tt| j| _t| j| _|| _|| _d| _d| _d S )	N                 ?{Gz?g      @皙?c                 S   s   g | ]}t |qS r   r   .0vr   r   r   
<listcomp>m   s   z$SpinLaw.__init__.<locals>.<listcomp>)list	leg_speedcom_holdbank_kbank_dcent_kbank_ffskaterabsk_yawminr   t_maxk_head	spin_ratekpkdstationclampr   r   mathradiansClean_trim_degtrimleg_ratebrxdtfdxwz_ramp)
selfgainsr   wheel_gainsbracer@   r0   r2   r3   r4   r   r   r   __init__e   s$    


zSpinLaw.__init__Nr   c
                 C   s$  |dur8d}
t t|| jd| j  | _t| j|}nFt|	tdk oTt|dk }
|
r^dn| j}tt	
| j|	 | |}|  j| jd | j| | j  7  _tt	
| j| | j|  | j|  dd	}|t d
t|d 9 }t d| jdt| j t|  }tt	
| jdd}ttdt d|d | |\}}ttdt d|d | |\}}t| j|d| j| j|d| j }t| j| j|||| jtt	
| j| | dd }tt	
| j  ||  | j! | j!}tt	
|d
t|  d
t| }t	"||||g| j#|||
fS )z=One control tick. Returns (leg_want, leg_rate, u, t, landed).NF      @      @皙?333333?{Gz?g皙?r   r   g      Y@gQgQ?r!    rh?     @@r         п      ?)$r1   r/   rB   r@   r9   copysignr:   r4   r   npclipr3   rA   r)   r*   r+   r-   r   r.   r;   stance_to_legr   lean_referencer7   r8   r?   updatewheel_commandr5   r6   r=   r,   r0   r2   arrayr>   )rC   leanlratespeedgzrollrrratecom_offhold_wz	remainingZlandedZwz_cmdZwz_capdhZsk_mmZdxchlklhrkrrefutr   r   r   tickw   sR    
$

$zSpinLaw.tick)Nr   __name__
__module____qualname____doc__STAGErG   rk   r   r   r   r   r   W   s
   
 r   drivec                   @   s$   e Zd ZdZdZdd Zdd ZdS )DriveLawa  The baked cruise skill - hold a commanded road speed.

    Verbatim from cruise_episode (baked 7.09): the speed loop's own
    filter before the lean target (raw speed feedback is the drive
    limit cycle), the acceleration budget on the command, odometry of
    the SPEED ERROR so asking for speed does not wind the station term
    against you, and the shove brace fed its own throttle so it cannot
    brace against the operator.cruisec                 C   sh   t |ddg d d \| _| _| _| _}}tt|| _t	dt|| _
|| _|| _d| _d| _d S )N皙?333333?   rK   r   )r'   r5   r6   r7   r8   r;   SpeedFilterr/   filtr   accelr?   r@   v_cmdoerr)rC   rD   rF   r@   v_taurz   r   r   r   rG      s    zDriveLaw.__init__c              	   C   s   |  j tt|| j  | j | j | j| j 7  _ | j|| j}|  j|| j  | j 7  _t	
| j|| j  | j| j| j|| j | j }t	| j| j|||}|| j fS )z%One control tick. Returns (u, v_cmd).)r{   r   rS   rT   rz   r@   ry   rW   r|   r;   rV   r7   r8   r?   rX   r5   r6   )rC   want_vrZ   r[   r\   r%   rh   ri   r   r   r   rk      s    

zDriveLaw.tickNrl   r   r   r   r   rs      s   	
rs   c                   @   s    e Zd ZdZdd Zdd ZdS )GovernedDrivea  INTERIM drive - bench wheels + cruise's governance, until
    cruise is relearned on live legs.

    The baked cruise law was learned on its exam's wheels-only body:
    the legs there are rigid. On the real robot's live position-servo
    legs it runs away at EVERY speed (measured: pitch grows to the
    law's own clamp, wheels saturate, 2.4 m/s faceplant from a 0.4
    command). Until the cruise exam runs with live legs (planned with
    the rock meter, SCORE_VERSION 94) the firmware drives on the
    balance bench's wheel law at the stand - with trim - governed by
    cruise's two learned slots: the speed filter and the acceleration
    budget. This composition measured 1.0 m/s with steering, released
    clean, before the refactor.c                 C   s   t |pg g d d d }dd |D \| _| _| _| _t|| _tdt|d | _	tdt|d | _
|| _|| _d	| _d	| _d S )
N)g      (@g       @r   rL   ru   rv   rw   c                 S   s   g | ]}t |qS r   r"   r#   r   r   r   r&      s   z*GovernedDrive.__init__.<locals>.<listcomp>gQ?   rK      r   )r'   r5   r6   r7   r8   r   r=   r   r/   r}   rz   r?   r@   v_fr{   )rC   Zcruise_gZwheel_gr=   rF   r@   gr   r   r   rG      s    
zGovernedDrive.__init__c              	   C   s   |  j tt|| j  | j | j | j| j 7  _ |  j| j| j || j  7  _t	| j
| j| j  d| j| j|| j | j }t| j| j|||| j}|| j fS )Nr   )r{   r   rS   rT   rz   r@   r   r}   r;   rV   r7   r8   r?   rW   rX   r5   r6   r=   )rC   r~   rZ   r[   r\   rh   ri   r   r   r   rk      s    

 zGovernedDrive.tickN)rm   rn   ro   rp   rG   rk   r   r   r   r   r      s   r   stancec                   @   s&   e Zd ZdZdZdd Zd	ddZdS )
	StanceLawa  Stand at a height and catch whatever arrives.

    Verbatim from recover_episode (the standing law every exam rides
    on): feet slide TOWARD the fall, putting the contact patch back
    under the mass faster than the wheels can drive it there, and the
    shove brace leans into a detected push. Wheel gains and trim are
    supplied by the caller so the same law serves recover's exam (its
    own baked slots at 460) and the firmware's stance command
    (bench-interpolated gains at any gate height).recoverc	           	      C   sb   dd |D \| _ | _| _| _t|| _t|| _t|| _t	|| _	|| _
t|| _|| _d S )Nc                 S   s   g | ]}t |qS r   r"   r#   r   r   r   r&     s   z&StanceLaw.__init__.<locals>.<listcomp>)r5   r6   r7   r8   r   r=   shift_k	shift_maxr;   r>   r?   r   r@   )	rC   rE   r=   r   r   r(   rF   r   r@   r   r   r   rG     s    



zStanceLaw.__init__r   c                 C   s   t | j|| j}t | jd |\}}| j||| j}	t 	| j
||| j|	 }
t | j| j|||
| j}t||||g| j|fS )z2One control tick. Returns (leg_want, leg_rate, u).rO   )r;   
foot_shiftr   r   rU   r   r?   rW   r@   rV   r7   r8   rX   r5   r6   r=   rS   rY   r>   )rC   rZ   r[   r\   odor{   dxhpknbrh   ri   r   r   r   rk     s    zStanceLaw.tickN)r   rl   r   r   r   r   r      s   
r   wakec                   @   s4   e Zd ZdZdZdZdZdZdZdd Z	d	d
 Z
dS )WakeLawa  Stand up from the collapsed heap - the baked rise choreography.

    Phases: lay out flat, launch pose, then the rise drive BY HEIGHT
    (gating on roller contact cut the drive at the rock-over and
    dropped it every time) until the stand pose lands. A wake that has
    not stood inside its window re-seats and tries again - the rise
    skill is honestly imperfect (bake 6.36/12) and the real machine
    would do exactly this.rise)g      H@g      :@)g     J@g     ag      ,@   c                 C   s   t |pg ddgd  d d }dd |D | _tdtdt| jd d	 | _|| _d
| _d| _d
| _	d| _
td\}}t||gd | _tt| jd
 t| jd gd | _tt| jd
 t| jd gd | _d S )N)gb=y?g{Pk?g?g(\8@r   r   c                 S   s   g | ]}t |qS r   r"   r#   r   r   r   r&   -      z$WakeLaw.__init__.<locals>.<listcomp>r!   rN   r   rO   r   FQ?   r   )r'   r   r   r1   r/   hand_mr@   phaserj   triesgave_upr;   rU   rS   rY   standr9   r:   LAYlayLAUNCHlaunch)rC   rD   r@   r   ZsthZstkr   r   r   rG   *  s0     zWakeLaw.__init__c              
   C   s  |  j | j7  _ | j | jkrp|  jd7  _d| _ d| _| j| jkr\d| _|tddddfS |tddddfS | jr|tddddfS | jdkrt	t
t
|| j tdk rd| _| jtd	dddfS | jdkr"t	t
t
|| j td
k rd| _| jtddddfS d}|| jk r`t	t
| jd | jd |d  d  dd}| j}tdtdtdt| jd  }|d dkot	t
t
|| j td
k }||||dfS )a]  One control tick.

        Returns (leg_want, leg_rate, drive, done, reseat): drive is the
        open-loop wheel command during the ascent (None = balance owns
        the wheels), done means STOOD, reseat asks the caller to put
        the robot back in the heap for another try (sim: place_sitting;
        real robot: it is already down there).r   r   r   Tg      N@NFrI   g     A@g       @r   g     b@gQ?g(\?      r         ?ru   rO   g     r@)rj   r@   TIMEOUTr   r   TRIESr   r9   r:   r   rS   r   r/   r   r   r   rT   r   r   r1   )rC   hip_zcur_legsrr   wantratedoner   r   r   rk   ;  sJ    
$zWakeLaw.tickN)rm   rn   ro   rp   rq   r   r   r   r   rG   rk   r   r   r   r   r     s   	r   c                   @   sx   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d Zdd Z	dd Z
dd Zdd Zdd Zdd Zdd Zdd ZdS )Firmwarea  The robot's brain between the remote and the laws.

    Owns the bakes, the base layers, and the mode glue: which law has
    the legs and wheels right now, and how command changes hand over.
    The glue rules were each paid for with a measured fall:
      - the spin law engages only for on-the-spot turns (driving turns
        are differential - the CoM hold cancels the drive lean and
        diverges on long legs), and once engaged it LATCHES until the
        stick releases (the speed estimate wobbles past any entry gate
        mid-spin, and dropping the legs mid-spin fell a 350 crouch
        nearly every time);
      - every law exit hands the legs to a neutral stance hold at that
        height (freezing "where they are" kept the CoM hold's foot
        offset baked in - equilibrium off the trim lean, wheels
        accelerating forever: the mission-end shoot-off);
      - the stance hold's odometry rebases on entry: hold HERE, never
        home to where the odometer happened to start.
    c              	   C   s   || _ t|| _g | _tD ]6\}}| |}|r| j|t|d d t	f q| j
  | d| _d| _d | _d | _d | _d| _d| _d| _d S )Nr   r   idler   )r@   r   runsbptsBALANCE_STANCESbakeappendrS   rY   r   sortrecmodelawr   manual_legsr~   want_wzr   )rC   r@   Zruns_dirnmr   r   r   r   r   rG     s     

"
zFirmware.__init__c                 C   s:   z t t| j| d d W S  ty4   Y d S 0 d S )Nz_gains.jsonrD   )jsonloadopenr   	Exception)rC   stager   r   r   r     s    zFirmware.bakec                 C   s8   | j r,t| j dkr,t| j d | j d S tddS )z1Recover's reflex as a base layer under every law.
      	   r   rK   )r   lenr;   
ShoveBracerC   r   r   r   rF     s    zFirmware.bracec                 C   s   t | d| j| _d| _d S )Nr   r   )r   r   r@   r   r   r   r   r   r   cmd_wake  s    zFirmware.cmd_wakec                 C   sP   t t|dd}| jd u p,t|| j dk}|| _| jdkrL|rL| | d S )N     t@     |@r   r   )r   rS   rT   r   r/   r   _stand)rC   mmchangedr   r   r   
cmd_stance  s    
zFirmware.cmd_stancec                 C   sL   |du r| j dkrd| _ dS tdd |D | _| j dkrHd| _ d| _dS )z|Direct joint sculpting: legs verbatim, wheels balancing.
        None releases back to a stance hold at the measured height.Nmanualmanual_exitc                 S   s   g | ]}t |qS r   r"   r#   r   r   r   r&     r   z'Firmware.cmd_manual.<locals>.<listcomp>r   )r   rS   rY   r   r   )rC   Zlegs_radr   r   r   
cmd_manual  s    

zFirmware.cmd_manualc                 C   s    d| _ d| _| | jpd d S )Nr        p|@)r~   r   r   r   r   r   r   r   cmd_halt  s    zFirmware.cmd_haltc                 C   s   t t|dd}t t|dd}t|d t|d  }}|dk rJd}|dk rVd}| jdkrrt|dkrrd}n"|d| krd}n|d| krd}|| _|| _dS )	a  The semi-automated stick: raw operator axes in, LAW choice
        out. Forward = the drive law; twist at standstill = the spin
        law; release = a stance hold where you are.

        SHAPED for a real 2-axis stick (operator's diagnosis: the demo
        slider was one pure axis, the joystick is not): a full-left
        pull always carries vertical leakage, and 12% of wobble was
        measured flipping the firmware through four modes mid-spin.
        Deadzone per axis, then axis dominance - the axis you clearly
        mean wins, the leakage is discarded - and while a spin is
        latched the vertical axis is dead entirely until the twist
        releases: mode changes are deliberate acts, not thumb drift.g      r   r   r   ru   r   r   N)r   rS   rT   r/   r   r~   r   )rC   r%   ZwzavZawr   r   r   stick  s    zFirmware.stickc              	   C   s   t t|r|nddd}tt| j|tt	|| j
rRt| j
dkrR| j
d nd| j
rrt| j
dkrr| j
d nd| j
rt| j
dkr| j
d	 nd
|  || j| _d| _d| _d S )Nr   r   r   r   rw   r      ru   r   r   r   )r   rS   rT   r   r   r   r9   r:   r;   r<   r   r   rF   r@   r   r   r   )rC   r   r   r   r   r     s    
   zFirmware._standc                 C   sl   |  d}|d u rdS tt|r$|nddd}t||t| j||  | j| _	d| _
d| _d| _d | _dS )Nr   Fr   r   r   r   T)r   r   rS   rT   r   r   r   rF   r@   r   r   spin_settlehold_cmdspin_rel)rC   r   r   r   r   r   _spin  s    

zFirmware._spinc                 C   sr   t td| dd}t| j|}tt |d |dt |d }tt |d t |d |||t	t
|S )	zwBench-interpolated balance at the MEASURED height - the
        wheel law under manual sculpting and the wake's ascent.rO   r   r   r   r   r   r   r   )r   rS   rT   r   r   r;   rV   rX   r9   r:   r<   )rC   r   rZ   r[   r\   r   Zwgrh   r   r   r   _bench_u  s     zFirmware._bench_uc                 C   s^   t | dt| jdttd|  | j	| _
td\}}t||||g| _d| _d S )Nrt   r   r   rr   )r   r   r   r   r9   r:   r;   r<   rF   r@   r   rU   rS   rY   
drive_legsr   )rC   r   r   r   r   r   _drive  s    


	zFirmware._drivec              
   C   s  |d }|d }|d }|  j || j 7  _ ddddddd}| jdkr| j|d	 |d
 \}}}}	}
|j||||
d |	r| | jpd n| jjrd| _d| _| jdkr|du r| 	|d	 ||||d< | j|d< |S | jdkr|j| j
td| 	|d	 |||d | j|d< |S | jdkrJ| ttd|d	  dd | jdk}t| jdkopt| jdk}t| jdk}| jdkrd }}|r| jdkr|d	 d dk r| jjdk r| d n|   nl|r
| jdkr
t|dk rH| | j n>| jdkrH|sHt| jjdk rHt|dk rH| | j | jdkrt| jdkrj| jnd}|  jtt|| j d | j d!| j 7  _|d" }t|dks| jdu rt| jdkrd| _| jj|||||d# |d$ |d% t| jd&kr
| jnd&d'\}}}}}n| jdu r6d| _d| _| jj|||||d# |d$ |d% d&d'\}}}}}t|d(k r|  j| j7  _nd| _t| jdkp| jduot| j| jj d)k}| jd*krd+|d< |r
| jd*kst|d,k r
| | jp| jj |j||||d- n| jdkr| j| j|||\}}ttd.| j d/d(}tt|d0t|  d0t| }|j||| jtdd1 n6| jdkr| j|||| j \}}}|j|||d d|vs|ddu s|d | jkr| j|d< |S )2zOne firmware tick.

        sense: dict with lean, lrate, speed, gz, rollr, rrate, com_off,
        hip_z, cur_legs. Returns dict with leg_want, leg_rate, u, t,
        wake_drive, reseat, mode.rZ   r[   r\   r   NF)ri   rj   
wake_drivereseatleg_wantr>   r   r   r   )r   r>   r   r   r   r   ri   r   r   g      n@)r   r>   ri   r   rO   r   r   r   r    rM   r   g     z@g     `|@rK   rr   g333333?g      rH   r]   r^   r_   r`   gư>)ra   rQ   r   rJ   holdg333333?)r   r>   ri   rj   gffffffֿrP   r   )ri   rj   r   r>   )r   r@   r   r   rk   rW   r   r   r   r   r   r9   r:   r   rS   rT   r/   r   r~   r   r   r{   r   r   r   r   get)rC   ZsenserZ   r[   r\   outr   r   rr   r   r   ZspinningZ	want_spinZ
want_drivetargetr]   ri   rj   _Z
_wants_outr   r   r   rk     s    










$
zFirmware.tickN)rm   rn   ro   rp   rG   r   rF   r   r   r   r   r   r   r   r   r   rk   r   r   r   r   r   k  s   #	r   )rp   r   r9   pathlibr   numpyrS   
controllerr;   r   r
   r   r   r   rs   r   r   r   r   r   r   r   r   <module>   s&    P$(#Q