Repository navigation
Add a self-running demo and a one-step Windows start - #34
Conversation
Replace the placeholder with the five-joint arm exported from full-arm-smaller.SLDASM. The tools in scripts/cad regenerate the URDF and meshes after CAD changes. demo_mode:=true sweeps each joint in turn and reports a pass/fail row per joint on /diagnostics. Supersedes #7.
Drive the five joints with an Xbox controller through placeholder MKS SERVO42D/57D CAN drives, simulated until the real drives are connected. On Windows, a Python-only XInput bridge sends the controller state to the new wslg-teleop Compose service over UDP. The RViz camera now follows the tool so the end of the arm stays in view.
# Conflicts: # waybionic_bringup/launch/ground_station.launch.py
# Conflicts: # waybionic_bringup/CMakeLists.txt
demo_mode:=True or 1 started joint_state_publisher_gui next to the demo, because the launch compared the raw text with 'true'. And/Not substitutions parse booleans the same way IfCondition does. The joint demo test now uses True and fails without this change. joint_demo rejects a speed_deg_s of zero or less at startup instead of never finishing a move, and receives use_sim_time like the other nodes. The CAD script's help now says which export --check-moves compares.
# Conflicts: # waybionic_bringup/launch/ground_station.launch.py
follow_camera:=false stopped the camera follower, so RViz lost the view_focus frame it orbits. The follower now always runs and holds its default focus when follow is false, and a launch test checks the frame. teleop and joy_source use And/Or/Equals substitutions, so teleop:=True works like IfCondition; the teleop test now uses True. The new nodes receive use_sim_time, and the UDP receiver reads at most 100 packets per poll so a flood cannot starve its other callbacks.
The arm is a base yaw, three parallel pitch joints and a roll through the tool tip, so kinematics.py solves any tip position and tool tilt in closed form from the URDF. The new Cartesian group (Y) moves the tip along straight lines in base_link with the tilt fixed; LB keeps only the strongest axis. At a joint speed limit or the workspace edge the whole step shrinks, so the tip stops on the line, and the roll turns against the yaw to keep the tool's heading. Each drive now gets the next setpoint one period ahead and the speed that reaches it in exactly one period, measured from its encoder, so all drives arrive together. acc is 255 because a gentle drive ramp adds the same rpm/s to every drive and the one with the largest change falls behind. In simulation a sideways cut at 30:1 gearing stays within 7-17 um of the line, compared with up to 186 um with the old 1.5x speed rule. At the 1:1 placeholder gearing it stays within about 0.13 mm, one encoder count at the tip.
D-pad left/right changes the tool pitch while the tip stays in place, so the shoulder, elbow and wrist move around a fixed point. The tilt uses the same closed-form step as straight moves, so a joint limit stops it with the tip still in place. It runs at up to 30 deg/s, scaled by the speed level.
- Start is refused while a tilt button or A is held, like the sticks. - Homing clears the Cartesian rates, so releasing A can't resume the last move. - Cartesian roll ramps with the acceleration limit and scales with the accepted step. - The drives' setpoint one period ahead stays within the URDF joint limits, which the drive node now reads from robot_description. - If one drive would pass max_rpm, every drive slows by the same factor, so they still arrive together. - The roll compensation is documented as stopping the tool spinning about its own axis; that keeps a blade's heading only when the tool points straight down. - The Cartesian launch test is registered where it won't conflict with #22.
Reviewed in #24, which was merged into its stacked base after #23 had already been squash-merged into main, so it never reached main. This is the same change, applied to main. Drive the five joints with an Xbox controller through placeholder MKS SERVO42D/57D CAN drives, simulated until the real drives are connected. On Windows, a Python-only XInput bridge sends the controller state to the new wslg-teleop Compose service over UDP. The RViz camera follows the tool so the end of the arm stays in view; follow_camera:=false keeps a fixed view. teleop and joy_source use And/Or/Equals substitutions, so teleop:=True works like IfCondition. The new nodes receive use_sim_time, and the UDP receiver reads at most 100 packets per poll so a flood cannot starve its other callbacks.
The drive node can now send its frames to real MKS SERVO42D/57D drives through any python-can interface, chosen with the drive_interface and drive_channel launch arguments. The simulation stays the default. Because the encoders count from power-on, nothing is published or driven until the arm is zeroed (92h) through ~/zero. Simulated drives are zeroed at start-up. A drive that stops answering stops every drive and must be zeroed again. Stale commands hold the last target, and the drives are stopped when the node exits. mks_drive_sim answers on a CAN interface like the drives do. A new CI job runs the end-to-end test over vcan0. UDP multicast hands every frame back to its sender, and MKS replies reuse the command's CAN ID, so the bus drops its own echoes.
autoplay:=true puts a node between the controller and teleop. After 30 s without input, it plays the controller: it enables teleop, sets 50% speed, homes, lifts the arm in the upper group, then draws a square and a vertical line and tilts the tool about its tip in the Cartesian group. RViz shows the path and a caption. Teleop's diagnostics close the loop, so every run starts the same way whatever group, speed or pose was left behind. Touching the controller stops the demo and hands the arm over. Autoplay only runs with simulated drives. scripts/windows-demo.cmd starts Docker Desktop if needed, the controller bridge when Python is installed, and the new wslg-demo service.
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 9 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Startup bypasses the documented idle delay, and Python detection can select Python 2 instead of an available Python 3 installation.
Review effort: Balanced
Findings: 2
Open (2)
What changed in this PR
Adds a self-running simulated-arm demo with controller takeover and one-step Windows startup.
Changes:
- Adds autoplay sequencing, takeover logic, RViz markers, and tests.
- Integrates autoplay into ROS launch and Docker Compose.
- Adds Windows launch scripts and usage documentation.
| File | Description |
|---|---|
waybionic_teleop/waybionic_teleop/demo_script.py |
Defines the scripted demonstration. |
waybionic_teleop/waybionic_teleop/autoplay_node.py |
Switches between autoplay and operator input. |
waybionic_teleop/test/test_demo_script.py |
Tests sequencing and state recovery. |
waybionic_teleop/setup.py |
Registers the autoplay executable. |
waybionic_teleop/package.xml |
Adds the geometry message dependency. |
waybionic_bringup/test/test_autoplay_launch.py |
Tests launch-level playback and takeover. |
waybionic_bringup/launch/ground_station.launch.py |
Adds autoplay launch configuration and validation. |
waybionic_bringup/CMakeLists.txt |
Registers the launch test. |
scripts/windows-demo.ps1 |
Starts Docker, controller bridge, and demo. |
scripts/windows-demo.cmd |
Provides a double-clickable Windows entry point. |
compose.yaml |
Adds the WSLg demo service. |
BuildInstructions.md |
Documents autoplay startup and operation. |
.gitattributes |
Enforces CRLF for command scripts. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Each drive must now confirm its mode, reply, enable and heartbeat settings before the arm moves. Unconfirmed settings are sent again every 0.5 s, so a lost frame can't leave a drive running without its heartbeat stop. Both buses report whether a frame went out, and a target the interface refused is not recorded as sent, so the next tick sends it again.
windows-demo.ps1 now asks each candidate (py -3, python, python3) whether it is Python 3, instead of accepting any interpreter that prints a version. The docs and the node's docstring now say the demo starts as soon as teleop is ready, and again after 30 s of idle.
actionlint (shellcheck SC2046) flagged the unquoted uname expansion.
On a busy CI runner the teleop node could still be loading the robot description when the test pressed Start, so the press was lost and the arm never moved. The test now sends idle packets until teleop reports its group and fresh controller input.
The tests could press Start before the teleop node had loaded the robot description or received controller input, so the press was lost on a busy machine. They now send idle packets until teleop reports its group and fresh input. Same fix as on #28.
- Joint commands that stop mid-move, say because teleop exited, now stop every drive where it is instead of letting it run on to the last target. - The simulated drives integrate the full time since the last tick in 10 ms steps, so a pause longer than the heartbeat stops a moving drive, as on hardware. - Axis coordinates take the whole int24 range, down to -0x800000.
A second robot_description replaced an enabled teleop with a disabled one without sending a hold, so the drives kept their last target.
A position-only JointState leaves every velocity at zero, so the stale-command check never fired for an external publisher: the drives ran on to the last target after the publisher died. The check now looks only at how long it has been since the last command, latched to one stop and one warning so start-up and idle holds stay quiet.
absolute_axis rejected -0x800000, which the manual's 3-byte signed coordinate encodes. The node also capped the elapsed time it handed the drives at 100 ms, so a scheduler stall longer than the heartbeat never stopped a moving drive the way hardware would. The drive splits a long step into 10 ms motion steps and counts all of it toward the heartbeat.

