车辆运动学方程推导和代码实现

文章目录

    • 1. 运动学方程
    • 2. 模型实现

1. 运动学方程

自行车模型(Bicycle Model)是车辆数字化模型中最常见的一种运动学模型。其除了可以反映车辆的一些基础特性外,更重要的是简单易用。通常情况下我们会把车辆模型简化为二自由度的自行车模型。

自行车模型主要基于以下假设:

  • 车辆是在一个二维平面上运动,不考虑车辆垂直平面的(Z轴)方向上的移动。
  • 车辆左右两个轮胎的运动可以合并为一个轮胎来描述,即假设车辆左右轮胎在任意时刻都拥有相同(或者近乎相同)的转向角度和速度。
  • 车辆处于低速运动,可以忽略前后轴载荷的偏移。
  • 车辆整体是刚体结构。

一般情况下,我们可以将车辆运动学模型简化为如下形式:

在这里插入图片描述

  • δ f \delta_f δf:前轮转角
  • δ r \delta_r δr:后轮转角
  • β \beta β:质心侧偏角,即质心速度与车身坐标系 X X X轴的夹角
  • φ \varphi φ:横摆角,即车的轴线与 X X X轴的夹角
  • β + φ \beta+\varphi β+φ:航向角
  • v v v:质心速度
  • O O O:速度瞬心
  • C C C:质心
  • L f L_f Lf:质心 C C C到前轴距离
  • L r L_r Lr:质心 C C C到后轴距离
  • L L L:轴距, L = L f + L r L=L_f+L_r L=Lf+Lr

我们对质心速度 v v v进行矢量分解,如上图中的 X ˙ \dot{X} X˙ Y ˙ \dot{Y} Y˙所示,可以得到下式子 ( 1 ) (1) (1) ( 2 ) (2) (2),根据理论力学刚体角速度公式可得公式 ( 3 ) (3) (3)。由此得到单车模型下的车辆运动学微分模型为
X ˙ = v c o s ( β + φ ) (1) \dot{X} = vcos(\beta+\varphi) \tag{1} X˙=vcos(β+φ)(1)

Y ˙ = v s i n ( β + φ ) (2) \dot{Y} = vsin(\beta+\varphi) \tag{2} Y˙=vsin(β+φ)(2)

φ ˙ = v R (3) \dot{\varphi} = \frac{v}{R} \tag{3} φ˙=Rv(3)

:一个刚体的角速度 = 线速度/线速度到速度瞬心的距离

根据图中几何关系和正弦定理可知:
L f s i n ( δ f − β ) = R s i n ( π 2 − δ f ) (4) \frac{L_f}{sin(\delta_f - \beta)} = \frac{R}{sin(\frac{\pi}{2} - \delta_f)} \tag{4} sin(δfβ)Lf=sin(2πδf)R(4)

L r s i n ( δ r + β ) = R s i n ( π 2 − δ r ) (5) \frac{L_r}{sin(\delta_r + \beta)} = \frac{R}{sin(\frac{\pi}{2} - \delta_r)} \tag{5} sin(δr+β)Lr=sin(2πδr)R(5)

由上式展开可得
L f R = s i n δ f c o s β − c o s δ f s i n β c o s δ f = t a n δ f c o s β − s i n β (6) \frac{L_f}{R} = \frac{sin\delta_f cos\beta - cos\delta_f sin\beta}{cos\delta_f} = tan\delta_fcos\beta - sin\beta\tag{6} RLf=cosδfsinδfcosβcosδfsinβ=tanδfcosβsinβ(6)

L r R = s i n δ r c o s β + c o s δ r s i n β c o s δ r = t a n δ r c o s β + s i n β (7) \frac{L_r}{R} = \frac{sin\delta_r cos\beta + cos\delta_r sin\beta}{cos\delta_r} = tan\delta_rcos\beta+sin\beta \tag{7} RLr=cosδrsinδrcosβ+cosδrsinβ=tanδrcosβ+sinβ(7)

