当前位置: 首页 > news >正文

用MATLAB符号工具建立机器人的动力学模型

目录

  • 介绍
  • 代码功能演示
  • 拉格朗日方法回顾
  • 求解符号表达式
  • 数值求解

介绍

开发机器人过程中经常需要用牛顿-拉格朗日法建立机器人的动力学模型,表示为二阶微分方程组。本文以一个二杆系统为例,介绍如何用MATLAB符号工具得到微分方程表达式,只需要编辑好物点的位置公式和系统动能、势能,就能得到微分方程组,避免繁琐的手工推导工作。

代码功能演示

先放运行效果:没有外力:
请添加图片描述
施加5N向上外力:
请添加图片描述
采用《机器人学导论——分析、控制及应用(第二版)》一书中例4.4的自由二连杆,原例题和建模过程如下:
在这里插入图片描述
在这里插入图片描述
全部代码如下,直接复制到Matlab中即可运行,其中第一节是建模,得到加速度项的表达式,第二节是带入数据进行数值求解:

% 以自由二连杆为例,展示Matlab符号工具建立牛顿-拉格朗日动力学方程,并用ODE45函数数值求解的过程%% 建模
syms m1 m2 L1 L2 g % 结构常量
syms t x1(t) x2(t) % 将系统的广义坐标定义为时间函数IA=1/3*m1*L1^2; % 连杆1惯量
ID=1/12*m2*L2^2;% 连杆2惯量pD=[L1*cos(x1)+1/2*L2*cos(x1+x2); %连杆2质心位置L1*sin(x1)+1/2*L2*sin(x1+x2)];
vD=diff(pD,t); %对时间求导得到速度K=1/2*IA*diff(x1,t)^2+1/2*ID*(diff(x1,t)+diff(x2,t))^2+1/2*m2*sum(vD.^2); %系统动能P=m1*g*L1/2*sin(x1)+m2*g*(L1*sin(x1)+L2/2*sin(x1+x2)); % 系统势能L=K-P; %拉格朗日函数syms dx1 dx2 ddx1 ddx2
temp=diff(diff(L,diff(x1,t)),t)-diff(L,x1); % 展开拉格朗日函数,得到二阶微分式
temp=subs(temp,diff(x1,t,t),ddx1); % 用新定义的符号代替广义坐标的一二阶导数,简化公式表达
temp=subs(temp,diff(x2,t,t),ddx2);
temp=subs(temp,diff(x1,t),dx1);
temp=subs(temp,diff(x2,t),dx2);
diff1=collect(temp,[ddx1,ddx2,dx1^2,dx2^2,dx1*dx2,dx1,dx2]); % 合并同类项,整理成便于阅读的形式temp=diff(diff(L,diff(x2,t)),t)-diff(L,x2); % 对第二个广义坐标θ2做同样操作
temp=subs(temp,diff(x1,t,t),ddx1);
temp=subs(temp,diff(x2,t,t),ddx2);
temp=subs(temp,diff(x1,t),dx1);
temp=subs(temp,diff(x2,t),dx2);
diff2=collect(temp,[ddx1,ddx2,dx1^2,dx2^2,dx1*dx2,dx1,dx2]);syms T1 T2 Fx Fy
syms x1v x2v %求雅可比矩阵不能用时间函数x1(t)和x2(t),因此先定义临时变量,求雅可比后再替换为x1和x2
p_end = [L1*cos(x1v)+L2*cos(x1v+x2v);L1*sin(x1v)+L2*sin(x1v+x2v)];% 末端位置
J_end = jacobian(p_end,[x1v;x2v]);% 末端雅可比矩阵
J_end = subs(J_end,{x1v,x2v},{x1,x2});
eqn = [diff1;diff2] == [T1;T2] + J_end.'*[Fx;Fy]; %构建动力学矩阵方程式
sol = solve(eqn,[ddx1;ddx2]); %求出二阶项[ddx1;ddx2]的解析解%输出求解结果,需要把结果表达式粘贴到新文件中,将其中的(t)全都删掉,然后粘贴到最下面odefun中v1和v2的表达式
fprintf("ddx1=");
disp(sol.ddx1);
fprintf("ddx2=");
disp(sol.ddx2);
fprintf("需要把结果表达式中的(t)全都删掉,然后粘贴到最下面odefun中ddx1和ddx2的表达式\n");%% 数值求解
clear;
global m1 m2 L1 L2 g d1 d2 Fx Fy
m1=1; m2=1; L1=0.5; L2=0.5; g=9.81;d1=0.8;d2=d1;Fx=0;Fy=0;%设置机器人参数,关节阻尼和外力tspan = 0:0.01:5; % 时间范围
[t,y] = ode45(@odefun,tspan,[0;0;0;0]); % 求解figure(1);clf; % 绘制运动动画
set(gcf,'Position',[0 300 600 350]);
for i = 1:10:size(y,1)x1=y(i,1);x2=y(i,2);x_loc = [0 L1*cos(x1) L1*cos(x1)+L2*cos(x1+x2)];y_loc = [0 L1*sin(x1) L1*sin(x1)+L2*sin(x1+x2)];clf; hold on;plot(x_loc,y_loc,'k - o','LineWidth',2);arrow_rate = 0.05;%箭头大小比例quiver(x_loc(end), y_loc(end), Fx*arrow_rate, Fy*arrow_rate, 0, 'LineWidth', 2, 'MaxHeadSize', 1, 'Color', 'r');  xlim([-1 1]);ylim([-1.22 0]);tit = sprintf("%.2f s",t(i));title(tit);
%     saveas(gcf,['Fig/',sprintf('%03d',size(y,1)-i),'.jpg']);pause(0.001);
endfigure(2); % 绘制关节角变化曲线
set(gcf,'Position',[0+600 300 600 500]);
subplot(211);
plot(t,rad2deg(y(:,1)+pi/2));
xlabel('Time (s)');ylabel('\theta_1');grid on;
subplot(212);
plot(t,rad2deg(y(:,2)));
xlabel('Time (s)');ylabel('\theta_2');grid on;% 为了求数值解需要化为一阶系统,以下为一阶系统的状态向量:
% x = [x1 x2 dx1 dx2]
% dxdt = [dx1 dx2 ddx1 ddx2]
function dxdt=odefun(t,x)global m1 m2 L1 L2 g d1 d2 Fx Fyx1=x(1);x2=x(2);dx1=x(3);dx2=x(4); %状态向量即为θ1,θ及其一阶导数T1 = -d1*dx1;T2 = -d2*dx2;% 根据符号工具的求解结果,得到θ1,θ2二阶导的表达式如下,需要删掉符号表达式中所有的(t)以免报错ddx1 = -(3*(2*L2*T2 - 2*L2*T1 - 6*L2*T1*sin(x1 + x2)^2 + 6*L2*T2*sin(x1 + x2)^2 - 6*L2*T1*cos(x1 + x2)^2 + 6*L2*T2*cos(x1 + x2)^2 + 12*L1*T2*sin(x1)*sin(x1 + x2) - 2*Fy*L1*L2*cos(x1)...  + 2*Fx*L1*L2*sin(x1) + 12*L1*T2*cos(x1)*cos(x1 + x2) + L1*L2*g*m1*cos(x1) + 2*L1*L2*g*m2*cos(x1) + 6*Fy*L1*L2*cos(x1)*cos(x1 + x2)^2 + 6*Fx*L1*L2*sin(x1)*cos(x1 + x2)...^2 - 6*Fy*L1*L2*cos(x1)*sin(x1 + x2)^2 - 6*Fx*L1*L2*sin(x1)*sin(x1 + x2)^2 + 3*L1*L2*g*m1*cos(x1)*cos(x1 + x2)^2 + 3*L1*L2*g*m1*cos(x1)*sin(x1 + x2)^2 + 6*L1*L2*g*m2*cos(x1)...    *sin(x1 + x2)^2 - L1*L2^2*dx1^2*m2*cos(x1)*sin(x1 + x2) + L1*L2^2*dx1^2*m2*sin(x1)*cos(x1 + x2) - L1*L2^2*dx2^2*m2*cos(x1)*sin(x1 + x2) + L1*L2^2*dx2^2*m2*sin(x1)*cos(x1 + x2)...  - 12*Fx*L1*L2*cos(x1)*cos(x1 + x2)*sin(x1 + x2) - 3*L1*L2^2*dx1^2*m2*cos(x1)*sin(x1 + x2)^3 + 3*L1*L2^2*dx1^2*m2*sin(x1)*cos(x1 + x2)^3 - 3*L1*L2^2*dx2^2*m2*cos(x1)*sin(x1 + x2)...^3 + 3*L1*L2^2*dx2^2*m2*sin(x1)*cos(x1 + x2)^3 + 12*Fy*L1*L2*sin(x1)*cos(x1 + x2)*sin(x1 + x2) + 6*L1^2*L2*dx1^2*m2*cos(x1)*sin(x1)*cos(x1 + x2)^2 - 6*L1^2*L2*dx1^2*m2*cos(x1)...  *sin(x1)*sin(x1 + x2)^2 - 6*L1*L2*g*m2*sin(x1)*cos(x1 + x2)*sin(x1 + x2) - 3*L1*L2^2*dx1^2*m2*cos(x1)*cos(x1 + x2)^2*sin(x1 + x2) - 6*L1^2*L2*dx1^2*m2*cos(x1)^2*cos(x1 + x2)...    *sin(x1 + x2) - 3*L1*L2^2*dx2^2*m2*cos(x1)*cos(x1 + x2)^2*sin(x1 + x2) - 2*L1*L2^2*dx1*dx2*m2*cos(x1)*sin(x1 + x2) + 2*L1*L2^2*dx1*dx2*m2*sin(x1)*cos(x1 + x2)...+ 3*L1*L2^2*dx1^2*m2*sin(x1)*cos(x1 + x2)*sin(x1 + x2)^2 + 6*L1^2*L2*dx1^2*m2*sin(x1)^2*cos(x1 + x2)*sin(x1 + x2) + 3*L1*L2^2*dx2^2*m2*sin(x1)*cos(x1 + x2)*sin(x1 + x2)...^2 - 6*L1*L2^2*dx1*dx2*m2*cos(x1)*sin(x1 + x2)^3 + 6*L1*L2^2*dx1*dx2*m2*sin(x1)*cos(x1 + x2)^3 - 6*L1*L2^2*dx1*dx2*m2*cos(x1)*cos(x1 + x2)^2*sin(x1 + x2)...+ 6*L1*L2^2*dx1*dx2*m2*sin(x1)*cos(x1 + x2)*sin(x1 + x2)^2))/(2*(L1^2*L2*m1 + 3*L1^2*L2*m2*cos(x1)^2 + 3*L1^2*L2*m2*sin(x1)^2 + 3*L1^2*L2*m1*cos(x1 + x2)...^2 + 3*L1^2*L2*m1*sin(x1 + x2)^2 + 9*L1^2*L2*m2*cos(x1)^2*sin(x1 + x2)^2 + 9*L1^2*L2*m2*sin(x1)^2*cos(x1 + x2)^2 - 18*L1^2*L2*m2*cos(x1)*sin(x1)*cos(x1 + x2)*sin(x1 + x2)));ddx2 = (3*(8*L1^2*T2*m1 - 2*L2^2*T1*m2 + 2*L2^2*T2*m2 + 24*L1^2*T2*m2*cos(x1)^2 + 24*L1^2*T2*m2*sin(x1)^2 - 6*L2^2*T1*m2*cos(x1 + x2)^2 + 6*L2^2*T2*m2*cos(x1 + x2)...^2 - 6*L2^2*T1*m2*sin(x1 + x2)^2 + 6*L2^2*T2*m2*sin(x1 + x2)^2 - 2*Fy*L1*L2^2*m2*cos(x1) + 2*Fx*L1*L2^2*m2*sin(x1) + 8*Fy*L1^2*L2*m1*cos(x1 + x2) - 8*Fx*L1^2*L2*m1*sin(x1 + x2)...+ 2*L1*L2^2*g*m2^2*cos(x1) - 4*L1^2*L2*g*m1*m2*cos(x1 + x2) - 3*L1*L2^3*dx1^2*m2^2*cos(x1)*sin(x1 + x2)^3 + 3*L1*L2^3*dx1^2*m2^2*sin(x1)*cos(x1 + x2)...^3 - 12*L1^3*L2*dx1^2*m2^2*cos(x1)^3*sin(x1 + x2) + 12*L1^3*L2*dx1^2*m2^2*sin(x1)^3*cos(x1 + x2) - 3*L1*L2^3*dx2^2*m2^2*cos(x1)*sin(x1 + x2)^3 + 3*L1*L2^3*dx2^2*m2^2*sin(x1)...   *cos(x1 + x2)^3 + 6*Fy*L1*L2^2*m2*cos(x1)*cos(x1 + x2)^2 + 12*Fy*L1^2*L2*m2*cos(x1)^2*cos(x1 + x2) + 6*Fx*L1*L2^2*m2*sin(x1)*cos(x1 + x2)^2 - 24*Fx*L1^2*L2*m2*cos(x1)...^2*sin(x1 + x2) - 6*Fy*L1*L2^2*m2*cos(x1)*sin(x1 + x2)^2 + 24*Fy*L1^2*L2*m2*sin(x1)^2*cos(x1 + x2) - 6*Fx*L1*L2^2*m2*sin(x1)*sin(x1 + x2)^2 - 12*Fx*L1^2*L2*m2*sin(x1)...^2*sin(x1 + x2) - 12*L1*L2*T1*m2*cos(x1)*cos(x1 + x2) + 24*L1*L2*T2*m2*cos(x1)*cos(x1 + x2) - 12*L1*L2*T1*m2*sin(x1)*sin(x1 + x2) + 24*L1*L2*T2*m2*sin(x1)*sin(x1 + x2)...- L1*L2^3*dx1^2*m2^2*cos(x1)*sin(x1 + x2) + L1*L2^3*dx1^2*m2^2*sin(x1)*cos(x1 + x2) - L1*L2^3*dx2^2*m2^2*cos(x1)*sin(x1 + x2) + L1*L2^3*dx2^2*m2^2*sin(x1)*cos(x1 + x2)...+ 6*L1*L2^2*g*m2^2*cos(x1)*sin(x1 + x2)^2 - 12*L1^2*L2*g*m2^2*sin(x1)^2*cos(x1 + x2) + L1*L2^2*g*m1*m2*cos(x1) - 6*L1*L2^2*g*m2^2*sin(x1)*cos(x1 + x2)*sin(x1 + x2)...- 12*L1^2*L2^2*dx1^2*m2^2*cos(x1)^2*cos(x1 + x2)*sin(x1 + x2) - 6*L1^2*L2^2*dx2^2*m2^2*cos(x1)^2*cos(x1 + x2)*sin(x1 + x2) - 6*L1*L2^3*dx1*dx2*m2^2*cos(x1)*sin(x1 + x2)...^3 + 6*L1*L2^3*dx1*dx2*m2^2*sin(x1)*cos(x1 + x2)^3 + 12*L1^2*L2^2*dx1^2*m2^2*sin(x1)^2*cos(x1 + x2)*sin(x1 + x2) + 6*L1^2*L2^2*dx2^2*m2^2*sin(x1)^2*cos(x1 + x2)*sin(x1 + x2)...+ 12*Fx*L1^2*L2*m2*cos(x1)*sin(x1)*cos(x1 + x2) - 12*Fy*L1^2*L2*m2*cos(x1)*sin(x1)*sin(x1 + x2) + 12*L1^3*L2*dx1^2*m2^2*cos(x1)^2*sin(x1)*cos(x1 + x2) - 12*Fx*L1*L2^2*m2*cos(x1)...*cos(x1 + x2)*sin(x1 + x2) - 12*L1^3*L2*dx1^2*m2^2*cos(x1)*sin(x1)^2*sin(x1 + x2) + 12*Fy*L1*L2^2*m2*sin(x1)*cos(x1 + x2)*sin(x1 + x2) - 3*L1*L2^3*dx1^2*m2^2*cos(x1)*cos(x1 + x2)...^2*sin(x1 + x2) - 3*L1*L2^3*dx2^2*m2^2*cos(x1)*cos(x1 + x2)^2*sin(x1 + x2) + 3*L1*L2^2*g*m1*m2*cos(x1)*cos(x1 + x2)^2 + 6*L1^2*L2*g*m1*m2*cos(x1)^2*cos(x1 + x2)...+ 12*L1^2*L2*g*m2^2*cos(x1)*sin(x1)*sin(x1 + x2) - 2*L1*L2^3*dx1*dx2*m2^2*cos(x1)*sin(x1 + x2) + 2*L1*L2^3*dx1*dx2*m2^2*sin(x1)*cos(x1 + x2) + 3*L1*L2^3*dx1^2*m2^2*sin(x1)...*cos(x1 + x2)*sin(x1 + x2)^2 + 3*L1*L2^3*dx2^2*m2^2*sin(x1)*cos(x1 + x2)*sin(x1 + x2)^2 - 4*L1^3*L2*dx1^2*m1*m2*cos(x1)*sin(x1 + x2) + 4*L1^3*L2*dx1^2*m1*m2*sin(x1)*cos(x1 + x2)...+ 12*L1^2*L2^2*dx1^2*m2^2*cos(x1)*sin(x1)*cos(x1 + x2)^2 + 6*L1^2*L2^2*dx2^2*m2^2*cos(x1)*sin(x1)*cos(x1 + x2)^2 + 3*L1*L2^2*g*m1*m2*cos(x1)*sin(x1 + x2)...^2 - 12*L1^2*L2^2*dx1^2*m2^2*cos(x1)*sin(x1)*sin(x1 + x2)^2 - 6*L1^2*L2^2*dx2^2*m2^2*cos(x1)*sin(x1)*sin(x1 + x2)^2 - 12*L1^2*L2^2*dx1*dx2*m2^2*cos(x1)*sin(x1)*sin(x1 + x2)...^2 - 12*L1^2*L2^2*dx1*dx2*m2^2*cos(x1)^2*cos(x1 + x2)*sin(x1 + x2) + 12*L1^2*L2^2*dx1*dx2*m2^2*sin(x1)^2*cos(x1 + x2)*sin(x1 + x2) - 6*L1*L2^3*dx1*dx2*m2^2*cos(x1)*cos(x1 + x2)...^2*sin(x1 + x2) + 6*L1*L2^3*dx1*dx2*m2^2*sin(x1)*cos(x1 + x2)*sin(x1 + x2)^2 + 6*L1^2*L2*g*m1*m2*cos(x1)*sin(x1)*sin(x1 + x2) + 12*L1^2*L2^2*dx1*dx2*m2^2*cos(x1)*sin(x1)...*cos(x1 + x2)^2))/(2*(3*L1^2*L2^2*m2^2*cos(x1)^2 + 3*L1^2*L2^2*m2^2*sin(x1)^2 + L1^2*L2^2*m1*m2 + 9*L1^2*L2^2*m2^2*cos(x1)^2*sin(x1 + x2)^2 + 9*L1^2*L2^2*m2^2*sin(x1)...^2*cos(x1 + x2)^2 + 3*L1^2*L2^2*m1*m2*cos(x1 + x2)^2 + 3*L1^2*L2^2*m1*m2*sin(x1 + x2)^2 - 18*L1^2*L2^2*m2^2*cos(x1)*sin(x1)*cos(x1 + x2)*sin(x1 + x2)));dxdt=[dx1;dx2;ddx1;ddx2];%返回状态向量的一阶导
end

