# Piolín Control Arbitration Flowchart
This flowchart represents Piolín's **steering-control arbitration system**.
Several subsystems may generate steering requests at the same time, but Motor B can only follow **one final steering target**.
The purpose of control arbitration is to decide which controller currently has authority.
```mermaid
flowchart TD
START([CONTROL CYCLE])
READ["Read current system state
Navigation state
S2 LEFT
S3 RIGHT
Pixy target
Course progress
Current steering"]
SAFETY["Evaluate critical wall safety"]
DANGER{"Critical physical
wall danger?"}
SAFE_CMD["Generate SAFETY steering command
and reduced safe speed"]
STATE{"Current navigation
state?"}
NORMAL["NORMAL controller
Wall geometry / straight navigation"]
TARGET["TARGET_ACQUIRE controller
Hold stable trajectory while
confirming obstacle"]
AVOID["AVOID controller
Execute pillar trajectory
Red → RIGHT
Green → LEFT"]
PASS["PASS_CONFIRM controller
Maintain committed trajectory
while checking physical clearance"]
RECOVER["RECOVER controller
Countersteer and reacquire
safe lateral geometry"]
CORNER["CORNER controller
Execute corner trajectory
with reduced straight-wall assumptions"]
PARK["PARKING controller
Approach → Entry → Align → Final"]
STOP["STOP controller
No navigation command"]
ACTIVE["Select active maneuver command"]
LIMIT["Clamp command
to valid steering range"]
SMOOTH["Apply steering-target smoothing
where appropriate"]
SPEED["Select state-dependent drive speed"]
FINAL["FINAL STEERING COMMAND"]
STEER["Motor B
Steering actuator"]
DRIVE["Motor A
Drive-speed command"]
FEEDBACK["Read actual Motor B position
for next control cycle"]
END([NEXT CONTROL CYCLE])
START --> READ
READ --> SAFETY
SAFETY --> DANGER
DANGER -- "YES" --> SAFE_CMD
SAFE_CMD --> LIMIT
DANGER -- "NO" --> STATE
STATE -- "NORMAL" --> NORMAL
STATE -- "TARGET_ACQUIRE" --> TARGET
STATE -- "AVOID" --> AVOID
STATE -- "PASS_CONFIRM" --> PASS
STATE -- "RECOVER" --> RECOVER
STATE -- "CORNER" --> CORNER
STATE -- "PARKING" --> PARK
STATE -- "STOP" --> STOP
NORMAL --> ACTIVE
TARGET --> ACTIVE
AVOID --> ACTIVE
PASS --> ACTIVE
RECOVER --> ACTIVE
CORNER --> ACTIVE
PARK --> ACTIVE
STOP --> ACTIVE
ACTIVE --> LIMIT
LIMIT --> SMOOTH
SMOOTH --> FINAL
STATE --> SPEED
FINAL --> STEER
SPEED --> DRIVE
STEER --> FEEDBACK
FEEDBACK --> END
DRIVE --> END
```
---
## Arbitration Principle
Piolín does not allow every controller to influence steering with equal authority.
A naive system could behave like this:
```text
wall command
+
pillar command
+
corner command
+
recovery command
=
final steering
```
This can create controller conflict.
For example:
```text
Red pillar
→ obstacle controller requests RIGHT
```
while:
```text
wall controller
→ requests LEFT
```
If both commands are mixed directly, Piolín may produce:
```text
weak steering
oscillation
late avoidance
apparently inverted behavior
```
The arbitration system instead determines which maneuver currently owns the trajectory.
---
## Priority Structure
The intended control hierarchy is:
```text
1. CRITICAL SAFETY
2. ACTIVE MANEUVER
3. NORMAL NAVIGATION
```
Conceptually:
```text
CRITICAL WALL DANGER?
│
YES
│
▼
SAFETY OVERRIDE
│
▼
FINAL COMMAND
```
Otherwise:
```text
CURRENT STATE
↓
ACTIVE CONTROLLER
↓
FINAL COMMAND
```
---
## Critical Safety Override
Critical wall safety is separate from ordinary wall following.
The two should not be confused.
```text
NORMAL WALL CONTROL
→ tries to maintain useful track geometry
```
while:
```text
CRITICAL WALL SAFETY
→ prevents dangerous physical proximity
```
If the safety layer detects a serious condition, it may override the active maneuver.
Conceptually:
```python
wall_active, wall_cmd, safe_speed = side_wall_guard(
left_mm,
right_mm
)
if wall_active:
final_cmd = wall_cmd
drive_speed = safe_speed
```
This means that even during:
```text
AVOID
CORNER
RECOVER
PARKING
```
the robot can still protect itself from an immediately dangerous wall condition.
---
## State-Dependent Authority
Each navigation state gives priority to a different controller.
| State | Primary Steering Authority |
| :--- | :--- |
| `NORMAL` | Wall / track geometry |
| `TARGET_ACQUIRE` | Stable approach while confirming target |
| `AVOID` | Pillar-avoidance trajectory |
| `PASS_CONFIRM` | Continue committed pass until clearance is verified |
| `RECOVER` | Post-pillar recentering |
| `CORNER` | Corner trajectory |
| `PARKING` | Parking controller |
| `STOP` | No navigation movement |
The same sensor can therefore have different influence depending on the current state.
For example:
```text
S2 / S3 during NORMAL
→ strong navigation influence
```
but:
```text
S2 / S3 during AVOID
→ physical context and safety
```
This prevents normal wall centering from cancelling a deliberate obstacle maneuver.
---
## NORMAL
During `NORMAL`, Piolín is not committed to a special maneuver.
The primary objective is:
```text
maintain useful track geometry
```
using:
```text
S2 LEFT
S3 RIGHT
```
and the normal wall / geometry controller.
Conceptually:
```text
S2 + S3
↓
geometry estimate
↓
position error
↓
normal steering command
```
This is the state where ordinary wall-following authority is strongest.
---
## TARGET_ACQUIRE
When Pixy2.1 identifies a possible relevant pillar, Piolín should not immediately create a maximum steering maneuver.
During `TARGET_ACQUIRE`, the system is still:
```text
validating
ranking
confirming
```
the candidate.
The controller should preserve a stable approach while the perception system decides whether the target is trustworthy.
This reduces the risk that:
```text
one unstable detection
```
creates:
```text
one large steering correction
```
---
## AVOID
Once a target is confirmed, the obstacle controller becomes the primary maneuver controller.
The competition rules remain fixed:
```text
sig2
→ RED
→ PASS RIGHT
```
```text
sig3
→ GREEN
→ PASS LEFT
```
During `AVOID`:
```text
pillar trajectory
→ primary authority
```
while:
```text
normal wall centering
→ reduced authority
```
and:
```text
critical wall safety
→ remains available
```
This is one of the most important arbitration decisions in Piolín.
Without it, wall following can fight the required obstacle trajectory.
---
## PASS_CONFIRM
`PASS_CONFIRM` exists because:
```text
pillar not visible
```
does not automatically mean:
```text
pillar physically passed
```
During this state, Piolín preserves the committed maneuver while checking:
```text
Pixy history
S2 / S3 geometry
vehicle progression
```
The obstacle controller should not be released simply because one camera frame is missing.
Once pass evidence becomes sufficient:
```text
PASS_CONFIRM
→ RECOVER
```
---
## RECOVER
After the pillar is passed, Piolín must return toward useful track geometry.
This is not the same as normal wall following.
Recovery is a deliberate maneuver:
```text
pillar passed
↓
countersteer
↓
reacquire corridor
↓
stabilize
↓
NORMAL
```
During `RECOVER`, the recovery controller has primary authority until the robot reaches an acceptable geometry.
Then control returns to normal navigation.
---
## CORNER
During a corner, the straight-wall model becomes temporarily less reliable because:
```text
chassis rotates
sensor beams rotate
wall geometry changes
```
Therefore:
```text
NORMAL wall controller
```
should not retain full authority.
The `CORNER` controller becomes primary until:
```text
new corridor geometry
```
is reacquired.
Then:
```text
CORNER
→ NORMAL
```
---
## PARKING
Once:
```text
course progression complete
+
Pink sig1 confirmed
```
parking becomes the active navigation objective.
At that point:
```text
PARKING
```
takes authority over normal navigation.
Its internal phases are:
```text
APPROACH
→ ENTRY
→ ALIGN
→ FINAL
```
Normal wall following should not try to pull the robot back into its previous corridor during a deliberate parking entry.
Critical physical safety may still remain active.
---
## STOP
`STOP` is terminal.
Once parking is complete:
```text
state = STOP
```
the arbitration result becomes:
```text
no further navigation command
```
The controller should not return to:
```text
NORMAL
AVOID
CORNER
PARKING
```
because of later sensor detections.
Conceptually:
```python
if state == STOP:
drive.stop(...)
steer.stop(...)
```
and the state remains `STOP`.
---
## Command Limiting
Even after a controller wins arbitration, its output should remain bounded.
Conceptually:
```python
final_cmd = clamp(
requested_cmd,
-MAX_STEER,
MAX_STEER
)
```
This prevents one controller from requesting a steering value outside the calibrated physical range.
Command limiting is therefore separate from controller priority.
```text
ARBITRATION
→ decides WHICH controller wins
```
```text
CLAMP
→ limits HOW LARGE its command can become
```
---
## Steering Smoothing
After arbitration and command limiting, Piolín may apply target smoothing.
Conceptually:
```python
diff = target - current_target
diff = clamp(
diff,
-TARGET_STEP,
TARGET_STEP
)
current_target += diff
```
This reduces:
```text
steering shock
oscillation
rapid target changes
```
However, smoothing must be calibrated carefully.
Too much smoothing can make:
```text
AVOID
```
react too slowly.
For this reason, smoothing belongs **after state selection**, not before the system knows which maneuver is active.
---
## State-Dependent Speed
Arbitration applies to drive speed as well as steering.
Different states require different control margins.
Conceptually:
```text
NORMAL
→ normal drive speed
```
```text
TARGET_ACQUIRE
→ controlled approach speed
```
```text
AVOID
→ obstacle maneuver speed
```
```text
RECOVER
→ recovery speed
```
```text
CORNER
→ corner speed
```
```text
PARKING
→ parking speed
```
```text
critical safety
→ reduced safe speed
```
The exact numerical speeds remain calibration parameters.
---
## Complete Command Pipeline
```text
SENSORS
↓
PERCEPTION
↓
STATE MACHINE
↓
CONTROLLER REQUESTS
↓
┌──────────────────────────────┐
│ CONTROL ARBITRATION │
│ │
│ Critical danger? │
│ │ │
│ YES → SAFETY │
│ │ │
│ NO │
│ ↓ │
│ Use active-state controller │
└──────────────┬───────────────┘
↓
CLAMP COMMAND
↓
SMOOTH TARGET
↓
FINAL MOTOR B TARGET
↓
MOTOR B ACTUATION
↓
ACTUAL STEERING FEEDBACK
```
---
## Why Arbitration Matters
The purpose of this architecture is not simply to make the software more organized.
It solves a physical control problem.
Piolín has:
```text
many possible objectives
```
but only:
```text
one steering actuator
```
Therefore the system must answer:
> **Which objective has the right to control Motor B right now?**
The answer depends on:
```text
physical danger
current state
committed maneuver
```
rather than on which controller happens to produce the largest numerical output.
The central arbitration principle is:
> **Piolín does not combine every steering opinion equally. Safety receives the highest authority, the currently committed maneuver controls the trajectory, and normal navigation resumes only when that maneuver has been completed.**