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
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.13.2
rev: v0.14.10
hooks:
- id: ruff
- id: ruff-check
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format

Expand All @@ -13,11 +13,11 @@ repos:
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
args: [--maxkb=5000]

- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
hooks:
- id: codespell # See pyproject.toml for args
additional_dependencies:
- tomli
args: [--maxkb=5000]
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,14 @@ exclude = ["tests/artifacts/**/*.safetensors", "*_pb2.py", "*_pb2_grpc.py"]
# N: pep8-naming
# TODO: Uncomment rules when ready to use
select = [
"E", "W", "F", "I", "B", "C4", "T20", "N" # "SIM", "A", "S", "D", "RUF", "UP"
"ASYNC", "B", "C4", "E", "F", "I", "N", "PERF", "T20", "W" # "A", "D", "RUF", "S", "SIM", "UP"
]
ignore = [
"B008", # Perform function call in argument defaults
"E501", # Line too long
"PERF203", # try-except-in-loop
"T201", # Print statement found
"T203", # Pprint statement found
"B008", # Perform function call in argument defaults
]

[tool.ruff.lint.per-file-ignores]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,7 @@ def set_requires_grad(self):
"lm_head",
"text_model.model.norm.weight",
]
for layer in last_layers:
frozen_layers.append(f"text_model.model.layers.{layer}.")
frozen_layers.extend(f"text_model.model.layers.{layer}." for layer in last_layers)

for name, params in self.vlm.named_parameters():
if any(k in name for k in frozen_layers):
Expand Down
45 changes: 21 additions & 24 deletions src/beavr/lerobot/common/robot_devices/cameras/opencv.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,34 +46,31 @@


def find_cameras(raise_when_empty=False, max_index_search_range=MAX_OPENCV_INDEX, mock=False) -> list[dict]:
cameras = []
if platform.system() == "Linux":
Comment on lines 48 to 49

Copilot AI Sep 27, 2025

Copy link

Choose a reason for hiding this comment

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

The function signature indicates it returns list[dict] but the implementation has two different return paths that may not execute. After the Linux block, there's no else clause, so the Mac/Windows code will always execute regardless of platform, potentially causing the Linux case to return twice or fall through incorrectly.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Both possible paths return a list[dict]. The fall through is correct because Mac/Windows code will never execute in the Linux case and the Linux code will never execute in the Mac/Windows case. Returning twice is not possible in Python code because the first return leaves the function.

print("Linux detected. Finding available camera indices through scanning '/dev/video*' ports")
possible_ports = [str(port) for port in Path("/dev").glob("video*")]
ports = _find_cameras(possible_ports, mock=mock)

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

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

The refactoring removed the early return logic and changed the control flow. The raise_when_empty parameter is no longer being passed to the _find_cameras function calls. The original code had implicit fall-through to the else block, but now when the Linux branch returns early, the function doesn't handle the raise_when_empty parameter for either path.

Copilot uses AI. Check for mistakes.
for port in ports:
cameras.append(
{
"port": port,
"index": int(port.removeprefix("/dev/video")),
}
)
else:
print(
"Mac or Windows detected. Finding available camera indices through "
f"scanning all indices from 0 to {MAX_OPENCV_INDEX}"
)
possible_indices = range(max_index_search_range)
indices = _find_cameras(possible_indices, mock=mock)
for index in indices:
cameras.append(
{
"port": None,
"index": index,
}
)

return cameras
return [
{
"port": port,
"index": int(port.removeprefix("/dev/video")),
}
for port in ports
]

print(
"Mac or Windows detected. Finding available camera indices through "
f"scanning all indices from 0 to {MAX_OPENCV_INDEX}"
)
possible_indices = range(max_index_search_range)
indices = _find_cameras(possible_indices, mock=mock)

Copilot AI Dec 29, 2025

Copy link

Choose a reason for hiding this comment

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

The refactoring removed the early return logic and changed the control flow. The raise_when_empty parameter is no longer being passed to the _find_cameras function call. The original code passed this parameter through, but now it's missing.

Copilot uses AI. Check for mistakes.
return [
{
"port": None,
"index": index,
}
for index in indices
]
Comment on lines +61 to +73