拉格朗日方法回顾

系统广义坐标为两个关节角 θ 1 \theta_1 θ1 θ 2 \theta_2 θ2,拉格朗日公式为:
L = K − P . . . . . . ( 1 ) d d t ( ∂ L ∂ θ ˙ i ) − ∂ L ∂ θ i = Q i , i = 1 , 2...... ( 2 ) L=K-P......(1)\newline \frac{d}{dt}(\frac{\partial L}{\partial \dot \theta_i})-\frac{\partial L}{\partial \theta_i}=Q_i, i=1,2 ......(2) L=KP......(1)dtd(θ˙iL)θiL=Qi,i=1,2......(2)
其中L是拉格朗日函数,K是系统动能,P是系统势能, Q i Q_i Qi表示关节力矩 + 所有外力等效到该关节的力矩。将公式(2)求导展开,并考虑机器人末端受力,会得到如下形式:
M ( Θ ) Θ ¨ + C ( Θ , Θ ˙ ) = Q + J e T F e , where  Θ = ( θ 1 θ 2 ) . . . . . . ( 3 ) \bold M(\Theta)\ddot\Theta +\bold C(\Theta,\dot\Theta)=\bold Q+\bold J_{e}^\text T\bold F_{e}, \text{where } \Theta=\begin{pmatrix} \ \theta_1 \\ \ \theta_2 \end{pmatrix}......(3) M(Θ)Θ¨+C(Θ,Θ˙)=Q+JeTFe,where Θ=( θ1 θ2)......(3)
其中M是加速度矩阵,C是低阶项,Q表示关节自身力矩,如关节阻尼力、电机驱动力等,Fe是末端受到的外力,Je是机器人末端雅可比矩阵,可以把笛卡尔坐标系下定义的外力映射到关节空间,化为等效的关节力矩。
实际上这个“末端”也可以是机器人身上任意一点,把每个受力点对应的 J T F J^\text{T}F JTF项直接加在公式(3)右边就行了。本文案例中只有一个全局坐标系,Fe也是定义在这个坐标系下。

