Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d0ff591
feat(pick): wrist-camera visual-servo grasp for pick_any_object + tun…
theo-michel Jul 15, 2026
d7acf1b
refactor(skills): shared skill_lib for arm primitives + camera geometry
theo-michel Jul 16, 2026
2b8cfc5
Merge pull request #548 from innate-inc/theo/skill-lib
theo-michel Jul 16, 2026
a4c98e8
feat(pick): route vision calls through the Innate proxy (gemini service)
theo-michel Jul 16, 2026
3230bda
Merge pull request #549 from innate-inc/theo/skill-lib
theo-michel Jul 16, 2026
e89b072
feat(pick): two-camera grasp verify, skill_lib vision/gemini, cancell…
theo-michel Jul 17, 2026
d13b1e0
fix(webapp): pick/wrist boxes track the skill's live params again
theo-michel Jul 17, 2026
ab2ae76
fix(pick): address review — retire stale custom_skills copies, format…
theo-michel Jul 17, 2026
7a0b43b
revert(update): drop custom_skills retirement migration
theo-michel Jul 17, 2026
6eb3b3a
docs: trim excessive comments in pick skill + overlay
theo-michel Jul 17, 2026
920ace3
fix(pick): abort the grasp when the gripper will not open
theo-michel Jul 17, 2026
dd1f603
feat(webapp): pick tuning panel + draggable wrist box; overlay draws …
theo-michel Jul 17, 2026
e407a38
fix(pick): address Greptile review — safe rest pose, close_strength cap
theo-michel Jul 17, 2026
36cc3e1
fix(pick): loosen move_checked tolerance 0.05 -> 0.07m
theo-michel Jul 17, 2026
35769c8
tune(pick): raise close_strength to 0.8 — firmer grip holds better
theo-michel Jul 17, 2026
52c03c1
refactor(pick): extract shared mobility + arm helpers into skill_lib
theo-michel Jul 18, 2026
7ea6d33
wip from the robot side
theo-michel Jul 18, 2026
f206164
Merge branch 'theo/pick-wrist-servo' of https://github.com/innate-inc…
theo-michel Jul 18, 2026
1eacf39
Picking last update
theo-michel Jul 18, 2026
d9d78c2
fix(pick): address Greptile review — safe REST teardown, checked sear…
theo-michel Jul 18, 2026
e06f201
style: apply ruff format (fix CI)
theo-michel Jul 18, 2026
84f54bb
Remove pick tuning panel from teleop cockpit
theo-michel Jul 20, 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 pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ ignore = [
]

[tool.ruff.lint.isort]
known-first-party = ["mars_nav", "mars_control", "mars_cam", "mars_arm", "brain_client", "manipulation"]
known-first-party = ["mars_nav", "mars_control", "mars_cam", "mars_arm", "brain_client", "manipulation", "workspace"]

[tool.ruff.format]
quote-style = "double"
Expand Down
14 changes: 14 additions & 0 deletions pyrightconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"pythonVersion": "3.10",
"pythonPlatform": "Linux",
"extraPaths": [
"ros2_ws/src/brain/brain_client",
"ros2_ws/src/cloud/clients/proxy-client",
"ros2_ws/install/brain_client/local/lib/python3.10/dist-packages",
"ros2_ws/install/brain_messages/local/lib/python3.10/dist-packages",
"ros2_ws/install/innate_cloud_msgs/local/lib/python3.10/dist-packages",
"ros2_ws/install/mars_msgs/local/lib/python3.10/dist-packages",
"/opt/ros/humble/lib/python3.10/site-packages",
"/opt/ros/humble/local/lib/python3.10/dist-packages"
]
}
16 changes: 16 additions & 0 deletions ros2_ws/src/brain/brain_client/brain_client/skills/catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import os
import re
import shutil
import sys
import threading
import time
import types
Expand Down Expand Up @@ -269,8 +270,22 @@ def _compute_skill_id(self, path: str | Path) -> str:
return f"{prefix}/{basename}"

