Skip to content

Conversation

@mustafab0
Copy link
Contributor

Summary

  • Refactored manipulation planning stack for clean backend-agnostic/backend-specific separation
  • Protocol based architecture
  • Planning stack is comprised of 3 pieces - World (Sim enginer), Kinematics (IK,FK solvers), Planners

@mustafab0
Copy link
Contributor Author

@greptile

@mustafab0 mustafab0 requested review from a team, paul-nechifor and spomichter January 21, 2026 06:41
@greptile-apps
Copy link

greptile-apps bot commented Jan 21, 2026

Greptile Summary

  • Refactored manipulation planning stack to use protocol-based architecture with clean separation between backend-agnostic and backend-specific components
  • Implemented modular design comprised of three main components: World (simulation engines), Kinematics (IK/FK solvers), and Planners
  • Introduced comprehensive factory pattern and specification protocols to enable pluggable backends like Drake while maintaining consistent interfaces

Important Files Changed

Filename Overview
dimos/manipulation/manipulation_module.py New comprehensive manipulation module implementing state machine-based planning with multi-robot support and orchestrator integration
dimos/manipulation/planning/world/drake_world.py New Drake-based world simulation providing physics, collision detection, and kinematics through protocol interface
dimos/manipulation/planning/spec/protocols.py New protocol definitions establishing interfaces for World, Kinematics, and Planner components with thread-safe context management
dimos/manipulation/planning/planners/rrt_planner.py New RRT-Connect planner implementation with potential infinite loop risk in tree connection algorithm
dimos/manipulation/planning/kinematics/jacobian_ik.py New backend-agnostic Jacobian-based IK solver with adaptive damping and collision checking integration

Confidence score: 4/5

  • This PR represents a significant architectural improvement that should provide better modularity and maintainability for the manipulation planning system
  • Score reflects the comprehensive refactoring with good separation of concerns and protocol-based design, but reduced due to potential infinite loop in RRT planner and complexity of changes requiring thorough testing
  • Pay close attention to dimos/manipulation/planning/planners/rrt_planner.py for the infinite loop risk in the _connect_tree method and ensure proper testing of the new manipulation module state machine

Sequence Diagram

sequenceDiagram
    participant User
    participant ManipulationModule
    participant WorldMonitor
    participant DrakeWorld
    participant RRTPlanner
    participant JacobianIK
    participant Orchestrator
    
    User->>ManipulationModule: "plan_to_joints([0.1, 0.2, 0.3])"
    ManipulationModule->>WorldMonitor: "get_current_positions()"
    WorldMonitor->>DrakeWorld: "get_positions(ctx, robot_id)"
    DrakeWorld-->>WorldMonitor: "current_joints"
    WorldMonitor-->>ManipulationModule: "current_joints"
    
    ManipulationModule->>RRTPlanner: "plan_joint_path(world, robot_id, start, goal)"
    RRTPlanner->>DrakeWorld: "check_config_collision_free(robot_id, q)"
    DrakeWorld-->>RRTPlanner: "collision_free"
    RRTPlanner-->>ManipulationModule: "PlanningResult(path)"
    
    ManipulationModule->>ManipulationModule: "generate_trajectory(path)"
    ManipulationModule-->>User: "plan_success"
    
    User->>ManipulationModule: "execute()"
    ManipulationModule->>ManipulationModule: "translate_trajectory_to_orchestrator()"
    ManipulationModule->>Orchestrator: "execute_trajectory(task_name, trajectory)"
    Orchestrator-->>ManipulationModule: "execution_accepted"
    ManipulationModule-->>User: "execution_started"
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

33 files reviewed, 14 comments

Edit Code Review Agent Settings | Greptile

@mustafab0 mustafab0 force-pushed the modular_manipulation_stack branch 2 times, most recently from 2b839fb to 21a0403 Compare January 21, 2026 18:53
…ith real world. manages obstacle lifecycle and more
… implementation, split kinematics into JacobianIK and DrakeOptimizationIK
manipulation_blueprint - Added optional add_gripper: bool = True to make xarm6 ans xarm7 config consistent.
world_obstacle_monitor - Added warning log when obstacle not found during cleanup
manipulation_client - Removed unused numpy impor
path_utils - Added explicit tolerances atol=1e-6, rtol=0 to np.allclose() for stricter joint-space duplicate detection
jacobian_ik - Added division-by-zero protection for velocity limits using nonzero_mask to skip zero-valued limits
drake_world.py - added more specific exception handling (avoids hiding bugs)
mesh_utils - regex fixes
@mustafab0 mustafab0 force-pushed the modular_manipulation_stack branch from ec17f02 to 8d2020a Compare January 22, 2026 23:01
# =============================================================================


def _get_xarm_urdf_path() -> str:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ideally you can use Path objects for paths, they are super convinient, so no need for str conversion


def _get_xarm_urdf_path() -> str:
"""Get path to xarm URDF."""
return str(get_data("xarm_description") / "urdf/xarm_device.urdf.xacro")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_data("xarm_description/urdf/xarm_device.urdf.xacro") is now supported - returns full Path

return RobotModelConfig(
name=name,
urdf_path=_get_xarm_urdf_path(),
base_pose=np.array(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a bit suspicious, why not Pose

Attributes:
name: Unique name for the obstacle
obstacle_type: Type of geometry (BOX, SPHERE, CYLINDER, MESH)
pose: 4x4 homogeneous transform
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you have Pose :)

def solve(
self,
world: WorldSpec,
robot_id: str,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm never sure if we should use robot_ids or actual instances/classes, it's more easy to not maintain ID -> something layer if you can use something directly

(world.robot1) ?

self,
world: WorldSpec,
robot_id: str,
target_pose: NDArray[np.float64],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PoseStamped?

q_start: NDArray[np.float64],
q_goal: NDArray[np.float64],
timeout: float = 10.0,
) -> PlanningResult:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be a full joint space "path" so that it can be fed into a controller immediately?

because we might want to use planners that plan in full joint space for difficult situations

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.

3 participants