├── 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