Spot Quadruped Robot
This example demonstrates quadruped robot control inspired by Boston Dynamics Spot with MATLAB/Simulink.
Overview
| Property |
Value |
| Type |
Quadruped Robot |
| Difficulty |
Advanced |
| Control Method |
Gait Control / MPC |
| DOF |
12 (3 per leg) |
Features
- Dynamic Walking: Trot, walk, bound gaits
- Terrain Adaptation: Uneven ground navigation
- Balance Control: Real-time stability
- 12-DOF Control: Hip, knee, ankle per leg
Specifications
| Property |
Value |
| Body Length |
1.1 m |
| Body Width |
0.5 m |
| Standing Height |
0.6 m |
| Mass |
32 kg |
| DOF |
12 (3 per leg) |
Control Architecture
Hierarchical Locomotion Control
flowchart TB
subgraph HighLevel["High-Level Planner"]
CMD[/"Velocity Command<br/>(vx, vy, omega)"/]
GAIT["Gait<br/>Scheduler"]
FOOT["Footstep<br/>Planner"]
end
subgraph MidLevel["Mid-Level Control"]
MPC["Model Predictive<br/>Control"]
SWING["Swing Leg<br/>Controller"]
STANCE["Stance Leg<br/>Controller"]
end
subgraph LowLevel["Low-Level Control"]
IK["Inverse<br/>Kinematics"]
JC["Joint PD<br/>Controllers"]
end
subgraph Legs["Leg Subsystems"]
FL["Front Left<br/>Hip-Knee-Ankle"]
FR["Front Right<br/>Hip-Knee-Ankle"]
RL["Rear Left<br/>Hip-Knee-Ankle"]
RR["Rear Right<br/>Hip-Knee-Ankle"]
end
subgraph Sensors["Sensor Suite"]
IMU["IMU<br/>Body Pose"]
ENC["Joint<br/>Encoders"]
FORCE["Force<br/>Sensors"]
end
CMD --> GAIT
GAIT --> FOOT
FOOT --> MPC
MPC --> SWING & STANCE
SWING --> IK
STANCE --> IK
IK --> JC
JC --> FL & FR & RL & RR
FL & FR & RL & RR --> ENC & FORCE
IMU --> MPC
ENC --> JC
FORCE --> STANCE
style HighLevel fill:#e1f5fe
style MidLevel fill:#e8f5e9
style LowLevel fill:#fff3e0
style Legs fill:#fce4ec
style Sensors fill:#c8e6c9
Leg Kinematic Structure
flowchart LR
subgraph LegKinematics["Single Leg - 3 DOF"]
BODY["Body<br/>Frame"] --> HAA["Hip Abduction<br/>q1"]
HAA --> HFE["Hip Flexion<br/>q2"]
HFE --> KFE["Knee Flexion<br/>q3"]
KFE --> FOOT["Foot<br/>Position"]
end
subgraph Dimensions["Leg Dimensions"]
L1["L_hip = 0.08 m"]
L2["L_thigh = 0.34 m"]
L3["L_calf = 0.35 m"]
end
style LegKinematics fill:#bbdefb
style Dimensions fill:#fff9c4
Gait State Machine
stateDiagram-v2
[*] --> Stand
Stand --> Trot: Trot Command
Stand --> Walk: Walk Command
Stand --> Bound: Bound Command
Trot --> TrotPhase1: FL+RR Swing
TrotPhase1 --> TrotPhase2: FR+RL Swing
TrotPhase2 --> TrotPhase1: Continue
Walk --> WalkPhase1: FL Swing
WalkPhase1 --> WalkPhase2: RR Swing
WalkPhase2 --> WalkPhase3: FR Swing
WalkPhase3 --> WalkPhase4: RL Swing
WalkPhase4 --> WalkPhase1: Continue
Trot --> Stand: Stop
Walk --> Stand: Stop
Bound --> Stand: Stop
MPC Control Structure
flowchart TB
subgraph Reference["Reference Trajectory"]
REF_X["Body Position<br/>Reference"]
REF_V["Body Velocity<br/>Reference"]
REF_R["Body Orientation<br/>Reference"]
end
subgraph MPC_Block["MPC Controller"]
PRED["Prediction<br/>Model"]
OPT["QP<br/>Optimizer"]
CONST["Constraints"]
end
subgraph Outputs["Control Outputs"]
GRF["Ground Reaction<br/>Forces"]
TORQUE["Joint<br/>Torques"]
end
subgraph Constraints_Detail["Constraints"]
FRIC["Friction<br/>Cone"]
FMAX["Force<br/>Limits"]
CONT["Contact<br/>Schedule"]
end
REF_X & REF_V & REF_R --> PRED
PRED --> OPT
CONST --> OPT
Constraints_Detail --> CONST
OPT --> GRF
GRF --> TORQUE
style Reference fill:#e1f5fe
style MPC_Block fill:#c8e6c9
style Outputs fill:#fff9c4
style Constraints_Detail fill:#fce4ec
State-Space Model
Single Rigid Body Dynamics
% State vector: x = [p, theta, v, omega]' (13 states)
% p: body position [x, y, z]
% theta: body orientation [roll, pitch, yaw]
% v: body velocity [vx, vy, vz]
% omega: angular velocity [wx, wy, wz]
% Physical parameters
m = 32; % Body mass [kg]
g = 9.81; % Gravity [m/s^2]
% Body inertia tensor
Ixx = 0.946; % [kg*m^2]
Iyy = 1.756;
Izz = 2.012;
I_body = diag([Ixx, Iyy, Izz]);
% Continuous-time dynamics
% m * v_dot = sum(f_i) + m*g
% I * omega_dot = sum(r_i x f_i)
% Linearized state-space (hover/standing)
A = zeros(12, 12);
A(1:3, 7:9) = eye(3); % Position from velocity
A(4:6, 10:12) = eye(3); % Orientation from angular rate
B = zeros(12, 12); % 4 legs x 3 force components
for i = 1:4
B(7:9, (i-1)*3+1:i*3) = eye(3) / m; % Linear acceleration from forces
% Angular acceleration requires Jacobian (configuration dependent)
end
% MPC uses this model for prediction
Leg Inverse Kinematics
function q = leg_ik(p_foot, leg_id)
% Inverse kinematics for single leg
% p_foot: foot position in body frame [x, y, z]
% leg_id: 'FL', 'FR', 'RL', 'RR'
% Leg parameters
L_hip = 0.08; % Hip offset [m]
L_thigh = 0.34; % Thigh length [m]
L_calf = 0.35; % Calf length [m]
% Sign conventions for left/right
if strcmp(leg_id, 'FL') || strcmp(leg_id, 'RL')
sign_y = 1;
else
sign_y = -1;
end
% Position relative to hip
x = p_foot(1);
y = p_foot(2) - sign_y * L_hip;
z = p_foot(3);
% Hip abduction angle
q(1) = atan2(y, -z);
% Distance from hip to foot in sagittal plane
L = sqrt(y^2 + z^2);
d = sqrt(x^2 + L^2);
% Knee angle (law of cosines)
cos_knee = (L_thigh^2 + L_calf^2 - d^2) / (2 * L_thigh * L_calf);
q(3) = pi - acos(max(-1, min(1, cos_knee)));
% Hip flexion angle
alpha = atan2(x, L);
beta = acos((L_thigh^2 + d^2 - L_calf^2) / (2 * L_thigh * d));
q(2) = alpha + beta;
end
Swing Leg Trajectory
function [p_foot, v_foot] = swing_trajectory(phase, p_start, p_end, swing_height)
% Bezier curve swing trajectory
% phase: normalized time [0, 1]
% Control points for Bezier curve
p0 = p_start;
p1 = p_start + [0; 0; swing_height];
p2 = p_end + [0; 0; swing_height];
p3 = p_end;
% Cubic Bezier
t = phase;
p_foot = (1-t)^3 * p0 + 3*(1-t)^2*t * p1 + 3*(1-t)*t^2 * p2 + t^3 * p3;
% Bezier derivative for velocity
v_foot = 3*(1-t)^2 * (p1-p0) + 6*(1-t)*t * (p2-p1) + 3*t^2 * (p3-p2);
end
Gait Patterns
Gait Timing Diagram
Trot Gait:
Time: 0% 25% 50% 75% 100%
FL: [SWING ][STANCE ]
FR: [STANCE ][SWING ]
RL: [STANCE ][SWING ]
RR: [SWING ][STANCE ]
Walk Gait:
Time: 0% 25% 50% 75% 100%
FL: [SW][STANCE ]
FR: [STANCE ][SW][STANCE]
RL: [STANCE ][SW][ST]
RR: [ST][STANCE ][SW]
Bound Gait:
Time: 0% 25% 50% 75% 100%
FL: [SWING ][STANCE ]
FR: [SWING ][STANCE ]
RL: [STANCE ][SWING ]
RR: [STANCE ][SWING ]
Gait Parameters
% Trot gait parameters
trot_params = struct();
trot_params.stance_duration = 0.3; % [s]
trot_params.swing_duration = 0.2; % [s]
trot_params.swing_height = 0.08; % [m]
trot_params.step_length = 0.15; % [m]
trot_params.phase_offset = [0, 0.5, 0.5, 0]; % FL, FR, RL, RR
% Walk gait parameters
walk_params = struct();
walk_params.stance_duration = 0.4;
walk_params.swing_duration = 0.15;
walk_params.swing_height = 0.06;
walk_params.step_length = 0.12;
walk_params.phase_offset = [0, 0.5, 0.75, 0.25];
Convex MPC
function [f_opt, status] = convex_mpc(x0, x_ref, contact_schedule, params)
% Convex MPC for quadruped locomotion
% Optimizes ground reaction forces
N = params.horizon; % Prediction horizon
dt = params.dt; % Time step
mu = params.friction; % Friction coefficient
% Decision variables: forces at each leg for each timestep
n_forces = 12 * N; % 4 legs x 3 forces x N steps
% Build QP matrices
% min (1/2) * f' * H * f + g' * f
% subject to: A_eq * f = b_eq
% A_ineq * f <= b_ineq
% Cost: tracking error + force regularization
Q = blkdiag(params.Q_pos, params.Q_vel, params.Q_ori, params.Q_omega);
R = params.R_force * eye(12);
H = zeros(n_forces, n_forces);
g = zeros(n_forces, 1);
x = x0;
for k = 1:N
% Propagate dynamics
[A_k, B_k] = get_linearized_dynamics(x, contact_schedule(:, k));
% Add to cost
idx = (k-1)*12 + 1 : k*12;
H(idx, idx) = H(idx, idx) + B_k' * Q * B_k + R;
g(idx) = g(idx) + B_k' * Q * (A_k * x - x_ref(:, k));
x = A_k * x;
end
% Friction cone constraints (linearized)
A_fric = [];
b_fric = [];
for k = 1:N
for leg = 1:4
if contact_schedule(leg, k)
idx = (k-1)*12 + (leg-1)*3 + 1;
% |fx| <= mu * fz, |fy| <= mu * fz, fz >= 0
A_fric = [A_fric;
1, 0, -mu;
-1, 0, -mu;
0, 1, -mu;
0,-1, -mu;
0, 0, -1];
b_fric = [b_fric; zeros(5, 1)];
end
end
end
% Solve QP
options = optimoptions('quadprog', 'Display', 'off');
[f_opt, ~, status] = quadprog(H, g, A_fric, b_fric, [], [], [], [], [], options);
end
Simulink Integration
Initialization Script
TIME_STEP = 2; % Fast update for dynamic locomotion
% Initialize IMU
imu = wb_robot_get_device('imu');
wb_inertial_unit_enable(imu, TIME_STEP);
gyro = wb_robot_get_device('gyro');
wb_gyro_enable(gyro, TIME_STEP);
accel = wb_robot_get_device('accelerometer');
wb_accelerometer_enable(accel, TIME_STEP);
% Initialize leg motors (12 total)
leg_names = {'front_left', 'front_right', 'rear_left', 'rear_right'};
joint_names = {'hip_abduction', 'hip_flexion', 'knee_flexion'};
motors = cell(4, 3);
sensors = cell(4, 3);
for i = 1:4
for j = 1:3
name = [leg_names{i} '_' joint_names{j}];
motors{i, j} = wb_robot_get_device(name);
sensors{i, j} = wb_robot_get_device([name '_sensor']);
wb_position_sensor_enable(sensors{i, j}, TIME_STEP);
end
end
% Initialize force sensors
force_sensors = cell(1, 4);
for i = 1:4
force_sensors{i} = wb_robot_get_device([leg_names{i} '_force']);
wb_touch_sensor_enable(force_sensors{i}, TIME_STEP);
end
% Gait parameters
gait = 'trot';
gait_phase = 0;
Locomotion Control Loop
while wb_robot_step(TIME_STEP) ~= -1
% Read body state
orientation = wb_inertial_unit_get_roll_pitch_yaw(imu);
angular_vel = wb_gyro_get_values(gyro);
linear_accel = wb_accelerometer_get_values(accel);
% Read joint states
q = zeros(12, 1);
for i = 1:4
for j = 1:3
q((i-1)*3 + j) = wb_position_sensor_get_value(sensors{i, j});
end
end
% Read contact forces
contact = zeros(4, 1);
for i = 1:4
contact(i) = wb_touch_sensor_get_value(force_sensors{i}) > 10;
end
% Gait scheduler
[swing_legs, stance_legs] = gait_scheduler(gait, gait_phase);
% MPC for stance legs (compute ground reaction forces)
x_state = [position; orientation; velocity; angular_vel];
f_grf = convex_mpc(x_state, x_ref, stance_legs, mpc_params);
% Swing leg controller
for leg = swing_legs
[p_foot_des, v_foot_des] = swing_trajectory(phase(leg), ...
p_foot_start(leg), p_foot_end(leg), swing_height);
q_leg = leg_ik(p_foot_des, leg);
q_des((leg-1)*3+1 : leg*3) = q_leg;
end
% Stance leg controller (force to torque)
for leg = stance_legs
J_leg = leg_jacobian(q((leg-1)*3+1 : leg*3), leg);
tau((leg-1)*3+1 : leg*3) = J_leg' * f_grf((leg-1)*3+1 : leg*3);
end
% Apply joint commands
for i = 1:4
for j = 1:3
wb_motor_set_torque(motors{i, j}, tau((i-1)*3 + j));
end
end
% Update gait phase
gait_phase = mod(gait_phase + TIME_STEP/1000 / gait_period, 1);
end
Quick Start
- Open Webots and load
examples/spot_quadruped/worlds/spot_terrain.wbt
- Configure MATLAB controller
- Run simulation for locomotion demo
Gait Comparison
| Gait |
Speed |
Stability |
Terrain |
Energy |
| Walk |
Slow |
High |
Rough |
Low |
| Trot |
Medium |
Medium |
Moderate |
Medium |
| Bound |
Fast |
Low |
Flat |
High |
References
- Boston Dynamics Spot
- Raibert, M. (1986). "Legged Robots That Balance"
- Di Carlo, J. et al. (2018). "Dynamic Locomotion in the MIT Cheetah 3"
- Kim, D. et al. (2019). "Highly Dynamic Quadruped Locomotion via Whole-Body Impulse Control"