求解符号表达式

首先用syms语句定义结构常量和广义坐标,需要把广义坐标定义为关于时间的函数,方便后面求导:

syms m1 m2 L1 L2 g % 结构常量
syms t x1(t) x2(t) % 将系统的广义坐标定义为时间函数

定义连杆转动惯量,后面要用:

IA=1/3*m1*L1^2; % 连杆1惯量
ID=1/12*m2*L2^2;% 连杆2惯量

定义连杆2中心的位置公式,用diff函数对时间求导得到速度:

pD=[L1*cos(x1)+1/2*L2*cos(x1+x2); %连杆2质心位置L1*sin(x1)+1/2*L2*sin(x1+x2)];
vD=diff(pD,t); %对时间求导得到速度

编辑动能和势能,得到拉格朗日函数:

K=1/2*IA*diff(x1,t)^2+1/2*ID*(diff(x1,t)+diff(x2,t))^2+1/2*m2*sum(vD.^2); %系统动能
P=m1*g*L1/2*sin(x1)+m2*g*(L1*sin(x1)+L2/2*sin(x1+x2)); % 系统势能
L=K-P; %拉格朗日函数

按公式2展开:

temp=diff(diff(L,diff(x1,t)),t)-diff(L,x1); % 展开拉格朗日函数,得到二阶微分式

