Skip to content

feat(a750): initial A-750 robot support and blueprint#1911

Open
adob wants to merge 2 commits intodimensionalOS:devfrom
adob:dev
Open

feat(a750): initial A-750 robot support and blueprint#1911
adob wants to merge 2 commits intodimensionalOS:devfrom
adob:dev

Conversation

@adob
Copy link
Copy Markdown

@adob adob commented Apr 24, 2026

Implement initial support the A-750 robotic arm.

The following additional changes were made:

Bug fix: JogState.from_fk was computing the initial EE pose at the all-zeros joint configuration (q = [0,0,0,0,0,0]), while the A-750 home configuration is [0, 0, -90°, 0, 0, 0]. The immediately-published target pose was unreachable from home within the 15°/tick safety limit. Fixed by adding an optional q_home parameter to JogState.from_fk and threading home_joints from RobotConfig through KeyboardTeleopConfigKeyboardTeleopModule.

The following additional changes are contemplated but not yet implemented:

Gripper control: Added O/C key bindings to KeyboardTeleopModule for open/close. The module gains a gripper_command: Out[float] stream, and KeyboardTeleopConfig gains gripper_joint, gripper_open_pos, gripper_closed_pos fields. The a750 blueprint wires these from RobotConfig.gripper and routes gripper_command over LCM to the coordinator. CartesianIKTask and ControlCoordinator were extended to accept and forward gripper position commands alongside arm joint commands.

Breaking Changes

None. All new fields are optional with safe defaults; existing blueprints that don't configure a gripper are unaffected.

How to Test

# Start the keyboard teleop blueprint
dimos run keyboard-teleop-a750

Contributor License Agreement

  • I have read and approved the CLA.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 24, 2026

Greptile Summary

