Architecture

What we are building
ROS 2 Jazzy software on a Jetson (Nav2, MoveIt 2, ros2_control for the arms), a micro-ROS base controller, and a hardwired safety chain under everything.
Where we are
Nav2 output passes through the smoother and collision monitor; reactive stopping has not been demonstrated (S2). Four other motion sources bypass them; twist_mux is in review (platform-sw #90), not on main. The 2.0 base firmware (CubeMX project platform-fw #22 in review) and the combined upper-body model are not on main.
What is next
One arbitrated command path (twist_mux ahead of the smoother), a reactive layer that is never switched off and has its own lifecycle, a latched software stop and base command timeouts, before rc.1.

Critical gaps before rc.1

Critical: Commands held at zero across E-stop and RESET (AR-05)

After RESET, the last non-zero command from a direct source reaches the base (inferred from S6 and S7 plus no consumer on main).

Closure test: After RESET, no motion from any source until an explicit operator release; verified in simulation and on the bench.

NR-03 · owner @skunal3318 (navigation lead) · closes when: After RESET, no motion from any source until an explicit operator release; verified in simulation and on the bench

Each is detailed in the architecture review below. These are functional software layers; the hardwired E-01 chain remains the only safety function.

Three layers

19 items

Click or press Enter on a package for its repository, nodes, topics, owner, status and findings. Status colours use the delivery status legend.

runtime: works in simulation with fixture runtime evidence (rounded) · physical: none recorded physical acceptance (box)
Layer 1: ROS 2 packages on main (generated from package.xml)openamrobot_bringup depends on openamrobot_dockingopenamrobot_bringup depends on openamrobot_driversopenamrobot_bringup depends on openamrobot_nav2openamrobot_bringup depends on openamrobot_perceptionopenamrobot_docking depends on openamrobot_descriptionopenamrobot_docking depends on openamrobot_gazeboopenamrobot_docking depends on openamrobot_nav2openamrobot_gazebo depends on openamrobot_descriptionopenamrobot_nav2 depends on openamr_nav_msgsopenamrobot_upperbody_bringup depends on openamrobot_upperbody_controlopenamrobot_upperbody_bringup depends on openamrobot_upperbody_descriptionopenamrobot_upperbody_bringup depends on openamrobot_upperbody_gazeboopenamrobot_upperbody_bringup depends on openamrobot_upperbody_moveitopenamrobot_upperbody_gazebo depends on openamrobot_upperbody_descriptionopenamrobot_upperbody_moveit depends on openamrobot_upperbody_descriptioninterface_install_consumer depends on openamr_nav_msgsinterface_install_consumer depends on openamr_ui_msgsopenamr_ui_bringup depends on openamr_ui_packageopenamr_ui_package depends on openamr_ui_msgsopenamr_ui_package depends on openamrobot_descriptionopenamr-platform-swopenamrobot_bringupopenamrobot_descriptionopenamrobot_dockingopenamrobot_driversopenamrobot_gazeboopenamrobot_nav2openamrobot_perceptionopenamr-upperbody-swopenamrobot_upperbody_bringupopenamrobot_upperbody_controlopenamrobot_upperbody_descriptionopenamrobot_upperbody_gazeboopenamrobot_upperbody_moveitopenamrobot-manipulationopenarm_v2_fake_baselineopenamrobot-interfacesopenamr_nav_msgsopenamr_ui_msgsinterface_install_consumeropenamrobot-uiopenamr_ui_bringupopenamr_ui_msgsopenamr_ui_package

Layer 2: firmware

Teensy 4.0 Gate A overlay MERGED UNTESTED
openamr-platform-fw

Jetson + Teensy + ZBLD/PWM drivetrain; micro-ROS over USB serial; 200 ms /cmd_vel watchdog (owner statement, no physical test recorded). pid.h is missing on main, so Gate A cannot be rebuilt from the repository.

STM32H723ZG base controller (Gate B) IN REVIEW
openamr-platform-fw

Portable core plus board port: ZLAC8015D on CAN1, Daly BMS on CAN2, CAN3 auxiliary, IMU on SPI, near-field UARTs, safety-chain diagnostics; micro-ROS over UDP. boards/stm32 is empty on main; the CubeMX project is in review (platform-fw #22; change requests as comments). Lift interlock #20 and micro-ROS transport #21 are due 13 Nov; safety state machine #17 is not started.

Lift interface (CAN3) DOCUMENTED ONLY
openamr-upperbody-fw

Small CAN-to-lift interface drives the lift stage and reads Hall and end switches; motion interlocks in the base controller.

Link to the host: micro-ROS over UDP on Ethernet (USB on the bench and on Gate A). No bus carries a safety function.

Layer 3: hardware

Jetson Orin NX on J401 (compute)STM32H723ZG or Teensy (base controller)ZLAC8015D + 2 hub motors (drive)Daly BMS + 8S LF105 (battery)PNOZ s3 + K1/K2 (hardwired safety chain)DOLD Hexalift V4 (lift)2 x OpenArm 2.0 (arms, CAN-FD)Sensors: RPLIDAR S3, Gemini 336L, ZED Mini, 2 x TF-Luna, 4 x MB7060, ICM-42688-P

Hardware page for blocks and connections.

Motion command path: current and planned

Current motion path (nodes and topics)

What this shows: every node that publishes velocity commands on main and which of them pass the smoother and the collision monitor.

How to read it: green arrows pass the reactive motion layer (smoother and collision monitor, functional); red dashed arrows bypass it; grey boxes are topics and the base.

Scroll sideways inside the diagram.

Current (main 8a7a5b1), as observed in simulationNav2 controller and behaviors/cmd_vel_nav -> velocity_smoother ->collision_monitor -> /cmd_velUI teleop, blocks, voice (browser)rosbridge -> /cmd_vel (final)UI Maps page: second Nav2 controllersecond controller_server -> /cmd_velUI recording replayreplay -> /cmd_vel and other live topicsDocking (dock_trigger)-> /cmd_vel (final); switchescollision_monitor offvelocity_smoother/cmd_vel_smoothedcollision_monitorpolygons; never engagedin S2/cmd_velfinal topicbaseno commandtimeout insimRed dashed: bypasses the smoother and the collision monitor (observed in S3, S4, S5).

Source: audit pass 2 scenarios S3, S4, S5 and the node review at platform-sw 8a7a5b1 and UI c5558ad5 · 24 Oct 2026

Planned motion path (nodes, topics, twist_mux priorities)

What this shows: all motion sources entering twist_mux with their priorities, then the smoother and the collision monitor under their own lifecycle manager, the base with a command timeout, and the stop lock.

How to read it: boxes with ? are open decisions; the dashed blue frame is the planned lifecycle manager of the reactive motion layer; the red band is the hardwired chain below all software.

Scroll sideways inside the diagram.

Planned (proposal from the #35 branch and audit pass 2; not yet approved)Nav2 controller and behaviorscmd_vel_nav · priority 10Dockingcmd_vel_dock · priority 20UI teleop / manualcmd_vel_joy (or /ui/cmd_vel) · priority100 · 0.25 s deadmanSoftware stopcmd_vel_stop + stop_lock · priority 255 ·lock? Docking implementationopennav_docking or the dock_triggersequencer (open)twist_muxout: /cmd_vel_muxvelocity_smootherout:/cmd_vel_smoothedcollision_monitorout: /cmd_velown lifecycle manager with respawn; zero output while inactivebasecommandtimeout? rc.1 base controllerTeensy Gate A or STM32 (open decision)Hardwired E-01 chain below all software: 2 E-stops, PNOZ s3, K1/K2 (the only safety function)

Source: #35 review branch (issue #35) and audit pass 2 actions 1, 2, 8 and 12; proposal, not an approved decision · 24 Oct 2026

Every source that reaches the wheels today

  • passes the smoother and collision monitor (reactive stop not demonstrated, S2): Nav2 controller and behaviors: /cmd_vel_nav -> velocity_smoother -> collision_monitor -> /cmd_vel
  • bypass: UI teleop, blocks, voice (browser): rosbridge -> /cmd_vel (final)OA-02-P2 · scenario S4 · code openamrobot-ui/web/src/shared/constants/index.js:7 · owner @parthkeshari (UI lead); reviewers @panthera-momagdii (tech lead, UI reviewer) and UI reviewer (GitHub handle to be confirmed); simulation checks @nikgolobo (simulation checks) · closes when: Rerun S4 and S7a: rosbridge is not a /cmd_vel publisher; robot stops before the wall; stops within 0.5 s after the UI connection drops
  • bypass: UI Maps page: second Nav2 controller: second controller_server -> /cmd_velOA-03-P2 · scenario S5 · owner @parthkeshari (UI lead); reviewers @panthera-momagdii (tech lead, UI reviewer) and UI reviewer (GitHub handle to be confirmed); simulation checks @nikgolobo (simulation checks) · closes when: Rerun S5: build_map leaves one controller_server; platform map_server and AMCL states unchanged
  • bypass: UI recording replay: replay -> /cmd_vel and other live topicsOA-48 · scenario S5 · code location withheld (security) · owner @parthkeshari (UI lead); reviewers @panthera-momagdii (tech lead, UI reviewer) and UI reviewer (GitHub handle to be confirmed); simulation checks @nikgolobo (simulation checks) · closes when: Replay of a recording that contains command topics: robot travel 0
  • bypass: Docking (dock_trigger): -> /cmd_vel (final); switches collision_monitor offOA-04-P2 · scenario S3 · code openamr-platform-sw/ros2/src/openamrobot_docking/scripts/dock_trigger.py:833 · owner @skunal3318 (navigation lead) · closes when: Rerun S3: 5 of 5 docked; collision_monitor the sole /cmd_vel publisher; zero lifecycle transitions

Failure modes observed

Planned chain

twist_mux > velocity_smoother > collision_monitor (stop, slowdown and approach polygons; LiDAR plus near-field sources) > /cmd_vel > base (controller watchdog; simulated base with cmd_vel timeout)

Reactive motion layer (velocity_smoother and collision_monitor) under its own lifecycle manager with respawn; the arbiter outputs zero when the monitor is inactive. Base command timeout: firmware watchdog (Gate A 200 ms) and diff_drive_controller cmd_vel_timeout in simulation. Software stop: stop_lock at priority 255, latched until released.

Below all software: the hardwired E-01 chain (two E-stops, PNOZ s3, K1/K2). Software stops are functional stops only.

Names and priorities are proposals from the #35 branch and audit pass 2, now implemented for review in platform-sw #90 (stop lock priority 255, UI on /ui/cmd_vel); they change only through a contract change request, and none is open yet.

Open decisions (drawn with ? above)

rc.1 base controller: Teensy Gate A or STM32H723ZG OPEN

Gate A uses the Teensy with a 200 ms /cmd_vel watchdog (owner statement; no physical test recorded). P-02 rev19.2 sets an STM32 go/no-go on 6 Nov 2026 with the validated Teensy build as fallback (a P-02 date, not a register date). Input: the CubeMX project in platform-fw #22 (ready for review). No approved decision says which one rc.1 runs.

Options: Teensy Gate A (legacy ZBLD drivetrain) / STM32H723ZG on NUCLEO-H723ZG (Gate B, ZLAC8015D)

Owner to be confirmed · date 6 Nov 2026 go/no-go (P-02 rev19.2)

Docking: opennav_docking routed through arbitration, or keep the dock_trigger sequencer OPEN

Tag detection uses upstream apriltag_ros; the custom part is dock_trigger.py. The docking work package names opennav_docking as the baseline; the #35 branch keeps the sequencer and evaluates migration later.

Options: opennav_docking (SimpleNonChargingDock) with cmd_vel into the docking mux input / dock_trigger sequencer with cancel, node clock and tests

Owner @fridayali (docking and robot description) · date not set

DockStop polygon in place of switching the collision monitor off OPEN

The #35 prototype replaces the collision-monitor pause with a DockStop polygon. It met its arbitration criteria in simulation (single publisher, monitor never deactivated) but docked 0 of 2 under sim time in pass 2; the branch's own notes report 2 of 3. Still under evaluation.

Options: DockStop polygon / Approach-only near the dock with reduced speed

Owner @skunal3318 (navigation lead) · date not set

Motion chain and contracts: designed and on main

Motion chain IN REVIEW

As designed (P-03 rev19.7, register): every motion source enters twist_mux, then velocity_smoother, then collision_monitor; collision_monitor is the only publisher of /cmd_vel. A software stop lock sits at the top priority.

On platform-sw main (8a7a5b1): Nav2, then velocity_smoother, then collision_monitor, then /cmd_vel (since #54). There is no twist_mux; docking and the UI still publish /cmd_vel directly drift 14. Issue #35 is open, due 30 Oct.

In review: platform-sw #90 adds twist_mux and a stop gate (stop lock priority 255) and relays UI TwistStamped on /ui/cmd_vel; changes requested on 23 Oct, DCO sign-off missing. Recovery behaviour tree: #91 changes requested (the retry counter stays NOT_READY; one failing test); #92 approved once on 24 Oct, needs sign-off and a second approval.

Simulation: the LiDAR body filter on main assumes an unrotated LiDAR; with the URDF yaw of pi it removes the robot front and keeps the body (#74, due 6 Nov, no PR). The real-robot filter is consistent but not re-measured on the S3.

Contracts MERGED AND TESTED

Navigation status: CONTRACT_VERSION 1 on interfaces main (d048fb8); interfaces #28 (SiteConfigStatus) bumps it to 2, is approved and is blocked only on DCO sign-off; platform-sw #73 (producer) has changes requested. Repositories pin three different interfaces commits (87ab8a9, fa7c438, d048fb8) drift 10.

/estop: platform-fw #17 and platform-sw #66 need an /estop contract; no contract change request exists drift 11.

I8 base-status contract: freeze due 6 Nov with the STM32 go/no-go (P-02 rev19.2).

C9 capture schema: 26/26 metadata checks pass; acceptance issue interfaces #18 open. The embodied-AI scaffold (C10 dataset QA validator with tests, registry, recorder and policy-runner skeletons) is on main; no capture session, no trained policy.

Source: P-03 rev19.7; decisions register at 1c3f516; platform-sw main 8a7a5b1 and PRs #90 to #93; interfaces main d048fb8 and #28; platform-fw issues #17, #20, #21 · 24 Oct 2026

Planned 2.0 structure

Layered stack, planned 2.0

What this shows: the 2.0 software and hardware layers from tasks down to hardware, each with its delivery status; the base electrical layer is shared with release 001.

How to read it: each box has a coloured left bar, a status chip with an icon and the status in words; rounded pills are runtime evidence, pointed boxes are physical acceptance; the legend buttons highlight every item with that status; the layer with two rings is the shared base.

10 items
Planned 2.0 layer stack (top: tasks; bottom: hardware)Data captureC9 schema merged and tested; C10 quality gate in review; no capture sessionTask and UI layerUI motion bypasses the reactive motion layer on main; arbitration input plannedManipulationCombined model and MoveIt in PR #14 (draft)Navigation and dockingFails on main in simulation; docking 0 of 5Reactive motion layertwist_mux, smoother, collision monitor, stop lock (functional); #35 branchROS 2 driversLiDAR and micro-ROS agent not installable from the manifest (fix in manifest #18)micro-ROS linkUSB serial on the Teensy (merged); UDP over Ethernet for the STM32 (documented)FirmwareSTM32 in review (fw #22); Teensy overlay merged; state machine not startedBase electrical: power, safety chain, charging, E-stop, proximityShared with release 001; contactor order blocked (BUY-32)Drive, compute, lift, armsBOM 7.10; nothing built for 2.0
Planned 2.0 layer stack (top: tasks; bottom: hardware)Data captureC9 schema merged and tested; C10 qualitygate in review; no capture sessionTask and UI layerUI motion bypasses the reactive motionlayer on main; arbitration input plannedManipulationCombined model and MoveIt in PR #14(draft)Navigation and dockingFails on main in simulation; docking 0of 5Reactive motion layertwist_mux, smoother, collision monitor,stop lock (functional); #35 branchROS 2 driversLiDAR and micro-ROS agent notinstallable from the manifest (fix inmanifest #18)micro-ROS linkUSB serial on the Teensy (merged); UDPover Ethernet for the STM32 (documented)FirmwareSTM32 in review (fw #22); Teensyoverlay merged; state machine notstartedBase electrical: power, safetychain, charging, E-stop,Shared with release 001; contactor orderblocked (BUY-32)Drive, compute, lift, armsBOM 7.10; nothing built for 2.0

Source: P-03 rev19.7; E-01 rev1.4; GitHub state and audit pass 2 · 24 Oct 2026

Lifecycle managers: today and planned

What this shows: which lifecycle manager brings each Nav2 node up and down, today on main and in the plan.

How to read it: red: today one manager handles the planners and the reactive motion layer together, so one crash takes the reactive motion layer down; green: the planned separate manager for the reactive motion layer with respawn.

Scroll sideways inside the diagram.

Lifecycle managers: today (left) and planned (right)lifecycle_manager_navigation (one manager)controller_serverplanner_serversmoother_serverbehavior_serverbt_navigatorwaypoint_followervelocity_smoother (reactive motion layer)collision_monitor (reactive motion layer)S7b: after controller_server was killed, the manager deactivatedcollision_monitor, velocity_smoother and bt_navigator 4.4 s later(OA-56). use_respawn default False.lifecycle_manager_navigationcontroller_serverplanner_serversmoother_serverbehavior_serverbt_navigatorwaypoint_followerreactive motion layer manager (name open) with respawnvelocity_smoothercollision_monitortwist_mux (not lifecycle; supervised)Planned: respawn on; zero output while the monitor is inactive.Source: audit pass 2 action 8 (proposed target 13 Nov); not yet adecision.

Source: navigation_launch.py on main (8a7a5b1); audit pass 2 S7b (OA-56); plan: audit pass 2 action 8 (proposal) · 24 Oct 2026

Deployment

What this shows: the processes on the Jetson, the base controller and the operator device, and the links between them (USB, Ethernet UDP, CAN, Wi-Fi, rosbridge).

How to read it: boxes are devices with their delivery status; arrows are links with the bus or protocol; the red box is the hardwired safety chain. UI security is shown at area level only.

5 items
Deployment: processes per device and the links between themOperator devicebrowser: operator UIJetson Orin NX (2.0 compute)ROS 2 Jazzy: Nav2, reactive motion layer,docking, MoveIt, arm ros2_control, micro-ROSagent, UI backend and rosbridge, data captureBase controllerSTM32H723ZG (2.0) or Teensy 4.0(Gate A): micro-ROS client,drive, BMS, lift interface, IMU,near-field sensors, safety-chaindiagnostics (functional)Field devicesZLAC8015D drive, Daly BMS, lift interface, OpenArm arms, RPLIDARS3, camerasHardwired safety chainE-stops, PNOZ s3, K1/K2: below allsoftware1234561Wi-Fi: rosbridge and UI backend (security reviewed at area level: UI command path hardening)2Ethernet UDP micro-ROS (STM32); USB serial (Teensy, bench and Gate A)3CAN1 drive, CAN2 BMS, CAN3 lift; UART near-field; SPI IMU4CAN-FD arms; USB cameras and LiDAR5cuts actuator power (SAFE bus)6diagnostic inputs only
Deployment: processes per device and the links between themOperator devicebrowser: operator UIlink to Jetson Orin NX (2.0 compute):Wi-Fi: rosbridge and UI backend (securityreviewed at area level: UI command pathhardening)Jetson Orin NX (2.0 compute)ROS 2 Jazzy: Nav2, reactive motion layer,docking, MoveIt, arm ros2_control,micro-ROS agent, UI backend androsbridge, data capturelink to Base controller: Ethernet UDPmicro-ROS (STM32); USB serial (Teensy,bench and Gate A)link to Field devices: CAN-FD arms; USBcameras and LiDARBase controllerSTM32H723ZG (2.0) or Teensy 4.0 (Gate A):micro-ROS client, drive, BMS, liftinterface, IMU, near-field sensors,safety-chain diagnostics (functional)link to Field devices: CAN1 drive, CAN2BMS, CAN3 lift; UART near-field; SPI IMUField devicesZLAC8015D drive, Daly BMS, liftinterface, OpenArm arms, RPLIDAR S3,camerasHardwired safety chainE-stops, PNOZ s3, K1/K2: below allsoftwarelink to Field devices: cuts actuatorpower (SAFE bus)link to Base controller: diagnosticinputs only

Source: P-03 rev19.7 items 14 and 22; E-01 rev1.4; BOM 7.10; architecture firmware layer · 24 Oct 2026

TF tree

What this shows: the coordinate frames of the robot description and their parents (REP 105: base_footprint, base_link, then sensors, lift and arms).

How to read it: each row is a frame; lines connect a frame to its parent; frames in grey are roots or parents not listed in the frames table.

TF tree (URDF on main and PR #14)-base_footprintbase_linklidar_linkimu_linkbase_camera_linktof_frontus_front_leftlift_column_base_linklift_carriage_linkopenarm_left_base_linkopenarm_right_base_linkhead_camera_link

Source: URDF on platform-sw main 8a7a5b1 and PR #14 (in review) · 24 Oct 2026

Interfaces and contracts

What this shows: the messages and contracts in openamrobot-interfaces with their publishers and consumers, including SiteConfigStatus (in review).

How to read it: each box has a coloured left bar, a status chip with an icon and the status in words; select a message for its definition or PR; publishers on the left, consumers on the right.

7 items
Interfaces and contracts: publishers, messages and consumersPublisherMessage or contractConsumernavigation_status_node (platform-sw)NavigationStatus (/navigation/status)CONTRACT_VERSION 1 on main; embeds Localization, NavStack,Protection, MotionSourceCoverage, Sensor, Recovery, NavTask,Docking and Parking statusno consumer on main (UI planned)platform-sw #73 (open)SiteConfigStatus (in NavigationStatus, version 2)interfaces #28 bumps CONTRACT_VERSION 1 to 2 (issue #27)none yetopenamrobot_docking (issue #33)DockingStatus (native producer)Message merged; native producer not startednavigation_status_nodeUI backend nodesArrayPoseStampedWithCovarianceRoute waypointsUI web clientno server found on mainMoveBase (action)Defined, unusedno client found on main-I1 manipulation and I8 base-status contractsProposed; not accepted (OA-15)-no recorder yetC9 whole-body capture schemaSchema tests in CIC10 quality gate (in review)

Source: openamrobot-interfaces main 87ab8a9; interfaces #27 and #28; platform-sw #73; code search on main · 24 Oct 2026

Architecture review

Assessment of the architecture against current upstream practice. Each point: what we do, what good practice is, the gap, a recommendation and its priority. Software stops named here are functional stops; the hardwired E-01 chain stays the only safety function.

AR-01 Command arbitration critical before rc.1
What we do
Only Nav2 output passes the smoother and collision monitor; reactive stopping has not been demonstrated (S2). The UI, docking, a UI-launched controller and replay publish the final /cmd_vel.
Good practice
Every source enters a multiplexer with priorities, per-input timeouts and lock topics, ahead of the smoother and the collision monitor (twist_mux pattern used by common ROS 2 platforms).
Gap
Four bypasses observed at runtime (S3, S4, S5).
Recommendation
Land #35 with twist_mux ahead of velocity_smoother; move the UI to a teleop input with a deadman; remove UI-launched navigation; restrict replay. Add one launch test per source that asserts collision_monitor is the only /cmd_vel publisher.
Sources
twist_mux, Nav2 Velocity Smoother, Nav2 Collision Monitor
OA-02-P2 · scenario S4 · code openamrobot-ui/web/src/shared/constants/index.js:7 · owner @parthkeshari (UI lead); reviewers @panthera-momagdii (tech lead, UI reviewer) and UI reviewer (GitHub handle to be confirmed); simulation checks @nikgolobo (simulation checks)OA-04-P2 · scenario S3 · code openamr-platform-sw/ros2/src/openamrobot_docking/scripts/dock_trigger.py:833 · owner @skunal3318 (navigation lead)OA-03-P2 · scenario S5 · owner @parthkeshari (UI lead); reviewers @panthera-momagdii (tech lead, UI reviewer) and UI reviewer (GitHub handle to be confirmed); simulation checks @nikgolobo (simulation checks)OA-48 · scenario S5 · code location withheld (security) · owner @parthkeshari (UI lead); reviewers @panthera-momagdii (tech lead, UI reviewer) and UI reviewer (GitHub handle to be confirmed); simulation checks @nikgolobo (simulation checks)
AR-02 Reactive layer never switched off critical before rc.1
What we do
Docking deactivates collision_monitor through its lifecycle service for the final approach.
Good practice
The collision monitor stays active; docking-specific behaviour uses polygons (stop, slowdown, approach) or a reduced-speed zone, and the docking controller runs through the same chain.
Gap
No reactive layer during docking; the lifecycle manager resets the stack; status misreports the monitor.
Recommendation
Remove the lifecycle calls (#35 option b). Evaluate the DockStop polygon; until decided, cap docking speed and keep the monitor active.
Sources
Nav2 Collision Monitor, Nav2 Docking Server
OA-04-P2 · scenario S3 · code openamr-platform-sw/ros2/src/openamrobot_docking/scripts/dock_trigger.py:833 · owner @skunal3318 (navigation lead)OA-68 · scenario S3 · code openamr-platform-sw/ros2/src/openamrobot_docking/scripts/dock_trigger.py:833 · owner @fridayali (docking and robot description)
AR-03 Lifecycle isolation of the reactive motion layer critical before rc.1
What we do
velocity_smoother and collision_monitor share one lifecycle manager with the Nav2 servers; bond_timeout 60 s.
Good practice
Keep the last-stage filters alive independently of planner and controller faults; use bond heartbeats and respawn with bounded startup.
Gap
One controller crash removed the monitor (S7b); 1 of 16 starts stalled.
Recommendation
Own lifecycle manager for smoother and monitor with respawn; arbiter outputs zero when the monitor is not active; startup watchdog and retries.
Sources
Nav2 Lifecycle Manager
OA-56 · scenario S7 · code openamr-platform-sw/ros2/src/openamrobot_nav2/launch/navigation_launch.py · owner @skunal3318 (navigation lead)OA-57 · scenario S1, S11 · code openamr-platform-sw/ros2/src/openamrobot_bringup/launch/bringup.launch.py · owner @skunal3318 (navigation lead)
AR-04 Latched software stop and base command timeout critical before rc.1
What we do
The UI stop sends one zero Twist and cancels a goal; the simulated base keeps the last command forever.
Good practice
A lock topic at top priority holds zero until released; the base or its controller zeroes on command timeout in both simulation and robot.
Gap
No latch; publisher loss cannot be validated in simulation (S7).
Recommendation
stop_lock at priority 255; diff_drive_controller with cmd_vel_timeout through gz_ros2_control in simulation; keep the firmware watchdog on the robot.
Sources
twist_mux, diff_drive_controller
OA-16-P2 · scenario S6 · code openamrobot-ui/web/src/components/StatusBar.jsx:63 · owner @parthkeshari (UI lead); reviewers @panthera-momagdii (tech lead, UI reviewer) and UI reviewer (GitHub handle to be confirmed); simulation checks @nikgolobo (simulation checks)OA-45-P2 · scenario S7 · code openamr-platform-sw/ros2/src/openamrobot_description/urdf/gazebo_control.xacro:5 · owner @skunal3318 (navigation lead)
AR-05 Commands held at zero across E-stop and RESET critical before rc.1 new in this review
What we do
No host node consumes the safety-relay state; sources keep publishing while actuator power is off.
Good practice
When actuator power is removed, the command path locks and requires a fresh zero then an explicit new command after RESET.
Gap
After RESET, the last non-zero command from a direct source reaches the base (inferred from S6 and S7 plus no consumer on main).
Recommendation
Publish the Y32 state from the base controller (diagnostic) and engage the stop lock while it is low; owners implement, and it is not a safety function.
Closure test
After RESET, no motion from any source until an explicit operator release; verified in simulation and on the bench.
Sources
twist_mux
NR-03 · owner @skunal3318 (navigation lead)
AR-06 Collision monitor coverage before v2.0.0
What we do
One approach polygon and one LiDAR source; ToF and ultrasonic unused; inflation below the inscribed radius.
Good practice
Stop and slowdown polygons around the footprint plus approach, with every near-field sensor as an observation source (range sources are supported).
Gap
Monitor never engaged in S2; first 0.40 m unobserved in simulation.
Recommendation
Add stop and slowdown polygons; add ToF and ultrasonic range sources; raise inflation to at least the inscribed radius.
Sources
Nav2 Collision Monitor
OA-35-P2 · scenario S2 · code openamr-platform-sw/ros2/src/openamrobot_nav2/config/nav2_params.yaml:177 · owner @skunal3318 (navigation lead)OA-69 · scenario S10, S12 · code openamr-platform-sw/ros2/src/openamrobot_nav2/config/nav2_params.yaml · owner @skunal3318 (navigation lead)
AR-07 Docking framework before v2.0.0
What we do
Custom 2,765-line sequencer with wall-clock deadlines, no cancel and no tests; upstream apriltag_ros for detection.
Good practice
opennav_docking: dock database, staging pose, external detection with filtering, retries, cancellable DockRobot and UndockRobot actions, collision checking.
Gap
No cancel, wrong clock under sim time, blind undock, no site ArUco alignment.
Recommendation
Decide (open decision D-DOCKING). Minimum before rc.1: node clock, cancel, undock only from docked.
Sources
Nav2 Docking Server
OA-36 · code openamr-platform-sw/ros2/src/openamrobot_docking/scripts/dock_trigger.py · owner @fridayali (docking and robot description)OA-53 · scenario S3 · code openamr-platform-sw/ros2/src/openamrobot_docking/scripts/dock_trigger.py:942 · owner @fridayali (docking and robot description)OA-54 · scenario S3 · code openamr-platform-sw/ros2/src/openamrobot_docking/scripts/dock_trigger.py:1800 · owner @fridayali (docking and robot description)OA-46 · code openamr-platform-sw/README.md:3 · owner @fridayali (docking and robot description)
AR-08 Base control and simulation parity before v2.0.0
What we do
Gazebo DiffDrive plugin in simulation; micro-ROS Twist to the firmware on the robot; no ros2_control base.
Good practice
diff_drive_controller in both, giving the same limits, timeout and odometry.
Gap
No shared controller configuration or timeout behaviour.
Recommendation
gz_ros2_control in simulation now; a hardware interface after I8 if the owners choose it. No safety function moves into software.
Sources
ros2_control (Jazzy), diff_drive_controller
OA-45 · code openamr-platform-sw/ros2/src/openamrobot_description/urdf/gazebo_control.xacro:5 · owner @skunal3318 (navigation lead)
AR-09 Frames and naming (REP 103, 105, 144) before v2.0.0
What we do
REP 105 chain in simulation; Gate A real path roots at base_link with static transforms; mixed package prefixes.
Good practice
map, odom, base_footprint, base_link on robot and simulation; unique package names.
Gap
No 2.0 real bringup; ToF and ultrasonic frames differ from the register.
Recommendation
2.0 real profile loading the URDF with the EKF on base_footprint; frame check in CI against the register.
Sources
REP 103, REP 105, REP 144
OA-17 · code openamr-platform-sw/ros2/src/openamrobot_bringup/launch/real_bringup.launch.py:78 · owner @skunal3318 (navigation lead)OA-10 · code openamr-platform-sw/ros2/src/openamrobot_description/urdf/sensors.xacro:125 · owner to be confirmedOA-11 · code openamr-platform-sw/ros2/src/openamrobot_description/urdf/sensors.xacro:116 · owner to be confirmedNR-04 · owner to be confirmed
AR-10 Stamped velocity commands before v2.0.0 new in this review
What we do
geometry_msgs/Twist on the whole chain.
Good practice
TwistStamped through the chain so consumers can reject stale commands by age.
Gap
Only the base watchdog bounds a stale command.
Recommendation
Enable stamped commands in Nav2 and the arbiter once the UI path moves; check the firmware contract (I8) for the stamp.
Sources
Nav2 Jazzy migration notes (TwistStamped)
NR-01 · code openamr-platform-sw/ros2/src/openamrobot_nav2/config/nav2_params.yaml · owner @skunal3318 (navigation lead)
AR-11 UI command path hardening and network security critical before rc.1
What we do
Area-level: UI command path hardening is open. No SROS2 configuration was found in the inspected repositories.
Good practice
Operator interfaces limited to the people and commands that need them, reviewed before deployment; DDS security where the network is shared.
Gap
Open UI command path hardening items (area-level wording; detail withheld for security).
Recommendation
Security review of the UI backend before any shared-network deployment; plan DDS security for v2.0.0.
Sources
rosbridge_suite, ROS 2 security (SROS2)
OA-49 · scenario S4, S5 · code location withheld (security) · owner @parthkeshari (UI lead); reviewers @panthera-momagdii (tech lead, UI reviewer) and UI reviewer (GitHub handle to be confirmed); simulation checks @nikgolobo (simulation checks)OA-50 · scenario S5 · code location withheld (security) · owner @parthkeshari (UI lead); reviewers @panthera-momagdii (tech lead, UI reviewer) and UI reviewer (GitHub handle to be confirmed); simulation checks @nikgolobo (simulation checks)NR-02 · owner @parthkeshari (UI lead)
AR-12 Dual-arm and lift planning before v2.0.0
What we do
Per-arm groups and controllers on the branch; lift height is a stub; no both-arms group.
Good practice
A controlled torso or lift joint inside a planning group; a combined both-arms group; limits from the vendor.
Gap
Lift not planned; a park pose at a joint limit; upstream acceleration limits missing.
Recommendation
Lift controller in simulation and mock; both_arms group; the real lift stays behind the base-controller interlock.
Sources
MoveIt 2
OA-64 · scenario S8 · code PR #14 (branch evidence) · owner @Tushargoel230 (upper-body model)OA-40 · code openamrobot-manipulation/integration/README.md · owner to be confirmed
AR-13 Driverless operation among people (ISO 3691-4) before v2.0.0 new in this review
What we do
Navigation sensors are functional only; the hardwired chain has two E-stops.
Good practice
A documented risk assessment that sets protective fields with a performance level, or operating-zone restrictions and speed limits.
Gap
No risk assessment found in the inspected repositories; no zone concept found either.
Recommendation
Record the operating concept and risk assessment for Use Case 1 before physical acceptance; until then, supervised operation only.
Sources
ISO 3691-4:2023 (a revision is in progress)
NR-05 · owner to be confirmed
AR-14 Arms near people (ISO 10218-1, ISO 10218-2, ISO/TS 15066) before v2.0.0 new in this review
What we do
Arms on the SAFE bus; no base-arm interlock; placeholder planning limits.
Good practice
Risk assessment; power and force limits or separation monitoring for shared space; base and arm motion interlocked.
Gap
No risk assessment found in the inspected repositories; no collaborative limits and no interlock.
Recommendation
Specify the interlock in I1 and I8, then implement with tests (owners, not agents); set collaborative speed limits from the risk assessment.
Sources
ISO 10218-1:2025, ISO 10218-2:2025, ISO/TS 15066:2016
NR-06 · code PR #14 (branch) · owner @Tushargoel230 (upper-body model)OA-37 · code openamr-platform-sw (main) · owner to be confirmed

References: applicability review needed

No qualified assessment has established which standards apply to this robot and deployment. The standards below are cited as references for the review points, not as requirements. Gaps are worded as findings: no risk assessment found in the inspected repositories; no SROS2 configuration found in the inspected repositories.

New in this review: findings with code evidence

NR-01: The final velocity command is an unstamped Twist on every path should-fix confidence: verified new in this review

Nav2 is configured without stamped velocity commands and the UI publishes geometry_msgs/Twist. A consumer of /cmd_vel cannot reject a stale or reordered command by its age; only the base watchdog bounds it. Nav2 Jazzy supports TwistStamped on the whole chain (enable_stamped_cmd_vel).

Evidence: nav2_params.yaml on main has no enable_stamped_cmd_vel; live graph in pass 2 shows geometry_msgs/msg/Twist on /cmd_vel, /cmd_vel_nav and /cmd_vel_smoothed.

Code: openamr-platform-sw/ros2/src/openamrobot_nav2/config/nav2_params.yaml

Priority: before v2.0.0 · owner @skunal3318 (navigation lead)

NR-02: No SROS2 configuration found in the inspected repositories should-fix confidence: verified new in this review

No SROS2 configuration was found in the inspected repositories (launch files, configuration and documents on main of every product repository). Whether DDS security is required depends on the deployment network; no qualified assessment has decided it. Area-level gap; it does not replace the UI command path hardening work.

Evidence: Search of launch, YAML and package files on main of every product repository for SROS2 settings returned no configuration hit.

Priority: before v2.0.0 · owner @parthkeshari (UI lead)

NR-03: Motion sources keep publishing while actuator power is off; nothing holds them at zero across a RESET blocker confidence: inferred new in this review

The host has no consumer of the safety-relay state (Y32 or /estop). Navigation, docking and UI sources continue to publish while the E-stop has removed actuator power. After a deliberate RESET the last non-zero command from any direct source reaches the base. P-03 item 20 requires that motion still needs a command after RESET; today a stale command satisfies that.

Evidence: No subscriber to an E-stop or safety-relay state topic on main; S7a shows a direct source keeps its last command; the UI stop does not latch (S6).

Priority: critical before rc.1 · owner @skunal3318 (navigation lead)

NR-04: Package names mix two prefixes and one name exists twice later confidence: verified new in this review

Packages use openamr_ and openamrobot_ prefixes, and openamr_ui_msgs exists in two repositories. REP 144 asks for unique, consistent package names.

Evidence: package.xml files on main (see the package graph on this page).

Priority: later · owner to be confirmed

NR-05: No risk assessment found in the inspected repositories should-fix confidence: opinion new in this review

No risk assessment was found in the inspected repositories. The LiDAR, ToF and ultrasonic sensors are functional sensing only, and the hardwired safety function is the two E-stops. Standards such as ISO 3691-4 describe personnel detection set from a risk assessment, or operating-zone restrictions; no qualified assessment has established which standards apply to this robot and deployment.

Evidence: decisions.yaml NAV-LIDAR and TELEMETRY-NOT-SAFETY-EVIDENCE; P-03 item 13 (functional sensing only); no risk assessment file in any repository.

Priority: before v2.0.0 · owner to be confirmed

NR-06: No speed limit or contact policy for the arms near people should-fix confidence: opinion new in this review

No risk assessment was found in the inspected repositories for the arms. ISO 10218-1, ISO 10218-2 and ISO/TS 15066 describe risk assessment and, for shared spaces, power and force limits or separation monitoring; their applicability to this robot has not been assessed. The MoveIt configuration on the branch uses placeholder accelerations with no collaborative speed limit.

Evidence: openamrobot-manipulation fake profile and PR #14 joint_limits.yaml (placeholder values labelled not for hardware).

Code: PR #14 (branch)

Priority: before v2.0.0 · owner @Tushargoel230 (upper-body model)

Baseline conflicts

7 of 20 parameter checks contradict the plans (S12); 4 tuning observations and 2 configuration observations

Audit pass 2 counted 13 contradictions. On review (openamrobot-docs #48, 20 Oct 2026) P13 and P17 became tuning observations; in this review P02 and P07 (tuning) and P10 and P16 (configuration) were reclassified because their expected values cite no project source. A contradiction here cites P-03, E-01, the BOM, the register, Use Case 1 or a work package (issue #35 is counted as the Gate A work item).

Static check at the recorded SHAs (platform-sw 8a7a5b1, UI c5558ad5). Owner shown only where the area owner is confirmed. Observations keep their observed values and notes.

IDCheckObservedExpectedSourceNoteOwnerResult
P01local inflation radius vs inscribed radius0.15>= inscribed 0.290
Project source: P-03 item 18 footprint 780 x 582 mm (inscribed radius 0.290); Nav2 rejects a smaller inflation radius (logs an ERROR at start)
nav2_params.yaml:177pass 1 OA-35 (Nav2 logs an ERROR at start)@skunal3318 (navigation lead)contradiction
P02global inflation radius vs circumscribed radius0.35>= circumscribed 0.456 for cost-aware planningnav2_params.yaml:241planner may route the footprint corner closer than the inscribed halo
Reclassified in this review: the expected value is Nav2 guidance for cost-aware planning; no approved project requirement sets the global inflation radius.
-tuning observation
P03controller max_vel_x == smoother max_velocity[0]0.20.2nav2_params.yaml:118,341-ok
P04controller max_vel_theta == smoother max_velocity[2]0.50.5nav2_params.yaml-ok
P05controller accel/decel == smoother accel/decel[1.0, -2.5][1.0, -2.5]nav2_params.yaml-ok
P06P-03 item 10 software ceiling 1.5 m/s enforced on every path{"nav": 0.2, "ui_client_side": 0.2, "docking": 0.1, "undock": 0.1}one enforced ceiling in front of the base
Project source: P-03 rev19.7 item 10 (software command ceiling on every path)
P-03 rev19.7 item 10only the Nav2 path is limited (smoother); UI and docking publish the final /cmd_vel with their own limits, the UI limit is browser-side only@skunal3318 (navigation lead)contradiction
P07UI angular limit vs navigation angular limit2.0<= 0.5web/src/shared/constants/index.js:54UI 2.0 rad/s is 4x Nav2; footprint corner speed 0.91 m/s at the circumscribed radius 0.456 m, unsmoothed
Reclassified in this review: no approved project requirement sets an angular velocity limit (P-03 item 10 sets only the linear ceiling); the UI value stays visible as an observation.
-tuning observation
P08hold-and-move limits 0.05 / 0.3 m/s (UC1-41, P-03 item 15)nullconfigured value for drawer reversal and carry
Project source: P-03 rev19.7 item 15; UC1-41
P-03 rev19.7 item 15; UC1-41no parameter, mode or limit exists in any repository@Tushargoel230 (upper-body model)contradiction
P09collision monitor has a stop polygon["FootprintApproach"]at least one stop or slowdown polygon besides approach
Project source: Gate A work item, platform-sw issue #35 (scope: add and test stop, slowdown and reverse/limit polygons)
nav2_params.yaml:358-369only FootprintApproach (time_before_collision 0.8 s); nothing stops for an obstacle already inside the footprint margin except the approach model@skunal3318 (navigation lead)contradiction
P10collision monitor observation sources["scan"]scan plus near-field sources (ToF, ultrasonic) for the 2.0 sensor setnav2_params.yaml:371ToF cliff and MB7060 ultrasonic topics exist in simulation but are not sources; the LiDAR rear sector is masked
Reclassified in this review: no project document assigns the near-field sensors to the collision monitor.
-configuration observation
P11approach stop distance at max speed0.16documented stopping distance requirementnav2_params.yaml:367informational: 0.8 s x 0.20 m/s = 0.16 m; braking at -2.5 m/s2 adds 0.008 m-ok
P12velocity smoother timeout vs base watchdog1.0documented base /cmd_vel watchdog (Gate A firmware 200 ms)nav2_params.yaml:349smoother zeros after 1.0 s; the base watchdog is shorter on Gate A; direct publishers (UI, docking) have neither-ok
P13lifecycle bond timeout60.0Nav2 default 4.0 snavigation_launch.py lifecycle_manager parameters; S7non-default; at runtime (S7) the manager still reset the stack 4.4 s after controller_server was killed and deactivated collision_monitor with it, so the effect of 60 s is NOT VERIFIED; the reset itself removes the reactive layer for direct publishers
Reclassified on review (openamrobot-docs #48, 20 Oct 2026): the expected value is the Nav2 default; no approved project requirement sets the bond timeout.
-tuning observation
P14reverse allowed while rear is unobserved-0.050 when rear_coverage UNAVAILABLE
Project source: platform-sw issue #35 (reverse/limit polygons); UC1-93 (no contact); the merged status profile declares rear coverage UNAVAILABLE
nav2_params.yaml:111; navigation_status_profile.yamlDWB may reverse at 0.05 m/s; the status profile declares rear coverage UNAVAILABLE; undock reverses 0.7 m blind at 0.10 m/s@skunal3318 (navigation lead)contradiction
P15docking final stop distance vs collision monitor horizon{"stop_lidar_distance": 0.13, "cm_paused": true}docking inside the protective chain
Project source: platform-sw issue #35 (no source bypasses the approved chain; re-enable the docking guard)
dock_trigger.yamlcollision monitor is deactivated for the dock; obstacle_check_enabled false@fridayali (docking and robot description)contradiction
P16opennav docking_server parameters without a docking_server nodedocking_server block presentparameters only for launched nodesnav2_params.yaml:380-422dead configuration (simple_charging_dock) that suggests Nav2 docking is used
Reclassified in this review: unused docking_server parameters; no project requirement is contradicted.
-configuration observation
P17DWB transform_tolerance0.6<= 0.2 s typicalnav2_params.yamlstale TF up to 0.6 s accepted by the local planner
Reclassified on review (openamrobot-docs #48, 20 Oct 2026): the expected value is a typical value; no approved project requirement sets transform_tolerance.
-tuning observation
P18progress checker allowance vs speed12.0nav2_params.yaml12 s to move 0.05 m-ok
P19goal tolerance vs docking staging and UC1-470.35Nav2 +-0.05 m before final alignment (UC1-47)
Project source: UC1-47 (Nav2 to about +-5 cm and +-3 deg before marker alignment)
nav2_params.yaml:91; UC1-47xy_goal_tolerance 0.35 m; UC1-47 asks Nav2 to about +-5 cm then marker alignment@skunal3318 (navigation lead)contradiction
P20AMCL laser max range vs costmap obstacle range12.0consistent with sensornav2_params.yamlAMCL 12.0 m, costmap obstacle 2.5 m-ok

15 source conflicts

Definition: a value on which two sources disagree. The design value (P-03, then E-01, then BOM) is shown first. Kinds: document conflict (design documents disagree; the later item governs), register drift (the decisions register disagrees with the documents), implementation drift (main differs from the design value), in-review record (a PR or branch differs). Both values and both sources are shown; none is resolved here.

TopicKindDesign or governing value (source)Other value (source)OwnerFinding
imu_link positiondocument conflict(0.0544, 0, 0.1210), yaw -90 deg
P-03 rev19.7 item 19 D5 update (16 Oct); register FRAMES-REP105 agrees; URDF on main agrees
x +0.195, z -0.044 (base plate)
P-03 item 15 text and item 19 D5 (12 Oct); BOM Issue 7.10 IMU-001 row
to be confirmed
LiDAR mountdocument conflict17.5 mm printed spacer, no standoffs, scan plane 222.05 mm
P-03 item 19 D4 update (16 Oct); register NAV-LIDAR agrees
10 mm standoffs and 7.1 mm spacer, scan plane 222.0 mm
P-03 item 13 and item 19 D4 (12 Oct); BOM Issue 7.10 MEC-008 and NAV text
to be confirmed
base_camera_link positiondocument conflict(0.387, 0, 0.046)
P-03 item 19 D3 update (16 Oct); register CAMERAS and URDF on main agree
(0.376, 0, 0.0445)
P-03 item 19 D3 (12 Oct)
to be confirmed
Base camera height above the floorregister drifthousing centre 142.5 mm
P-03 rev19.7 items 15 and 19 D3
camera centre 144 mm (0.098 + 0.046)
register CAMERAS (1c3f516)
to be confirmed
Base camera height in Use Case 1document conflict142.5 mm (housing centre)
P-03 items 15 and 19
about 243 mm (superseded by P-03 item 15)
Use Case 1 Requirements Baseline rev18.5 (UC1-52)
to be confirmedOA-31
Charging in 2.0document conflictTZBOT WCM-500-24 wireless charging is the 2.0 path
P-03 item 20; E-01 rev1.4 s7; BOM 7.10 WCH-001 to WCH-007; register CHARGING agrees
Wired only in 2.0; TZBOT in 3.0
BOM Issue 7.10 Decisions sheet, charging row
to be confirmed
ToF framesimplementation driftx +/-0.390
P-03 item 19 D6; register NEAR-FIELD-FRAMES agrees
x +/-0.399
platform-sw main (sensors.xacro, sensor_frames.yaml)
to be confirmedOA-10
Ultrasonic framesimplementation driftx +/-0.390
P-03 item 19 D1; register NEAR-FIELD-FRAMES agrees
x +0.3906 / -0.3886
platform-sw main (sensors.xacro)
to be confirmedOA-11
Chassis widthimplementation drift582 mm (y +/-0.291)
P-03 item 18
580 mm collision box
platform-sw main URDF (simplified collision)
to be confirmed
IMU topic ownershipimplementation driftFirmware /imu/data_raw; host filter /imu/data
register IMU-TOPIC-OWNERSHIP; Team document (firmware scope)
Simulation publishes /imu; Gate A feeds /imu/data from firmware
platform-sw main
to be confirmedOA-34
Upper-body base_plate defaultimplementation driftno default stated (bp000 or bp050)
P-03 items 17 and 18; register CONFIG-IDS
bp000
PR #14 combined_robot.xacro and upperbody_configuration.yaml (in review)
@Tushargoel230 (upper-body model)
Contactorsin-review recordGigavac GX11CAC
P-03 item 22; E-01 rev1.4; register SAFETY-PROCUREMENT agrees
LEV100 family (superseded)
draft platform-hw #27; earlier hardware diagram (BOM Issue 7.3)
to be confirmedOA-01
Docking frameworkin-review recordopennav_docking baseline
docking work package
keep the custom sequencer, evaluate later
#35 review branch
@fridayali (docking and robot description)
K1/K2 order code in the BOM linkdocument conflictGigavac GX11CAC
E-01 BOM rev1.2 K1/K2 row (model column); BOM 7.10 SAF-003; register SAFETY-PROCUREMENT
link points to a product page for GX11CC (a different order code)
E-01 BOM rev1.2 K1/K2 row (link column)
@Evans-Kwashie (electrical and firmware lead)OA-01
PR #14 headin-review recorde5cf613 (19 Oct; evidence and docs on top of code fe2e821)
GitHub PR #14 head commit
49d2368
PR #14 description
@Tushargoel230 (upper-body model)