由载荷的影响,质心 C C C位置会发生变化,导致 L f L_f Lf L r L_r Lr的长度发生变化,但是由于 L = l f + L r L = l_f +L_r L=lf+Lr是不变的,因此由式子 ( 6 ) ( 7 ) (6)(7) (6)(7)可得
L f + L r R = L R = ( t a n δ f + t a n δ r ) c o s β (8) \frac{L_f + L_r}{R} = \frac{L}{R} = (tan\delta_f + tan\delta_r)cos\beta \tag{8} RLf+Lr=RL=(tanδf+tanδr)cosβ(8)
( 3 ) (3) (3) ( 8 ) (8) (8)可得
φ ˙ = v R = v ( t a n δ f + t a n δ r ) c o s β L (9) \dot{\varphi} = \frac{v}{R} =\frac{v(tan\delta_f + tan\delta_r)cos\beta}{L} \tag{9} φ˙=Rv=Lv(tanδf+tanδr)cosβ(9)
由于低速条件下,我们可以假设车辆不会发生侧向滑动(漂移),此时我们可以将 v y ≈ 0 v_y \approx 0 vy0,因此 β ≈ 0 \beta \approx 0 β0,则横摆角 φ \varphi φ约等于航向角 φ + β \varphi + \beta φ+β 。又由于大部分车辆不具备后轮转向的功能,因此我们可以假设后轮转角 δ r ≈ 0 \delta_r\approx0 δr0,因此基于我们假设的前提下的运动学微分方程化简为
X ˙ = v c o s φ Y ˙ = v s i n φ φ ˙ = v t a n δ f L (10) \dot{X} = vcos\varphi \\ \dot{Y} = vsin\varphi \tag{10} \\ \dot{\varphi} = \frac{vtan\delta_f}{L} X˙=vcosφY˙=vsinφφ˙=Lvtanδf(10)

2. 模型实现

python代码如下:

#!/usr/bin/python
# -*- coding: UTF-8 -*-import math
import matplotlib.pyplot as plt
import numpy as np
from celluloid import Cameraclass Vehicle:def __init__(self,x=0.0,y=0.0,yaw=0.0,v=0.0,dt=0.1,l=3.0):self.steer = 0self.x = xself.y = yself.yaw = yawself.v = vself.dt = dtself.L = l  # 轴距self.x_front = x + l * math.cos(yaw)self.y_front = y + l * math.sin(yaw)def update(self, a, delta, max_steer=np.pi):delta = np.clip(delta, -max_steer, max_steer)self.steer = deltaself.x = self.x + self.v * math.cos(self.yaw) * self.dtself.y = self.y + self.v * math.sin(self.yaw) * self.dtself.yaw = self.yaw + self.v / self.L * math.tan(delta) * self.dtself.v = self.v + a * self.dtself.x_front = self.x + self.L * math.cos(self.yaw)self.y_front = self.y + self.L * math.sin(self.yaw)class VehicleInfo:# Vehicle parameterL = 3.0  #轴距W = 2.0  #宽度LF = 3.8  #后轴中心到车头距离LB = 0.8  #后轴中心到车尾距离MAX_STEER = 0.6  # 最大前轮转角TR = 0.5  # 轮子半径TW = 0.5  # 轮子宽度WD = W  #轮距LENGTH = LB + LF  # 车辆长度def draw_trailer(x, y, yaw, steer, ax, vehicle_info=VehicleInfo, color='black'):vehicle_outline = np.array([[-vehicle_info.LB, vehicle_info.LF, vehicle_info.LF, -vehicle_info.LB, -vehicle_info.LB],[vehicle_info.W / 2, vehicle_info.W / 2, -vehicle_info.W / 2, -vehicle_info.W / 2, vehicle_info.W / 2]])wheel = np.array([[-vehicle_info.TR, vehicle_info.TR, vehicle_info.TR, -vehicle_info.TR, -vehicle_info.TR],[vehicle_info.TW / 2, vehicle_info.TW / 2, -vehicle_info.TW / 2, -vehicle_info.TW / 2, vehicle_info.TW / 2]])rr_wheel = wheel.copy() #右后轮rl_wheel = wheel.copy() #左后轮fr_wheel = wheel.copy() #右前轮fl_wheel = wheel.copy() #左前轮rr_wheel[1,:] += vehicle_info.WD/2rl_wheel[1,:] -= vehicle_info.WD/2#方向盘旋转rot1 = np.array([[np.cos(steer), -np.sin(steer)],[np.sin(steer), np.cos(steer)]])#yaw旋转矩阵rot2 = np.array([[np.cos(yaw), -np.sin(yaw)],[np.sin(yaw), np.cos(yaw)]])fr_wheel = np.dot(rot1, fr_wheel)fl_wheel = np.dot(rot1, fl_wheel)fr_wheel += np.array([[vehicle_info.L], [-vehicle_info.WD / 2]])fl_wheel += np.array([[vehicle_info.L], [vehicle_info.WD / 2]])fr_wheel = np.dot(rot2, fr_wheel)fr_wheel[0, :] += xfr_wheel[1, :] += yfl_wheel = np.dot(rot2, fl_wheel)fl_wheel[0, :] += xfl_wheel[1, :] += yrr_wheel = np.dot(rot2, rr_wheel)rr_wheel[0, :] += xrr_wheel[1, :] += yrl_wheel = np.dot(rot2, rl_wheel)rl_wheel[0, :] += xrl_wheel[1, :] += yvehicle_outline = np.dot(rot2, vehicle_outline)vehicle_outline[0, :] += xvehicle_outline[1, :] += yax.plot(fr_wheel[0, :], fr_wheel[1, :], color)ax.plot(rr_wheel[0, :], rr_wheel[1, :], color)ax.plot(fl_wheel[0, :], fl_wheel[1, :], color)ax.plot(rl_wheel[0, :], rl_wheel[1, :], color)ax.plot(vehicle_outline[0, :], vehicle_outline[1, :], color)# ax.axis('equal')if __name__ == "__main__":vehicle = Vehicle(x=0.0,y=0.0,yaw=0,v=0.0,dt=0.1,l=VehicleInfo.L)vehicle.v = 1trajectory_x = []trajectory_y = []fig = plt.figure()# 保存动图用# camera = Camera(fig)for i in range(600):plt.cla()plt.gca().set_aspect('equal', adjustable='box')vehicle.update(0, np.pi / 10)draw_trailer(vehicle.x, vehicle.y, vehicle.yaw, vehicle.steer, plt)trajectory_x.append(vehicle.x)trajectory_y.append(vehicle.y)plt.plot(trajectory_x, trajectory_y, 'blue')plt.xlim(-12, 12)plt.ylim(-2.5, 21)plt.pause(0.001)#     camera.snap()# animation = camera.animate(interval=5)# animation.save('trajectory.gif')

