Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions dimos/robot/all_blueprints.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@

all_blueprints = {
"alfred-nav": "dimos.robot.diy.alfred.blueprints.alfred_nav:alfred_nav",
"booster-k1-basic": "dimos.robot.booster.k1.blueprints.basic.booster_k1_basic:booster_k1_basic",
"booster-k1-coordinator": "dimos.robot.booster.k1.blueprints.basic.booster_k1_coordinator:booster_k1_coordinator",
"booster-k1-coordinator-keyboard-teleop": "dimos.robot.booster.k1.blueprints.basic.booster_k1_coordinator_keyboard_teleop:booster_k1_coordinator_keyboard_teleop",
"booster-k1-keyboard-teleop": "dimos.robot.booster.k1.blueprints.basic.booster_k1_keyboard_teleop:booster_k1_keyboard_teleop",
"coordinator-basic": "dimos.control.blueprints.basic:coordinator_basic",
"coordinator-cartesian-ik-mock": "dimos.control.blueprints.teleop:coordinator_cartesian_ik_mock",
"coordinator-cartesian-ik-piper": "dimos.control.blueprints.teleop:coordinator_cartesian_ik_piper",
Expand Down Expand Up @@ -178,6 +182,7 @@
"hosted-twist-teleop-module": "dimos.teleop.quest_hosted.hosted_extensions.HostedTwistTeleopModule",
"joint-trajectory-controller": "dimos.manipulation.control.trajectory_controller.joint_trajectory_controller.JointTrajectoryController",
"joystick-module": "dimos.robot.unitree.b1.joystick_module.JoystickModule",
"k1-connection": "dimos.robot.booster.k1.connection.K1Connection",
"keyboard-teleop": "dimos.robot.unitree.keyboard_teleop.KeyboardTeleop",
"keyboard-teleop-module": "dimos.teleop.keyboard.keyboard_teleop_module.KeyboardTeleopModule",
"local-planner": "dimos.navigation.nav_stack.modules.local_planner.local_planner.LocalPlanner",
Expand Down
88 changes: 88 additions & 0 deletions dimos/robot/booster/k1/blueprints/basic/booster_k1_basic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Copyright 2025-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.

"""Basic Booster K1 blueprint: connection + camera visualization."""

import platform
from typing import Any

from dimos.constants import DEFAULT_CAPACITY_COLOR_IMAGE
from dimos.core.coordination.blueprints import autoconnect
from dimos.core.global_config import global_config
from dimos.core.transport import pSHMTransport
from dimos.msgs.sensor_msgs.Image import Image
from dimos.robot.booster.k1.connection import K1Connection
from dimos.visualization.vis_module import vis_module

# High-bandwidth camera frames go over shared memory (esp. needed on macOS UDP).
_mac_transports: dict[tuple[str, type], pSHMTransport[Image]] = {
("color_image", Image): pSHMTransport(
"color_image", default_capacity=DEFAULT_CAPACITY_COLOR_IMAGE
),
}

_transports_base = (
autoconnect() if platform.system() == "Linux" else autoconnect().transports(_mac_transports)
)


def _convert_camera_info(camera_info: Any) -> Any:
return camera_info.to_rerun(
image_topic="/world/color_image",
optical_frame="camera_optical",
)


def _k1_rerun_blueprint() -> Any:
"""Camera feed + 3D world view side by side."""
import rerun.blueprint as rrb

return rrb.Blueprint(
rrb.Horizontal(
rrb.Spatial2DView(origin="world/color_image", name="Camera"),
rrb.Spatial3DView(origin="world", name="3D"),
column_shares=[1, 2],
),
rrb.TimePanel(state="hidden"),
rrb.SelectionPanel(state="hidden"),
)


rerun_config = {
"blueprint": _k1_rerun_blueprint,
"visual_override": {
"world/camera_info": _convert_camera_info,
},
"max_hz": {
"world/color_image": 0,
},
}

_with_vis = autoconnect(
_transports_base,
vis_module(
viewer_backend=global_config.viewer,
rerun_config=rerun_config,
),
)

booster_k1_basic = autoconnect(
_with_vis,
K1Connection.blueprint(),
).global_config(n_workers=4, robot_model="booster_k1")

__all__ = [
"booster_k1_basic",
"rerun_config",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!/usr/bin/env python3
# Copyright 2025-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.

"""Booster K1 ControlCoordinator: basic blueprint + coordinator via the LCM transport adapter.

Like unitree_go2_coordinator, a twist base (vx, vy, wz) is driven through the
ControlCoordinator velocity task and the `transport_lcm` adapter (which republishes base
velocity on /booster_k1/cmd_vel). The K1 reports no odometry, so there is no odom wiring.
Built on `booster_k1_basic`, so it carries the rerun viewer + camera; the viewer-side WASD
surfaces (tele_cmd_vel) are remapped onto /cmd_vel to feed the coordinator's twist_command.
Add KeyboardTeleop for a pygame window (booster_k1_coordinator_keyboard_teleop).

Control path:
WASD -> /cmd_vel -> Coordinator.twist_command -> velocity task
-> transport_lcm adapter -> /booster_k1/cmd_vel -> K1Connection.move()

Usage:
dimos --robot-ip <ip> --viewer rerun run booster-k1-coordinator
"""

from __future__ import annotations

from dimos.control.components import HardwareComponent, HardwareType, make_twist_base_joints
from dimos.control.coordinator import ControlCoordinator, TaskConfig
from dimos.core.coordination.blueprints import autoconnect
from dimos.core.transport import LCMTransport
from dimos.msgs.geometry_msgs.Twist import Twist
from dimos.msgs.sensor_msgs.JointState import JointState
from dimos.robot.booster.k1.blueprints.basic.booster_k1_basic import booster_k1_basic
from dimos.robot.booster.k1.connection import K1Connection
from dimos.visualization.rerun.websocket_server import RerunWebSocketServer
from dimos.web.websocket_vis.websocket_vis_module import WebsocketVisModule

_k1_joints = make_twist_base_joints("booster_k1")

booster_k1_coordinator = (
autoconnect(
booster_k1_basic,
ControlCoordinator.blueprint(
hardware=[
HardwareComponent(
hardware_id="booster_k1",
hardware_type=HardwareType.BASE,
joints=_k1_joints,
adapter_type="transport_lcm",
),
],
tasks=[
TaskConfig(
name="vel_booster_k1",
type="velocity",
joint_names=_k1_joints,
priority=10,
),
],
),
)
.remappings(
[
# Free up the bare `cmd_vel` name for the teleop/twist publishers; the
# connection now listens on /booster_k1/cmd_vel (what the adapter emits).
(K1Connection, "cmd_vel", "k1_cmd_vel"),
# Route the viewer-side WASD surfaces onto /cmd_vel so they feed the
# coordinator's twist_command instead of a dead-end topic.
(WebsocketVisModule, "tele_cmd_vel", "cmd_vel"),
(RerunWebSocketServer, "tele_cmd_vel", "cmd_vel"),
]
)
.transports(
{
("cmd_vel", Twist): LCMTransport("/cmd_vel", Twist),
("twist_command", Twist): LCMTransport("/cmd_vel", Twist),
("k1_cmd_vel", Twist): LCMTransport("/booster_k1/cmd_vel", Twist),
("joint_state", JointState): LCMTransport("/coordinator/joint_state", JointState),
}
)
.global_config(n_workers=6, robot_model="booster_k1")
)

__all__ = ["booster_k1_coordinator"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
# Copyright 2025-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.

"""Booster K1 keyboard teleop through the ControlCoordinator path.

WASD -> KeyboardTeleop.cmd_vel -> /cmd_vel -> Coordinator.twist_command
-> velocity task -> transport_lcm adapter -> /booster_k1/cmd_vel -> K1Connection.

Usage:
dimos --robot-ip <ip> run booster-k1-coordinator-keyboard-teleop
"""

from __future__ import annotations

from dimos.core.coordination.blueprints import autoconnect
from dimos.robot.booster.k1.blueprints.basic.booster_k1_coordinator import booster_k1_coordinator
from dimos.robot.unitree.keyboard_teleop import KeyboardTeleop

booster_k1_coordinator_keyboard_teleop = autoconnect(
booster_k1_coordinator,
KeyboardTeleop.blueprint(publish_only_when_active=True),
)

__all__ = ["booster_k1_coordinator_keyboard_teleop"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Copyright 2025-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.

"""Booster K1 keyboard teleop: WASD direct to the connection, camera in rerun.

Two control surfaces both publish Twist on /cmd_vel -> K1Connection.move(): the
pygame KeyboardTeleop window, and the rerun/Dashboard WASD overlay (its tele_cmd_vel
remapped to cmd_vel). Camera renders in the rerun viewer (from booster_k1_basic).

Controls: W/S fwd/back, Q/E strafe, A/D turn, Space e-stop, Shift 2x, Ctrl 0.5x, ESC quit.

Usage:
dimos --robot-ip <ip> --viewer rerun run booster-k1-keyboard-teleop
"""

from __future__ import annotations

from dimos.core.coordination.blueprints import autoconnect
from dimos.robot.booster.k1.blueprints.basic.booster_k1_basic import booster_k1_basic
from dimos.robot.unitree.keyboard_teleop import KeyboardTeleop
from dimos.visualization.rerun.websocket_server import RerunWebSocketServer
from dimos.web.websocket_vis.websocket_vis_module import WebsocketVisModule

# publish_only_when_active keeps each surface silent while idle (one zero Twist on
# release, then quiet) so the two /cmd_vel publishers don't fight; the connection's
# dead-man timer halts the robot when commands stop. The viewer WASD output
# (tele_cmd_vel) is remapped onto cmd_vel so it reaches K1Connection.
booster_k1_keyboard_teleop = autoconnect(
booster_k1_basic,
KeyboardTeleop.blueprint(publish_only_when_active=True),
).remappings(
[
(WebsocketVisModule, "tele_cmd_vel", "cmd_vel"),
(RerunWebSocketServer, "tele_cmd_vel", "cmd_vel"),
]
)

__all__ = ["booster_k1_keyboard_teleop"]
Loading
Loading