matlab生成的表达式中,会用diff(x1,t)表示速度,用diff(x1,t,t)表示加速度,为了便于后面代入数值,需要定义新的符号变量,表示关节角的速度和加速度,然后替换到公式里:

syms dx1 dx2 ddx1 ddx2
temp=subs(temp,diff(x1,t,t),ddx1); % 用新定义的符号代替广义坐标的一二阶导数,简化公式表达
temp=subs(temp,diff(x2,t,t),ddx2);
temp=subs(temp,diff(x1,t),dx1);
temp=subs(temp,diff(x2,t),dx2);

按关节角的加速度项、平方项、交叉项,对微分式进行合并同类项:

diff1=collect(temp,[ddx1,ddx2,dx1^2,dx2^2,dx1*dx2,dx1,dx2]); % 合并同类项,整理成便于阅读的形式

这时候用pretty(diff1)指令,可以看到微分式的内容:
在这里插入图片描述
matlab不会把sin^2+cos^2替换为1,不会合并因子,所以比较冗长,但也够用了。
对第二个关节角做同样操作,得到第二行微分式:

temp=diff(diff(L,diff(x2,t)),t)-diff(L,x2); % 对第二个广义坐标θ2做同样操作
temp=subs(temp,diff(x1,t,t),ddx1);
temp=subs(temp,diff(x2,t,t),ddx2);
temp=subs(temp,diff(x1,t),dx1);
temp=subs(temp,diff(x2,t),dx2);
diff2=collect(temp,[ddx1,ddx2,dx1^2,dx2^2,dx1*dx2,dx1,dx2]);

