Введение
Данная статья являет собой логическое продолжение темы, начатой в предыдущей публикации . Как и было обещано в комментариях, рассмотрим применимость метода избыточных координат к динамическому анализу механических систем движущихся под действием сил сухого кулоновского трения. В качестве иллюстративного примера решим следующую задачу

Тонкий однородный стержень массы m = 2 кг, длины AB = 2l = 1 м в точке A шарнирно прикрепле�� к невесомому ползуну, перемещающемуся в горизонтальных шероховатых направляющих. В начальный момент времени стержень расположен вертикально, затем его отклоняют от вертикали на ничтожно малый угол и отпускают без начальной скорости. Необходимо составить уравнения движения данной механической системы и найти закон её движения. Коэффициент трения между ползуном и направляющими равен f = 0,1.
Прежде чем приступить к решению задачи предлагаемым автором методом, рассмотрим немножко элементарной теории, касающейся сухого трения.
1. Что может быть «проще» трения?
Нет более страшного наказания для механика, чем сила трения. Появляясь в задаче, эта сила сразу делает её существенно нелинейной, ибо ведет себя достаточно интересным образом.
Рассмотрим довольно простой пример. Пусть на шероховатой поверхности покоится горизонтальный брусок.

Пусть в начале к нему не прилагают никаких сил (кроме силы тяжести и нормальной реакции). В этом случае и сила трения между бруском и плоскостью будет равна нулю.
Теперь приложим к бруску небольшую горизонтальную силу. Брусок не сдвинется с места, так как в ответ на наше воздействие со стороны поверхности на него станет действовать сила трения, которая будет удовлетворять условию

Будем постепенно увеличивать силу
и, согласно (1) расти будет и сила трения, которая в этом случае называется силой трения покоя. Так будет продолжаться до тех пор, пока сила трения покоя не достигнет величины 
называемой предельной величиной силы трения покоя. Здесь f — коэффициент сухого трения между бруском и плоскостью; N — нормальная реакция со стороны плоскости. После этого сила трения расти перестанет, а при дальнейшем увеличении горизонтальной силы начнется скольжение бруска. Сила трения перейдет в силу трения скольжения, равную
где
— скорость бруска.Пример весьма тривиальный, однако он раскрывает суть поведения силы сухого трения. Таким образом, получаем следующий алгоритм расчета силы трения:
Если точка, где приложена сила трения неподвижна:
- Расчитываем силу трения покоя и нормальную реакцию
- Проверяем условие

при нарушении которого принимаем силу трения равной предельной силе трения покоя
Если точка приложения силы трения движется:
- Вычисляем нормальную реакцию
- Вычисляем силу трения скольжения, согласно выражению (2)
2. Моделирование движения системы с трением
Теперь решим нашу задачу. Рассматриваемая нами система имеет две степени свободы, однако из-за необходимости определения нормальной реакции расширяем число степеней свободы до трех и получаем следующую расчетную схему

