From 209f9b6a3da2dbc8f5f1caafa8e292a248168eba Mon Sep 17 00:00:00 2001 From: cedric Date: Tue, 31 Mar 2026 22:09:37 +0000 Subject: [PATCH 01/12] feat: MLP related design decisions doc --- docs/design_decisions/mlp_architecture.md | 24 ++++++++++++++++++++++ docs/img/mlp_pipe.png | Bin 0 -> 39662 bytes 2 files changed, 24 insertions(+) create mode 100644 docs/design_decisions/mlp_architecture.md create mode 100644 docs/img/mlp_pipe.png diff --git a/docs/design_decisions/mlp_architecture.md b/docs/design_decisions/mlp_architecture.md new file mode 100644 index 0000000..0cdb381 --- /dev/null +++ b/docs/design_decisions/mlp_architecture.md @@ -0,0 +1,24 @@ +# Network Architecture (MLP Pipeline per Module) + +In the decentralized architectures (arm-level and segment-level), each controller/module follows the same **shared MLP-based pipeline** inspired by NerveNet-style message passing. The pipeline consists of 5 MLPs (4 in the case of centralized, with no messager): + +- **INPUT_ACTOR**: Processes local observations for the actor branch. +- **INPUT_CRITIC**: Processes local observations for the critic branch. +- **MESSAGER**: Processes incoming hidden states from neighboring modules (via the chosen communication scheme) and produces an aggregated hidden state. +- **ACTOR**: Takes the aggregated hidden state and outputs the action distribution (mean and log_std). +- **CRITIC**: Takes the aggregated hidden state and outputs a scalar value estimate. + +## MLP Pipeline + +![MLP Pipeline - Actor-Critic with Message Passing](../img/mlp_pipe.png "MLP neural network pipeline per module") + + +## Implementation Details Related To PPO + +Inspired by: https://iclr-blog-track.github.io/2022/03/25/ppo-implementation-details/ + +For starters we will execute our tests with simple models. Each MLP will have only 1 hidden layer. This will be expanded as needed. The exceptions are the input networks / feature extractors — they will be given 2 hidden layers and 64 nodes per layer as advised in the blog. This might change as we make progress in our experiments. + +Our policy and value networks use separate networks as advised by the paper and the blog. For continuous actions this should allow better learning at a small cost. + +We use mean and log_std to represent the action distribution, because it is advised by previous research for learning stability and other reasons. \ No newline at end of file diff --git a/docs/img/mlp_pipe.png b/docs/img/mlp_pipe.png new file mode 100644 index 0000000000000000000000000000000000000000..174d521c01243ba8e11f204d095a78213f9ba1ab GIT binary patch literal 39662 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 Date: Tue, 31 Mar 2026 22:17:32 +0000 Subject: [PATCH 02/12] fix: clarified seperate policy and value network part --- docs/design_decisions/mlp_architecture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design_decisions/mlp_architecture.md b/docs/design_decisions/mlp_architecture.md index 0cdb381..045cf01 100644 --- a/docs/design_decisions/mlp_architecture.md +++ b/docs/design_decisions/mlp_architecture.md @@ -19,6 +19,6 @@ Inspired by: https://iclr-blog-track.github.io/2022/03/25/ppo-implementation-det For starters we will execute our tests with simple models. Each MLP will have only 1 hidden layer. This will be expanded as needed. The exceptions are the input networks / feature extractors — they will be given 2 hidden layers and 64 nodes per layer as advised in the blog. This might change as we make progress in our experiments. -Our policy and value networks use separate networks as advised by the paper and the blog. For continuous actions this should allow better learning at a small cost. +Our policy and value networks use separate input networks / feature extractors as advised by the SEL3 course assistants and the blog. For continuous actions this should allow better learning at a small cost. We use mean and log_std to represent the action distribution, because it is advised by previous research for learning stability and other reasons. \ No newline at end of file From f487d5725fd1d286a4a910eb323dd60b10dfbc5c Mon Sep 17 00:00:00 2001 From: cedric Date: Tue, 31 Mar 2026 22:20:19 +0000 Subject: [PATCH 03/12] feat: extended reward function doc with efficiency idea --- docs/design/reward_function.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/design/reward_function.md b/docs/design/reward_function.md index 1bf8725..2237aa5 100644 --- a/docs/design/reward_function.md +++ b/docs/design/reward_function.md @@ -7,6 +7,7 @@ inputs must be distributed fairly to guarantee an objective comparison between d - Positions and joints, which are normalized to floating-point values between 0 and 1, are considered local inputs. - The reward function is centered around minimizing the distance to the goal or maximizing the movement towards the goal within a finite number of timesteps $T$. +- To motivate efficient movement, the amount of timesteps taken to reach the goal will be used as penalty. ## Rationale From b2527e1a3cacfd3862a8ed9b5fc327984ef9d6ce Mon Sep 17 00:00:00 2001 From: cedric Date: Thu, 2 Apr 2026 08:30:04 +0000 Subject: [PATCH 04/12] fix: renamed MLPs with new names --- docs/design_decisions/mlp_architecture.md | 4 ++-- docs/img/mlp_pipe.png | Bin 39662 -> 40184 bytes 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/design_decisions/mlp_architecture.md b/docs/design_decisions/mlp_architecture.md index 045cf01..93a6e51 100644 --- a/docs/design_decisions/mlp_architecture.md +++ b/docs/design_decisions/mlp_architecture.md @@ -2,8 +2,8 @@ In the decentralized architectures (arm-level and segment-level), each controller/module follows the same **shared MLP-based pipeline** inspired by NerveNet-style message passing. The pipeline consists of 5 MLPs (4 in the case of centralized, with no messager): -- **INPUT_ACTOR**: Processes local observations for the actor branch. -- **INPUT_CRITIC**: Processes local observations for the critic branch. +- **SENSOR**: Processes local observations for the actor branch. +- **FEATURE EXTRACTOR**: Processes local observations for the critic branch. - **MESSAGER**: Processes incoming hidden states from neighboring modules (via the chosen communication scheme) and produces an aggregated hidden state. - **ACTOR**: Takes the aggregated hidden state and outputs the action distribution (mean and log_std). - **CRITIC**: Takes the aggregated hidden state and outputs a scalar value estimate. diff --git a/docs/img/mlp_pipe.png b/docs/img/mlp_pipe.png index 174d521c01243ba8e11f204d095a78213f9ba1ab..39773299c66c3e28ba8ec6ca278904a03e6040f7 100644 GIT binary patch literal 40184 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 literal 39662 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 Date: Wed, 8 Apr 2026 11:03:56 +0200 Subject: [PATCH 05/12] chore: use design dir --- docs/{design_decisions => design}/mlp_architecture.md | 0 docs/design_decisions/.keep | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename docs/{design_decisions => design}/mlp_architecture.md (100%) delete mode 100644 docs/design_decisions/.keep diff --git a/docs/design_decisions/mlp_architecture.md b/docs/design/mlp_architecture.md similarity index 100% rename from docs/design_decisions/mlp_architecture.md rename to docs/design/mlp_architecture.md diff --git a/docs/design_decisions/.keep b/docs/design_decisions/.keep deleted file mode 100644 index e69de29..0000000 From b9c0358b295d88101622a3ceba820cd695883750 Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Wed, 8 Apr 2026 12:08:54 +0200 Subject: [PATCH 06/12] docs: add docs README landing/starting point --- README.md | 11 +++++++++-- docs/README.md | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 docs/README.md diff --git a/README.md b/README.md index 085f3d5..a858341 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,11 @@ # Brittle Star +> What is the impact of different levels of controller-modularity on the learning-speed, coordination and tolerance for +defects (e.g. amputations) in brittle-star-like robots trained with Reinforcement Learning? + ## Usage -### UV +### Local setup To set up the UV module, you can run the following command: @@ -16,6 +19,10 @@ example command: uv run src/train.py --model_name my_model --epochs 50 --batch_size 32 ``` -## HPC +### HPC setup See **[docs/HPC.md](docs/HPC.md)** for the full guide, including environment setup, cluster selection, interactive debugging, and job submission. + +## Documentation + +Please find all documentation and a starting point for more information in [corresponding README](./docs/README.md). diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..8c8d659 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,14 @@ +# Documentation + +## Design & architecture ([`/design`](./design/)) + +- [Controllers](./design/controllers.md): Macroscopig brain toplogy, centralized, arm-level, segment-level. +- [MLP architecture](./design/mlp_architecture.md): Module wiring, state/action spaces, and actor-critic pipeline. +- [Communication](./design/communication.md): Message propagation, Nerve-Net style. +- [Learning algorithm](./design/learning_algorithm.md): RL techniques, i.e. PPO. +- [Reward function](./design/learning_algorithm.md): Goals, fitness tracking, and reward structures. + +## API reference ([`/api`](./api/)) + +- [Environment](./api/environment.md): MuJoCo environment interaction, state retrieval, and configuration. +- [Simulate](./api/simulate.md): Simulation rendering. From 9bec02594e70423ab3f18c58411a0b5d7932b20c Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Wed, 8 Apr 2026 12:29:15 +0200 Subject: [PATCH 07/12] docs: model input/output --- docs/README.md | 5 +-- docs/design/input_action_spaces.md | 52 ++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 docs/design/input_action_spaces.md diff --git a/docs/README.md b/docs/README.md index 8c8d659..ac56e31 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,10 +2,11 @@ ## Design & architecture ([`/design`](./design/)) -- [Controllers](./design/controllers.md): Macroscopig brain toplogy, centralized, arm-level, segment-level. -- [MLP architecture](./design/mlp_architecture.md): Module wiring, state/action spaces, and actor-critic pipeline. - [Communication](./design/communication.md): Message propagation, Nerve-Net style. +- [Controllers](./design/controllers.md): Macroscopig brain toplogy, centralized, arm-level, segment-level. +- [Input/output](./design/input_action_spaces.md): Description of the model's input and output. - [Learning algorithm](./design/learning_algorithm.md): RL techniques, i.e. PPO. +- [MLP architecture](./design/mlp_architecture.md): Module wiring, state/action spaces, and actor-critic pipeline. - [Reward function](./design/learning_algorithm.md): Goals, fitness tracking, and reward structures. ## API reference ([`/api`](./api/)) diff --git a/docs/design/input_action_spaces.md b/docs/design/input_action_spaces.md new file mode 100644 index 0000000..7860e16 --- /dev/null +++ b/docs/design/input_action_spaces.md @@ -0,0 +1,52 @@ +# Input (state) and output (action) spaces + +To effectively learn locomotion and navigation, the agent requires a well-defined observation space (inputs) and action +space (outputs). The control models map these observations directly to physical movements. + +**Inputs (state space)** + +The observation space provides the agent with its current physical state and its objective. + +- Joint positions: the current angles of all joints in the morphology. +- Joint velocities: the current moving speed of the joints. +- Goal vector: instad of just a scalar distance, the goal is represented asa a vector (distance and ange/direction) to + the target. + +**Outputs (action space)** + +The action space defines how the agent interacts with the environment. + +- Joint offsets: *absolute* target positions (offsets) for the joints, i.e. the exact angle the joint should move to. + +## Rationale + +When designing the state space, we must ask: *Could a human operator perform this task given only these inputs?* + +- Inclusion of Joint Velocities: Because our control models do not inherently possess memory of previous timesteps, + providing only the joint position is insufficient to determine the direction a limb is currently moving. By + explicitly including joint velocities, the agent can immediately infer momentum and movement direction without + needing to memorize past states. +- Goal Vector (Distance + Angle): Providing only the scalar "distance to the goal" as an input is akin to blindfolding + the robot and asking it to find a target by playing "hot or cold." By providing a full vector, the agent knows + exactly where the target is relative to its current orientation, allowing for directed and efficient locomotion. +- Absolute Joint Offsets: The physical Brittle Star robot relies on servo motors (if we were to build this simulated + robot), which are inherently position-controlled devices. (Continuous rotation servos exist, but they are less + commonly used for joints.) If our network outputted continuous torques (forces), a significant portion of the + reinforcement learning process would be wasted on learning low-level PID control dynamics (i.e., how much force to + apply to hold a position). Abstracting this away forces the learning algorithm to focus entirely on higher-level gait + generation and locomotion. + +## Limitations and alternatives + +Alternative state and action formulations include: + +- Torque-based continuous control: In many continuous control tasks (like standard MuJoCo benchmarks), actions + represent continuous torques applied to joints. While this provides more granular, low-level physical control, it + heavily complicates training and does not align well with the physical reality of servo-driven hardware. +- Recurrent Neural Networks (RNNs) / Frame Stacking: Instead of explicitly passing velocities in the state space, the + network could infer momentum by observing a history of past states. Using RNNs or frame stacking allows the agent to + build an internal memory of movement. However, this significantly increases architectural complexity and training + time compared to explicitly providing the velocity data. +- Scalar Goal Distance: Giving the agent only the scalar distance to the target would force it to learn a localized + searching behavior (e.g., spiraling or random walks) to determine the correct direction. While biologically plausible + for simpler organisms following chemical gradients, it drastically increases the difficulty of the learning task. From d8c2917923ba9bab076dfb3b6fe89bf3182c0ae7 Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Wed, 8 Apr 2026 14:21:44 +0200 Subject: [PATCH 08/12] docs: detailed actor-critic pipelines --- docs/design/actor-critic.md | 68 +++++++++++++++++++++++++++++++++ docs/design/mlp_architecture.md | 24 ------------ 2 files changed, 68 insertions(+), 24 deletions(-) create mode 100644 docs/design/actor-critic.md delete mode 100644 docs/design/mlp_architecture.md diff --git a/docs/design/actor-critic.md b/docs/design/actor-critic.md new file mode 100644 index 0000000..81fbc3e --- /dev/null +++ b/docs/design/actor-critic.md @@ -0,0 +1,68 @@ +# Actor-Critic Architecture + +To process observations into actions, our controllers utilize an Actor-Critic architecture. Because we use Proximal +Policy Optimization (PPO), the pipeline fundamentally requires separate networks for the policy (Actor) and the value +estimation (Critic). + +**Centralized Architecture (Baseline)** + +This pipeline treats the agent as a single entity and uses standard Proximal Policy Optimization (PPO). + +- Centralized Actor: Composed of two chained MLPs (Sensor $\rightarrow$ Motor) passing a hidden state between them. The + centralized sensor receives the concatenated global state vector of all limbs at once and processes it into a hidden + state. The centralized motor receives this hidden state and outputs the joint offsets for all actuators + simultaneously. This is mathematically equivalent to using one large MLP with hidden layers, but splitting makes the + implementation easier by allowing us to reuse the same components for the decentralized modules. +- Centralized Critic: Composed of two sequential MLPs (Feature Extractor $\rightarrow$ Critic). Because PPO evaluates + the state-value function, this network only receives the concatenated global state vector (no actions). It outputs a + single scalar estimating the expected future reward for the entire agent. + +Our policy and value networks use separate input networks/feature extractors as advised by the SEL3 course assistants and the blog. For continuous actions this should allow better learning at a small cost. + +**Decentralized Architecture** + +This pipeline utilizes the "Centralized Training with Decentralized Execution" principle, specifically the NerveNet-MLP +variant. + +- Decentralized Actor, split into three distinct models: + - Sensor: A local model at each node. It receives its local state plus the goal vector directly, processing them into + an initial hidden state. + - Propagation: Nodes synchronously compute and exchange messages with connected neighbors for $N$ steps to update + their hidden states. See [communication.md](./communication.md) for details. + - Motor: A local model uses its final updated hidden state to output the joint offset strictly for its own actuator. +- Centralized Critic: Composed of two sequential MLPs (Feature Extractor $\rightarrow$ Critic). During training, it + acts globally by taking the concatenated state vectors from all sensors to output a single, global state-value scalar + evaluating the entire agent's pose. + +To keep the implementation simple, we should use one critic per node in our architecture, but only a single, global +critic for all nodes at once, for the following reasons: + +1. Credit Assignment Problem (Ha, 2017): The MuJoCo simulator provides an overall reward based on the brittle star + movement progression, e.g. total distance travelled. Using an isolated critic for each node in the network would not + allow to determine which local action contributed to the global success. A global critic solves this by evaluating + the combined state of the agent at once. +2. Implementation simplicity: Building a second decentralized message-passing graph for the critic (NerveNet-2) would + require more coding. Using a standard MLP that concatenates all raw input vectors is much easier to program while + mathematically equivalent. + +## Implementation Details (Network Depth) + +Inspired by: https://iclr-blog-track.github.io/2022/03/25/ppo-implementation-details/ + +The MLPs used in both pipelines are defined with specific hidden layer configurations to balance learning capability +and computational cost. As of right now, though this might change as we make progress in our experiments, we use: + +- Input Networks (Sensors & Feature Extractors): These networks map the raw state inputs to internal hidden states. + They are configured as standard dense networks with 2 hidden layers of 64 nodes each (`[64, 64]`) and utilize `tanh` + activation functions. +- Output Networks (Motors, Actors & Critics): The final output models are intentionally kept shallow. The Actor + directly projects the hidden state to a continuous action distribution (`mean` and `log_std`) using a single dense + output layer (zero hidden layers) initialized orthogonally. The Critic functions similarly, mapping the hidden + representation to a single scalar value. + +Note: For the continuous action distributions outputted by the Actor/Motor, we explicitly use mean and log_std as advised by previous research to maintain learning stability.ReferencesPPO Algorithm: Schulman et al. (2017), Proximal Policy Optimization Algorithms.Decentralized Message-Passing & NerveNet-MLP: Wang et al. (2018), NerveNet: Learning Structured Policy with Graph Neural Networks. + +**References** + +- Ha, D. (2017, October 29). A Visual Guide to Evolution Strategies. 大トロ ・ Machine Learning. https://blog.otoro.net/2017/10/29/visual-evolution-strategies/ +- Schulman, John, Filip Wolski, Prafulla Dhariwal, Alec Radford, and Oleg Klimov. ‘Proximal Policy Optimization Algorithms’. arXiv:1707.06347. Preprint, arXiv, 28 August 2017. https://doi.org/10.48550/arXiv.1707.06347. diff --git a/docs/design/mlp_architecture.md b/docs/design/mlp_architecture.md deleted file mode 100644 index 93a6e51..0000000 --- a/docs/design/mlp_architecture.md +++ /dev/null @@ -1,24 +0,0 @@ -# Network Architecture (MLP Pipeline per Module) - -In the decentralized architectures (arm-level and segment-level), each controller/module follows the same **shared MLP-based pipeline** inspired by NerveNet-style message passing. The pipeline consists of 5 MLPs (4 in the case of centralized, with no messager): - -- **SENSOR**: Processes local observations for the actor branch. -- **FEATURE EXTRACTOR**: Processes local observations for the critic branch. -- **MESSAGER**: Processes incoming hidden states from neighboring modules (via the chosen communication scheme) and produces an aggregated hidden state. -- **ACTOR**: Takes the aggregated hidden state and outputs the action distribution (mean and log_std). -- **CRITIC**: Takes the aggregated hidden state and outputs a scalar value estimate. - -## MLP Pipeline - -![MLP Pipeline - Actor-Critic with Message Passing](../img/mlp_pipe.png "MLP neural network pipeline per module") - - -## Implementation Details Related To PPO - -Inspired by: https://iclr-blog-track.github.io/2022/03/25/ppo-implementation-details/ - -For starters we will execute our tests with simple models. Each MLP will have only 1 hidden layer. This will be expanded as needed. The exceptions are the input networks / feature extractors — they will be given 2 hidden layers and 64 nodes per layer as advised in the blog. This might change as we make progress in our experiments. - -Our policy and value networks use separate input networks / feature extractors as advised by the SEL3 course assistants and the blog. For continuous actions this should allow better learning at a small cost. - -We use mean and log_std to represent the action distribution, because it is advised by previous research for learning stability and other reasons. \ No newline at end of file From 9f4a5c57ba55d9bda9728553b0bde083b7053e1b Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Wed, 8 Apr 2026 14:37:54 +0200 Subject: [PATCH 09/12] chore: cleanup unused files --- docs/.gitkeep | 0 docs/img/mlp_pipe.png | Bin 40184 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/.gitkeep delete mode 100644 docs/img/mlp_pipe.png diff --git a/docs/.gitkeep b/docs/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/img/mlp_pipe.png b/docs/img/mlp_pipe.png deleted file mode 100644 index 39773299c66c3e28ba8ec6ca278904a03e6040f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40184 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 From 16cc86b30c14d08d752dffe274bdf7f3ed03a855 Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Wed, 8 Apr 2026 14:44:09 +0200 Subject: [PATCH 10/12] docs: add actor/critic pipeline figures --- docs/design/actor-critic.md | 60 +++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/docs/design/actor-critic.md b/docs/design/actor-critic.md index 81fbc3e..e01ea96 100644 --- a/docs/design/actor-critic.md +++ b/docs/design/actor-critic.md @@ -19,6 +19,29 @@ This pipeline treats the agent as a single entity and uses standard Proximal Pol Our policy and value networks use separate input networks/feature extractors as advised by the SEL3 course assistants and the blog. For continuous actions this should allow better learning at a small cost. +```mermaid +%%{ init: { 'flowchart': {'defaultRenderer': 'elk' } } }%% +graph TD + Obs([Global Observation]) + + Sens[Sensor] + Act[Motor] + OutAct([Action Distribution
mean, log_std]) + + Feat[Feature extractor] + Crit[Critic] + OutCrit([Value Estimate
scalar]) + + Obs --> Sens + Obs --> Feat + + Sens -->|"Hidden state"| Act + Feat -->|"Hidden state"| Crit + + Act --> OutAct + Crit --> OutCrit +``` + **Decentralized Architecture** This pipeline utilizes the "Centralized Training with Decentralized Execution" principle, specifically the NerveNet-MLP @@ -27,7 +50,7 @@ variant. - Decentralized Actor, split into three distinct models: - Sensor: A local model at each node. It receives its local state plus the goal vector directly, processing them into an initial hidden state. - - Propagation: Nodes synchronously compute and exchange messages with connected neighbors for $N$ steps to update + - Propagator: Nodes synchronously compute and exchange messages with connected neighbors for $N$ steps to update their hidden states. See [communication.md](./communication.md) for details. - Motor: A local model uses its final updated hidden state to output the joint offset strictly for its own actuator. - Centralized Critic: Composed of two sequential MLPs (Feature Extractor $\rightarrow$ Critic). During training, it @@ -45,6 +68,37 @@ critic for all nodes at once, for the following reasons: require more coding. Using a standard MLP that concatenates all raw input vectors is much easier to program while mathematically equivalent. +```mermaid +%%{ init: { 'flowchart': {'defaultRenderer': 'elk' } } }%% +graph TD + Obs([Local Observation]) + + Sens[Sensor] + Prop[Propagator] + Feat[Feature extractor] + + Mot[Motor] + Crit[Critic] + + OutMot([Action Distribution
mean, log_std]) + OutCrit([Value Estimate
scalar]) + + Obs --> Sens + Sens -->|"Hidden state"| Prop + Obs --> Feat + + Prop -->|"Hidden state"| Mot + + + Feat -->|"Hidden state"| Crit + + Mot --> OutMot + Crit --> OutCrit + + Prop -.->|"message passing"|Prop + +``` + ## Implementation Details (Network Depth) Inspired by: https://iclr-blog-track.github.io/2022/03/25/ppo-implementation-details/ @@ -60,9 +114,11 @@ and computational cost. As of right now, though this might change as we make pro output layer (zero hidden layers) initialized orthogonally. The Critic functions similarly, mapping the hidden representation to a single scalar value. -Note: For the continuous action distributions outputted by the Actor/Motor, we explicitly use mean and log_std as advised by previous research to maintain learning stability.ReferencesPPO Algorithm: Schulman et al. (2017), Proximal Policy Optimization Algorithms.Decentralized Message-Passing & NerveNet-MLP: Wang et al. (2018), NerveNet: Learning Structured Policy with Graph Neural Networks. +Note: For the continuous action distributions outputted by the Motor, we explicitly use `mean` and `log_std` as advised +by previous research to maintain learning stability. **References** - Ha, D. (2017, October 29). A Visual Guide to Evolution Strategies. 大トロ ・ Machine Learning. https://blog.otoro.net/2017/10/29/visual-evolution-strategies/ - Schulman, John, Filip Wolski, Prafulla Dhariwal, Alec Radford, and Oleg Klimov. ‘Proximal Policy Optimization Algorithms’. arXiv:1707.06347. Preprint, arXiv, 28 August 2017. https://doi.org/10.48550/arXiv.1707.06347. +- Wang, Tingwu, Renjie Liao, Jimmy Ba, and S. Fidler. ‘NerveNet: Learning Structured Policy with Graph Neural Networks’. Conference paper presented at International Conference on Learning Representations. 15 February 2018. https://www.semanticscholar.org/paper/NerveNet:-Learning-Structured-Policy-with-Graph-Wang-Liao/249408527106d7595d45dd761dd53c83e5a02613. From 67027e890540eed7dd0c124720b6f4007ba9a106 Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Wed, 8 Apr 2026 15:43:12 +0200 Subject: [PATCH 11/12] fix: update file link --- docs/README.md | 2 +- docs/design/actor-critic.md | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/README.md b/docs/README.md index ac56e31..a4f4ea0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,11 +2,11 @@ ## Design & architecture ([`/design`](./design/)) +- [Actor/critic architecture](./design/actor-critic.md): Description of the actor-critic pipeline. - [Communication](./design/communication.md): Message propagation, Nerve-Net style. - [Controllers](./design/controllers.md): Macroscopig brain toplogy, centralized, arm-level, segment-level. - [Input/output](./design/input_action_spaces.md): Description of the model's input and output. - [Learning algorithm](./design/learning_algorithm.md): RL techniques, i.e. PPO. -- [MLP architecture](./design/mlp_architecture.md): Module wiring, state/action spaces, and actor-critic pipeline. - [Reward function](./design/learning_algorithm.md): Goals, fitness tracking, and reward structures. ## API reference ([`/api`](./api/)) diff --git a/docs/design/actor-critic.md b/docs/design/actor-critic.md index e01ea96..245fefd 100644 --- a/docs/design/actor-critic.md +++ b/docs/design/actor-critic.md @@ -20,7 +20,6 @@ This pipeline treats the agent as a single entity and uses standard Proximal Pol Our policy and value networks use separate input networks/feature extractors as advised by the SEL3 course assistants and the blog. For continuous actions this should allow better learning at a small cost. ```mermaid -%%{ init: { 'flowchart': {'defaultRenderer': 'elk' } } }%% graph TD Obs([Global Observation]) @@ -69,7 +68,6 @@ critic for all nodes at once, for the following reasons: mathematically equivalent. ```mermaid -%%{ init: { 'flowchart': {'defaultRenderer': 'elk' } } }%% graph TD Obs([Local Observation]) @@ -96,7 +94,6 @@ graph TD Crit --> OutCrit Prop -.->|"message passing"|Prop - ``` ## Implementation Details (Network Depth) From 268c0461f94a3d11fc374be18f1b1653ff142fc1 Mon Sep 17 00:00:00 2001 From: cedric Date: Wed, 8 Apr 2026 17:56:26 +0000 Subject: [PATCH 12/12] feat(docs): expanded reward function docs --- docs/design/reward_function.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/design/reward_function.md b/docs/design/reward_function.md index 2237aa5..a72fb33 100644 --- a/docs/design/reward_function.md +++ b/docs/design/reward_function.md @@ -9,6 +9,10 @@ inputs must be distributed fairly to guarantee an objective comparison between d within a finite number of timesteps $T$. - To motivate efficient movement, the amount of timesteps taken to reach the goal will be used as penalty. +## From reward to PPO + +The resulting reward is passed to our PPO library. Our critic network (value function) predicts how good our eventual reward will be for the current state, this value is combined with the reward from the reward function to get advantages. These advantages are then used to calculate the losses to update both our critic and actor pipeline. + ## Rationale Using a light source (or a gradient) is biologically plausible for many simple organisms. By normalizing all signals