# --- reload ---
@staticmethod
def _evict_skill_lib() -> None:
"""Drop cached workspace.skill_lib modules so a skills reload picks up
lib edits too — skill files re-import the lib as they load. Without
this, sys.modules keeps serving the pre-edit lib to reloaded skills.

After a selective reload, skills NOT on the reload list keep the module
objects they imported at their own load time — two lib copies coexist.
Fine while the lib is stateless helpers; revisit if it ever holds state
shared across skills."""
for name in [m for m in sys.modules if m.startswith("workspace.skill_lib")]:
del sys.modules[name]

def reload_all(self) -> None:
self._logger.info("Reloading skills...")
self._evict_skill_lib()
self._skills_directories = self._resolve_skills_directories()
new_code_skills = self._load_code_skills(self._skills_directories)
new_physical, new_in_training = self._load_physical_skills(self._skills_directories)
Expand All @@ -291,6 +306,7 @@ def reload_selective(self, skill_ids: list[str]) -> list[str]:
return list(self._code_skills.keys()) + list(self._physical_skills.keys())

self._logger.info(f"Selectively reloading skills: {skill_ids}")
self._evict_skill_lib()
reloaded = []
for skill_id in skill_ids:
basename = skill_id.split("/", 1)[-1] if "/" in skill_id else skill_id
Expand Down
8 changes: 4 additions & 4 deletions ros2_ws/src/brain/brain_client/brain_client/skills/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,15 +208,15 @@ def __init__(self, logger):

@property
@abstractmethod
def name(self):
def name(self) -> str:
"""
The name of the skill.
Must be defined by every subclass.
"""
pass

@abstractmethod
def execute(self, *args, **kwargs):
def execute(self, *args, **kwargs) -> tuple:
"""
Execute the skill.

Expand Down Expand Up @@ -389,14 +389,14 @@ def inject_interface(self, interface_type: InterfaceType, interface_instance):
return True
return False

def guidelines(self):
def guidelines(self) -> str | None:
"""
Optionally provide guidelines for this skill.
Subclasses may override this method if guidelines are available.
"""
return None

def guidelines_when_running(self):
def guidelines_when_running(self) -> str | None:
"""
Optionally provide guidelines for this skill when it is running.
Subclasses may override this method if guidelines are available.
Expand Down
13 changes: 13 additions & 0 deletions scripts/update/post_update.sh
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,19 @@ if [ -f "$ENV_FILE" ]; then
fi
fi

# Enforce ownership/mode on an existing /etc/innate.env no matter how it got there:
# a hand-created file (sudo redirect/tee) ends up 600 root:root, which the non-root
# launch readers can't open — print_runtime_env.py then treats it as absent and the
# service key silently drops out of the runtime env (proxy "not configured").
# Idempotent; matches the seeded state above. Contents are never touched.
if [ -f "$SYSTEM_ENV_FILE" ]; then
if [ "$(stat -c '%U:%G %a' "$SYSTEM_ENV_FILE")" != "root:$ACTUAL_USER 640" ]; then
chown "root:$ACTUAL_USER" "$SYSTEM_ENV_FILE"
chmod 640 "$SYSTEM_ENV_FILE"
log "Fixed $SYSTEM_ENV_FILE ownership/mode to root:$ACTUAL_USER 640 so launch readers can read the service key"
fi
fi

# -----------------------------------------------------------------------------
# 0a. Migrate user-created data into the post-refactor layout.
# The refactor moved agents/skills/inputs under workspace/ and maps + nav-state
Expand Down
90 changes: 90 additions & 0 deletions webapp/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,96 @@ button {
color: var(--accent);
}

/* ---- pick overlays (Teleop) ---------------------------------------------- */

/* Grasp reticle over the live head video: the skill's grasp pixel, centered
on (left,top) set in JS. Lives in .video-stage, drawn above the frame but
below the glass overlays. */
.picktune-grab {
position: absolute;
z-index: 1;
transform: translate(-50%, -50%);
color: var(--accent);
pointer-events: none;
filter: drop-shadow(0 0 2px rgb(0 0 0 / 70%));
}