为了加入外力,需要求末端雅可比矩阵,这里有个技巧,如果直接用刚才定义的x1(t), x2(t)会导致 jacobian() 返回一个时间函数,不能参与矩阵运算了,因此需要先定义普通符号变量x1v, x2v,求出雅可比后再用x1(t)和x2(t)替换。

syms x1v x2v %求雅可比矩阵不能用时间函数x1(t)和x2(t),因此先定义临时变量,求雅可比后再替换为x1和x2
p_end = [L1*cos(x1v)+L2*cos(x1v+x2v);L1*sin(x1v)+L2*sin(x1v+x2v)]; % 末端位置
J_end = jacobian(p_end,[x1v;x2v]); % 末端雅可比矩阵
J_end = subs(J_end,{x1v,x2v},{x1,x2}); %替换时间变量

定义符号变量表示关节力矩和外力,然后构建微分方程组,即为机器人动力学模型:

syms T1 T2 Fx Fy
eqn = [diff1;diff2] == [T1;T2] + J_end.'*[Fx;Fy]; %构建动力学矩阵方程式

这时候标准做法是通过对比公式(3)得到矩阵M,C的表达式,在后面数值求解函数中带入数据得到M,C的数值,再解出 Θ ¨ \ddot\Theta Θ¨
但是对于自由度较少的系统,可以直接让matlab解出 Θ ¨ \ddot\Theta Θ¨的解析式,更加省事:

sol = solve(eqn,[ddx1;ddx2]); %求出二阶项[ddx1;ddx2]的解析解
fprintf("ddx1=");%输出求解结果
disp(sol.ddx1);
fprintf("ddx2=");
disp(sol.ddx2);

会得到很长的代数式,为了方面后面使用,在GPT帮助下生成了一段python代码,它先把表达式中所有"(t)"去掉,再把公式拆分为多行,python代码如下:

