Skip to content

ui: load the robot description from platform-sw; start the battery node - #25

Merged
BotshareAI merged 2 commits into
mainfrom
description-from-platform-sw
Oct 12, 2026
Merged

BotshareAI merged 2 commits into
mainfrom
description-from-platform-sw

Conversation

@BotshareAI

@BotshareAI BotshareAI commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Pull request

  • Removes the copied robo_urdf.urdf.xacro and gazebo_control.xacro; the UI loads openamrobot_description from openamr-platform-sw (REP 105 frames and 2.0 sensor frames from platform-sw PR ui: read-only upper-body view on the Robot Description page (prototype) #55).
  • battery.py now has an entry point and starts from the UI launch file (simulated fallback without a serial device); fixes the gap found in the UI simulation test report of 11 Oct (finding F2).

Source: openamr-platform-sw main, P-03 rev19.0 item 19.

Change, scope and decisions taken in this session

  • Base / head: base main @ 8b981adde859f97531561dec34adb52ff39be579; head description-from-platform-sw @ a9a45d9490ed803419705d5265f0cf0a75ab2347. Two commits, both DCO signed off: d312d3e (description and battery) and a9a45d9 (Docker).
  • Source: openamr-platform-sw main @ 174731c6a593870276fa47d8f92db2ef79eda79a. It contains PR ui: read-only upper-body view on the Robot Description page (prototype) #55 (935efc7) and PR [integration candidate, do not merge] rc.1 UI stack #56 (174731c). PR [integration candidate, do not merge] rc.1 UI stack #56 changes comments only: it marks the 0.400 m track as a placeholder.
  • Robot description (flask_app.py):
    • /api/robot-description/{manifest,urdf,assets} now resolve the installed openamrobot_description share directory per request through get_package_share_directory.
    • If the package is absent, the UI still starts and the page reports the model as unavailable.
    • The asset path containment check is kept.
  • Deleted copies (decided in session): the whole copied folder openamr_ui_package/robot_description/ is removed, not only the two xacro files.
    • That folder held the two xacro files and 12 STL meshes, all byte-identical to platform-sw (cmp).
    • Before this change those meshes were served from the copy in this repository, not from platform-sw. The setup.py install entry for the folder is removed.
  • Pinning (decided in session): the repository had no pin mechanism for platform-sw.
    • docs/installation.md now records the validated commit 174731c. That is the existing "pin as deployment configuration" convention, so no .repos file is added.
    • package.xml gains exec_depend openamrobot_description.
    • The rosdep command gains --skip-keys openamrobot_description, because the package is not in rosdistro.
    • The UI start snippets now source the platform-sw workspace first.
  • Docker image (a9a45d9):
    • The Dockerfile fetches openamr-platform-sw at the pinned commit (build arg OPENAMR_PLATFORM_SW_COMMIT=174731c6...) and checks that the fetched HEAD equals it.
    • It colcon-builds only openamrobot_description into /opt/openamr_platform_sw and records the commit in /opt/openamr_platform_sw/PLATFORM_SW_COMMIT. git is added to the apt layer.
    • scripts/container_entrypoint.sh sources that workspace before the UI workspace.
    • Existing defect fixed in the same file: with the current ros:jazzy-ros-base (digest 066420e0), main's entrypoint exits before the UI starts. Its set -u is active while /opt/ros/jazzy/setup.bash reads the unset AMENT_TRACE_SETUP_FILES. The entrypoint now relaxes nounset only while sourcing setup scripts.
    • README notes that the image builds the description at the pinned commit.
  • Legacy camera_link: no UI code consumes it. platform-sw still publishes it (legacy IMX708, Gate A) and the served URDF contains it.
  • Battery node (decided in session, opt-in):
    • The launch path is ros2 launch openamr_ui_bringup ui.launch.py start_battery_monitor:=true. The default is false.
    • The serial port is now the port parameter (launch arg battery_port), with no default. Without it the node never opens a serial device and publishes the simulated value. A named port that fails to open also falls back to the simulated value.
    • Reason for the change:
      • battery.py hard-coded /dev/ttyUSB0 at 9600 baud. The RPLIDAR is a CP2102 device, and the platform-sw drivers README uses it with lidar_port:=/dev/ttyUSB0. Opening that port from the UI would reconfigure and read the lidar's line.
      • The simulated value is fabricated data, so it must not appear on a real robot by default.
    • setup.py gains the console script battery.
    • A comment records that on 2.0 the battery state comes from the base controller (Daly BMS on CAN2 via the STM32), and that the serial path is Gate A legacy.
  • Docs:
    • Lesson 05 now says the launch path exists and is opt-in.
    • Lesson 06 describes the model: base_footprint root; base_link at the drive axle, 0.098 m up; lidar_link (0.330, 0, 0.124) with yaw pi; base camera Gemini 336L (base_camera_link); legacy camera_link; imu_link; 4 ultrasonic and 2 ToF frames.
    • README and the package README are updated, and so is the UI's "model unavailable" hint.
  • Not changed: safety code, E-stop, auth, interfaces, Demo Mode, NOTICE, LICENSING and CODEOWNERS. No generated directories are committed, and the tracked static/app bundle is not re-synced.

Safety impact

  • The battery change reduces a hardware risk: the UI launch no longer has any path that opens /dev/ttyUSB0 implicitly.
  • The battery value shown in the UI is still not a safety signal. While start_battery_monitor:=true is set without a port, the value is simulated, and the node says so in its log.
  • The robot description is display only. The frame values come from platform-sw. This PR does not validate them mechanically or physically.

Validation (local, in this cloud session)

Environment: Ubuntu 24.04, Node v20.20.2, ROS 2 Jazzy, Python 3.12.3. The platform-sw openamrobot_description package at 174731c was built into a separate overlay and sourced before the UI workspace.

Command Result
cd web && npm ci exit 0
cd web && CI=true npm test -- --watchAll=false exit 1, "No tests found". main has 0 web test files. Not a pass. Hosted CI runs this with --passWithNoTests.
cd web && npm run build exit 0, "Compiled successfully"
source /opt/ros/jazzy/setup.bash, source the overlay, then python3.12 -m colcon build --symlink-install (clean build) exit 0, 3 packages
python3.12 -m colcon test --packages-select openamr_ui_package openamr_ui_msgs && python3.12 -m colcon test-result --verbose 14 tests: 11 passed, 0 failed, 3 skipped. The skips are the existing copyright, flake8 and pep257 tests and are not passes. The 7 new tests in test/test_description_and_battery.py all ran and passed, none skipped, because the description package was sourced.
python3 tools/watchdog.py --root <clone> --report-only (openAMRobot/.github main @ 13796dc; the clone directory is named openamrobot-ui) Before this change: 19 findings (public extract 13, shared rules 1, workflow policy 5). After both commits: the same 19. The 13 README price findings only moved down because of text added above them. This change introduced no new findings.

New tests:

  • The platform-sw description is served, with all of base_footprint, base_link, lidar_link, base_camera_link, camera_link, imu_link, the four us_* frames and the two tof_* frames, and a mesh from the platform-sw share.
  • When the package is missing, the manifest reports it unavailable and the URDF and assets return 404.
  • No vendored copy is installed.
  • Deep plain and percent-encoded asset path traversal is rejected.
  • The battery node never calls serial.Serial without a port.
  • A named port is the only device opened.
  • The battery entry point is registered.

Deliberate broken variants (each run, then restored byte-for-byte):

Variant Result
battery.py default port set back to /dev/ttyUSB0 1 failing: test_no_port_never_opens_serial_and_publishes_simulated
Asset path containment check removed 1 failing: test_asset_path_traversal_is_rejected. Without the check, the running app returned 200 and served /etc/hostname, so the check is load-bearing.

Headless launch: the repository has no launch test (no launch_testing), so the UI launch path was run headless by hand with ros2 launch openamr_ui_bringup ui.launch.py.

  • Default launch:
    • /ui/flask_app, /ui/map_volatile_relay and /ui/nav_relays start, and no battery node starts.
    • The manifest reports "available": true. /urdf returns the platform-sw URDF with 25 links. base_link.STL returns 200 (12379084 bytes).
    • rosbridge, rosapi and web_video_server are not installed in this container, so those optional nodes logged "not installed" and were skipped.
  • With start_battery_monitor:=true:
    • battery_monitor runs with port set to an empty string and logs the SIMULATED warning.
    • ros2 topic echo --once /battery_status returned data: 99.83.
    • The process's open file descriptors held no tty or serial device.
  • Shutdown: the tracebacks at shutdown were KeyboardInterrupt from the SIGINT sent to stop the run, which is existing node behaviour.

Docker check (image built from the committed Dockerfile at a9a45d9):

The build ran in this session's local Docker daemon:

docker buildx build --load --network host \
  --build-context ros:jazzy-ros-base=docker-image://<session base> \
  --build-arg HTTPS_PROXY --build-arg NO_PROXY -t openamrobot-ui:pr25 .

<session base> is ros:jazzy-ros-base (digest 066420e0) plus this sandbox's egress proxy CA and CA env vars. That base is session-local only and is not part of this PR; it was needed only because this sandbox reaches GitHub and npm through a proxy. The committed Dockerfile was used unchanged.

  • Build: exit 0, all 8 steps.
    • The platform-sw step fetched 174731c, the HEAD check passed, and openamrobot_description built ("1 package finished").
    • In the image: /opt/openamr_platform_sw/PLATFORM_SW_COMMIT = 174731c6a593870276fa47d8f92db2ef79eda79a; urdf/ contains robo_urdf.urdf.xacro, gazebo_control.xacro and robot.sdf; there are 12 visual meshes; no robot_description copy is in the UI install.
  • Run (docker run --network host, image's own entrypoint, as in docker-compose.yml):
    • The container stays running and starts all six UI nodes: map_relay, nav_relay, flask, rosbridge_websocket, rosapi_node and web_video_server.
    • It logs no errors, and no battery node starts.
  • Robot page, queried with docker exec ... curl inside the container:
    • GET /api/robot-description/manifest returns "available": true and "package": "openamrobot_description".
    • GET /api/robot-description/urdf returns HTTP 200 with 25 links and root base_footprint. It contains base_footprint_joint (0, 0, 0.098), lidar_joint (0.330, 0, 0.124, yaw pi), base_camera_joint (0.376, 0, 0.0445, pitch -0.1745 with the default bt10), camera_link, imu_link, us_front_left, us_front_right, us_rear_left, us_rear_right, tof_front and tof_rear.
    • GET /api/robot-description/assets/meshes/visual/base_link.STL returns HTTP 200, model/stl, 12379084 bytes. Its sha256 equals the pinned platform-sw install file (dfedb492ae1b6ca8...).
    • Deep percent-encoded traversal to /etc/passwd returns 404. GET /robot (SPA route) returns 200.
  • Existing entrypoint defect, before and after: main's unchanged scripts/container_entrypoint.sh, run in the same image, and a plain ros:jazzy-ros-base with set -euo pipefail; source /opt/ros/jazzy/setup.bash, both fail with /opt/ros/jazzy/setup.bash: line 8: AMENT_TRACE_SETUP_FILES: unbound variable. The first run of the new image failed the same way. After the set +u / set -u change, the container starts, as shown above.

Container notes: this session container's python3 is 3.11, so colcon and ros2 were run with python3.12. python3-flask and python3-serial (both declared exec_depend) were installed from Ubuntu apt into the session container only.

Informational lint (repository flake8 is skipped): the touched Python files add no new non-quote flake8 findings. One I100 import-order finding in flask_app.py already existed on main and now shows the changed import text.

Hosted checks: see the CI results on this PR. They are separate from the local results above. Hosted CI builds neither ROS nor the Docker image, so the colcon and Docker results above are local only.

Not verified

  • The UI simulation test report of 11 Oct and its finding F2 were not available to this session. Whether this PR closes F2 has not been checked against that report. Because the battery node is now opt-in, the default launch still publishes no battery data.
  • P-03 Decision Addendum rev19.0 item 19 was not readable from this session. Its content was not guessed.
  • The statement that on 2.0 the battery state comes from the base controller (Daly BMS on CAN2 via the STM32) is not documented anywhere in platform-sw main. It was written as given and is not verified against a source.
  • Gazebo simulation, a full simulation stack (openamrobot_docking bringup_sim.launch.py) and the browser rendering of the Robot page were not run. Only the HTTP API and node startup were checked.
  • Physical hardware and frame accuracy were not checked: the values are as published by platform-sw. platform-sw marks the 0.400 m track as a placeholder (DRIVE-TRACK open).
  • The tracked static/app bundle in ros2/ was not re-synced, so the updated "model unavailable" hint takes effect only after the next frontend sync.
  • The web unit test command found 0 tests on main, so it is not evidence of any web behaviour.

Dependencies and licences

  • New runtime dependency: openamrobot_description (openamr-platform-sw, MIT per its setup.py), at the validated commit recorded in docs/installation.md and the Dockerfile.
  • Docker image: adds the Ubuntu git package and fetches openamr-platform-sw from GitHub at build time.
  • No npm or lockfile changes. No Python test-only dependencies added.
  • Deleting the copied files removes duplicated MIT content from openamr-platform-sw. NOTICE and LICENSING had no reference to them and are unchanged.

AI disclosure

This change was written with material AI assistance (Claude Code), under the author's direction. That covers the code, tests, docs, the commit messages and this PR text. No third-party code was added.

Contribution terms

  • DCO: each commit carries Signed-off-by for the session's verified git identity, as authorized by the author.
  • CLA: maintainer to confirm CLA handling. The hosted license/cla status is reported on this PR.
  • Contributions are offered under the repository's Contributor Agreement and outbound licence. The checkboxes below are left for the author to tick.

Intellectual property and provenance

  • Every commit is signed off under the OpenAMRobot DCO.
  • I am covered by an accepted Individual or Corporate Contributor Agreement.
  • I have any required employer, university, client, sponsor, co-author, or institutional authorization.
  • I identified all third-party and material AI-assisted content with source, licence, modifications, and required notices.
  • I submitted no unauthorized confidential, personal, proprietary, credential, or export-controlled information.
  • I understand that accepted contributions are governed by the Contributor Agreement and the applicable outbound licence.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EwTn5mzG9oyoqiv9HiDdTP

BotshareAI and others added 2 commits October 12, 2026 22:00
- Remove the copied robo_urdf.urdf.xacro, gazebo_control.xacro and their
  12 STL meshes (byte-identical to platform-sw). Flask now xacro-processes
  and serves openamrobot_description from openamr-platform-sw (REP 105 base
  frames and 2.0 sensor frames, platform-sw PR #55), resolved per request so
  the UI still starts when the package is absent.
- package.xml: exec_depend openamrobot_description. docs/installation.md
  records the validated platform-sw commit 174731c and the rosdep skip key.
- battery.py: console_scripts entry point "battery"; serial port is a
  parameter with no default (never opens /dev/ttyUSB0, commonly the
  RPLIDAR); simulated fallback when no port is set. Started from
  new_ui_launch.py only with start_battery_monitor:=true. Comment: on 2.0
  battery state comes from the base controller; serial is Gate A legacy.
- Docs: lesson 05 launch path, lesson 06 model frames, READMEs, model
  unavailable hint.
- Tests: description served from platform-sw, unavailable when missing,
  path traversal rejected; battery never opens serial without a port.

Source: openamr-platform-sw main, P-03 rev19.0 item 19.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwTn5mzG9oyoqiv9HiDdTP
Signed-off-by: Alex Reznichenko <info@botshare.ai>
- Dockerfile: fetch openAMRobot/openamr-platform-sw at the pinned commit
  174731c6a593870276fa47d8f92db2ef79eda79a (OPENAMR_PLATFORM_SW_COMMIT build
  arg, checked after fetch), colcon build openamrobot_description only into
  /opt/openamr_platform_sw, and record the commit in PLATFORM_SW_COMMIT.
  git added to the apt layer.
- container_entrypoint.sh: source the platform-sw workspace before the UI
  workspace. Also relax nounset only while sourcing setup scripts: with the
  current ros:jazzy-ros-base, /opt/ros/jazzy/setup.bash reads the unset
  AMENT_TRACE_SETUP_FILES and the main-branch entrypoint exited before the
  UI started.
- README: note the image builds the description at the pinned commit.

Verified in the built container: manifest available, /api/robot-description/urdf
200 with base_footprint root and the 2.0 sensor frames, base_link.STL 200 and
byte-identical to the pinned platform-sw install.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwTn5mzG9oyoqiv9HiDdTP
Signed-off-by: Alex Reznichenko <info@botshare.ai>
@BotshareAI
BotshareAI marked this pull request as ready for review October 12, 2026 23:33
@BotshareAI
BotshareAI merged commit 2bf51e1 into main Oct 12, 2026
6 checks passed
@BotshareAI
BotshareAI deleted the description-from-platform-sw branch October 12, 2026 23:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant