Skip to content

Commit

Permalink
feat: basic joint controls via front
Browse files Browse the repository at this point in the history
  • Loading branch information
alberto-abarzua committed Oct 12, 2023
1 parent 318379c commit 1586b74
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 42 deletions.
47 changes: 45 additions & 2 deletions backend/src/routers/move.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# Post Models
# --------


class Move(BaseModel):
x: float
y: float
Expand All @@ -28,14 +29,22 @@ class Tool(BaseModel):
toolValue: float
wait: Optional[bool] = False


class MoveJoint(BaseModel):
joint_idx: int
joint_value: float
wait: Optional[bool] = False


class HomeJoint(BaseModel):
joint_idx: int
wait: Optional[bool] = False


class MoveJoints(BaseModel):
joint_values: list
wait: Optional[bool] = False

# --------
# General
# --------
Expand All @@ -47,12 +56,16 @@ def home(controller: ArmController = controller_dependency) -> Dict[Any, Any]:
controller.home()
return {"message": "Homed"}


@router.post("/home_joint/")
def home_joint(home_joint : HomeJoint,controller: ArmController = controller_dependency) -> Dict[Any, Any]:
def home_joint(
home_joint: HomeJoint, controller: ArmController = controller_dependency
) -> Dict[Any, Any]:
joint_idx = home_joint.dict().pop("joint_idx")
controller.home_joint(joint_idx)
return {"message": "Homed"}


