From ec16118f33b69cff3bfc6eb36db5fd026f61f384 Mon Sep 17 00:00:00 2001
From: Ben Kearns <35475+bkearns@users.noreply.github.com>
Date: Tue, 5 May 2026 14:29:59 -0700
Subject: [PATCH] feat(sim): drivable Perseverance + auto-run + cmd_vel helpers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
User reported nothing was moving. Two real causes plus a usability
gap:
1. Perseverance was true — visible but unable to
move. Restructure the model:
- Body link with mass/inertia carries deck, RTG, mast, arm,
and rocker rails as visuals.
- Six wheel links (fl/ml/rl/fr/mr/rr) with collisions, inertials,
and inner-pose-rotated cylinders so the link frame stays
upright and the joint axis is clean.
- Six revolute joints (axis 0 1 0 in the child frame) connect
each wheel to body.
- DiffDrive plugin (ignition-gazebo-diff-drive-system) lists all
three left + three right joints, wheel_separation 2.40,
wheel_radius 0.26, listens on /model/perseverance/cmd_vel.
2. Ignition Fortress opens paused by default. `ign gazebo` without
`-r` requires the user to click Play before commands take
effect. Add `-r` to the entrypoint so the world auto-runs.
3. ros_gz_bridge now wires /cmd_vel ↔ /model/perseverance/cmd_vel
(and the matching odometry topics) for the moon.sdf world, so
ROS-side controllers can drive the rover too.
4. New Make helpers — no ROS install needed on the host:
make sim-drive-fwd LX=0.5 (default) m/s
make sim-drive-back
make sim-drive-left spin
make sim-drive-right
make sim-drive-stop
Each shells `ign topic` inside the sim container.
Verified locally: DiffDrive subscribed to
/model/perseverance/cmd_vel; ros_gz_bridge created bidirectional
bridges; world unpause via /world/moon/control returns data: true;
cmd_vel publish accepted.
---
Makefile | 33 +++
docker/sim/entrypoint.sh | 18 +-
docker/sim/models/perseverance/model.sdf | 254 ++++++++++++++++-------
3 files changed, 224 insertions(+), 81 deletions(-)
diff --git a/Makefile b/Makefile
index 1829ed4..bfb9d60 100644
--- a/Makefile
+++ b/Makefile
@@ -229,6 +229,39 @@ sim-gui: ## Open the Gazebo GUI in the default browser
(command -v xdg-open >/dev/null && xdg-open "$$URL") || \
echo "Open it manually."
+# Drive helpers for the perseverance rover in the moon world. These
+# publish straight to the in-container Ignition topic (no ROS install
+# on the host required). Override LX / AZ to vary speed.
+LX ?= 0.5
+AZ ?= 0.0
+SIM_CONTAINER ?= temporal-hack-lab-sim-1
+GZ_DRIVE_TOPIC ?= /model/perseverance/cmd_vel
+
+.PHONY: sim-drive-fwd
+sim-drive-fwd: ## Drive the rover forward (LX m/s, override LX=)
+ @$(CONTAINER_ENGINE) exec $(SIM_CONTAINER) bash -c \
+ 'ign topic -t $(GZ_DRIVE_TOPIC) -m ignition.msgs.Twist -p "linear: {x: $(LX)}"'
+
+.PHONY: sim-drive-back
+sim-drive-back: ## Drive the rover backward
+ @$(CONTAINER_ENGINE) exec $(SIM_CONTAINER) bash -c \
+ 'ign topic -t $(GZ_DRIVE_TOPIC) -m ignition.msgs.Twist -p "linear: {x: -$(LX)}"'
+
+.PHONY: sim-drive-left
+sim-drive-left: ## Spin in place, left
+ @$(CONTAINER_ENGINE) exec $(SIM_CONTAINER) bash -c \
+ 'ign topic -t $(GZ_DRIVE_TOPIC) -m ignition.msgs.Twist -p "angular: {z: 0.5}"'
+
+.PHONY: sim-drive-right
+sim-drive-right: ## Spin in place, right
+ @$(CONTAINER_ENGINE) exec $(SIM_CONTAINER) bash -c \
+ 'ign topic -t $(GZ_DRIVE_TOPIC) -m ignition.msgs.Twist -p "angular: {z: -0.5}"'
+
+.PHONY: sim-drive-stop
+sim-drive-stop: ## Stop the rover
+ @$(CONTAINER_ENGINE) exec $(SIM_CONTAINER) bash -c \
+ 'ign topic -t $(GZ_DRIVE_TOPIC) -m ignition.msgs.Twist -p "linear: {x: 0}, angular: {z: 0}"'
+
# =============================================================================
# CI cluster (smoke / pre-push parity) — alternate ports so it can run
# alongside `make lab-up` on the same host. Used by .git-hooks/installer-smoke.sh
diff --git a/docker/sim/entrypoint.sh b/docker/sim/entrypoint.sh
index 3112a6b..a17efeb 100644
--- a/docker/sim/entrypoint.sh
+++ b/docker/sim/entrypoint.sh
@@ -60,7 +60,10 @@ else
# OGRE2 will use Mesa's llvmpipe and render to the virtual fb.
export LIBGL_ALWAYS_SOFTWARE=1
export OGRE2_RTSHADERSYSTEM_WRITE_SHADERS_TO_DISK=0
- ign gazebo -v 4 "$SIM_WORLD" &
+ # -r: start running (not paused). Without this Fortress opens
+ # paused and the user has to click Play before any movement
+ # commands have effect.
+ ign gazebo -r -v 4 "$SIM_WORLD" &
PIDS+=($!)
echo
@@ -88,8 +91,19 @@ case "$SIM_WORLD" in
-r /model/vehicle_blue/odometry:=/odom &
PIDS+=($!)
;;
+ moon.sdf)
+ sleep 4
+ echo "[sim] starting ros_gz_bridge: /cmd_vel ↔ /model/perseverance/cmd_vel"
+ ros2 run ros_gz_bridge parameter_bridge \
+ /model/perseverance/cmd_vel@geometry_msgs/msg/Twist@ignition.msgs.Twist \
+ /model/perseverance/odometry@nav_msgs/msg/Odometry@ignition.msgs.Odometry \
+ --ros-args \
+ -r /model/perseverance/cmd_vel:=/cmd_vel \
+ -r /model/perseverance/odometry:=/odom &
+ PIDS+=($!)
+ ;;
*)
- echo "[sim] no ros_gz_bridge for world '$SIM_WORLD' (no vehicle_blue)"
+ echo "[sim] no ros_gz_bridge wired for world '$SIM_WORLD'"
;;
esac
diff --git a/docker/sim/models/perseverance/model.sdf b/docker/sim/models/perseverance/model.sdf
index 35fa6ad..ddb92b0 100644
--- a/docker/sim/models/perseverance/model.sdf
+++ b/docker/sim/models/perseverance/model.sdf
@@ -1,28 +1,35 @@
- true
-
+
0 0 0.65 0 0 0
700
- 120140200
+
+ 120140200
+ 000
+
+
+ 1.20 1.40 0.40
+
1.20 1.40 0.40
@@ -36,12 +43,10 @@
1.10 1.30 0.04
0.30 0.28 0.25 10.55 0.51 0.46 1
-
-
-
- -0.85 0 0.85 0 1.5708 0
-
+
+
+ -0.85 0 0.20 0 1.5708 0
0.160.45
0.25 0.22 0.18 1
@@ -49,105 +54,196 @@
0.25 0.25 0.25 1
-
-
-
- 0.50 0 1.30 0 0 0
-
+
+
+ 0.50 0 0.65 0 0 0
0.051.20
0.30 0.28 0.25 10.55 0.51 0.46 1
-
-
- 0.50 0 1.95 0 0 0
-
+
+ 0.50 0 1.30 0 0 0
0.32 0.20 0.18
0.20 0.20 0.22 10.30 0.30 0.32 1
- 0.18 0.06 0 0 1.5708 0
+ 0.68 0.06 1.30 0 1.5708 0
0.0350.06
0.05 0.05 0.05 10.10 0.10 0.10 10.6 0.6 0.6 1
- 0.18 -0.06 0 0 1.5708 0
+ 0.68 -0.06 1.30 0 1.5708 0
0.0350.06
0.05 0.05 0.05 10.10 0.10 0.10 10.6 0.6 0.6 1
-
-
-
- 0.50 -0.65 0.85 0 0 -0.4
-
+
+
+ 0.50 -0.65 0.20 0 0 -0.4
0.050.55
0.30 0.28 0.25 10.55 0.51 0.46 1
-
-
- 0.78 -0.55 0.85 0 1.2 -0.4
-
+
+ 0.78 -0.55 0.20 0 1.2 -0.4
0.0450.45
0.30 0.28 0.25 10.55 0.51 0.46 1
-
-
- 0.95 -0.40 0.45 0 0 0
-
+
+ 0.95 -0.40 -0.20 0 0 0
0.18 0.18 0.16
0.20 0.20 0.22 10.30 0.30 0.32 1
+
+
+
+ 0 0.95 -0.15 0 0 0
+ 2.30 0.06 0.06
+ 0.30 0.28 0.25 10.55 0.51 0.46 1
+
+
+ 0 -0.95 -0.15 0 0 0
+ 2.30 0.06 0.06
+ 0.30 0.28 0.25 10.55 0.51 0.46 1
+
-
+
+
+
+
- 1.10 1.20 0.26 1.5708 0 0
- 0.260.20
- 0.06 0.06 0.06 10.12 0.12 0.12 1
+ 1.10 1.20 0.26 0 0 0
+ 30
+ 1.61.01.6
+
+ 0 0 0 1.5708 0 0
+ 0.260.20
+
+ 0 0 0 1.5708 0 0
+ 0.260.20
+ 0.06 0.06 0.06 10.12 0.12 0.12 1
+
- 0.00 1.20 0.26 1.5708 0 0
- 0.260.20
- 0.06 0.06 0.06 10.12 0.12 0.12 1
+ 0.00 1.20 0.26 0 0 0
+ 30
+ 1.61.01.6
+
+ 0 0 0 1.5708 0 0
+ 0.260.20
+
+ 0 0 0 1.5708 0 0
+ 0.260.20
+ 0.06 0.06 0.06 10.12 0.12 0.12 1
+
- -1.10 1.20 0.26 1.5708 0 0
- 0.260.20
- 0.06 0.06 0.06 10.12 0.12 0.12 1
+ -1.10 1.20 0.26 0 0 0
+ 30
+ 1.61.01.6
+
+ 0 0 0 1.5708 0 0
+ 0.260.20
+
+ 0 0 0 1.5708 0 0
+ 0.260.20
+ 0.06 0.06 0.06 10.12 0.12 0.12 1
+
- 1.10 -1.20 0.26 1.5708 0 0
- 0.260.20
- 0.06 0.06 0.06 10.12 0.12 0.12 1
+ 1.10 -1.20 0.26 0 0 0
+ 30
+ 1.61.01.6
+
+ 0 0 0 1.5708 0 0
+ 0.260.20
+
+ 0 0 0 1.5708 0 0
+ 0.260.20
+ 0.06 0.06 0.06 10.12 0.12 0.12 1
+
- 0.00 -1.20 0.26 1.5708 0 0
- 0.260.20
- 0.06 0.06 0.06 10.12 0.12 0.12 1
-
-
- -1.10 -1.20 0.26 1.5708 0 0
- 0.260.20
- 0.06 0.06 0.06 10.12 0.12 0.12 1
-
-
-
-
- 0 0.95 0.50 0 0 0
-
- 2.30 0.06 0.06
- 0.30 0.28 0.25 10.55 0.51 0.46 1
+ 0.00 -1.20 0.26 0 0 0
+ 30
+ 1.61.01.6
+
+ 0 0 0 1.5708 0 0
+ 0.260.20
+
+ 0 0 0 1.5708 0 0
+ 0.260.20
+ 0.06 0.06 0.06 10.12 0.12 0.12 1
-
- 0 -0.95 0.50 0 0 0
-
- 2.30 0.06 0.06
- 0.30 0.28 0.25 10.55 0.51 0.46 1
+
+ -1.10 -1.20 0.26 0 0 0
+ 30
+ 1.61.01.6
+
+ 0 0 0 1.5708 0 0
+ 0.260.20
+
+ 0 0 0 1.5708 0 0
+ 0.260.20
+ 0.06 0.06 0.06 10.12 0.12 0.12 1
+
+
+
+ bodywheel_fl
+ 0 1 0
+
+
+ bodywheel_ml
+ 0 1 0
+
+
+ bodywheel_rl
+ 0 1 0
+
+
+ bodywheel_fr
+ 0 1 0
+
+
+ bodywheel_mr
+ 0 1 0
+
+
+ bodywheel_rr
+ 0 1 0
+
+
+
+
+ wheel_fl_joint
+ wheel_ml_joint
+ wheel_rl_joint
+ wheel_fr_joint
+ wheel_mr_joint
+ wheel_rr_joint
+ 2.40
+ 0.26
+ /model/perseverance/cmd_vel
+ 20
+ perseverance/odom
+ perseverance/base_link
+ 2
+ 3
+