.picktune-grab-tag {
position: absolute;
top: 50%;
left: calc(100% + 4px);
transform: translateY(-50%);
font-size: 10px;
color: inherit;
white-space: nowrap;
}

/* Detection marker (box corners at Gemini's pixel) — green to read apart
from the amber grasp target. Its tag hangs left so the two labels don't
collide when detection and grasp target sit centimeters apart (the usual
case: they differ only by the fingertip offset). */
.picktune-seen {
color: var(--ok);
}

.picktune-seen .picktune-grab-tag {
left: auto;
right: calc(100% + 4px);
}

/* The positioning goal square on the video: put the detection inside this and
the base stops. Green while the skill reports the detection inside. */
.picktune-boxgoal {
position: absolute;
z-index: 1;
transform: translate(-50%, -50%);
border: 1.5px dashed var(--accent-dim);
border-radius: 2px;
pointer-events: none;
}

/* Inner accept box: the point must land in THIS to stop positioning. Solid
amber guide; greens (with the tag) when the skill reports the point inside. */
.picktune-boxaccept {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
border: 1.5px solid var(--accent-dim);
border-radius: 2px;
pointer-events: none;
}

.picktune-boxgoal.inside .picktune-boxaccept {
border-color: var(--ok);
}

.picktune-boxgoal-tag {
position: absolute;
bottom: calc(100% + 3px);
left: 0;
font-size: 10px;
color: var(--accent-dim);
white-space: nowrap;
}

.picktune-boxgoal.inside .picktune-boxgoal-tag {
color: var(--ok);
}

/* The wrist goal box is the one interactive overlay: drag it to re-aim the
wrist servo (publishes wrist_box_u/v as a live tuning override). */
.picktune-wristbox {
pointer-events: auto;
cursor: grab;
touch-action: none; /* pointer events own the gesture on touch screens */
}

.picktune-wristbox.dragging {
cursor: grabbing;
border-style: solid;
}

/* ---- record HUD (Collect page) ------------------------------------------ */

.overlay-record {
Expand Down
7 changes: 7 additions & 0 deletions webapp/js/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@ export const PINNED_SKILLS = ["navigate with vision", "navigate with position",
export const EXECUTE_SKILL_ACTION = "/execute_skill";
export const EXECUTE_SKILL_ACTION_TYPE = "brain_messages/action/ExecuteSkill";

// pick_any_object stage events (std_msgs/String JSON: {ev, t, ...}) — drive
// the Teleop aim overlays. The tuning panel and draggable wrist box publish
// partial TUNABLE dicts (String JSON) back on the tuning topic; the running
// skill applies them mid-run and acks with a params event.
export const PICK_DEBUG_TOPIC = "/pick_any_object/debug";
export const PICK_TUNING_TOPIC = "/pick_any_object/tuning";

export const HEAD_MIN_DEG = -40;
export const HEAD_MAX_DEG = 70;

Expand Down
2 changes: 2 additions & 0 deletions webapp/js/teleop/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { createTtsBar } from "./ttsBar.js";
import { createTelemetry } from "./telemetry.js";
import { createArmPanel } from "./armPanel.js";
import { createProfilingPanel } from "./profilingPanel.js";
import { createPickOverlay } from "./pickOverlay.js";
import { createSkillsMenu } from "./skillsMenu.js";
import { createCameraSwitch } from "./cameraSwitch.js";

Expand Down Expand Up @@ -92,6 +93,7 @@ function buildCockpit(root) {
createSkillsMenu(ttsOverlay, ros),
createArmPanel(armOverlay, ros, { hideServices: !!config.simControls }),
...(config.simControls ? [] : [createProfilingPanel(root, session)]),
createPickOverlay(root, ros, session),
createCameraSwitch(root, session, ros),
keyboard,
);
Expand Down
Loading
Loading