├── README.md
├── fpp_planner
└── fpp_planner.m
├── hardware_params.m
├── main.m
├── math
├── rot_x.m
├── rot_y.m
├── rot_z.m
├── rot_zyx.m
└── skew_mat.m
├── mpc_controller
├── add_state_boundaries.asv
├── add_state_boundaries.m
├── form_mpc_prob.m
└── unpacks_sol.m
├── srb_dynamics
└── get_srb_dynamics.m
├── state_estimation
└── state_estimation.m
├── video
├── 2d_walk_1.gif
├── 2d_walk_1.mp4
├── 3d_running_6.mp4
├── 3d_walk_1.gif
├── 3d_walk_1.mp4
├── 3d_walk_2.gif
├── 3d_walk_2.mp4
├── 3d_walk_3.gif
├── 3d_walk_3.mp4
├── 3d_walk_4.gif
├── 3d_walk_4.mp4
├── 3d_walk_5.mp4
├── 3d_walk_6.mp4
└── walking_combined.mp4
└── visualization
├── leg_ik.m
├── plot_cube.m
└── rbt_anime.m
/README.md:
--------------------------------------------------------------------------------
1 | # bipedal_pointfoot_mpc_ctr
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/fpp_planner/fpp_planner.m:
--------------------------------------------------------------------------------
1 | function [ref_traj_v] = fpp_planner(world_p, body_p, ctr_p, path)
2 |
3 | addpath(path.casadi);
4 | import casadi.*;
5 |
6 | %% Generate reference trajectory
7 | ref_traj_v.x_ref_val = zeros(12,ctr_p.N+1);
8 | ref_traj_v.f_ref_val = zeros(6,ctr_p.N);
9 | ref_traj_v.fp_ref_val = zeros(6,ctr_p.N);
10 |
11 | for i = 1:6
12 | ref_traj_v.x_ref_val(i,:) = linspace(ctr_p.x_init_tar_val(i),ctr_p.x_final_tar_val(i),ctr_p.N+1); %rpy xyz
13 | ref_traj_v.x_ref_val(i+6,:) = linspace(ctr_p.dx_init_tar_val(i),ctr_p.dx_final_tar_val(i),ctr_p.N+1); %velocity
14 | end
15 |
16 | % spine on the z axis
17 | s_a = [ref_traj_v.x_ref_val(4,1),ref_traj_v.x_ref_val(4,ctr_p.N/2),ref_traj_v.x_ref_val(4,ctr_p.N)]; % x axis
18 | s_b = [ctr_p.x_init_tar_val(6),ctr_p.x_final_tar_val(6),ctr_p.x_init_tar_val(6)+0]; % z axis
19 | ref_traj_v.x_ref_val(6,:) = interp1(s_a,s_b,ref_traj_v.x_ref_val(4,:),'spline');
20 |
21 |
22 | % calcuate foot placement points, openloop one
23 | t_stance = ctr_p.contact_state_ratio(1)*ctr_p.dt_val(1);
24 | ww = body_p.width;
25 | ll = 0;
26 |
27 | for k=1:ctr_p.N
28 |
29 | fp_symmetry = t_stance/2*ctr_p.dx_final_tar_val(4:6);
30 |
31 | fp_centrifugal = cross((1/2*sqrt(ctr_p.init_z/world_p.g)*ctr_p.dx_final_tar_val(4:6)), ctr_p.dx_final_tar_val(1:3));
32 |
33 | rot_mat_z_arr = rotz(ref_traj_v.x_ref_val(3,k));
34 | hip_com_g_vec_r = ref_traj_v.x_ref_val(4:6,k) + rot_mat_z_arr* [0; 1*ww/2; 0] - [0;0;ctr_p.init_z];
35 | hip_com_g_vec_l = ref_traj_v.x_ref_val(4:6,k) + rot_mat_z_arr* [0; -1*ww/2; 0] - [0;0;ctr_p.init_z];
36 |
37 | ref_traj_v.fp_ref_val(:,k) = [hip_com_g_vec_r;hip_com_g_vec_l]+...
38 | repmat(fp_symmetry,2,1)+...
39 | repmat(fp_centrifugal,2,1);
40 |
41 | contact_v = ctr_p.contact_state_val(:,k);
42 |
43 | % calcuate ref pos for swing foot
44 | period_now = mod(k,ctr_p.N/ctr_p.gait_num);
45 | period_now_rad = pi*(period_now/(ctr_p.N/ctr_p.gait_num));
46 |
47 | for leg_k = 1:2
48 | if(contact_v(leg_k) == 0)
49 | ref_traj_v.fp_ref_val(leg_k*3,k) = 0.2 * sin(period_now_rad); % sine foot swing height
50 | end
51 | end
52 |
53 | end
54 |
55 | % combine all ref traj
56 | ref_traj_v.p = [reshape(ref_traj_v.x_ref_val,body_p.state_dim*(ctr_p.N+1),1);...
57 | reshape(ref_traj_v.f_ref_val,body_p.f_dim*ctr_p.N,1);...
58 | reshape(ref_traj_v.fp_ref_val,body_p.fp_dim*ctr_p.N,1);...
59 | reshape(ctr_p.contact_state_val,2*ctr_p.N,1)]; % * 2 legs
60 |
61 | ref_traj_v.x0 = [reshape(ref_traj_v.x_ref_val,body_p.state_dim*(ctr_p.N+1),1);...
62 | reshape(ref_traj_v.f_ref_val,body_p.f_dim*ctr_p.N,1);...
63 | reshape(ref_traj_v.fp_ref_val,body_p.fp_dim*ctr_p.N,1)]; % initial states
64 |
65 | end
66 |
67 |
--------------------------------------------------------------------------------
/hardware_params.m:
--------------------------------------------------------------------------------
1 | function [world, body, ctr, path] = hardware_params()
2 | %% Casadi path
3 | % Change to your casadi path
4 | path.casadi = 'D:\matlab_lib\casadi-3.6.0-windows64-matlab2018b';
5 |
6 | %% Simulation params
7 | world.fk = 0.5; % friction
8 | world.g = 9.81; % gravity constant
9 |
10 | world.friction_cone = [1/world.fk, 0 -1;...
11 | -1/world.fk, 0 -1;...
12 | 0, 1/world.fk, -1;...
13 | 0, -1/world.fk, -1];
14 |
15 | %% Controller params
16 | ctr.phase_num = 4;
17 | ctr.N = 80*3; % mpc period window
18 | ctr.T = 3*3; % mpc period time
19 | ctr.dt_val = (ctr.T/ctr.N) .* ones(ctr.N,1); % dt vector
20 |
21 | ctr.max_jump_z = 0.55; % max jumping height, constraints
22 | ctr.min_dump_z = 0.15; % min standing height
23 | ctr.max_lift_vel_z = 6.5; % max jumping velocity
24 | ctr.init_z = 0.3;
25 |
26 | ctr.x_init_tar_val = [0; 0; 0; 0; 0; ctr.init_z]; % init state
27 | ctr.dx_init_tar_val = [0; 0; 0; 0; 0; 0]; % init d state
28 | ctr.x_final_tar_val = [0; 0; 0.2*9*0; 0.4*9*1.5; 0.4*9*0; 0.3]; % final target state r p y; x y z
29 | ctr.dx_final_tar_val = [0; 0; 0.2*0; 0.4*1.5; 0.4*0; 0];
30 |
31 | %ctr.contact_state_ratio = ctr.N.*[0.35 0.15 0.475 0.025]; % pull, jump, flight, impact
32 | ctr.contact_state_ratio = ctr.N.*[1/48 1/12 1/12 1/12 1/12 1/12 1/12 1/12 1/12 1/12 1/12 1/12]; % pull, jump, flight, impact
33 |
34 | % ctr.contact_state_val = [ones(2, ctr.contact_state_ratio(1)),...
35 | % 0 * ones(2, ctr.contact_state_ratio(2)),...
36 | % ones(2, ctr.contact_state_ratio(3)),...
37 | % 0 * ones(2, ctr.contact_state_ratio(4)),...
38 | % ones(2, ctr.contact_state_ratio(1)),...
39 | % 0 * ones(2, ctr.contact_state_ratio(2)),...
40 | % ones(2, ctr.contact_state_ratio(3)),...
41 | % 0 * ones(2, ctr.contact_state_ratio(4)),...
42 | % ones(2, ctr.contact_state_ratio(1)),...
43 | % 0 * ones(2, ctr.contact_state_ratio(2)),...
44 | % ones(2, ctr.contact_state_ratio(3)),...
45 | % 0 * ones(2, ctr.contact_state_ratio(4))]; % no foot contact during last 2 phases
46 |
47 | ctr.gait_num = 12;
48 | ctr.contact_state_val = [repmat([1;0], 1, ctr.contact_state_ratio(1)),...
49 | repmat([0;0], 1, ctr.contact_state_ratio(1)),...
50 | repmat([0;1], 1, ctr.contact_state_ratio(1)),...
51 | repmat([0;0], 1, ctr.contact_state_ratio(1))];
52 | ctr.contact_state_val = repmat(ctr.contact_state_val, 1, ctr.gait_num);
53 | % no foot contact during last 2 phases
54 |
55 | % cost gains
56 | ctr.weight.QX = [10 10 0, 10 10 10, 10 10 10, 10 10 10 ]'; % state error
57 | ctr.weight.QN = [10 10 0, 50 50 50, 10 10 10, 10 10 10 ]'; % state error, final
58 | ctr.weight.Qc = 100*[50 50 50]'; % foot placement error on 3 axis
59 | ctr.weight.Qf = [0.1 0.1 0.1]'; % input error on 3 axis
60 |
61 | % casadi optimal settings
62 | ctr.opt_setting.expand =true;
63 | ctr.opt_setting.ipopt.max_iter=1500;
64 | ctr.opt_setting.ipopt.print_level=0;
65 | ctr.opt_setting.ipopt.acceptable_tol=1e-4;
66 | ctr.opt_setting.ipopt.acceptable_obj_change_tol=1e-6;
67 | ctr.opt_setting.ipopt.tol=1e-4;
68 | ctr.opt_setting.ipopt.nlp_scaling_method='gradient-based';
69 | ctr.opt_setting.ipopt.constr_viol_tol=1e-3;
70 | ctr.opt_setting.ipopt.fixed_variable_treatment='relax_bounds';
71 |
72 | %% Robot params
73 |
74 | body.state_dim = 12; % number of dim of state, rpy xyz dot_rpy dot_xyz
75 | body.f_dim = 6; % number of dim of leg force, 3*2, 2 leg
76 | body.fp_dim = 6; % number of dim of leg pos, 3*2, 2 leg
77 |
78 |
79 | body.m = 5;
80 | body.i_vec = [0.06 0.1 0.05]*2;
81 | body.i_mat = [body.i_vec(1) 0 0;... % roll
82 | 0 body.i_vec(2) 0;... % pitch
83 | 0 0 body.i_vec(3)]; % yaw
84 |
85 | %body.length = 0.34;
86 | body.width = 0.3; % distance between 2 legs
87 |
88 | % leg length
89 | body.l1 = 0.2;
90 | body.l2 = 0.17;
91 |
92 | % foot motion range, in m
93 | body.foot_x_range = 0.15;
94 | body.foot_y_range = 0.15;
95 | body.foot_z_range = 0.3;
96 |
97 | % output force range
98 | body.max_zforce = 100;
99 |
100 | % calaute 2 hip positions
101 | body.hip_vec = [0; body.width/2; 0];
102 | body.hip_dir_mat = [0 0; 1 -1; 0 0]; % 3,2 hip vec
103 | body.hip_pos = body.hip_dir_mat .* repmat(body.hip_vec,1,2); % 3,2 hip pos vec
104 | body.foot_pos = repmat([0; 0; -0.6*ctr.init_z],1,2); % init foot pos
105 |
106 | body.phip_swing_ref = body.hip_pos + body.foot_pos;
107 | % ref foot pos at swing phase
108 |
109 | body.phip_swing_ref_vec = reshape(body.phip_swing_ref,[],1);
110 |
111 | % the range foot can move within
112 | body.foot_convex_hull = [1 0 0 -body.foot_x_range;
113 | -1 0 0 -body.foot_x_range;
114 | 0 1 0 -body.foot_y_range;
115 | 0 -1 0 -body.foot_y_range;
116 | 0 0 1 -ctr.min_dump_z;
117 | 0 0 -1 -body.foot_z_range];
118 |
119 |
120 | end
--------------------------------------------------------------------------------
/main.m:
--------------------------------------------------------------------------------
1 | %% Add casadi path
2 |
3 | clc; clear;
4 | close all; warning off;
5 |
6 | %% Get hardware params & set package path
7 | [world_params, body_params, ctr_params, path] = hardware_params();
8 |
9 | addpath(path.casadi);
10 | import casadi.*;
11 |
12 | addpath('math\');
13 | addpath('srb_dynamics\');
14 | addpath('mpc_controller\');
15 | addpath('fpp_planner\');
16 | addpath('visualization\');
17 |
18 | %% Get dynamics
19 | [dyn_f] = get_srb_dynamics(world_params, body_params, path);
20 |
21 | %% Form the mpc problem
22 | [mpc_v, mpc_c, mpc_p] = form_mpc_prob(world_params, body_params, ctr_params, dyn_f, path);
23 | %%
24 | [boundray_v] = add_state_boundaries(mpc_v, mpc_c, world_params, body_params, ctr_params, path);
25 | %%
26 | [ref_traj_v] = fpp_planner(world_params, body_params, ctr_params, path);
27 |
28 | %% Slove the NLP prob
29 | sol = mpc_p.solver('x0',ref_traj_v.x0,...
30 | 'lbx',boundray_v.lbx,...
31 | 'ubx',boundray_v.ubx,...
32 | 'lbg',boundray_v.lbg,...
33 | 'ubg',boundray_v.ubg,...
34 | 'p',ref_traj_v.p);
35 |
36 | %% Unpack data
37 | [x_sol, f_sol, fp_l_sol, fp_g_sol] = unpacks_sol(sol, body_params, ctr_params, path);
38 |
39 | %%
40 | rbt_anime(x_sol,f_sol,fp_g_sol,ref_traj_v,ctr_params.T,ctr_params.N,body_params);
41 |
42 | % %% Plots
43 | % clf;
44 | % subplot(1,2,1);
45 | % plot(x_sol(4,:),"LineWidth",1.5);
46 | % hold on;
47 | % plot(x_sol(5,:),"LineWidth",1.5);
48 | % hold on;
49 | % plot(x_sol(6,:),"LineWidth",1.5);
50 | % hold on;
51 | % title("Body's Position")
52 | % xlabel("Step")
53 | % ylabel("m")
54 | % legend("Position X","Position Y","Position Z");
55 | % grid on
56 | %
57 | % subplot(1,2,2);
58 | % plot(x_sol(1,:),"LineWidth",1.5);
59 | % hold on;
60 | % plot(x_sol(2,:),"LineWidth",1.5);
61 | % hold on;
62 | % plot(x_sol(3,:),"LineWidth",1.5);
63 | % hold on;
64 | % title("Body's Euler Angle")
65 | % xlabel("Step")
66 | % ylabel("rad")
67 | % legend("Roll","Pitch","Yaw");
68 | % grid on
69 | %
70 | % %% velocity plot
71 | % clf;
72 | % plot(x_sol(9,:),"LineWidth",1.5);
73 | % hold on;
74 | % plot(x_sol(10,:),"LineWidth",1.5);
75 | % hold on;
76 | % title("Body's Velocity")
77 | % xlabel("Step")
78 | % ylabel("m/s or rad/s")
79 | % legend("Yaw Velocity rad/s","X Velocity m/s");
80 | % grid on
81 | %
82 | % %% fpp
83 | % clf;
84 | % subplot(1,2,1);
85 | % plot(fp_g_sol(1,:),"LineWidth",1.5);
86 | % hold on;
87 | % plot(fp_g_sol(2,:),"LineWidth",1.5);
88 | % hold on;
89 | % plot(fp_g_sol(3,:),"LineWidth",1.5);
90 | % hold on;
91 | % title("Foot Placement Point global of FL leg")
92 | % xlabel("Step")
93 | % ylabel("m")
94 | % legend("Position X","Position Y","Position Z");
95 | % grid on
96 | %
97 | % subplot(1,2,2);
98 | % plot(f_sol(1,:),"LineWidth",1.5);
99 | % hold on;
100 | % plot(f_sol(2,:),"LineWidth",1.5);
101 | % hold on;
102 | % plot(f_sol(3,:),"LineWidth",1.5);
103 | % hold on;
104 | % title("Ground Reaction Force of FL leg")
105 | % xlabel("Step")
106 | % ylabel("N")
107 | % legend("Force X","Force Y","Force Z");
108 | % grid on
109 |
--------------------------------------------------------------------------------
/math/rot_x.m:
--------------------------------------------------------------------------------
1 | function rot_mat_x = rot_x(rad)
2 |
3 | c = cos(rad);
4 | s = sin(rad);
5 |
6 | rot_mat_x = [1 0 0;...
7 | 0 c -1*s;...
8 | 0 s c];
9 | end
--------------------------------------------------------------------------------
/math/rot_y.m:
--------------------------------------------------------------------------------
1 | function rot_mat_y = rot_y(rad)
2 |
3 | c = cos(rad);
4 | s = sin(rad);
5 |
6 | rot_mat_y = [c 0 s;...
7 | 0 1 0;...
8 | -1*s 0 c];
9 | end
--------------------------------------------------------------------------------
/math/rot_z.m:
--------------------------------------------------------------------------------
1 | function rot_mat_z = rot_z(rad)
2 |
3 | c = cos(rad);
4 | s = sin(rad);
5 |
6 | rot_mat_z = [c -1*s 0;...
7 | s c 0;...
8 | 0 0 1];
9 | end
--------------------------------------------------------------------------------
/math/rot_zyx.m:
--------------------------------------------------------------------------------
1 | function rot_mat = rot_zyx(vec_)
2 |
3 | psi_ = vec_(3);
4 | theta_ = vec_(2);
5 | phi_ = vec_(1);
6 |
7 | %z-psi-偏航-yaw
8 | %y-theta-俯仰-pitch
9 | %x-phi-横滚-roll
10 |
11 | rot_mat_z = rot_z(psi_);
12 | rot_mat_y = rot_y(theta_);
13 | rot_mat_x = rot_x(phi_);
14 |
15 | rot_mat = rot_mat_z * rot_mat_y * rot_mat_x;
16 |
17 | end
18 |
--------------------------------------------------------------------------------
/math/skew_mat.m:
--------------------------------------------------------------------------------
1 | function s_mat = skew_mat(vec_)
2 |
3 | % generate a skew-symmetric matrix for 3d vec's corss product
4 | % [a]x b = a x b
5 |
6 | a1 = vec_(1);
7 | a2 = vec_(2);
8 | a3 = vec_(3);
9 |
10 | s_mat = [0 -1*a3 a2;...
11 | a3 0 -1*a1;...
12 | -1*a2 a1 0];
13 |
14 | end
--------------------------------------------------------------------------------
/mpc_controller/add_state_boundaries.asv:
--------------------------------------------------------------------------------
1 | %% lower & upper boundary of eq & ieq constrains
2 |
3 | function [outputArg1,outputArg2] = add_state_boundaries(mpc_v, mpc_c, world_p, body_p, ctr_p, path)
4 |
5 | addpath(path.casadi);
6 | import casadi.*;
7 |
8 | % eq constraint equation Ac = 0
9 | args.lbg(1:mpc_c.eq_con_dim) = 0;
10 | args.ubg(1:mpc_c.eq_con_dim) = 0;
11 |
12 | % ineq constraint equation -inf0, stance phase -> < max z force, 2 legs *
43 |
44 | mpc_c.ineq_con_dim = 2*6*N + 2*4*N + N + 2*N; % dim for ieq constraints
45 |
46 | fprintf('%d eq constraints and %d ineq constraints', mpc_c.eq_con_dim, mpc_c.ineq_con_dim);
47 |
48 | % define cost function and constraints
49 | for k = 1:N
50 | x_t = mpc_v.x_arr(:,k); % current state
51 | x_next = mpc_v.x_arr(:,k+1); %next state
52 |
53 | f_t = mpc_v.f_arr(:,k); % current force
54 | fp_t = mpc_v.fp_arr(:,k); % current foot placement point
55 | fp_g_t = repmat(x_t(4:6),2,1)+fp_t; %foot pos in global coord * 2 legs
56 |
57 | x_ref_t = mpc_v.x_ref_arr(:,k); % current reference state
58 | f_ref_t = mpc_v.f_ref_arr(:,k); % current reference force
59 | fp_ref_t = mpc_v.fp_ref_arr(:,k); % current reference foot placement point
60 |
61 | contact_mat_t = mpc_v.contact_mat_arr(:,k); % current foot contact point
62 | dt_t = ctr_p.dt_val(k);
63 |
64 | % constraints
65 | % dynamic equation constraint
66 | mpc_c.eq_con_dynamic(state_dim*(k-1)+1:state_dim*k) = x_next - (x_t + dyn_f(x_t,f_t,fp_t)*dt_t);
67 | % zforce direction always point up
68 | mpc_c.ineq_con_zforce_dir(k) = -1*dot(f_t,repmat([0;0;1],2,1)); % * 2 legs, dot out 1 value
69 | % zforce range < max z force
70 | mpc_c.ineq_con_zforce_range(2*(k-1)+1:2*k) = f_t([3,6]) - contact_mat_t.*repmat(body_p.max_zforce,2,1); % * 2 legs
71 |
72 | for leg_k = 1:2
73 |
74 | xyz_i = 3*(leg_k-1)+1:3*leg_k; % index for xyz dir
75 |
76 | % constrant leg on ground, z=0
77 | mpc_c.eq_con_foot_contact_ground(2*(k-1)+leg_k) = contact_mat_t(leg_k)*fp_g_t(3*(leg_k-1)+3); % * 2 legs
78 |
79 | % keep foot within motion range
80 | rot_zyx_t = rot_zyx(x_t(1:3));
81 | hip_pos_global_t = rot_zyx_t*body_p.phip_swing_ref + x_t(4:6);
82 | leg_vec_t = (fp_g_t(xyz_i) - hip_pos_global_t(:,leg_k)); % leg vector, from hip to foot
83 |
84 | mpc_c.ineq_con_foot_range(2*6*(k-1)+6*(leg_k-1)+1: 2*6*(k-1)+6*leg_k) =...
85 | body_p.foot_convex_hull*[leg_vec_t;1]; % leg's motion range limit * 2 legs
86 |
87 | % ground reaction force within friction cone
88 | % * 2 legs * 2 +- dir * 2 axis, xy // 4 = 2 axis * 2 dir
89 | mpc_c.ineq_con_foot_friction(2*4*(k-1)+2*2*(leg_k-1)+1: 2*4*(k-1)+2*2*leg_k) = world_p.friction_cone*f_t(xyz_i);
90 |
91 | % non-slip, if leg touches the ground, ffp_now = ffp_next
92 | if(k