运行结果如下:

在这里插入图片描述

文章首发公众号:iDoitnow如果喜欢话,可以关注一下

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/604595.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

游戏Lua调用01.lua的编译及测试

一、lua库下载与编译 进入lua官网 Lua: version history 找到lua5.1 选择lua5.1是因为大部分游戏使用的都是lua5.1的库,也可以选择高版本,影响不大 下载完了后使用vs建立一个静态库或者动态库的工程 这里以动态库为例子,静态库也是一样的…

flink table view datastream互转

case class outer(f1:String,f2:Inner) case class outerV1(f1:String,f2:Inner,f3:Int) case class Inner(f3:String,f4:Int) 测试代码 package com.yy.table.convertimport org.apache.flink.streaming.api.scala.StreamExecutionEnvironment import org.apache.flink.tabl…

w18认证崩溃之暴力破解DVWA

一、实验环境 攻击工具:burpsuite2021.12 靶场:DVWA二、实验目的 演示暴破DVWA的medium和high两个级别,low级别请查看w18认证崩溃之暴力破解4种攻击模式 三、实验步骤 1.设置靶场medium级别 2.开启谷歌代理插件,开启bp拦截&…

AI教我学编程之AI自刀

AI教我学编程系列学习第二课 — C#变量类型 上节回顾知识梳理C#基本变量类型 对话AI分歧产生本段总结 它说得对吗?我随即发问经典AI自刀他来了 总结 上节回顾 在上一节中我们发现,AI工具似乎还不能达到教学的水平,所以在本节中,…

数据在内存中的存储方式

前言: 期末临近,继续复习! 今天要复习的内容是数据在内存中的存储,主要是整型与浮点两种,还有大小端的介绍。 提出问题 打印结果是255 -1 为什么? 首先我们要知道数据都是以二进制的形式存…

Spring Framework和SpringBoot的区别

目录 一、前言 二、什么是Spring 三、什么是Spring Framework 四、什么是SpringBoot 五、使用Spring Framework构建工程 六、使用SpringBoot构建工程 七、总结 一、前言 作为Java程序员,我们都听说过Spring,也都使用过Spring的相关产品&#xff0…

uni-app 前后端调用实例 基于Springboot 详情页实现