Covers item 7 from the Oct 3 list: a demo that runs on its own, gives way when someone touches the controller, resumes when the controller is left idle, and starts in one step on Windows. Biomedical's project manager asked for this for orientation. Stacked on #31, because autoplay checks that
drive_interfaceissim.What changes
autoplay:=true(withteleop:=true): the controller source now publishesjoy_operator, and a newautoplaynode passes it through to teleop'sjoy.idle_s(30 s) without a button, stick or trigger moving.teleop.state,teleop.group) close the loop, and a second of silence makes teleop disable itself. So every loop starts the same way, whatever group, speed or pose the last person left behind.scripts/windows-demo.cmd(callswindows-demo.ps1):py,python,python3, because the Store placeholderpython.exefails);wslg-demoCompose service..gitattributeskeeps.cmdfiles in CRLF.How I tested it
test_demo_script.pyruns the player againstArmTeleop, with teleop's 0.5 s input timeout and 2 Hz state reports:test_autoplay_launch.py: with no controller, the demo reaches the enabled Cartesian group with the shoulder lifted. Then B on/joy_operatordisables teleop, and the arm stays put for 3 s.docker compose --progress plain build test: all 779 tests pass.Windows script, short of the GUI run:
wslg-demoservice renders withdocker compose config;I didn't run the full RViz window here.