# 把很长的Matlab符号表达式拆分为多行def format_matlab_expression(expression, line_length=180):  # 定义运算符  operators = ['+', '-', '*', '/', '(', ')']  # 初始化变量  formatted_expression = ""  current_line = ""  # 遍历表达式中的每个字符  for char in expression:  current_line += char  # 检查当前行的长度  if len(current_line) >= line_length:  # 找到最近的运算符  for op in reversed(operators):  if op in current_line:  # 找到运算符的位置  op_index = current_line.rfind(op)  # 在运算符前换行  formatted_expression += current_line[:op_index + 1] + "...\n"  # 更新当前行  current_line = current_line[op_index + 1:].lstrip()  # 去掉运算符前的空格  break  # 添加最后一行(如果有剩余内容)  if current_line:  formatted_expression += current_linereturn formatted_expression  # 示例 MATLAB 表达式  
matlab_expression = "-(3*(2*L2*T2 - 2*L2*T1 - 6*L2*T1*sin(x1(t) + x2(t))^2 + 6*L2*T2*sin(x1(t) + x2(t))^2 - 6*L2*T1*cos(x1(t) + x2(t))^2 + 6*L2*T2*cos(x1(t) + x2(t))^2 + 12*L1*T2*sin(x1(t))*sin(x1(t) + x2(t)) - 2*Fy*L1*L2*cos(x1(t)) + 2*Fx*L1*L2*sin(x1(t)) + 12*L1*T2*cos(x1(t))*cos(x1(t) + x2(t)) + L1*L2*g*m1*cos(x1(t)) + 2*L1*L2*g*m2*cos(x1(t)) + 6*Fy*L1*L2*cos(x1(t))*cos(x1(t) + x2(t))^2 + 6*Fx*L1*L2*sin(x1(t))*cos(x1(t) + x2(t))^2 - 6*Fy*L1*L2*cos(x1(t))*sin(x1(t) + x2(t))^2 - 6*Fx*L1*L2*sin(x1(t))*sin(x1(t) + x2(t))^2 + 3*L1*L2*g*m1*cos(x1(t))*cos(x1(t) + x2(t))^2 + 3*L1*L2*g*m1*cos(x1(t))*sin(x1(t) + x2(t))^2 + 6*L1*L2*g*m2*cos(x1(t))*sin(x1(t) + x2(t))^2 - L1*L2^2*dx1^2*m2*cos(x1(t))*sin(x1(t) + x2(t)) + L1*L2^2*dx1^2*m2*sin(x1(t))*cos(x1(t) + x2(t)) - L1*L2^2*dx2^2*m2*cos(x1(t))*sin(x1(t) + x2(t)) + L1*L2^2*dx2^2*m2*sin(x1(t))*cos(x1(t) + x2(t)) - 12*Fx*L1*L2*cos(x1(t))*cos(x1(t) + x2(t))*sin(x1(t) + x2(t)) - 3*L1*L2^2*dx1^2*m2*cos(x1(t))*sin(x1(t) + x2(t))^3 + 3*L1*L2^2*dx1^2*m2*sin(x1(t))*cos(x1(t) + x2(t))^3 - 3*L1*L2^2*dx2^2*m2*cos(x1(t))*sin(x1(t) + x2(t))^3 + 3*L1*L2^2*dx2^2*m2*sin(x1(t))*cos(x1(t) + x2(t))^3 + 12*Fy*L1*L2*sin(x1(t))*cos(x1(t) + x2(t))*sin(x1(t) + x2(t)) + 6*L1^2*L2*dx1^2*m2*cos(x1(t))*sin(x1(t))*cos(x1(t) + x2(t))^2 - 6*L1^2*L2*dx1^2*m2*cos(x1(t))*sin(x1(t))*sin(x1(t) + x2(t))^2 - 6*L1*L2*g*m2*sin(x1(t))*cos(x1(t) + x2(t))*sin(x1(t) + x2(t)) - 3*L1*L2^2*dx1^2*m2*cos(x1(t))*cos(x1(t) + x2(t))^2*sin(x1(t) + x2(t)) - 6*L1^2*L2*dx1^2*m2*cos(x1(t))^2*cos(x1(t) + x2(t))*sin(x1(t) + x2(t)) - 3*L1*L2^2*dx2^2*m2*cos(x1(t))*cos(x1(t) + x2(t))^2*sin(x1(t) + x2(t)) - 2*L1*L2^2*dx1*dx2*m2*cos(x1(t))*sin(x1(t) + x2(t)) + 2*L1*L2^2*dx1*dx2*m2*sin(x1(t))*cos(x1(t) + x2(t)) + 3*L1*L2^2*dx1^2*m2*sin(x1(t))*cos(x1(t) + x2(t))*sin(x1(t) + x2(t))^2 + 6*L1^2*L2*dx1^2*m2*sin(x1(t))^2*cos(x1(t) + x2(t))*sin(x1(t) + x2(t)) + 3*L1*L2^2*dx2^2*m2*sin(x1(t))*cos(x1(t) + x2(t))*sin(x1(t) + x2(t))^2 - 6*L1*L2^2*dx1*dx2*m2*cos(x1(t))*sin(x1(t) + x2(t))^3 + 6*L1*L2^2*dx1*dx2*m2*sin(x1(t))*cos(x1(t) + x2(t))^3 - 6*L1*L2^2*dx1*dx2*m2*cos(x1(t))*cos(x1(t) + x2(t))^2*sin(x1(t) + x2(t)) + 6*L1*L2^2*dx1*dx2*m2*sin(x1(t))*cos(x1(t) + x2(t))*sin(x1(t) + x2(t))^2))/(2*(L1^2*L2*m1 + 3*L1^2*L2*m2*cos(x1(t))^2 + 3*L1^2*L2*m2*sin(x1(t))^2 + 3*L1^2*L2*m1*cos(x1(t) + x2(t))^2 + 3*L1^2*L2*m1*sin(x1(t) + x2(t))^2 + 9*L1^2*L2*m2*cos(x1(t))^2*sin(x1(t) + x2(t))^2 + 9*L1^2*L2*m2*sin(x1(t))^2*cos(x1(t) + x2(t))^2 - 18*L1^2*L2*m2*cos(x1(t))*sin(x1(t))*cos(x1(t) + x2(t))*sin(x1(t) + x2(t))))"
matlab_expression = matlab_expression.replace("(t)", "") # 删掉所有(t)
# 格式化表达式  
formatted = format_matlab_expression(matlab_expression)  # 打印结果  
print(formatted)

把python脚本输出的表达式放在最后的自定义函数odefun中,用来数值求解。

数值求解

网上已经有很多ode45函数的用法了。在这部分是先把公式中的参数定义为全局变量并赋值,以便被脚本代码和odefun函数共享:

global m1 m2 L1 L2 g d1 d2 Fx Fy
m1=1; m2=1; L1=0.5; L2=0.5; g=9.81;d1=0.8;d2=d1;Fx=0;Fy=0;%设置机器人参数,关节阻尼和外力

然后设置好时间向量和初始状态,执行ode45函数:

tspan = 0:0.01:5; % 时间范围
initial_state = [0;0;0;0]; % 初始状态
[t,y] = ode45(@odefun,tspan,initial_state); % 求解

为了用ode45求解,需要把公式(3)化为一阶系统,新的状态向量是:
x = [ θ 1 θ 2 θ ˙ 1 θ ˙ 2 ] \bold{x}= \begin{bmatrix} \ \theta_1 \\ \ \theta_2 \\ \ \dot\theta_1 \\ \ \dot\theta_2 \end{bmatrix} x=  θ1 θ2 θ˙1 θ˙2
一阶系统方程为
x ˙ = [ θ ˙ 1 θ ˙ 2 θ ¨ 1 θ ¨ 2 ] = [ x ( 3 ) x ( 4 ) θ ¨ 1 θ ¨ 2 ] \bold{\dot x}= \begin{bmatrix} \ \dot\theta_1 \\ \ \dot\theta_2 \\ \ \ddot\theta_1 \\ \ \ddot\theta_2 \end{bmatrix}= \begin{bmatrix} \ \bold{x}(3) \\ \ \bold{x}(4) \\ \ \ddot\theta_1 \\ \ \ddot\theta_2 \end{bmatrix} x˙=  θ˙1 θ˙2 θ¨1 θ¨2 =  x(3) x(4) θ¨1 θ¨2
其中 θ ¨ 1 \ddot\theta_1 θ¨1 θ ¨ 2 \ddot\theta_2 θ¨2就来自前面求解得到的加速度项解析式。

