Conversation
Add Pixi package manager configuration with RoboStack channel for ROS 2 Jazzy dependencies. - pixi.toml with workspace tasks and ROS 2 dependencies - pixi.lock for reproducible dependency resolution - .env.example for environment configuration - .gitattributes to handle pixi.lock as binary - .gitignore for Pixi artifacts - repos.json: add micro_ros_agent and audio_common
Refactor Lucy install and launch workflows to use Pixi environment instead of Docker containers. - install.sh: Pixi-based dependency management and colcon build - launch_lucy.sh: native tmux launcher with Pixi environment - launcher.py: updated TUI for Pixi workflow - Lucy.py: Pixi-aware bootstrap and environment setup - config/launcher_config.json: simplified native launch config - config/launcher_config.json.local.example: local override template - scripts/pixi_*.sh: helper scripts for dev shell and launch - scripts/nix_gl_env.sh: NixOS host GL passthrough support
Remove Docker-based workflow in favor of native Pixi environment. Add comprehensive CI and unit tests for the new workflow. - Remove docker/Dockerfile.jazzy, ensure_image.sh, gpu_detect.sh, gui_desktop.sh - Update .github/workflows/install-and-launch.yml for Pixi CI - Add pytest.ini and tests/conftest.py - Add tests/test_install_optional_repos.py - Add tests/test_launcher_pixi.py - Add tests/test_repos_config.py - Add scripts/ci_colcon_test.sh and ci_tmux_launcher_smoke.sh
Update Windows tooling for Pixi workflow and refresh documentation for the new native launch experience. Windows: - windows/Lucy.py: Pixi-aware Windows launcher - windows/install_ops.py: updated install operations - windows/README.md: updated Windows setup guide - windows/installer/Lucy.nsi: updated NSIS installer Documentation: - README.md: updated quickstart and workflow overview - docs/developer_lucy_packages.md: Pixi development guide - docs/launcher_packages.md: updated launcher docs - docs/pixi_setup.md: new Pixi setup guide - docs/pixi_release.md: Pixi release notes stub Scripts: - scripts/build_local_realsense.sh: RealSense local build helper
…restart Split monolithic launcher.py into a launcher/ module tree with backward-compatible imports. Orphan cleanup during core/modifier restarts no longer kills control panel Vite when that service is still selected and running. - launcher/: constants, config, platform, process, shell, tmux, package, state, apply, tui; entry via python -m launcher - launcher.py: thin shim for python launcher.py - launch_lucy.sh: python -m launcher - process: protect_vite from package readiness/command metadata; clear pending preserve after debounced teardown - apply: preserve running interfaces/tools during partial teardown - docs/README: update launcher entry points - tests: expand test_launcher_pixi.py (28 tests)
Signed-off-by: Mael-RABOT <mael.rabot@epitech.eu>
…pixi refactor(launcher): run install and launch through Pixi
…al-ci-tests chore(docker): remove Docker, add Pixi CI and tests
evol(windows,docs): update Windows support and documentation
…archi refactor(launcher): split into package and preserve services on core restart
Signed-off-by: Charles Madjeri <80175305+charlesmadjeri@users.noreply.github.com>
Signed-off-by: Charles Madjeri <80175305+charlesmadjeri@users.noreply.github.com>
Prepend Tegra/NVIDIA libraries ahead of conda Mesa, discover gz-rendering paths dynamically on Linux, and share Jetson detection between shell and Python. Headless EGL uses a private runtime dir; GUI sim keeps the session XDG_RUNTIME_DIR. Route rqt/lucy-cli through pixi_gui_launch.sh.
Signed-off-by: Charles Madjeri <80175305+charlesmadjeri@users.noreply.github.com>
fix(pixi): address PR review: docs, CI test, drop audio_common
fix(pixi): enable Gazebo ogre2 rendering on Jetson via host GL env
feat(pixi): add RoboStack Jazzy workspace foundation
* Use localhost unicast DDS discovery where multicast is blocked
The config nodes advertised /config/get correctly and rosbridge still answered
"Service /config/get does not exist", because no participant under the tmux
server could discover any other. Measured:
two processes outside tmux see each other
two processes inside tmux invisible to each other
core's /config/get, from tmux invisible
macOS gates local-network traffic behind the Local Network privacy permission,
granted per application. tmux daemonizes to PPID 1, so the tmux server holds its
own identity rather than the launching terminal's, and Homebrew's tmux is never
granted it. DDS multicast discovery is then dropped silently for every process
tmux spawns — the entire Lucy stack. Nothing logs an error; nodes simply never
find each other, which is why this looked like a dead service rather than a
broken transport.
Loopback is exempt, so default macOS to localhost-only unicast discovery via
scripts/dds_env.sh, sourced beside the existing nixGL hook in the innermost
shell. Nothing is lost: the stack is co-located, the panel reaches it over the
rosbridge websocket, and micro-ROS agents are serial. Linux keeps stock DDS.
Configurable in .env for multi-machine setups: LUCY_DDS_LOCALHOST=0 restores
stock DDS, LUCY_DDS_INTERFACE pins one interface address, LUCY_DDS_PEERS lists
unicast peers. A CYCLONEDDS_URI set by the user always wins.
* Link workspace typesupport dylibs into the Pixi env so rmw can find them
With discovery working, rosbridge found /config/get and then failed to build a
client for it:
failed to create client: type_support is null … rcl/client.c:146
rmw dlopen()s typesupport by bare name, so the library must be on the loader's
search path, and DYLD_LIBRARY_PATH cannot get it there reliably. macOS strips
DYLD_* on every exec of a SIP-protected binary; measured on macOS 26:
/usr/bin/env STRIPPED pixi python3 SURVIVES
/bin/sh -c STRIPPED python.org SURVIVES
/bin/bash -c STRIPPED
rosbridge is reached through both /bin/sh (ExecuteProcess shell=True in
web_ros_api.launch.py) and /usr/bin/env (its `#!/usr/bin/env python3` shebang),
so it always loses the variable, while nodes with an absolute shebang — such as
config_pipeline_node — keep it. That is why the server side worked and only the
client side failed, and why exporting the variable harder cannot fix it.
The Pixi env lib dir is already on the loader's rpath search: it appears in
dlopen's "tried:" list with no DYLD set. Symlinking the workspace's typesupport
dylibs there resolves them for every process no matter how many protected
binaries it was exec'd through.
Only rosidl typesupport artifacts are linked. Two kinds are deliberately kept
out: *_py.dylib Python bindings, which carry undefined libpython symbols and
would break any C++ loader that pulled them in, and pluginlib plugins, which are
loaded by absolute path and gain nothing here. The prune pass also removes links
an earlier, broader run may have left behind.
Wired into the build tasks so it re-runs after each build; idempotent, and never
replaces a real conda-env file. macOS only — it exits before any side effect
elsewhere.
* Gate core/panel readiness on the port, not a process name
Core's probe matched `pgrep -f '[r]osbridge_websocket'`, but core starts
rosbridge as `ros2 launch rosbridge_server rosbridge_websocket_launch.xml`.
That wrapper's own command line contains the pattern, so the probe matched the
launcher rather than the node:
22780 … ros2 launch rosbridge_server rosbridge_websocket_launch.xml … ← matched
22784 … lib/rosbridge_server/rosbridge_websocket --ros-args … ← the node
Core therefore flipped to [RUNNING] the instant `ros2 launch` was exec'd, well
before port 9090 was bound, so the TUI showed a green core while the browser
could not connect. It also stayed green when config_pipeline_node died at
startup: those nodes are children of the still-alive `ros2 launch`, and core's
window has no remain-on-exit, so pane-based crash detection cannot see them.
Probe the rosbridge port and require config_pipeline_node to be alive, so core
reads ready only once /config/* can actually be served. Same for the control
panel: check the published Vite port is accepting connections instead of
matching any `vite` process, which was green while Vite was still building.
The probe goes through scripts/port_open.sh rather than bash's /dev/tcp alone,
because Debian-family bash is sometimes built without net redirections, where
/dev/tcp fails identically to a closed port and would strand core at [CRASHED]
on the robot. It falls through /dev/tcp, nc, then a Python socket, and only an
exhausted list reports the port shut.
* Run the inner Pixi shell without --login so nodes get Pixi's Python
macOS /etc/profile runs path_helper, which rebuilds PATH with /usr/local/bin and
friends in front. A login shell therefore demotes the Pixi env below whatever
Python is installed system wide:
pixi run -- bash -lc 'command -v python3' -> /usr/local/bin/python3
pixi run -- bash -c 'command -v python3' -> .pixi/envs/default/bin/python3
Anything with a `#!/usr/bin/env python3` shebang then runs under the system
interpreter rather than Pixi's — rosbridge_websocket among them, which was
running under /Library/Frameworks/Python.framework. rclpy's compiled extension
is built for exactly one CPython minor version:
_rclpy_pybind11.cpython-312-darwin.so
so the import survives only where the system Python happens to be 3.12 too.
Measured against the same PYTEST/PYTHONPATH on one machine:
/usr/local/bin/python3 3.12.0 import OK
/opt/homebrew/bin/python3 3.14.7 needs _rclpy_pybind11.cpython-314-darwin.so
~/miniconda3/bin/python3 3.13.2 needs _rclpy_pybind11.cpython-313-darwin.so
/usr/bin/python3 3.9.6 needs _rclpy_pybind11.cpython-39-darwin.so
which surfaces as "No module named 'rclpy._rclpy_pybind11'" on any machine whose
system Python is not 3.12 — nothing the user did wrong, and invisible on
machines where the versions happen to line up.
Drop --login from the shell inside `pixi run` so Pixi's PATH ordering survives;
the outer shell keeps it, since that is where `pixi` itself is found. Verified:
rosbridge now runs under .pixi/envs/default/bin/python.
The two tests asserting `bash -lc` encoded exactly the broken behaviour, so they
now assert `bash -c`, plus one covering the login-shell regression directly.
* Make core readiness mean the robot can actually be driven
The probe checked that the rosbridge port was open and config_pipeline_node was
alive. Both stay true while every controller sits unconfigured/inactive, which is
exactly what a stale /robot_description or a held spawner lock produces, so the
TUI showed a green [RUNNING] next to a robot that could not move — twice, and in
both cases the launcher was the last place anyone thought to look.
Add scripts/controllers_active.sh and require at least one *active* controller.
Two costs had to be contained: `ros2 control list_controllers` needs a full Pixi
activation, far too slow for the 1s poll, so the answer is cached (8s TTL, and a
repeat call costs ~0.02s); and it blocks waiting for /controller_manager when the
manager is down, so the call is bounded by hand — macOS ships no `timeout`.
The match is anchored (`[[:space:]]active$`) so "inactive" does not count.
Verified against a live stack whose controllers had failed: the probe reports not
ready, where the old check reported ready.
readiness_timeout goes 60 -> 150: controllers legitimately take ~60s to come up
from a cold start, and the previous bound would have flipped a healthy stack to
[CRASHED] now that readiness waits for them.
* Reap orphaned description publishers and controller spawners
_matches_orphan_signature knew about gz sim, rosbridge, lucy.launch.py, rviz2 and
vite, but not the nodes the stack starts underneath them. A hard session kill
reparents those to init, and two of them actively poison the next run:
robot_state_publisher keeps a *latched* /robot_description alive. A later
Gazebo run spawns via `create -topic robot_description` and can pick up the
stale one, so gz_ros2_control tries to load the real-hardware plugin:
the class lucy_ros2_control/LucySystemHardware with base class type
gz_ros2_control::GazeboSimSystemInterface does not exist
All three hardware components then fail, no interfaces are registered,
joint_state_broadcaster reports "No state interfaces found to publish", and
every controller fails to activate.
a spawner holds $ROS_HOME/locks/ros2-control-controller-spawner.lock. Every
later spawner then burns 5 x 20s and gives up, leaving controllers loaded but
never activated.
Both were observed on this machine: two robot_state_publishers at PPID 1 feeding
a third publisher onto /robot_description, and a spawner holding the lock for
hours. Neither is visible as an error — the failures surface far downstream as a
robot that will not move.
Add robot_state_publisher, ros2_control_node, config_pipeline_node,
client_registry_node, parameter_bridge, control_supervisor_node and the
controller_manager spawner to the signature. The spawner match requires
"controller_manager" too, so an unrelated binary with "spawner" in its name is
not reaped, and the existing workspace-path guard still applies.
* Stop rviz2 before the teardown SIGINT so macOS stops reporting a crash
Stopping the stack raised a "rviz2 quit unexpectedly" dialog every time. The
cause is not the kill strategy but rviz2's own shutdown path: its rclcpp signal
handler throws std::system_error("mutex lock failed") on macOS, the exception
escapes, std::terminate calls abort(), and the process dies on SIGABRT — which
macOS files as a crash.
[rclcpp]: signal_handler(SIGINT/SIGTERM)
libc++abi: terminating due to uncaught exception of type
std::__1::system_error: mutex lock failed: Invalid argument
Abort trap: 6
Measured against ~/Library/Logs/DiagnosticReports on this machine:
SIGKILL -> new rviz2 crash reports: 0
SIGINT -> new rviz2 crash reports: 1
with 9 reports already banked from earlier stops. SIGTERM aborts identically, so
softening the teardown to SIGTERM first would not have helped — it would still
run the faulty handler.
Kill rviz2 outright before the teardown SIGINT reaches it, so the broken path is
never entered. RViz is a viewer with no state to persist, so nothing is lost, and
this is the only fix available short of patching rviz2. Everything else in the
teardown is unchanged: core still gets SIGINT and shuts down normally.
* Point the manifest at the macOS fix branches
install.sh runs `git checkout <pin>` on every sub-repo each run, so with the old
pins it moved a working checkout back onto branches without any of the macOS
fixes — the Gazebo server/GUI split, the hardware plugin link fix, and the
rosbridge error surfacing. A fresh install could not reach a working state on a
Mac, and re-running install.sh silently undid a manual checkout.
inmoov_urdf cma/feat-pixi-gpu-support -> aes/macos-gazebo
lucy_ros_packages cma/fix-skip-backward-ros-on-macOS -> aes/macos-hardware-plugin-link
lucy_control_panel cma/fix-commit-lock-file -> aes/macos-pixi-fix
Each new pin is its old pin plus the macOS work, so nothing is dropped:
lucy_ros_packages and inmoov_urdf already contain origin/dev, and the
lucy_control_panel branch has been rebased onto it.
These are staging pins, not a destination. Once the branches merge, the pins
should move to the merged branch.
* Share one port probe between launch_lucy.sh and the readiness checks
host_port_in_use duplicated what scripts/port_open.sh already does, and carried
the weakness that script exists to avoid: it tries lsof, then falls back to
bash's /dev/tcp, but Debian-family bash is sometimes built without net
redirections. There /dev/tcp fails exactly as a closed port does, so every
candidate port looks free and resolve_host_port hands back one already in use.
Delegate to the shared script, which falls through /dev/tcp, nc, then a Python
socket and only reports a port shut once all three fail. Verified against a live
listener: an occupied port still reports in use, a closed one still reports free.
* Match the control panel readiness fallback to the new default port
cma/feat-pixi-foundation moved the control panel default from 5000 to 4004, so
the readiness probe's fallback was left pointing at a port nothing listens on.
launch_lucy.sh exports LUCY_LCP_PUBLISHED_HOST_PORT in the normal path, so the
fallback only bites when the probe runs without it — where it would poll 5000
forever and the panel would never read ready.
* wip
Signed-off-by: Mael-RABOT <mael.rabot@epitech.eu>
* fix(docs): resolve committed conflict markers in README
The merge of PR #42 left an unresolved conflict block in README.md.
Keep the current Pixi-era developer-setup pointer and drop the stale
Docker/VNC section, which referenced docker/gui_desktop.sh and the
container GUI removed in PR #39.
Also align the control panel URL with the 4004 default already used by
.env.example, launch_lucy.sh, windows/README.md and the developer guide.
* fix(pixi): make pixi.toml parseable and keep launcher tasks cross-platform
The win-64 build tasks used multi-line inline tables, which TOML 1.0 does
not allow (inline tables must be on a single line), so pixi.toml failed to
parse at all. Collapse build/test onto one line each.
The standalone component tasks (control-panel, rqt, lucy-cli) followed
[feature.launcher.target.win-64.tasks] with no header of their own, so they
were parsed as win-64 tasks and disappeared on Linux and macOS. Put them
under [feature.launcher.tasks] so every platform gets them again.
* test: stop asserting on manifest contents that keep changing
test_tracked_repos_json_parses and test_optional_flag_preserved_in_json
hard-coded repo names, so they broke whenever config/repos.json changed:
audio_common was already gone, and micro_ros_agent has now been dropped
too. Assert the parser agrees with the manifest instead. Per-entry optional
behaviour is already covered by test_parse_repos_marks_optional_repos.
The two vite orphan tests only stubbed the POSIX probe (_read_proc_cwd).
On Linux process_workspace_markers() routes through it, but on Windows it
shells out to Win32_Process instead, so it ran against a fake pid and the
tests failed. Stub _process_workspace_markers as the neighbouring test
already does, leaving each test to cover the vite cwd fallback.
* refactor(install): one cross-platform install.py for every platform
install.sh and windows/install_ops.py were parallel implementations, and the
Windows one had drifted: optional repos were never marked COLCON_IGNORE (its
parse_repos dropped the flag outright), repair left build/install/log in place
and never re-solved the lock, there was no --skip-build, and a missing or old
pixi was an error rather than something the flow could fix.
install.py now holds that logic once. install.sh execs it, and install_ops keeps
only what is genuinely Windows-specific -- the install profile, host platform
detection, lucy_ws self-refresh -- re-exporting the shared names so Lucy.exe and
the NSIS installer keep working.
Two Windows-only checks come with it: a workspace path containing a space (every
Pixi console script fails there, since the shims pass an unquoted interpreter
path to CreateProcess) and a missing MSVC toolchain. pixi_colcon_build.py now
folds the vcvars environment into the build so cl.exe is found without anyone
having to open a Developer Command Prompt, and calls colcon via python -m.
test_install_optional_repos.py used to re-implement the bash function inline and
assert against the copy; it now calls the real code and needs no shell.
* fix(install): pass vcvars64.bat as its own argument
Folded into a single command string, list2cmdline re-quotes the spaces in the
Visual Studio path and cmd reports 'the specified path could not be found', so
msvc_environment() always returned None and the build never saw cl.exe.
* fix(install): generate Windows launcher stubs for conda ROS nodes
RoboStack ships its win-64 Python nodes as extensionless Unix scripts under
Library/lib/<pkg>/, and ros2 launch resolves exec= through PATHEXT, so
rosbridge_websocket and rosapi_node never started: 'executable not found on the
libexec directory'. Colcon-built packages are unaffected only because setuptools
gives them a .exe.
Write a .bat next to each such script after pixi install (which recreates the
environment). Windows needs a PATHEXT-matching file, so a stub is the only option;
the generator itself is Python and a no-op off Windows.
* fix(install): guard rosbridge asyncio signal handlers on Windows
rosbridge_suite 2.6.0 calls loop.add_signal_handler(), which raises
NotImplementedError on Windows for both asyncio event loops. It runs immediately
after the listening socket is bound, so port 9090 shows as open while nothing
ever answers the WebSocket handshake, and respawn restarts the crash in a loop
rather than surfacing it. The control panel could reach the port and then hang.
Guard the call in place after pixi install, falling back to signal.signal. This
is a workaround for an upstream bug; drop it once rosbridge_suite ships a fix.
* fix(windows): install curses so the launcher TUI can start
python windows/Lucy.py reached the launcher and stopped at 'launcher TUI
requires curses (not available on this platform)'. CPython ships no curses on
Windows; windows-curses supplies it but has no conda-forge build, so it comes in
as a win-64 pypi dependency. Only win-64 changes in the lock.
Also restore the curl preflight install.sh used to do before fetching pixi, so
Linux and macOS still get a clear message instead of a failed pipeline.
* fix(windows): point the launcher at Pixi instead of a TUI that cannot work
The TUI starts, stops and polls every package through tmux (apply.py and
package.py call it unconditionally), so on Windows it renders but can nothing:
launches fail, and every package reads as stopped. Installing curses only traded
a clear error for a dead UI, so drop that dependency again and say what is
actually true, naming the pixi tasks that do work.
Reverts the windows-curses pypi dependency; the lock is back to the two win-64
packages the build needs.
* feat(windows): add joint_state_publisher fallback for the control panel
controller_manager crashes on win-64 with an access violation, so
joint_state_broadcaster never spawns and nothing publishes /joint_states. The
panel subscribes to it and has no pose to draw.
joint_state_publisher supplies /joint_states from the URDF alone. Run
'pixi run joint-states' alongside core. Viewer only: without controllers the
robot renders but cannot be driven. Win-64 dependency, other platforms unchanged.
Also trims the comments added by the recent Windows fixes.
* feat(windows): start the joint_states fallback from core
Having to remember a second terminal for 'pixi run joint-states' is a trap, so
scripts/pixi_lucy_launch.py starts joint_state_publisher alongside the launch and
stops it on exit.
Windows only, and off when LUCY_JOINT_STATE_FALLBACK=0: elsewhere
joint_state_broadcaster already publishes /joint_states and a second publisher
would fight it. The standalone task stays for debugging.
* feat(windows): make the panel's joint commands move the model
The panel publishes JointTrajectory to <controller>/joint_trajectory, which
joint_trajectory_controller normally consumes before the broadcaster reports back
on /joint_states. Both live in controller_manager, which crashes on win-64, so
the commands went nowhere and the robot stayed in its default pose.
joint_command_echo subscribes to any */joint_trajectory topic and republishes the
commanded positions as JointState; joint_state_publisher merges that over the
URDF's full joint list via source_list. core starts both.
Open loop -- positions echo back as requested, with no dynamics and no hardware.
Verified: commanding a joint to 0.7 rad moves it in /joint_states.
* docs: correct the Windows setup and run instructions
The Windows quick start pointed at 'python windows\Lucy.py', which now reports
that the TUI needs tmux and exits; and install pointed at a Lucy.py --cli flow
rather than install.py, which is the cross-platform entry point.
Document the MSVC prerequisite, the no-spaces path requirement and the pixi run
tasks that replace the TUI on Windows.
* fix(windows): ramp echoed joints instead of teleporting them
The echo snapped each joint straight to its target, so sliders looked like
teleports rather than movement. Honour the goal's time_from_start and
interpolate at the publish rate, which is roughly what a trajectory controller
would produce. A new goal starts from the current interpolated value, so
overlapping commands stay continuous.
* Revert "fix(windows): ramp echoed joints instead of teleporting them"
This reverts commit 1238c13cbc7ff83fe12528ecafa20016ccea5382.
* feat(windows): install the MSVC build tools from install.py
Pixi already bootstraps itself when missing, but the C++ toolchain — equally
required, and the thing that actually blocks the build — was left as a manual
step in the docs.
ensure_msvc installs it through winget on the same terms as ensure_pixi: prompt
first, LUCY_MSVC_AUTO_INSTALL=1 to skip the prompt, LUCY_SKIP_MSVC_INSTALL=1 to
refuse. Only the compiler component is requested, since Windows usually ships the
SDK already and the full C++ workload is several GB larger. No-op off Windows and
when a toolchain is already detected.
* fix(windows): skip the toolchain install whenever one is already usable
find_vcvars() alone missed a perfectly good toolchain: inside a VS developer
prompt cl.exe is already on PATH, and vswhere can be blind to some installs, so
install.py would have offered to reinstall the Build Tools and the build wrapper
would have warned that C++ packages cannot configure.
msvc_available() is now the single gate for all three: the install prompt, the
prerequisite check and the build environment. It reports true if cl.exe is on
PATH or vcvars is locatable.
* fix: switched to feat-pixi branches
---------
Signed-off-by: Mael-RABOT <mael.rabot@epitech.eu>
Co-authored-by: Mael-RABOT <mael.rabot@epitech.eu>
Contributor
Author
|
See comments of #45 |
Contributor
Author
|
Pixi should be asked to be installed when doing |
* fix(pixi): bound ros2 doctor, green the Windows tests, stop stacking publishers `ros2 doctor --report` waits on the middleware, and on the macOS runner Cyclone picked a virtual interface and failed every multicast write to 239.255.0.1, so the job sat there for the full two hours. Scoping discovery to localhost gets a report in 9s. Pixi task tables take only cmd/depends-on/ description/args, so there is nowhere to hang env off a task; scripts/ ros_doctor.py sets it and also caps the runtime, so a stuck middleware can never cost two hours again. The headless launch smoke test ran the same bare command, so point it at the script too. tests: the tegra-release probe compared a str() of a Path, which is backslash-separated on Windows and never matched; compare as posix. Windows has no POSIX mode bits, so only assert 0o700 where it means something. The two detect_jetson.sh tests shell out to `bash`, which on Windows resolves to the WSL launcher — one failed outright and the other passed only because WSL also exits non-zero. Both are POSIX-only, so skip them honestly. pixi_lucy_launch: the /joint_states stand-in was started through `ros2 run`, which leaves the node running when the wrapper is terminated; five of them had piled up from earlier runs, all publishing stale poses against the live stack. Run the node directly, and only start it once the real stack has had time to come up and the topic still has no publisher. * evol(install): drop install.sh, fix Windows output, gate the joint-state stand-in install.sh only exec'd install.py, so the wrapper is gone and CI, README, the developer guide, launch_lucy.sh, Lucy.py and build_local_realsense.sh now call `python3 install.py` directly. launch_lucy.sh also lost two lines describing a docker/ tree this branch deleted. install.py printed em dashes that a cp1252 Windows console turns into mojibake, and finished by pointing at ./launch_lucy.sh, which does not run on Windows; there it now names the pixi tasks instead. The /joint_states stand-in never started: it probed the topic with `ros2 topic info`, whose daemon inherits the pipe, so capturing the output blocked forever even after the timeout killed the CLI. Ask the graph directly instead. repos.json points at the matching fix branches; move it back to feat-pixi / dev once those merge. Also shortens the .env.example DDS note and the Windows section of the README, which claimed the robot follows the sliders and is not driveable in one breath. * fix(installer): package install.py, not the deleted install.sh The NSIS script still listed install.sh as a fatal File entry, so both Windows installer jobs failed at makensis with "no files found". My earlier sweep for references filtered by extension and never looked at .nsi. * test: guard against spawning ROS nodes through the ros2 CLI The `ros2` CLI runs the node as its own child, so signalling or timing out the handle you hold orphans it. That one mistake caused five separate bugs on this branch. Fail the suite on any new `ros2 ...` argv built in this repo, with an allowlist naming why the two existing ones cannot orphan or block. Verified it fails: a planted `ros2 run` is reported as file:line.
* feat(launcher): make python3 Lucy.py the entry point on Windows too Lucy.py imported curses at module level, so on Windows it died before doing anything. There is also no tmux there to drive the TUI. Import curses lazily, and on Windows run install.py when the workspace is not built, then print the pixi tasks to start by hand. install.py runs with the terminal attached so its pixi and MSVC prompts are answerable. is_installed() also checks install/setup.bat, which is what colcon writes on Windows. * chore(windows): drop generated artifacts, the ps1 wrapper and dead re-exports releases.json and releases.nsh are written by generate_releases.py, which both build paths run first, so tracking them only commits an artifact that drifts. Gitignore them instead. build_installer.ps1 is referenced by nothing in CI and duplicates the steps CI already runs, in PowerShell. The Windows README now lists those steps directly, so there is no wrapper to fall out of sync. install_ops re-exported 25 names from install.py; install_runner.py, Lucy.py and CI together reach for 8. Keep those. * fix(windows): make the Lucy.exe shortcut do something that works With no arguments Lucy.exe handed off to launch_lucy.sh, which on Windows ends at the curses TUI — no curses, no tmux — so the Start Menu and Desktop shortcuts the installer creates have never worked. They point at Lucy.exe with no arguments, and the .exe is the main Windows install path. It now installs when the workspace is missing and otherwise names the pixi tasks, matching what python3 Lucy.py does. The --cli mode Lucy-Setup.exe drives is untouched. Dropping the launcher half also retires run_command, _find_git_bash and the subprocess import. Also corrects a comment claiming windows/Lucy.py reads install_ops directly; it reaches it through install_runner. * chore: drop the windows/ exe sources and the two jobs that built them Lucy.exe is built elsewhere now, so windows/ has no consumer here: Lucy.py, install_runner.py, install_ops.py, generate_releases.py, installer/Lucy.nsi and the icon all existed only to produce Lucy.exe and Lucy-Setup.exe. windows-build-test and build-and-release-windows-exe go with them — every step they ran pointed inside windows/, so they had nothing left to build. pixi-install-build-test is untouched and still covers all six platforms including windows-latest. One fact lived only in windows/README.md and is now in the root README and the developer guide: pixi resolves win-64 on Windows-on-ARM too, because pixi.lock has no win-arm64. * chore(ci): drop Release from the workflow, link the installer repo Nothing here releases any more, so the workflow is Install, Build & Test and the v* tag trigger goes with the job it used to fire. README and the developer guide now point end users at Lucy-Windows-Installer, which is where Lucy-Setup.exe is built. * fix: readme * chore(repos): track the inmoov_urdf Windows gazebo fixes Points inmoov_urdf at aes/fix-windows-gz-args so a fresh install picks up the three Windows launch fixes. Move it back to feat-pixi once that merges. * feat(windows): make Windows visualisation only and refuse Gazebo up front `pixi run sim` spent minutes doing nothing useful: gz_ros2_control runs a controller_manager inside the Gazebo server, and it dies there with an access violation, so the world never loads and every spawner times out. Reported as pal-robotics/pal_statistics#27. Say that immediately instead of hanging; core, rviz and the control panel are unaffected. The Windows plugin path was also written in POSIX syntax, so it never expanded and Gazebo could not find gz_ros2_control-system or mock_sensor at all: GZ_SIM_SYSTEM_PLUGIN_PATH = '$CONDA_PREFIX/Library/bin' <- literal %CONDA_PREFIX% expands. That is what surfaced the crash above; on its own it turned a silent no-controllers sim into a crashing one, which is why the guard lands with it. Gazebo support on Windows is left for a later PR. * chore(repos): point inmoov_urdf back at feat-pixi The Windows Gazebo fixes on aes/fix-windows-gz-args are no longer needed here now that sim refuses to start on Windows, so this branch does not have to depend on an unmerged one. They move to the Gazebo PR. * Enable workflow to trigger on version tags Signed-off-by: Antoine ESMAN <antoine.esman@epitech.eu> * Fix YAML syntax for tags in workflow file Signed-off-by: Antoine ESMAN <antoine.esman@epitech.eu> --------- Signed-off-by: Antoine ESMAN <antoine.esman@epitech.eu>
A Windows machine on the same network was publishing /robot_description into this graph. rviz2 on Linux resolved meshes against it and logged Could not load resource [file://C:\Users\...\meshes\dae\stand.dae] seconds after the local stack came up, and the panel's 3D viewer showed nothing. The URDF generated locally is clean, so the bad one was not ours: subnet discovery had merged two robots into one graph. Meshes are the visible half; two controller_managers on one graph command the same joints. Nothing here needs discovery beyond the local host. Every node runs on the machine that started the launcher, and the panel is reached over a websocket proxied to ws://127.0.0.1:9090 on the robot, so a Jetson driven over Tailscale needs what a laptop needs. Default ROS_AUTOMATIC_DISCOVERY_RANGE to LOCALHOST on every platform. It is rcl's knob rather than CycloneDDS's, which matters because RMW_IMPLEMENTATION is pinned to Cyclone on macOS only and a CYCLONEDDS_URI is inert under the rmw_fastrtps_cpp that Linux loads -- so the previous localhost scope protected one of the two platforms that needed it. LUCY_DDS_PEERS now also exports ROS_STATIC_PEERS, which rcl treats as reachable in addition to localhost. ROS_LOCALHOST_ONLY stays unset: deprecated, and it takes precedence.
The launcher status-probe tests landed in #54 assume a POSIX host and broke the windows-latest CI job: * controllers_active.sh needs its fake pixi to be executable and reaps the probe by process group. Path.chmod sets no exec bit on Windows, so `command -v pixi` fails and the script exits before probing. * curses has no Windows build, so importing it in the exit-prompt tests raises ModuleNotFoundError. confirm_exit_action reads KEY_UP/KEY_DOWN off the module, which launcher.tui already leaves None where curses is absent. Reuses the posix_only marker convention from tests/test_jetson_platform.py.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.