This PR adds initial support for the A-750 robotic arm: a new catalog entry, a keyboard-teleop blueprint, an LFS URDF bundle, and a bug fix that seeds JogState from the robot's actual home configuration rather than the all-zeros pose. The str() conversion applied to Piper and XArm6 model paths was not applied to XArm7, leaving keyboard-teleop-xarm7 broken in the same way that prompted the fix.

  • P1 — keyboard_teleop_xarm7 crash: model_path=XARM7_FK_MODEL (line 86 of xarm/blueprints.py) is passed without str(), so model_path.endswith(\".xml\") in JogState.from_fk will raise AttributeError — the same bug fixed for XArm6 in this PR.

Confidence Score: 4/5

Safe to merge after fixing the missing str() on the XArm7 model path.

One P1 regression: the XArm7 blueprint was left with an un-fixed LfsPath model path while XArm6 (and the new A-750) were corrected in this same PR. The A-750 additions themselves are solid; the q_home bug-fix is correct. Score is 4 rather than 5 because of the one definite startup crash on keyboard-teleop-xarm7.

dimos/robot/manipulators/xarm/blueprints.py line 86 — str() missing for XARM7_FK_MODEL.

Important Files Changed

Filename Overview
dimos/robot/manipulators/xarm/blueprints.py Applied str() fix to xarm6 model path but missed the identical fix for xarm7 — keyboard_teleop_xarm7 will crash at startup.
dimos/robot/manipulators/a750/blueprints.py New A-750 keyboard-teleop blueprint; gripper transport removed, copy-paste comment still references "Piper 6-DOF".
dimos/robot/catalog/a750.py A-750 RobotConfig factory; address correctly used; GripperConfig.joints=["finger"] doesn't match URDF joint names (joint7/joint8).
dimos/control/examples/cartesian_ik_jogger.py Added q_home parameter to JogState.from_fk; stale debug log still prints "at q=0" even when a non-zero home config is used.
dimos/teleop/keyboard/keyboard_teleop_module.py Added home_joints field to KeyboardTeleopConfig and threads it through to JogState.from_fk; clean and correct.
dimos/core/global_config.py Added device_path field for real-hardware serial port; straightforward addition.
dimos/robot/manipulators/piper/blueprints.py Applied str() conversion to Piper FK model path for consistency with the new A-750 blueprint.
dimos/robot/all_blueprints.py Registered keyboard-teleop-a750 entry point; straightforward one-liner addition.

Sequence Diagram

sequenceDiagram
    participant User as User (keyboard)
    participant KTM as KeyboardTeleopModule
    participant JogState as JogState.from_fk
    participant Pinocchio as Pinocchio (URDF)
    participant LCM as LCM Transport
    participant CC as ControlCoordinator
    participant A750 as A-750 Adapter

    KTM->>JogState: from_fk(model_path, ee_joint_id, q_home)
    JogState->>Pinocchio: buildModelFromUrdf(a750_rev1_no_gripper.urdf)
    Pinocchio-->>JogState: model (nq=6)
    JogState->>Pinocchio: forwardKinematics(model, data, q_init)
    Pinocchio-->>JogState: EE pose (translation + rotation)
    JogState-->>KTM: initial home_pose

    loop Each keyboard tick
        User->>KTM: keypress (WASD/arrows/QE)
        KTM->>KTM: delta applied to current_pose
        KTM->>LCM: publish PoseStamped /coordinator/cartesian_command
        LCM->>CC: cartesian_command
        CC->>CC: CartesianIK solve to joint angles
        CC->>A750: send joint commands
        CC->>LCM: publish JointState /coordinator/joint_state
        LCM->>KTM: joint_state (feedback)
    end
Loading

Comments Outside Diff (1)

  1. dimos/robot/manipulators/xarm/blueprints.py, line 86 (link)

    P1 str() conversion missing for XArm7 model path

    This PR applied str(XARM6_FK_MODEL) to the XArm6 blueprint (line 56) so that model_path.endswith(".xml") in JogState.from_fk doesn't crash on an LfsPath object — but the identical fix was not applied to the XArm7 blueprint here, leaving it broken in the same way.

Reviews (2): Last reviewed commit: "refactor(a750): remove duplicated licens..." | Re-trigger Greptile

Comment thread dimos/robot/catalog/a750.py Outdated
"model_path": LfsPath("a750_description") / "urdf/a750_rev1.urdf",
"end_effector_link": "gripper_base",
"adapter_type": adapter_type,
"device_path": device_path,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 device_path silently dropped — real hardware won't receive the port

device_path is placed in the defaults dict and passed to RobotConfig(**defaults), but RobotConfig has no device_path field — only address. Pydantic v2 silently ignores unknown fields, so the device path is discarded. When a user sets DEVICE_PATH=/dev/ttyACM0, the adapter type correctly switches to "a750", but the adapter never learns which serial port to open. The real-hardware path is broken.

Use address instead:

"address": device_path,
Suggested change
"device_path": device_path,
"address": device_path,

"/coordinator/cartesian_command", PoseStamped
),
("joint_state", JointState): LCMTransport("/coordinator/joint_state", JointState),
("gripper_command", float): LCMTransport("/coordinator/gripper_command", Float32),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 gripper_command transport registered but stream doesn't exist

The blueprint registers a transport for ("gripper_command", float), but KeyboardTeleopModule declares no gripper_command: Out[float] stream. The PR description acknowledges gripper control is not yet implemented. Depending on how autoconnect/.transports() validates stream names, this could raise a KeyError or AttributeError at startup, preventing the blueprint from launching entirely.

Either remove this transport entry until the stream is implemented, or add a no-op placeholder that can be wired up safely.

Comment thread dimos/robot/catalog/a750.py Outdated
Comment on lines +1 to +28
# Copyright 2026 Dimensional Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Copyright 2025-2026 Dimensional Inc.
# Copyright 2026 Aleksandr Dobkin
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Duplicate license header block

The file contains two separate Apache 2.0 license blocks (lines 1–13 and 15–28). The same pattern appears in dimos/robot/manipulators/a750/blueprints.py. One header is sufficient.

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.

1 participant