Copilot AI Sep 27, 2025

Copy link

Choose a reason for hiding this comment

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

The Linux branch returns early, but the non-Linux code now runs unconditionally. This should be wrapped in an else block to maintain the original behavior.

Copilot uses AI. Check for mistakes.

@cclauss cclauss Sep 27, 2025 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That would violate ruff rule RET505.

The else statement is not needed as the return statement will always break out of the enclosing function. Removing the else will reduce nesting and make the code more readable.



def _find_cameras(
Expand Down
4 changes: 2 additions & 2 deletions src/beavr/lerobot/common/robot_devices/robots/beavrbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ def teleop_stop(self):
logging.warning("WARNING: Not all subscribers acknowledged teleop stop – proceeding anyway")

# Send home signals
for _, home_info in self.home_publishers.items():
for home_info in self.home_publishers.values():
self.pub_manager.publish(
home_info["host"],
home_info["port"],
Expand Down Expand Up @@ -486,7 +486,7 @@ def teleop_resume(self):
logging.warning("WARNING: Not all subscribers acknowledged teleop resume – proceeding anyway")

# Send home signals
for _, home_info in self.home_publishers.items():
for home_info in self.home_publishers.values():
self.pub_manager.publish(
home_info["host"],
home_info["port"],
Expand Down
15 changes: 3 additions & 12 deletions src/beavr/lerobot/common/robot_devices/robots/manipulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,17 +502,11 @@ def teleop_step(
self.logs[f"read_follower_{name}_pos_dt_s"] = time.perf_counter() - before_fread_t

# Create state by concatenating follower current position
state = []
for name in self.follower_arms:
if name in follower_pos:
state.append(follower_pos[name])
state = [follower_pos[name] for name in self.follower_arms if name in follower_pos]
state = torch.cat(state)

# Create action by concatenating follower goal position
action = []
for name in self.follower_arms:
if name in follower_goal_pos:
action.append(follower_goal_pos[name])
action = [follower_goal_pos[name] for name in self.follower_arms if name in follower_goal_pos]
action = torch.cat(action)

# Capture images from cameras
Expand Down Expand Up @@ -549,10 +543,7 @@ def capture_observation(self):
self.logs[f"read_follower_{name}_pos_dt_s"] = time.perf_counter() - before_fread_t

# Create state by concatenating follower current position
state = []
for name in self.follower_arms:
if name in follower_pos:
state.append(follower_pos[name])
state = [follower_pos[name] for name in self.follower_arms if name in follower_pos]
state = torch.cat(state)

# Capture images from cameras
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,9 @@ def num_cameras(self):

@property
def available_arms(self):
available = []
for name in self.leader_arms:
available.append(get_arm_id(name, "leader"))
for name in self.follower_arms:
available.append(get_arm_id(name, "follower"))
return available
return [get_arm_id(name, "leader") for name in self.leader_arms] + [
get_arm_id(name, "follower") for name in self.follower_arms
]

def on_press(self, key):
try:
Expand Down
6 changes: 3 additions & 3 deletions src/beavr/lerobot/configs/policies.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,14 @@ def validate_features(self) -> None:

@property
def robot_state_feature(self) -> PolicyFeature | None:
for _, ft in self.input_features.items():
for ft in self.input_features.values():
if ft.type is FeatureType.STATE:
return ft
return None

@property
def env_state_feature(self) -> PolicyFeature | None:
for _, ft in self.input_features.items():
for ft in self.input_features.values():
if ft.type is FeatureType.ENV:
return ft
return None
Expand All @@ -135,7 +135,7 @@ def image_features(self) -> dict[str, PolicyFeature]:

@property
def action_feature(self) -> PolicyFeature | None:
for _, ft in self.output_features.items():
for ft in self.output_features.values():
if ft.type is FeatureType.ACTION:
return ft
return None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ def solve_ik(self, finger_positions, current_positions=None):

# Update with the provided finger positions
for finger, positions in finger_positions.items():
full_finger_positions[finger] = positions
full_finger_positions[finger] = positions # noqa: PERF403

# Check which fingers are active (have positions)
active_fingers = {
Expand Down