Repository navigation
Hazards, Recoil and Conflict Resolution
Hazards add a competing intention to the simulation: an arm may want to reach food while also needing to move away from danger. Local control handles the immediate response; central control receives significant reports and decides which intentions take precedence.
The Python application and web explorer use equivalent rules. These are experimental control rules, not a biological model of pain or a physical simulation of injury.
| Layer | Responsibility |
|---|---|
| Segment-local hazard response | Detect nearby danger and redirect local movement requests. |
| Arm signalling | Decide whether a hazard stays local, warrants a report, or requires withdrawal. |
| Central priority handling | Retain reported threats and request withdrawal where necessary. |
| Conflict resolution | Choose between safety, ownership, assigned tasks and competing resource claims. |
| Movement controller | Attempt the chosen action within joint, speed and obstacle constraints. |
Central control acts on reports and arm intentions. It does not scan the environment to find unreported hazards, calculate escape routes, or prescribe joint angles.
flowchart TD
Hazard["Hazard near an arm segment"] --> Local["Local avoidance or recoil"]
Hazard --> Signal["Arm evaluates severity and exposure"]
Signal --> Quiet["Mild: no central report"]
Signal --> Report["Significant: report to central control"]
Report --> Arbitration["Resolve safety and resource conflicts"]
Arbitration --> Intention["Reach, maintain grip, yield or withdraw"]
Local --> Movement["Constrained local movement"]
Intention --> Movement
Movement --> Sensing["Sense again on the next tick"]
Sensing --> Signal
Both are circular regions, but they have different meanings.
- An obstacle is a solid movement constraint. Collision checks reject unsafe crossings.
- A hazard produces aversion. It is traversable: detection and recoil do not create an invisible solid wall.
A hazard has a position, radius, severity and detection range. Severity lies in (0, 1]. The radius describes its actual region; the detection range extends sensing beyond that region. The displays identify hazards with H and show their sensing ring.
Sensing considers the nearest point on each complete arm segment, not just the joints or tip. A segment detects a hazard when its distance from the centre is at most:
hazard radius + detection range + sensing-strip radius
The sensing-strip radius is currently 3 world pixels. Detection includes equality at the boundary. Exposure is stricter: a segment's centreline must be inside the actual hazard radius. A nearby hazard can therefore trigger avoidance without contributing exposure time.
The arm keeps its assigned destination while temporarily changing its local movement requests.
| Local state | Meaning |
|---|---|
| Reaching | No active hazard response; ordinary task movement is available. |
| Recoiling | A timed outward response replaces attraction for segments with hazard observations. |
| Avoiding hazard | Local steering tries to route segment requests around the hazard. |
| Reassessing | The arm has left sensing range and briefly retains its observations before returning to ordinary reaching. |
Below severity 0.4, an encounter uses local avoidance without a timed recoil. If a segment's sensing strip penetrates a mild hazard, it continues requesting outward movement until it clears that region. This prevents boundary steering from leaving it settled inside the hazard.
A new encounter at severity 0.4 or above starts a recoil lasting:
recoil duration = 0.25 + 0.9 × severity (simulation seconds)
For example, severity 0.55 gives a nominal recoil duration of 0.745 seconds. A new encounter can extend an existing recoil, but continued detection of the same hazard does not restart it on every tick.
A sensing segment moves its requested endpoint away from the most severe hazard in its local observations. Severity strengthens that request; it does not increase the permitted joint speed or bypass bend limits. Segments without an observation do not acquire knowledge of the hazard merely because another segment detects it.
After recoil, local boundary steering remains active while danger is sensed. Once recoil has finished and sensing stays clear for 0.6 seconds, the local episode can reset. This local reassessment interval is separate from the configurable clearance interval used for central reports.
A solid obstacle may prevent the arm's combined proposed movement. During hazard response or withdrawal, the controller then tries safe subsets of the proposed joint turns, starting at the tip and working towards the base.
Every accepted combination is checked against the original pose's full movement sweep. This lets distal joints escape when a proximal turn is blocked, while preserving obstacle clearance, segment lengths, bend limits and turning speeds.
It is still local recovery, not a route planner. An arm can escape a hazard and subsequently become blocked nearby.
The default signalling policy is:
| Condition | Signal | Central effect |
|---|---|---|
| Severity below 0.4 | No report | Response remains local. |
| Severity at least 0.4 but below 0.75 | Priority 50 | Inform central control; continue locally. |
| Severity at least 0.75 | Priority 100 | Request withdrawal for the reporting arm. |
| Reportable hazard with 2.0 seconds of continuous exposure | Upgrade to priority 100 | Request withdrawal even if severity is below 0.75. |
| No detection for 0.6 seconds | Priority 0, Clear | Remove that arm's outstanding report for the hazard. |
The scenario's escalation settings can change these thresholds and intervals. The recoil threshold remains a separate local constant, so changing reporting thresholds does not automatically change when recoil starts.
Reports include the observed hazard identity, position, severity, affected segment count, exposure and reason. They are snapshots, not a live central map. Repeated identical observations do not produce a report every frame. An increase to withdrawal priority, or an upgrade across the critical-severity threshold, does produce a new report.
Once reported, the highest priority remains latched until clearance. A decrease in severity alone does not cancel withdrawal. Removing a hazard also requires the reporting arm's clearance interval to pass before central control forgets the report.
Two exposure measurements must not be confused:
- Local accumulated exposure totals time inside hazards across encounters, counting at most once per arm per tick.
- Signalling exposure measures continuous penetration of a particular hazard by a particular arm. It resets when penetration stops and drives sustained-exposure escalation.
A mild, unreportable hazard does not escalate solely because exposure continues. Signalling also runs for idle and holding arms, so inactivity does not hide serious danger from central control.
The resolver uses fixed priorities rather than adding competing scores:
| Intention | Priority |
|---|---|
| Withdrawal | 100 |
| Local hazard avoidance | 80 |
| Maintain grip | 60 |
| Assigned reach | 50 |
| Incidental discovery | 20 |
| Idle | 0 |
These action priorities and the signalling priorities above are separate concepts. For example, a priority-50 hazard report is informational; it is not a new reach instruction.
Withdrawal overrides an existing grip. The food is released in place before the arm attempts retreat. If it was carrying the food to a destination, that carry is abandoned; the arm remains unassigned after the alert clears rather than immediately recapturing the released food.
An ordinary unladen reach is retained during withdrawal and can resume after clearance. Local avoidance alone does not necessarily release a held object.
An outstanding priority-100 report whose severity reaches the configured critical_severity requests withdrawal from every arm, including idle arms. The default critical threshold is 0.95.
The body stays fixed. Each arm interprets withdrawal as a destination near its own attachment and attempts to draw its tip back using local movement rules. A broadcast expresses an intention; it does not guarantee that every arm reaches a fully retracted pose.
The broadcast remains active while a qualifying critical report is outstanding. Once it clears, remaining per-arm reports can still require individual withdrawal.
For a shared food or target claim, precedence is:
- The existing holder of the food.
- An eligible previous winner still claiming the same resource.
- The lowest arm number, to break a remaining tie deterministically.
When both claims identify food, object identity decides whether they match. If either lacks an identity, their destinations can match within target_tolerance, which defaults to 2 world pixels. A coordinate-only request can therefore compete with a named-food request at the same destination. Matching is anchored to a group's first target rather than extended through a chain of nearby targets.
Other claimants yield, keeping their assignments but suspending ordinary task movement. A yielding arm can become eligible when the winner releases its claim, becomes idle, or retreats. Safety steering is not suppressed by resource contention. An unladen arm avoiding a hazard is not eligible to win a resource claim during that response; a current owner retains ownership.
There is no fairness timeout or automatic reassignment when a winner gets stuck. A waiting arm may consequently keep yielding indefinitely.
An explicit named-food task takes precedence over a different food encountered along the way. The arm ignores that incidental capture opportunity rather than replacing its assignment.
A coordinate-only reach remains opportunistic and may capture food it contacts. Named-food identity constrains which food is eligible; it does not turn the assigned destination into continuous tracking of a moving food object.
Keep the selected action, actual movement state and historical events distinct.
- Withdraw is a conflict decision or command.
- Withdrawing means withdrawal is active without the movement guard currently reporting blockage.
- Withdrawal blocked means the command remains active but movement was rejected. The compact desktop roster uses Retreat blocked.
- Blocked can remain after an alert clears if the resumed task cannot move safely.
- Last capture records a past event; it does not establish that food is still held.
The desktop's three-row summary shows the overall goal and playback time, arm-state counts, and latest significant event. History pauses playback for inspection, provides arm/type filters, groups related safety events, and collapses repeated events with a count and latest timestamp. Its totals distinguish captures, releases and food currently held. Closing restores the previous playback state.
The web explorer's Priority Communication and Conflict Resolution cards show reported threats and arbitration outcomes beneath Eight Independent Arms. The selected-arm readout gives the current action, priority and explanation.
A conflict count measures per-arm entries into changed conflict decisions, not simulation frames or unique disputes. A capture remains in capture history after release. The desktop demonstration history is also distinct from the model's bounded raw report and conflict logs.
| Scenario | What to inspect |
|---|---|
local-hazard.json |
Local detection, recoil and avoidance while retaining the objective. |
hazard-escalation.json |
High-priority reporting and withdrawal. |
food-vs-danger.json |
Safety overriding a grip and releasing food. |
competing-arms.json |
Shared-resource arbitration and yielding. |
discovery-vs-task.json |
An assigned food task taking precedence over incidental discovery. |
reef-forage-extended.json |
Captures, local avoidance, escalation, critical withdrawal and competing claims in one scene. |
In an unedited extended reef run, Arms 1 and 4 capture food at about 1.53 and 2.19 seconds. Around 2.99 seconds, Arm 6 reports critical danger, all arms are instructed to withdraw, and the two grips are released. This scenario shortens sustained-exposure escalation to 0.75 seconds instead of the default 2.0 seconds.
The later scene is not a successful collection of every meal. Arms 1 and 4 become idle, several other arms can become blocked, and Arm 8 can keep yielding to Arm 7. Arm 3 clears its mild hazard but can remain blocked outside it. Repeated encounters are possible when retained reach tasks resume.
For a demonstration, watch the opening sequence at normal speed, then increase playback speed. Desktop V cycles 1×, 2×, 4× and 8×; the browser has a Playback speed selector. Both run more unchanged 1/120-second simulation ticks, preserving event order and simulated timing rather than changing arm dynamics. Single-step remains one tick at any speed.
| Python module | Web equivalent | Responsibility |
|---|---|---|
hazards.py |
hazards.js |
Segment-local observations, recoil and steering. |
escalation.py |
escalation.js |
Exposure episodes, signals and central reported knowledge. |
conflicts.py |
conflicts.js |
Fixed priorities and deterministic resource claims. |
organism.py |
organism.js |
Sensing/reporting order, application of decisions, grips and tasks. |
model.py |
model.js |
Constrained movement and safe recovery attempts. |
activity.py, view.py, app.py
|
UI in app.js and HTML |
Desktop significant-event history and interface presentation. |
See Central Controller for task assignment, Reach Controller and Arm Movement for the movement rules, and Architecture for the wider code structure.