Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4943a34
Merge pull request #843 from PickNikRobotics/merge-v10.0-main-29521
griswaldbrooks Aug 13, 2026
209ce2f
Merge branch 'v10.0' into merge-v10-main-987654321
JWhitleyWork Aug 14, 2026
b7eb40c
Fix Batch Merge Release Branch
JWhitleyWork Aug 14, 2026
945309b
Merge pull request #850 from PickNikRobotics/merge-v10-main-987654321
JWhitleyWork Aug 14, 2026
dc4852a
Merge remote-tracking branch 'origin/v10.0' into merge-v10.0-main-30472
actions-user Aug 18, 2026
28f8be2
Merge pull request #857 from PickNikRobotics/merge-v10.0-main-30472
JWhitleyWork Aug 18, 2026
95d0e61
Merge remote-tracking branch 'origin/v10.0' into merge-v10.0-main-2986
actions-user Aug 19, 2026
db62616
Merge pull request #859 from PickNikRobotics/merge-v10.0-main-2986
JWhitleyWork Aug 19, 2026
9ee82dc
Merge remote-tracking branch 'origin/v10.0' into merge-v10.0-main-28095
actions-user Aug 20, 2026
205d833
Merge pull request #868 from PickNikRobotics/merge-v10.0-main-28095
JWhitleyWork Aug 20, 2026
420c3e3
fix(hangar_sim): round out the mecanum wheels so the base stops judde…
L4co77 Aug 24, 2026
3149bfc
fix(hangar_sim): 12 rollers per mecanum wheel instead of 16
L4co77 Aug 24, 2026
ca24d95
fix(hangar_sim): size the mecanum rollers tangent, 20 per wheel
L4co77 Aug 24, 2026
5bdb123
fix(hangar_sim): keep the wheel outer radius at exactly 75.9 mm
L4co77 Aug 24, 2026
e9e84c2
docs(hangar_sim): state both failure modes the tangent sizing avoids
L4co77 Aug 24, 2026
8138b1e
Update phoebe workspace licensing
Aug 24, 2026
5e64813
Merge pull request #878 from PickNikRobotics/fix/21543-update-phoebe-…
kronk-bot[bot] Aug 24, 2026
8c767d4
fix(hangar_sim): replace deprecated behavior and ports in move boxes …
D-1shu Aug 21, 2026
a9df4e4
Merge pull request #877 from PickNikRobotics/merge-v10.0-main-12106
JWhitleyWork Aug 24, 2026
714b52d
Merge pull request #875 from PickNikRobotics/fix/21626-mecanum-roller…
L4co77 Aug 25, 2026
d7b8709
fix(hangar_sim): align teleoperation subtree with v10
noah-wardlow Aug 24, 2026
3bede45
chore: remove the moveit_pro_clipseg submodule
davetcoleman Aug 24, 2026
92e2277
Merge pull request #880 from PickNikRobotics/merge-v10.0-main-27721
JWhitleyWork Aug 25, 2026
c7b65d1
feat(hangar_sim): fuse odometry localization for the mobile base
bkanator Jul 28, 2026
253632b
fix(hangar_sim): extract slip_aware_odom logic, add localization stal…
bkanator Aug 6, 2026
76da740
fix(hangar_sim): take ground truth from the robot chain, not a MuJoCo…
bkanator Aug 13, 2026
e3609bb
fix(hangar_sim): drop the dead /odom_reliable relay
bkanator Aug 26, 2026
782d4f0
fix(hangar_sim): correct the mecanum wheel rolling radius to 0.0756
bkanator Aug 26, 2026
0662c5b
fix(hangar_sim): restore the AMCL motion-model alphas to their pre-dr…
bkanator Aug 26, 2026
2b0d3df
docs: record the mecanum rolling-radius vs outer-radius distinction
bkanator Aug 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/batch-merge-release-branch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,4 @@ jobs:
PR_BODY="⚠️Merge failed with conflicts! Please pull this branch and manually resolve the conflict by resetting this branch and re-merging."
fi
git push origin "$TEMP_BRANCH"
gh pr create --title "Merge $RELEASE_BRANCH into main" --body "$PR_BODY" --base main --head "$TEMP_BRANCH" --draft --reviewer infrastructure-devs
gh pr create --title "Merge $RELEASE_BRANCH into main" --body "$PR_BODY" --base main --head "$TEMP_BRANCH" --draft --reviewer JWhitleyWork
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@
path = src/external_dependencies/franka_config/franka_description
url = https://github.com/frankarobotics/franka_description.git
branch = main
[submodule "src/moveit_pro_clipseg"]
path = src/moveit_pro_clipseg
url = https://github.com/PickNikRobotics/moveit_pro_clipseg.git
[submodule "src/external_dependencies/phoebe_ws"]
path = src/external_dependencies/phoebe_ws
url = https://github.com/PickNikRobotics/phoebe_ws.git
Expand Down
22 changes: 22 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,28 @@ The two coupled numbers live in different files: the actuator `kv` is in the `<v

Rule of thumb when changing a sim `timestep`: for every velocity actuator, check `armature/kv < timestep`. The symptom of violation is a joint that ignores commands (pinned), not one that oscillates.

### Mecanum wheel radius: rolling radius != outer radius

`hangar_sim`'s mecanum wheels have no cylindrical tread — each is a ring of spheres
(`description/{front,rear}_{left,right}_wheel_link.xml`). Two different radii come out of
that geometry and they are NOT interchangeable:

- **Outer radius** `a + r` — ring radius plus sphere radius. This is the static ride height,
and is what the base's z-offset is derived from.
- **Rolling radius** — perimeter actually covered per revolution over 2*pi. The contact point
rides the scalloped convex hull of the spheres, not the enclosing circle, so this is
strictly smaller: `(n * 2*a*sin(pi/n) + 2*pi*r) / (2*pi)` for n spheres.

`kinematics.wheels_radius` in `config/control/picknik_ur.ros2_control.yaml` needs the
**rolling** radius. Getting it wrong is doubly bad and easy to miss: the controller divides
commanded body velocity by it (base drives too fast) while wheel odometry multiplies by it
(distance under-reported), so the base overshoots *and* believes it undershot. A value ~12%
low showed up as localization drift, which invites the wrong fix — inflating the AMCL
motion-model alphas in `params/nav2_params.yaml` to spread the particle cloud over it.

The value is geometry-dependent, so re-derive it whenever the roller count or ring radius
changes; the derivation is recorded in a comment at the constant itself.

### MuJoCo documentation

Refer to [docs.picknik.ai](https://docs.picknik.ai) for MuJoCo configuration guides:
Expand Down
2 changes: 1 addition & 1 deletion src/dual_arm_sim/objectives/sort_blocks.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<root BTCPP_format="4" main_tree_to_execute="Sort Blocks">
<BehaviorTree
ID="Sort Blocks"
_description="Clears colored blocks off the table by color, right arm for red and left for green. A CLIPSeg no-masks error is expected when a color runs out of blocks."
_description="Clears colored blocks off the table by color, right arm for red and left for green. An error is expected when a color runs out of blocks: segmentation returns no masks, and the downstream point cloud lookup then fails on the empty result."
_favorite="true"
>
<Control ID="Sequence">
Expand Down
41 changes: 41 additions & 0 deletions src/hangar_sim/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,31 @@ project(hangar_sim)

find_package(ament_cmake REQUIRED)
find_package(picknik_accessories REQUIRED)
find_package(rclcpp REQUIRED)
find_package(nav_msgs REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(tf2 REQUIRED)
find_package(tf2_geometry_msgs REQUIRED)
find_package(tf2_ros REQUIRED)

add_executable(slip_aware_odom src/slip_aware_odom.cpp)
target_include_directories(slip_aware_odom PRIVATE include)
target_compile_features(slip_aware_odom PRIVATE cxx_std_20)
ament_target_dependencies(slip_aware_odom rclcpp nav_msgs tf2 tf2_geometry_msgs)

add_executable(amcl_odom_gate src/amcl_odom_gate.cpp src/amcl_odom_gate_logic.cpp)
target_include_directories(amcl_odom_gate PRIVATE include)
target_compile_features(amcl_odom_gate PRIVATE cxx_std_20)
ament_target_dependencies(amcl_odom_gate rclcpp geometry_msgs tf2 tf2_geometry_msgs tf2_ros)

add_executable(odom_world_drift src/odom_world_drift.cpp)
target_include_directories(odom_world_drift PRIVATE include)
target_compile_features(odom_world_drift PRIVATE cxx_std_20)
ament_target_dependencies(odom_world_drift rclcpp nav_msgs sensor_msgs geometry_msgs tf2 tf2_geometry_msgs
tf2_ros)

install(TARGETS slip_aware_odom amcl_odom_gate odom_world_drift DESTINATION lib/${PROJECT_NAME})

install(
DIRECTORY
Expand Down Expand Up @@ -47,6 +72,22 @@ if(BUILD_TESTING)
ENV MOVEIT_CONFIG_PACKAGE=hangar_sim
MOVEIT_HOST_USER_WORKSPACE=${CMAKE_SOURCE_DIR}
ROS_LOG_DIR=${CMAKE_CURRENT_BINARY_DIR}/test_results/${PROJECT_NAME}/ros_logs)

# Unit tests for the pure amcl_odom_gate decision logic (no ROS/TF).
find_package(ament_cmake_gmock REQUIRED)
ament_add_gmock(test_amcl_odom_gate test/test_amcl_odom_gate.cpp src/amcl_odom_gate_logic.cpp)
target_include_directories(test_amcl_odom_gate PRIVATE include)
target_compile_features(test_amcl_odom_gate PRIVATE cxx_std_20)

# Unit tests for the pure odom_world_drift rail-joint index resolver (no ROS).
ament_add_gmock(test_odom_world_drift test/test_odom_world_drift.cpp)
target_include_directories(test_odom_world_drift PRIVATE include)
target_compile_features(test_odom_world_drift PRIVATE cxx_std_20)

# Unit tests for the pure slip_aware_odom re-anchor/stitch and covariance-ramp logic (no ROS).
ament_add_gmock(test_slip_aware_odom test/test_slip_aware_odom.cpp)
target_include_directories(test_slip_aware_odom PRIVATE include)
target_compile_features(test_slip_aware_odom PRIVATE cxx_std_20)
endif()

ament_package()
42 changes: 32 additions & 10 deletions src/hangar_sim/config/control/picknik_ur.ros2_control.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,17 @@ platform_velocity_controller:
rear_right_wheel_command_joint_name: "rear_right_wheel"
rear_left_wheel_command_joint_name: "rear_left_wheel"

kinematics.wheels_radius: 0.0666
# Effective ROLLING radius of the mecanum wheels: the perimeter actually covered
# per revolution, divided by 2*pi. The tread is a ring of 20 spheres of r=0.010 whose
# centres sit at a=0.0659002081 (see description/*_wheel_link.xml). Rolling on that
# ring traces the convex hull of the spheres: 20 tangent segments of 2*a*sin(pi/20)
# plus arcs summing to one full circle of radius r, so
# (20 * 2*a*sin(pi/20) + 2*pi*r) / (2*pi) = 0.07563.
# NOT the 0.0759 outer radius (a + r) — that is the static ride height and is what
# PR #882 derives the base height from; a wheel does not cover its outer circumference
# per turn because the contact point rides the scalloped hull, not the enclosing circle.
# Do not "correct" this back to 0.0759 or to the old 0.0666.
kinematics.wheels_radius: 0.0756
kinematics.sum_of_robot_center_projection_on_X_Y_axis: 0.59
wheel_separation_multiplier: 1.0
wheel_radius_multiplier: 1.0
Expand Down Expand Up @@ -107,7 +117,17 @@ platform_velocity_controller_nav2:
rear_right_wheel_command_joint_name: "rear_right_wheel"
rear_left_wheel_command_joint_name: "rear_left_wheel"

kinematics.wheels_radius: 0.0666
# Effective ROLLING radius of the mecanum wheels: the perimeter actually covered
# per revolution, divided by 2*pi. The tread is a ring of 20 spheres of r=0.010 whose
# centres sit at a=0.0659002081 (see description/*_wheel_link.xml). Rolling on that
# ring traces the convex hull of the spheres: 20 tangent segments of 2*a*sin(pi/20)
# plus arcs summing to one full circle of radius r, so
# (20 * 2*a*sin(pi/20) + 2*pi*r) / (2*pi) = 0.07563.
# NOT the 0.0759 outer radius (a + r) — that is the static ride height and is what
# PR #882 derives the base height from; a wheel does not cover its outer circumference
# per turn because the contact point rides the scalloped hull, not the enclosing circle.
# Do not "correct" this back to 0.0759 or to the old 0.0666.
kinematics.wheels_radius: 0.0756
kinematics.sum_of_robot_center_projection_on_X_Y_axis: 0.59
wheel_separation_multiplier: 1.0
wheel_radius_multiplier: 1.0
Expand Down Expand Up @@ -160,6 +180,7 @@ platform_velocity_controller_nav2:
joint_state_broadcaster:
ros__parameters:
use_local_topics: false
update_rate: 50 # publish /joint_states at 50 Hz, not the 600 Hz control loop
joints:
- shoulder_pan_joint
- shoulder_lift_joint
Expand Down Expand Up @@ -211,19 +232,19 @@ joint_trajectory_controller:
action_monitor_rate: 20.0
allow_partial_joints_goal: true
open_loop_control: false # this is closed loop
# Workaround for PickNikRobotics/moveit_pro#20766: trajectory generation
# leaves sub-epsilon terminal-velocity residuals that Jazzy's JTC rejects
# by default — measured ~1e-4 on `linear_x_joint` from the MTC Cartesian
# approach in "ML Move Boxes to Loading Zone", and ~4e-7 from joint
# interpolation in "Point-to-Point Trajectory" (previously skipped in
# test/objectives_integration_test.py for exactly this rejection).
# Workaround: trajectory generation leaves sub-epsilon terminal-velocity
# residuals that Jazzy's JTC rejects by default — measured ~1e-4 on
# `linear_x_joint` from the MTC Cartesian approach in "ML Move Boxes to
# Loading Zone", and ~4e-7 from joint interpolation in "Point-to-Point
# Trajectory" (previously skipped in test/objectives_integration_test.py
# for exactly this rejection).
# CAUTION: this flag is binary and controller-wide — it disables the
# endpoint-still-moving check for every magnitude on every joint,
# including the mecanum base, where a genuinely nonzero terminal velocity
# means the base is still translating at trajectory end and then snaps to
# a position hold. Acceptable for this sim config; re-evaluate before
# copying to real hardware. Remove when moveit_pro#20766 clamps the
# residuals at trajectory generation.
# copying to real hardware. Remove once trajectory generation clamps the
# residuals upstream.
allow_nonzero_velocity_at_trajectory_end: true
gains:
shoulder_pan_joint:
Expand Down Expand Up @@ -297,6 +318,7 @@ force_torque_sensor_broadcaster:

imu_sensor_broadcaster:
ros__parameters:
update_rate: 100 # 100 Hz IMU is plenty for fuse (optimizes at 10 Hz); was inheriting the 600 Hz control loop
sensor_name: imu_site
frame_id: ridgeback_base_link
# Static covariance values (row-major 3x3 matrices)
Expand Down
6 changes: 3 additions & 3 deletions src/hangar_sim/config/fuse/fuse.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
state_estimator:
ros__parameters:
# Fixed-lag smoother configuration
optimization_frequency: 20.0
optimization_frequency: 10.0 # match publish_frequency (10 Hz); optimizing 2x faster than we publish was wasted work
transaction_timeout: 0.01
lag_duration: 0.5
lag_duration: 0.3 # chosen: median 0.465deg (<0.5 goal), clean transient tail vs 0.25

# Motion model for mobile base (3D omnidirectional)
motion_models:
Expand Down Expand Up @@ -51,7 +51,7 @@ state_estimator:
# only the relative change between consecutive messages, preventing
# accumulated drift from corrupting the estimate.
wheel_odom_sensor:
topic: /platform_velocity_controller_nav2/odom
topic: /odom_slip_aware # slip_aware_odom republishes with spin-aware yaw covariance
queue_size: 10
pose_loss:
type: fuse_loss::HuberLoss
Expand Down
Loading
Loading