锋哥原创的uni-app视频教程: 2023版uniapp从入门到上天视频教程(Java后端无废话版),火爆更新中..._哔哩哔哩_bilibili2023版uniapp从入门到上天视频教程(Java后端无废话版),火爆更新中...共计23条视频,包括:第1讲 uni…

mysql进阶-重构表

目录 1. 原因 2. 如何重构表呢? 2.1 命令1: 2.2 命令2: 2.3 命令3: 1. 原因 正常的业务开发,为什么需要重构表呢? 原因1:某张表存在大量的新增和删除操作,导致表经历过大量的…

JavaScript异常处理实战

前言 之前在对公司的前端代码脚本错误进行排查,试图降低 JS Error 的错误量,结合自己之前的经验对这方面内容进行了实践并总结,下面就此谈谈我对前端代码异常监控的一些见解。 本文大致围绕下面几点展开讨论: JS 处理异常的方式…

解决 Postman 报错问题:一份综合指南

Postman 是一个流行的 API 测试工具,它可以帮助开发者和测试人员快速地创建和发送各种 HTTP 请求,并查看响应结果。但是,在使用 Postman 的过程中,有时候会遇到一些报错或异常情况,影响了正常的测试流程。本文将介绍一…

图像分割-Grabcut法

版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。 本文的C#版本请访问:图像分割-Grabcut法(C#)-CSDN博客 GrabCut是一种基于图像分割的技术,它可以用于将图像…

循环队列的队空队满情况

有题目: 循环队列放在一维数组A[0....M-1]中,end1指向队头元素,end2指向队尾元素的后一个位置。假设队列两端均可进行入队和出队操作,队列中最多能容纳M-1个元素。初始时为空。下列判断队空和队满的条件中,正确的是 …

vim/vi 模式切换和常用快捷键

vim/vi 切换模式: vim/vi 常用快捷键: 一般模式: gg:文件开头、G:文件结尾 shift^ :光标当前行首、shift^:光标当前行尾 yy:复制、p:粘贴、dd:删除当前行、…

CRM的request管理笔记

1 request类型 request有两种,device request和link request。 link request link req是对link进行精确控制。 link req是对每个link的请求,比如某一帧是否需要bubble recovery、某一帧是否需要长曝光等feature。device request 对一个设备进行每帧控制…

JDK 11:崭新特性解析

JDK 11:崭新特性解析 JDK 11:崭新特性解析1. HTTP Client(标准化)示例代码 2. 局部变量类型推断的扩展示例代码 3. 新的字符串方法示例代码 4. 动态类文件常量示例代码 5. Epsilon 垃圾收集器使用方式 结语 JDK 11:崭新…

MySQL数据库进阶-事务

事务 事务由单独单元的一个或多个SQL语句组成,在这 个单元中,每个MySQL语句是相互依赖的。而整个单独单 元作为一个不可分割的整体,如果单元中某条SQL语句一 旦执行失败或产生错误,整个单元将会回滚。所有受到影 响的数据将返回到…

现有网络模型的使用及修改(VGG16为例)

VGG16 修改默认路径 import os os.environ[TORCH_HOME] rD:\Pytorch\pythonProject\vgg16 # 下载位置太大了(140多G)不提供直接下载 train_set torchvision.datasets.ImageNet(root./data_image_net, splittrain, downloadTrue, transformtorchvis…

Informer:用于长序列时间序列预测的高效Transformer模型

最近在研究时间序列分析的的过程看,看到一篇精彩的文章,名为:《Informer: Beyond Efficient Transformer for Long Sequence Time-Series Forecasting》,特此撰写一篇博客。 文章主要研究了一种用于长序列时间序列预测的高效Trans…

Windows:笔记本电脑设置休眠教程

前言 不知道大家在使用【Windows】笔记本有没有这个习惯,我会把他的电池选项的【休眠】设置进行打开。因为作为我们开发人员电脑一般是一周关一次机,有时候一个月关一次机。这时候【休眠】功能就给我们提供了一个好处,我们选择了【休眠】后电…

PAT 乙级 1056 组合数的和

给定 N 个非 0 的个位数字,用其中任意 2 个数字都可以组合成 1 个 2 位的数字。要求所有可能组合出来的 2 位数字的和。例如给定 2、5、8,则可以组合出:25、28、52、58、82、85,它们的和为330。 输入格式: 输入在一行…