此外在odefun中需要指定关节力矩和外力的数值,示例代码中是给两个关节添加了阻尼力矩,给末端添加了一个恒定的外力。实际项目中可以根据需要,设置变化的力。odefun如下:

function dxdt=odefun(t,x)global m1 m2 L1 L2 g d1 d2 Fx Fyx1=x(1);x2=x(2);dx1=x(3);dx2=x(4); %状态向量即为θ1,θ及其一阶导数T1 = -d1*dx1;T2 = -d2*dx2;% 根据符号工具的求解结果,得到θ1,θ2二阶导的表达式如下,需要删掉符号表达式中所有的(t)以免报错ddx1 = (省略)ddx2 = (省略)dxdt=[dx1;dx2;ddx1;ddx2];%返回状态向量的一阶导
end

ode45返回的y的两列数据即为广义坐标θ1,θ2随时间变化的数值,绘制成曲线如下:
在这里插入图片描述

相关文章:

用MATLAB符号工具建立机器人的动力学模型

目录 介绍代码功能演示拉格朗日方法回顾求解符号表达式数值求解 介绍 开发机器人过程中经常需要用牛顿-拉格朗日法建立机器人的动力学模型,表示为二阶微分方程组。本文以一个二杆系统为例,介绍如何用MATLAB符号工具得到微分方程表达式,只需要…...

SQL优化与性能——数据库设计优化

数据库设计优化是提高数据库性能、确保数据一致性和支持业务增长的关键环节。无论是大型企业应用还是小型项目,合理的数据库设计都能够显著提升系统性能、减少冗余数据、优化查询响应时间,并降低维护成本。本章将深入探讨数据库设计中的几个关键技术要点…...

FPGA存在的意义:为什么adc连续采样需要fpga来做,而不会直接用iic来实现

FPGA存在的意义:为什么adc连续采样需要fpga来做,而不会直接用iic来实现 原因ADS111x连续采样实现连续采样功能说明iic读取adc的数据速率 VS adc连续采样的速率adc连续采样的速率iic读取adc的数据速率结论分析 FPGA读取adc数据问题一:读取adc数…...

我们来学mysql -- 事务之概念(原理篇)

事务的概念 题记一个例子一致性隔离性原子性持久性 题记 在漫长的编程岁月中,存在一如既往地贯穿着工作,面试的概念这类知识点,事不关己当然高高挂起,精准踩坑时那心情也的却是日了🐶请原谅我的粗俗,遇到B…...

基于特征子空间的高维异常检测:一种高效且可解释的方法

本文将重点探讨一种替代传统单一检测器的方法:不是采用单一检测器分析数据集的所有特征,而是构建多个专注于特征子集(即子空间)的检测器系统。 在表格数据的异常检测实践中,我们的目标是识别数据中最为异常的记录,这种异常性可以…...

看不见的彼方:交换空间——小菜一碟