Здесь в качестве обобщенных координат берем вектор
![\vec{q} = \left[x(t), y(t), \varphi(t) \right ]^T \quad \quad (3)](https://habrastorage.org/getpro/habr/post_images/15f/089/abf/15f089abf977cdc06e900ee132f2c5f2.gif)
где x,y — координаты точки A;
— угол наклона стержня к вертикали. Вооружаемся Maple'ом# Чистим память restart; # Подключаем линейную алгебру with(LinearAlgebra): # Подключаем лагранжеву механику read `/home/maisvendoo/work/maplelibs/mechanics/lagrange.m`:
Определяемся с кинематикой системы
# Обобщенные координаты системы q := [x(t), y(t), phi(t)]: # Координаты точки A xA := q[1]: yA := q[2]: # Координаты центра масс стержня xC := q[1] - L*sin(q[3]): yC := q[2] + L*cos(q[3]): # Радиус-векторы точек A и C rA := Vector([xA, yA]): rC := Vector([xC, yC]): # Вычисляем скорость ценра масс стержня VectorCalculus[BasisFormat](false): vC := VectorCalculus[diff](rC, t):
Вычисляем её кинетическую энергию
# Момент инерции стержня относительно центра масс J := m*(2*L)^2/12: # Кинетическая энергия системы T := simplify(J*diff(q[3], t)^2/2 + m*DotProduct(vC, vC, conjugate=false)/2);
Maple выдает такой результат

Довольно громоздко, но «ковырять» нам не руками на листочке, поэтому двигаемся дальше. Задаем векторы и точки приложения сил
# Векторы сил, приоложенных к системе Mg := Vector([0, -m*g]): # Сила тяжести F_A := Vector([-F, 0]): # Сила трения N_A := Vector([0, N]): # Нормальная реакция # Формируем массив сил Fk := [Mg, F_A, N_A]: # Формируем массив точек приложения сил rk := [rC, rA, rA]:
Получаем уравнения движения системы в форме Лагранжа 2 рода
# Составляем уравнения Лагранжа 2 рода EQs := LagrangeEQs(T, q, rk, Fk):
Получаем трёх «крокодилов»



Эти уравнения пришлось вбить в статью руками, ибо «копипаста» LaTeX-вывода Maple приводит к неприглядному виду результата. Но даже так видно — уравнения сложны и с учетом того что F — это сила трения, аналитически не интегрируемы.
Теперь введем уравнения связей. Во-первых, ползун движется по горизонтальным направляющим, поэтому

Кроме того, в том случае когда ползун неподвижен, а сила трения покоя не превысила предельного значения, включается ещё одна связь

где
— некоторая горизонтальная координата ползуна. Теперь преобразуем систему (4) — (6) с учетом уравнений (7) и (8) и найдем силу трения покоя и нормальную реакцию# Уравнения связей link_eq1 := q[1] = A: # Ползун неподвижен, если сила трения - сила трения покоя link_eq2 := q[2] = 0: # Ползун движется вдоль оси X Link_EQs := {link_eq1, link_eq2}: # Трансформируем систему для поиска силы трения покоя и нормальной реакции Reduced_EQs := ReduceSystem(EQs, Link_EQs, q): solv_reduced := SolveAccelsReacts(Reduced_EQs, [q[3]], [F, N]): # Выделяем силы из решения for i from 1 to numelems(solv_reduced) do if has(solv_reduced[i], F) then F1 := rhs(solv_reduced[i]); end if; if has(solv_reduced[i], N) then N1 := rhs(solv_reduced[i]); end if; end do:
Тут приведу результат непосредственно выданный Maple


Если посмотреть на полученные выражения, то они вполне соответствуют логике процесса. Теперь получим выражение для расчета нормальной реакции в случае, когда ползун скользит по направляющим, учитывая, что в этом случае сила трения будет определятся выражением

(минус уже имеется в уравнениях движения)
# Трансформируем систему для поиска нормальной реакции при скольжении ползуна Slade_EQs := ReduceSystem(EQs, {link_eq2}, q): # Силу трения принимаем равной силе трения скольжения for i from 1 to numelems(Slade_EQs) do Slade_EQs[i] := subs(F = f*N*signum(diff(q[1], t)), Slade_EQs[i]); end do: solv_slade := SolveAccelsReacts(Slade_EQs, [q[1], q[3]], [N]): # Выделяем нормальную реакцию из решения for i from 1 to numelems(solv_reduced) do if has(solv_slade[i], N) then N2 := rhs(solv_slade[i]); end if; end do:
М-да, «крокодилище» вышел ещё тот, особенно с учетом что Maple таки довольно избыточно генерирует LaTeX-код

Все необходимые нам выражения получены, теперь можно переходить к моделированию. В отличие от задачи с маятником, о которой я уже писал, тут мы честно трансформируем наши уравнения Maple-средствами для вида пригодного к численному решению. Прежде всего решим уравнения (4) — (6) относительно обобщенных ускорений
# Разрешаем основную систему уравнений относительно обобщенных ускорений Main_EQs := SolveAccelsReacts(EQs, q, []): # Число обобщенных координат s := numelems(q):
Результат уже не буду приводить — он тоже довольно громоздкий. Перейдем к фазовым координатам
# Воводим фазовые координаты # Y[1] -> x(t) # Y[2] -> y(t) # Y[3] -> phi(t) # Y[4] -> vx(t) - горизонтальная проекция скорости ползуна # Y[5] -> vy(t) - вертикальная проекция скорости ползуна # Y[6] -> omega(t) - угловая скорость стержня # Переходим к фазовым координатам в выражениях для реакций и трения покоя for i from 1 to s do N2 := subs(diff(q[i], t) = y[i+s], N2); N2 := subs(q[i] = y[i], N2); N1 := subs(diff(q[i], t) = y[i+s], N1); N1 := subs(q[i] = y[i], N1); F1 := subs(diff(q[i], t) = y[i+s], F1); F1 := subs(q[i] = y[i], F1); end do: # Переходим к фазовым координатам в уравнения движения for i from 1 to s do for j from 1 to s do eq := Main_EQs[j]; if has(eq, diff(diff(q[i], t), t)) then accel[i] := rhs(eq); end if; end do; for j from 1 to s do accel[i] := subs(diff(q[j], t) = y[j+s], accel[i]); accel[i] := subs(q[j] = y[j], accel[i]); end do: end do:
Формируем функции вычисления необходимых нам сил
# Вычисление нормальной реакции при покоящемся ползуне GetN1 := proc(mass, length, grav_accel, fric_coeff, Y) local react := subs(m = mass, L = length, g = grav_accel, f = fric_coeff, N1); local i; for i from 1 to numelems(Y) do react := subs(y[i] = Y[i], react); end do: return evalf(react); end proc: # Вычисление нормальной реакции при скользящем ползуне GetN2 := proc(mass, length, grav_accel, fric_coeff, Y) local react := subs(m = mass, L = length, g = grav_accel, f = fric_coeff, N2); local i; for i from 1 to numelems(Y) do react := subs(y[i] = Y[i], react); end do: return evalf(react); end proc: # Вычисление силы трения покоя GetF1 := proc(mass, length, grav_accel, fric_coeff, Y) local react := subs(m = mass, L = length, g = grav_accel, f = fric_coeff, F1); local i; for i from 1 to numelems(Y) do react := subs(y[i] = Y[i], react); end do: return evalf(react); end proc:
Приведенный код хоть и объемный, но довольно прост — выполняется подстановка численных параметров в соответствующие выражения и их вычисление. Такую же функцию формируем и для вычисления обобщенных ускорений
# Вычисление вектора обобщенных ускорений GetAccel := proc(mass, length, grav_accel, fric_force, normal_react, Y) local acc; local i, j; for i from 1 to numelems(Y)/2 do acc[i] := subs(m = mass, L = length, g = grav_accel, F = fric_force, N = normal_react, accel[i]); end do: for i from 1 to numelems(Y)/2 do for j from 1 to numelems(Y) do acc[i] := evalf(subs(y[j] = Y[j], acc[i])); end do: end do: return [seq(acc[i], i=1..numelems(Y)/2)]; end proc:
Задаем параметры, данные нам в условии задачи
# Задаем параметры системы m1 := 2.0; L1 := 0.5; f1 := 0.1; g1 := 9.81;
Время задать основную логику модели, которая определяет расчет силы трения. При этом задаемся погрешностью скорости ползуна, при которой будем считать её равной нулю.
# Вычисление силы трения и нормальной реакции GetFricNormal := proc(mass, length, grav_accel, fric_coeff, Y) local F1, N1; # Сила трения и нормальная реакция local eps_v := 1e-6; # Точность нуля скорости ползуна # Если ползун неподвижен if abs(Y[4]) < eps_v then # Вычисляем силу трения покоя и нормальную реакцию F1 := GetF1(mass, length, grav_accel, fric_coeff, Y); N1 := GetN1(mass, length, grav_accel, fric_coeff, Y); # Если трение покоя превышает максимально возможное значение, # то сила трения равна силе трения скольжения if abs(F1) > fric_coeff*abs(N1) then F1 := fric_coeff*abs(N1)*signum(F1); end if; else # Если ползун уже движется, считаем нормальную реакцию и трение скольжения N1 := GetN2(mass, length, grav_accel, fric_coeff, Y); F1 := fric_coeff*abs(N1)*signum(Y[4]); end if; return [F1, N1]; end proc:
Определяем callback для решателя:
# Вычисление правой части ОДУ в форме Коши для численного решателя (собственно математическая модель) EQs_func := proc(N, t, Y, dYdt) local F1, N1; # Сила трения и нормальная реакция local acc; # Обобщенные ускорения local ret; # Вычисляем силу трения и нормальную реакцию ret := GetFricNormal(m1, L1, g1, f1, Y); F1 := ret[1]; N1 := ret[2]; # Вычисляем производные фазовых координат acc := GetAccel(m1, L1, g1, F1, N1, Y); dYdt[1] := Y[4]; dYdt[2] := Y[5]; dYdt[3] := Y[6]; dYdt[4] := acc[1]; dYdt[5] := acc[2]; dYdt[6] := acc[3]; end proc:
Формируем для решателя список фазовых координат и начальные условия (угол отклонения стержня от вертикали делаем малым) и выполняем численное интегрирование (на самом деле последний вызов dsolve() лишь обозначает наши намерения по численному решению — оно будет поизведено при вычислении конкретных значений фазовых координат).
# Список фазовых координат vars := [X(t), Y(t), Phi(t), Vx(t), Vy(t), Omega(t)]; # Начальные условия initc := Array([0.0, 0.0, 1e-4, 0.0, 0.0, 0.0]); # Интгрируем уравнения dsolv := dsolve(numeric, number = 6, procedure = EQs_func, start = 0, initial = initc, procvars = vars, output=listprocedure);
Выполняем некоторые подготовительные операции
# Выделяем нужную нам часть решения x := eval(X(t), dsolv); y := eval(Y(t), dsolv); phi := eval(Phi(t), dsolv); vx := eval(Vx(t), dsolv); vy := eval(Vy(t), dsolv); omega := eval(Omega(t), dsolv);
Далее просчитаем движение системы в течение некоторого интервала времени и сформируем массивы данных для вывода на графики
# Рассчитываем движение системы на интересующем нас интервале времени t0 := 0.0: t1 := 10.0: num_plots := 1000: dt := (t1 - t0)/num_plots: t := t0: i := 1: while t <= t1 do Time[i] := t; Y := [x(t), y(t), phi(t), vx(t), vy(t), omega(t)]; x1[i] := Y[1]; phi1[i] := Y[3]; fric[i] := GetFricNormal(m1, L1, g1, f1, Y)[1]; norm_react[i] := GetFricNormal(m1, L1, g1, f1, Y)[2]; lim_fric[i] := f1*abs(norm_react[i])*fric[i]/abs(fric[i]); t := t + dt; i := i + 1; end do:
Ну вот, у нас практически всё готово
# Формируем графики G_x := [ [Time[k],x1[k]] $k=1..num_plots]: G_phi := [ [Time[k],phi1[k]] $k=1..num_plots]: G_fric := [ [Time[k],fric[k]] $k=1..num_plots]: G_norm_react := [ [Time[k],norm_react[k]] $k=1..num_plots]: G_lim_fric := [ [Time[k],lim_fric[k]] $k=1..num_plots]: # Выводим их на экран gr_opts := captionfont=['ROMAN', 16], axesfont=['ROMAN','ROMAN', 12],titlefont=['ROMAN', 14],gridlines=true: plot(G_x, gr_opts, view=[t0..t1, -1..1.0]); plot(G_phi, gr_opts, view=[t0..t1, 0.0..7.0]); plot({G_fric, G_lim_fric}, gr_opts, view=[t0..t1, -20..20]); plot(G_norm_react, gr_opts, view=[t0..t1, 0.0..200.0]);
Получаем графики. Красоты ради, графики были конвертированы из Maple в *.eps и немножко обработаны в inkscape.
Перемещение ползуна

Угол отклонения стержня от вертикали

Сила трения

Здесь синей линией показано предельное значение трения покоя, а красной — фактическое значение силы трения.
Нормальная реакция со стороны направляющих

Видно, что ползун покоится в течение чуть более двух секунд, а затем, после преодоления трения покоя приходит в движение, которое постепенно затухает и полностью прекращается через 6,5 секунд после начала движения. После этого сила трения никогда не превышает предельного для покоя значения, ползун остается на месте, а стержень совершает гармонические колебания около устойчивого положения равновесия.
Заключение
Рассмотрено применение метода избыточных координат и уравнений Лагранжа 2 рода к анализу движения систем с сухим трением. Видно, что при внешней громоздкости получаемых результатов, процес синтеза уравнений движения может быть автоматизирован средствами символьной математики, а это существенно для современных технических задач.
Благодарю за внимание к моему труду.