# --------
# Pose
# --------
Expand Down Expand Up @@ -95,8 +108,11 @@ def valid_pose(
else:
return JSONResponse(content={"message": "Pose is not valid"}, status_code=400)


@router.post("/joint/")
def move_joint(move: MoveJoint, controller: ArmController = controller_dependency) -> JSONResponse:
def move_joint(
move: MoveJoint, controller: ArmController = controller_dependency
) -> JSONResponse:
move_dict = move.dict()
wait = move_dict.pop("wait")

Expand All @@ -106,6 +122,33 @@ def move_joint(move: MoveJoint, controller: ArmController = controller_dependenc
if wait:
controller.wait_done_moving()


@router.post("/joint/relative/")
def move_joint_to_relative(
move: MoveJoint, controller: ArmController = controller_dependency
) -> JSONResponse:
move_dict = move.dict()
wait = move_dict.pop("wait")

joint_idx = move_dict.pop("joint_idx")
joint_value = move_dict.pop("joint_value")
controller.move_joint_relative(joint_idx, joint_value)
if wait:
controller.wait_done_moving()


@router.post("/joints/relative/")
def move_joints_to_relative(
move: MoveJoints, controller: ArmController = controller_dependency
) -> JSONResponse:
move_dict = move.dict()
wait = move_dict.pop("wait")

joint_values = move_dict.pop("joint_values")
controller.move_joints_to_relative(joint_values)
if wait:
controller.wait_done_moving()

# --------
# Pose
# --------
Expand Down
57 changes: 44 additions & 13 deletions controller/src/robot_arm_controller/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@ def __init__(
self.print_status = False
self.print_idx = 0

self.controller_server: ControllerServer = ControllerServer(self, server_port)
self.websocket_server: Optional[WebsocketServer] = WebsocketServer(self, websocket_port)
self.controller_server: ControllerServer = ControllerServer(
self, server_port)
self.websocket_server: Optional[WebsocketServer] = WebsocketServer(
self, websocket_port)

self.arm_params = arm_parameters
self.num_joints: int = len(self._current_angles)
Expand Down Expand Up @@ -85,7 +87,8 @@ def __init__(
}
self.joint_settings.append(current_joint_settings)
self.joint_settings_response_code.append(
{setting.code_get + 1: setting for setting in current_joint_settings.values()}
{setting.code_get +
1: setting for setting in current_joint_settings.values()}
)

"""
Expand Down Expand Up @@ -115,7 +118,8 @@ def start(self, wait: bool = False, websocket_server: bool = True) -> None:
while not self.is_ready:
time.sleep(0.1)
if time.time() - start_time > self.connection_controller_timeout:
raise TimeoutError("Controller took too long to start, check arm client")
raise TimeoutError(
"Controller took too long to start, check arm client")
console.log("\nController Started!", style="setup")

def stop(self) -> None:
Expand All @@ -137,7 +141,8 @@ def current_angles(self) -> List[float]:
@current_angles.setter
def current_angles(self, angles: List[float]) -> None:
if len(angles) != self.num_joints:
raise ValueError(f"Angles must be a list of length {self.num_joints}")
raise ValueError(
f"Angles must be a list of length {self.num_joints}")
with self.current_angles_lock:
self._current_angles = angles

Expand Down Expand Up @@ -206,7 +211,8 @@ def move_to(
self,
pose: ArmPose,
) -> bool:
target_angles = self.kinematics.pose_to_angles(pose, self.current_angles)
target_angles = self.kinematics.pose_to_angles(
pose, self.current_angles)
if target_angles is None:
console.log("Target pose is not reachable", style="error")
return False
Expand Down Expand Up @@ -241,14 +247,33 @@ def move_joint_to(self, joint_idx: int, angle: float) -> bool:
self.controller_server.send_message(message, mutex=True)
self.move_queue_size += 1
if self.print_status:
console.log(f"Moving joint {joint_idx} to angle: {angle}", style="move_joint")
console.log(
f"Moving joint {joint_idx} to angle: {angle}", style="move_joint")
return True

def home_joint(self, joint_idx: int) -> None:
message = Message(MessageOp.MOVE, 5, [joint_idx])
self.controller_server.send_message(message, mutex=True)
time.sleep(self.command_cooldown)

def move_joint_relative(self, joint_idx: int, angle: float) -> bool:
message = Message(MessageOp.MOVE, 11, [joint_idx, angle])
self.controller_server.send_message(message, mutex=True)
self.move_queue_size += 1
if self.print_status:
console.log(
f"Moving joint {joint_idx} relative angle: {angle}", style="move_joint_relative")
return True

def move_joints_relative(self, angles: List[float]) -> bool:
message = Message(MessageOp.MOVE, 13, angles)
self.controller_server.send_message(message, mutex=True)
self.move_queue_size += 1
if self.print_status:
console.log(
f"Moving joints relative angles: {angles}", style="move_joints_relative")
return True

def set_tool_value(self, angle: float) -> None:
message = Message(MessageOp.MOVE, 7, [angle])
self.controller_server.send_message(message, mutex=True)
Expand All @@ -262,14 +287,17 @@ def wait_until_angles_at_target(self, target_angles: List[float], epsilon: float

def wait_done_moving(self) -> None:
if self.print_status:
console.log(f"Waiting for arm to finish moving qsize: {self.move_queue_size}", style="waiting")
console.log(
f"Waiting for arm to finish moving qsize: {self.move_queue_size}", style="waiting")
while self.move_queue_size > 0:
time.sleep(0.2)
if self.print_status:
console.log(f"Arm finished moving qsize: {self.move_queue_size}", style="waiting")
console.log(
f"Arm finished moving qsize: {self.move_queue_size}", style="waiting")

def valid_pose(self, pose: ArmPose) -> bool:
target_angles = self.kinematics.pose_to_angles(pose, self.current_angles)
target_angles = self.kinematics.pose_to_angles(
pose, self.current_angles)
if target_angles is None:
return False
return True
Expand Down Expand Up @@ -307,23 +335,26 @@ def stop_movement(self) -> None:

def set_setting_joint(self, setting_key: Settings, value: float, joint_idx: int) -> None:
if setting_key not in self.joint_settings[joint_idx].keys():
raise ValueError(f"Invalid setting key for joint setting: {setting_key}")
raise ValueError(
f"Invalid setting key for joint setting: {setting_key}")
setting = self.joint_settings[joint_idx][setting_key]
code = setting.code_set
message = Message(MessageOp.CONFIG, code, [float(joint_idx), value])
self.controller_server.send_message(message, mutex=True)
setting.last_updated = -1

if self.print_status:
console.log(f"Setting {setting_key} to {value} for joint {joint_idx}", style="set_settings")
console.log(
f"Setting {setting_key} to {value} for joint {joint_idx}", style="set_settings")

def set_setting_joints(self, setting_key: Settings, value: float) -> None:
for joint_idx in range(self.num_joints):
self.set_setting_joint(setting_key, value, joint_idx)

def get_setting_joint(self, setting_key: Settings, joint_idx: int) -> float:
if setting_key not in self.joint_settings[joint_idx].keys():
raise ValueError(f"Invalid setting key for joint setting: {setting_key}")
raise ValueError(
f"Invalid setting key for joint setting: {setting_key}")

setting = self.joint_settings[joint_idx][setting_key]
code = setting.code_get
Expand Down
15 changes: 9 additions & 6 deletions firmware/components/controller/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ void Controller::message_handler_move(Message *message) {
}
}
} break;
case 9: { // Move Joint
case 9: { // Move Joint
uint8_t joint_idx = static_cast<uint8_t>(args[0]);
if (!called) {
this->joints[joint_idx]->set_target_angle(args[1]);
Expand All @@ -290,22 +290,25 @@ void Controller::message_handler_move(Message *message) {
}
}
} break;
case 11: { // Move Joint Relative to target
case 11: { // Move Joint Relative to target
uint8_t joint_idx = static_cast<uint8_t>(args[0]);
if (!called) {
float target_angle = this->joints[joint_idx]->get_target_angle();
this->joints[joint_idx]->set_target_angle(target_angle + args[1]);
float target_angle =
this->joints[joint_idx]->get_target_angle();
this->joints[joint_idx]->set_target_angle(target_angle +
args[1]);
message->set_called(true);
} else {
if (this->joints[joint_idx]->at_target()) {
message->set_complete(true);
}
}
} break;
case 13: { // Move Joints Relative to target
case 13: { // Move Joints Relative to target
if (!called) {
for (int i = 0; i < num_args; i++) {
this->joints[i]->set_target_angle(args[i] + this->joints[i]->get_target_angle());
this->joints[i]->set_target_angle(
args[i] + this->joints[i]->get_target_angle());
}
message->set_called(true);
} else {
Expand Down
7 changes: 2 additions & 5 deletions firmware/components/movement/endstops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ HallEffectSensor::HallEffectSensor(int8_t pin) : EndStop(pin) {}

HallEffectSensor::~HallEffectSensor() {}

void HallEffectSensor::hardware_setup() {
}
void HallEffectSensor::hardware_setup() {}

bool HallEffectSensor::hardware_read_state() {
return true;
}
bool HallEffectSensor::hardware_read_state() { return true; }
2 changes: 1 addition & 1 deletion frontend/src/components/ArmSimulation/ArmSimulation.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const ArmSimulation = () => {
const websocket_port = process.env.NEXT_PUBLIC_ARM_SIMULATION_WEBSOCKET_PORT;
const websocket_host = process.env.NEXT_PUBLIC_ARM_SIMULATION_WEBSOCKET_HOST;
return (
<div className="relative flex-none h-1/2 w-full">
<div className="relative h-1/2 w-full flex-none">
<iframe
src={`${simulation_url}/game.html?ip=${websocket_host}&port=${websocket_port}`}
className="absolute left-0 top-0 h-full w-full border-none"
Expand Down
26 changes: 12 additions & 14 deletions frontend/src/components/controls/JointsControls.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,18 @@ import MoveAxis from '@/components/controls/MoveAxis';
import TextVariableInfo from '@/components/general/text/TextVariableInfo';
import api from '@/utils/api';
import { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { useSelector } from 'react-redux';

const JointsControls = () => {
const currentAngles = useSelector(state => state.armPose.currentAngles);
console.log(currentAngles);
const [angles, setAngles] = useState(currentAngles);
const [angleStep, setAngleStep] = useState(0.1);

const callSetAngle = (index, value) => {
api.post('/move/joint/',{joint_idx: index, joint_value: value});
api.post('/move/joint/relative', { joint_idx: index, joint_value: value });
};

const callHomeJoint = (index) => {
api.post('/move/home_joint/',{joint_idx: index});
const callHomeJoint = index => {
api.post('/move/home_joint/', { joint_idx: index });
};

return (
Expand All @@ -31,22 +29,22 @@ const JointsControls = () => {
setValue={setAngleStep}
infoText={'The amount the arm moves for coordinate changes'}
></TextVariableInfo>
</div>
<div className="flex flex-row justify-center">
{angles.map((angle, index) => (
<div key={index}>
</div> <div className="flex flex-row justify-center gap-2"> {currentAngles.map((_, index) => (
<div className="flex flex-col items-center justify-center " key={index}>
<MoveAxis
label={`Joint ${index + 1}`}
value={currentAngles[index]}
setValue={value => {
const newAngles = [...angles];
newAngles[index] = value;
setAngles(newAngles);
callSetAngle(index, value);
}}
step_amount={angleStep}
></MoveAxis>
<button onClick={() => callHomeJoint(index)}>Home</button>
<button
className="w-fit rounded-md bg-green-500 px-3 py-2 text-white hover:bg-green-700"
onClick={() => callHomeJoint(index)}
>
Home
</button>
</div>
))}
</div>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/redux/ArmPoseSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const initialState = {
toolValue: 0,
isHomed: false,
moveQueueSize: 0,
currentAngles : [0,0,0,0,0,0],
currentAngles: [0, 0, 0, 0, 0, 0],
toMove: {
x: 0,
y: 0,
Expand Down

0 comments on commit 1586b74

Please sign in to comment.