有个蓝色的链接,先去看看两年前的题目的write up (https://github.com/USTC-Hackergame/hackergame2022-writeups/blob/master/official/%E7%9C%8B%E4%B8%8D%E8%A7%81%E7%9A%84%E5%BD%BC%E6%96%B9/README.md) 从别人的write up中了解到&…...

YOLO模型训练后的best.pt和last.pt区别

在选择YOLO模型训练后的权重文件best.pt和last.pt时,主要取决于具体的应用场景‌:‌12 ‌best.pt‌:这个文件保存的是在训练过程中表现最好的模型权重。通常用于推理和部署阶段,因为它包含了在验证集上表现最好的模型权重&#x…...

Pareidoscope - 语言结构关联工具

文章目录 关于 Pareidoscope安装使用方法输入格式语料库查询 将语料库转换为 SQLite3 数据库两种语言结构之间的关联简单词素分析关联共现和伴随词素分析相关的更大结构可视化关联结构 关于 Pareidoscope Pareidoscope 是一组 用于确定任意语言结构之间 关联的工具&#xff0c…...

GPT(Generative Pre-trained Transformer) 和 Transformer的比较

GPT(Generative Pre-trained Transformer) 和 Transformer 的比较 flyfish 1. Transformer 是一种模型架构 Transformer 是一种通用的神经网络架构,由 Vaswani 等人在论文 “Attention Is All You Need”(2017)中提…...

软件无线电(SDR)的架构及相关术语

今天简要介绍实现无线电系统调制和解调的主要方法,这在软件定义无线电(SDR)的背景下很重要。 外差和超外差 无线电发射机有两种主要架构——一种是从基带频率直接调制到射频频率(称为外差),而第二种超外差是通过两个调制阶段来实…...

Python将Excel文件转换为JSON文件

工作过程中,需要从 Excel 文件中读取数据,然后交给 Python 程序处理数据,中间需要把 Excel 文件读取出来转为 json 格式,再进行下一步数据处理。 这里我们使用pandas库,这是一个强大的数据分析工具,能够方便地读取和处理各种数据格式。需要注意的是还需要引入openpyxl库,…...

排序算法之选择排序篇

思想: 每次从未排序的部分找出最小的元素,将其放到已排序部分的末尾 从数据结构中找到最小值,放到第一位,放到最前面,之后再从剩下的元素中找出第二小的值放到第二位,以此类推。 实现思路: 遍…...

sizeof和strlen区分,(好多例子)

sizeof算字节大小 带\0 strlen算字符串长度 \0之前...

A050-基于spring boot物流管理系统设计与实现

🙊作者简介:在校研究生,拥有计算机专业的研究生开发团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹 赠送计算机毕业设计600…...

[自然语言处理] NLP-RNN及其变体-干货

一、认识RNN模型 1 什么是RNN模型 RNN(Recurrent Neural Network), 中文称作循环神经网络, 它一般以序列数据为输入, 通过网络内部的结构设计有效捕捉序列之间的关系特征, 一般也是以序列形式进行输出. 一般单层神经网络结构: RNN单层网络结构: 以时间步对RNN进行展开后的单层…...

Elasticsearch ILM 索引生命周期管理讲解与实战

ES ILM 索引生命周期管理讲解与实战 Elasticsearch ILM索引生命周期管理:深度解析与实战演练1. 引言1.1 背景介绍1.2 研究意义2. ILM核心概念2.1 ILM的四个阶段2.1.1 Hot阶段2.1.2 Warm阶段2.1.3 Cold阶段2.1.4 Delete阶段3. ILM实战指南3.1 定义ILM策略3.1.1 创建ILM策略3.1.…...

重塑视频新语言,让每一帧都焕发新生——Video-Retalking,开启数字人沉浸式交流新纪元!

模型简介 Video-Retalking 模型是一种基于深度学习的视频再谈话技术,它通过分析视频中的音频和图像信息,实现视频角色口型、表情乃至肢体动作的精准控制与合成。这一技术的实现依赖于强大的技术架构和核心算法,特别是生成对抗网络&#xff0…...

联想Lenovo SR650服务器硬件监控指标解读

随着企业IT架构的复杂性和业务需求的增长,服务器的稳定运行变得至关重要。联想Lenovo SR650服务器以其高性能和稳定性,在各类应用场景中发挥着关键作用。为了保障服务器的稳定运行,监控易作为一款专业的IT基础设施监控软件,为联想…...

二十一、QT C++

1.1QT介绍 1.1.1 QT简介 Qt 是一个跨平台的应用程序和用户界面框架,用于开发图形用户界面(GUI)应用程序以及命令行工具。它最初由挪威的 Trolltech (奇趣科技)公司开发,现在由 Qt Company 维护&#xff…...

微服务上下线动态感知实现的技术解析

序言 随着微服务架构的广泛应用,服务的动态管理和监控变得尤为重要。在微服务架构中,服务的上下线是一个常见的操作,如何实时感知这些变化,确保系统的稳定性和可靠性,成为了一个关键技术挑战。本文将深入探讨微服务上…...

指针与引用错题汇总

int *p[3]; // 定义一个包含 3 个指向 int 的指针的数组int a 10, b 20, c 30; p[0] &a; // p[0] 指向 a p[1] &b; // p[1] 指向 b p[2] &c; // p[2] 指向 c // 访问指针所指向的值 printf("%d %d %d\n", *p[0], *p[1], *p[2]); // 输出: 10 20 30…...

短视频账号矩阵系统源码--独立saas技术部署

短视频矩阵系统通过多账号在多个平台上发布内容,形成一种网络效应。对于抖音平台而言,技术公司需具备特定接口权限方能进行开发工作。然而,视频发布及企业号评论与回复等功能的接口权限往往难以获取。通过构建抖音账号矩阵,利用多…...

leaflet 介绍

目录 一、leaflet 官网 二、leaflet 在项目中的引用 1、在head中引入 2、在main.js中引入 leaflet目前版本是1.9.4,在leaflet插件库中,很多插件因长时间未更新,适配的是1.7版本的,在选用插件的时候要查看版本适配。 leaflet详…...

总结贴:Servlet过滤器、MVC拦截器

一:Servlet过滤器 1.1解析 Filter 即为过滤,用于请求到达Servlet之前(Request),以及再Servlet方法执行完之后返回客户端进行后处理(HttpServletResponse)。简单说就是对请求进行预处理,对响应进行后处理 在请求到达Servlet之前,可以经过多个Filt…...

鸿蒙开发:自定义一个任意位置弹出的Dialog

前言 鸿蒙开发中,一直有个问题困扰着自己,想必也困扰着大多数开发者,那就是,系统提供的dialog自定义弹窗,无法实现在任意位置进行弹出,仅限于CustomDialog和Component struct的成员变量,这就导致…...

在Windows下编译支持https的wsdl2h

下载源码 在官网下载源码 安装Openssl 下载OpenSSL并安装,安装完成后需要将OpenSSL的路径添加到环境变量中 配置VS 1、打开工程 2、因为前面安装的OpenSLL是64位的,因此需要创建一个X64的配置 打开配置管理器,然后选择新建&#xff0…...

PHP和GD库如何根据像素绘制图形

使用PHP和GD库,你可以根据像素绘制各种图形,比如点、线、矩形、圆形等。GD库是PHP的一个扩展,它提供了一系列用于创建和处理图像的函数。以下是一个简单的示例,展示如何使用GD库根据像素绘制图形。 安装GD库 首先,确…...

webpack(react)基本构建

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 Webpack 是一个现代 JavaScript 应用程序的静态模块打包工具。它的主要功能是将各种资源(如 JavaScript、CSS、图片等)视为模块,并将它们打包成一个或多个输出文件,以便…...

《Opencv》基础操作<1>

目录 一、Opencv简介 主要特点: 应用领域: 二、基础操作 1、模块导入 2、图片的读取和显示 (1)、读取 (2)、显示 3、 图片的保存 4、获取图像的基本属性 5、图像转灰度图 6、图像的截取 7、图…...

Oracle 11g R2 RAC 到单实例 Data Guard 搭建(RMAN备份方式)

一、配置方案 环境说明 角色主库主库备库主机名rac01rac02racdg公网IP10.10.10.14110.10.10.14310.10.10.191VIP10.10.10.14210.10.10.144-SCAN10.10.10.14010.10.10.140-INSTANCE_NAMEorcl1orcl2orclDB_NAMEorclorclorclSERVICE_NAMEorclorclorclDB_UNIQUE_NAMEorclorclorcl…...