【代码复现】NER之GlobalPointer解析

人工智能200

前言

在NER任务中,主要分为三类实体: 嵌套实体非嵌套实体不连续实体,今天分享方法以end-to-end的方式解决前两个问题,GlbalPointer,它利用全局归一化的思路来进行命名实体识别(NER),可以无差别地识别嵌套实体和非嵌套实体,在非嵌套(Flat NER)的情形下它能取得媲美CRF的效果,而在嵌套(Nested NER)情形它也有不错的效果。

核心思想

【代码复现】NER之GlobalPointer解析
GlobalPointer是一种基于span分类的解码方法,它将首尾视为一个整体去进行判别,所以它更有"全局观"(更Global)。而且也保证了训练、预测、上线评估都是以实体级进行评测。

任务建模,按照实体类型数量和max_len的长度生成三维矩阵(ent_type_size, max_seq_len, max_seq_len),按照实体类型dix,实体start_idx, 实体end_idx填入三维矩阵中并赋值为1

import numpy as np
labels = np.zeros((3,12,12)) # 代表 3种实体类型 句子长度为12
labels[1][0][1] = 1 # 实体类型为1 (start_idx,end_idx)=(0,1)填写为1

模型架构

模型 torch代码如下:

class GlobalPointer(nn.Module):
    def __init__(self, encoder, ent_type_size, inner_dim, RoPE=True):
        super().__init__()
        self.encoder = encoder
        self.ent_type_size = ent_type_size
        self.inner_dim = inner_dim
        self.hidden_size = encoder.config.hidden_size
        self.dense = nn.Linear(self.hidden_size, self.ent_type_size * self.inner_dim * 2)

        self.RoPE = RoPE # 是否使用RoPE

    def sinusoidal_position_embedding(self, batch_size, seq_len, output_dim):
        position_ids = torch.arange(0, seq_len, dtype=torch.float).unsqueeze(-1)

        indices = torch.arange(0, output_dim // 2, dtype=torch.float)
        indices = torch.pow(10000, -2 * indices / output_dim)
        embeddings = position_ids * indices
        embeddings = torch.stack([torch.sin(embeddings), torch.cos(embeddings)], dim=-1)
        embeddings = embeddings.repeat((batch_size, *([1]*len(embeddings.shape))))
        embeddings = torch.reshape(embeddings, (batch_size, seq_len, output_dim))
        embeddings = embeddings.to(self.device)
        return embeddings

    def forward(self, input_ids, attention_mask, token_type_ids):
        self.device = input_ids.device

        context_outputs = self.encoder(input_ids, attention_mask, token_type_ids)
        # last_hidden_state:(batch_size, seq_len, hidden_size)
        last_hidden_state = context_outputs[0]

        batch_size = last_hidden_state.size()[0]
        seq_len = last_hidden_state.size()[1]

        # outputs:(batch_size, seq_len, ent_type_size*inner_dim*2)
        outputs = self.dense(last_hidden_state)
        outputs = torch.split(outputs, self.inner_dim * 2, dim=-1)
        # outputs:(batch_size, seq_len, ent_type_size, inner_dim*2)
        outputs = torch.stack(outputs, dim=-2)
        # qw,kw:(batch_size, seq_len, ent_type_size, inner_dim)
        qw, kw = outputs[...,:self.inner_dim], outputs[...,self.inner_dim:] # TODO:修改为Linear获取?

        if self.RoPE:
            # pos_emb:(batch_size, seq_len, inner_dim)
            pos_emb = self.sinusoidal_position_embedding(batch_size, seq_len, self.inner_dim)
            # cos_pos,sin_pos: (batch_size, seq_len, 1, inner_dim)
            cos_pos = pos_emb[..., None, 1::2].repeat_interleave(2, dim=-1)
            sin_pos = pos_emb[..., None,::2].repeat_interleave(2, dim=-1)
            qw2 = torch.stack([-qw[..., 1::2], qw[...,::2]], -1)
            qw2 = qw2.reshape(qw.shape)
            qw = qw * cos_pos + qw2 * sin_pos
            kw2 = torch.stack([-kw[..., 1::2], kw[...,::2]], -1)
            kw2 = kw2.reshape(kw.shape)
            kw = kw * cos_pos + kw2 * sin_pos

        # logits:(batch_size, ent_type_size, seq_len, seq_len)
        logits = torch.einsum('bmhd,bnhd->bhmn', qw, kw)

        # padding mask
        pad_mask = attention_mask.unsqueeze(1).unsqueeze(1).expand(batch_size, self.ent_type_size, seq_len, seq_len)
        # pad_mask_h = attention_mask.unsqueeze(1).unsqueeze(-1).expand(batch_size, self.ent_type_size, seq_len, seq_len)
        # pad_mask = pad_mask_v&pad_mask_h
        logits = logits*pad_mask - (1-pad_mask)*1e12

        # 排除下三角
        mask = torch.tril(torch.ones_like(logits), -1)
        logits = logits - mask * 1e12

        return logits/self.inner_dim**0.5

生成RoPE:
GlobalPoint核心思想是引入了RoPE(旋转式位置编码):
对于位置m m m,RoPE会计算出一个正交矩阵 R m R_{m}R m ​,将 R m R_{m}R m ​与 q q q相乘便实现对 q q q进行旋转,如果 q q q 是二维,有:
【代码复现】NER之GlobalPointer解析
对于高阶偶数维的q q q ,有:
【代码复现】NER之GlobalPointer解析
其中θ i \theta_i θi ​ 是怎么得到的?
介绍一下Sinusoidal位置编码
【代码复现】NER之GlobalPointer解析
RoPE在θi的选择上,采用了Sinusoidal位置编码的方案,即 θ i = 1000 0 ( − 2 i / d ) \theta _i = 10000^{(-2i/d)}θi ​=1 0 0 0 0 (−2 i /d ) 它可以带来一定的远程衰减性。
有关torch.stack可参考这篇文章

def sinusoidal_position_embedding(self, batch_size, seq_len, output_dim):
        position_ids = torch.arange(0, seq_len, dtype=torch.float).unsqueeze(-1) # 生成绝对位置信息

        indices = torch.arange(0, output_dim // 2, dtype=torch.float) # 由Sinusoidal公式可知 i的范围是 0 -> d/2
        indices = torch.pow(10000, -2 * indices / output_dim)  # 公式计算得到theta_i
        embeddings = position_ids * indices # 生成带theta的embedding
        embeddings = torch.stack([torch.sin(embeddings), torch.cos(embeddings)], dim=-1) # 引入cosm sinm 在最后维度进行堆叠
        embeddings = embeddings.repeat((batch_size, *([1]*len(embeddings.shape)))) # 扩展到整个batch_size种
        embeddings = torch.reshape(embeddings, (batch_size, seq_len, output_dim)) # 修改为输出维度
        embeddings = embeddings.to(self.device)
        return embeddings

其中stack操作如下图所示:
【代码复现】NER之GlobalPointer解析
torch.repeat 操作 可以将1维信息扩展到多维信息中

x = torch.tensor([1, 2, 3])
>  tensor([1, 2, 3])
(3, *([1]*len(x.shape)))
> (3, 1)
x = x.repeat((3, *([1]*len(x.shape))))
>tensor([[1, 2, 3],
        [1, 2, 3],
        [1, 2, 3]])

苏神的讲解可知,RoPE的计算可以简化为如下图。
【代码复现】NER之GlobalPointer解析
在q q q 和k k k 中融入RoPE:
于是以此类推,如果将 k k k 也乘上旋转位置编码,此时span的分数 s ( i , j ) s(i,j)s (i ,j ) 就会带有相对位置信息(也就是 R i − j R_{i-j}R i −j ​):
【代码复现】NER之GlobalPointer解析

if self.RoPE:
            # pos_emb:(batch_size, seq_len, inner_dim)
            pos_emb = self.sinusoidal_position_embedding(batch_size, seq_len, self.inner_dim) # 上一步得到RoPE
            # cos_pos,sin_pos: (batch_size, seq_len, 1, inner_dim)
            cos_pos = pos_emb[..., None, 1::2].repeat_interleave(2, dim=-1)
            sin_pos = pos_emb[..., None,::2].repeat_interleave(2, dim=-1)
            qw2 = torch.stack([-qw[..., 1::2], qw[...,::2]], -1)
            qw2 = qw2.reshape(qw.shape)
            qw = qw * cos_pos + qw2 * sin_pos
            kw2 = torch.stack([-kw[..., 1::2], kw[...,::2]], -1)
            kw2 = kw2.reshape(kw.shape)
            kw = kw * cos_pos + kw2 * sin_pos

有关切片操作可看这篇文章
其中:
... 操作表示自动判断其中得到维度区间
None 增加一维
::2 两个冒号直接写表示从所有的数据中隔行取数据。从0开始
1::2 两个冒号直接写表示从所有的数据中隔行取数据。从1开始

repeat_interleave操作:复制指定维度的信息

x = torch.tensor([1, 2, 3])
>tensor([1, 2, 3])
x.repeat_interleave(2)
>tensor([1, 1, 2, 2, 3, 3])
y = torch.tensor([[1, 2], [3, 4]])
>tensor([[1, 2],
        [3, 4]])
torch.repeat_interleave(y, 2)
>tensor([1, 1, 2, 2, 3, 3, 4, 4])
torch.repeat_interleave(y, 3, dim=1)
>tensor([[1, 1, 1, 2, 2, 2],
        [3, 3, 3, 4, 4, 4]])

让我们再次看一遍这个计算公式:
【代码复现】NER之GlobalPointer解析
这时可以发现:

 cos_pos = pos_emb[..., None, 1::2].repeat_interleave(2, dim=-1)   # 是将奇数列信息抽取出来也就是cosm 拿出来并复制
 sin_pos = pos_emb[..., None,::2].repeat_interleave(2, dim=-1) # 是将偶数列信息抽取出来也就是sinm 拿出来并复制
 qw2 = torch.stack([-qw[..., 1::2], qw[...,::2]], -1) # 奇数列加上负号 得到第二个q的矩阵
 qw = qw * cos_pos + qw2 * sin_pos # 最后融入位置信息
计算kw 也是同理步骤

## 最后计算初logits 结果
logits = torch.einsum('bmhd,bnhd->bhmn', qw, kw) # 相等于先对qw做转置 然后qw与kw做矩阵乘法

torch.einsum :
可以简单实现向量内积,向量外积,矩阵乘法,转置和张量收缩(tensor contraction)等张量操作
可参考这篇文章

torch.expand
参考这篇文章

以上就是对GlobalPoint模型主要的要点进行解析,如果你还有什么问题,可以留言,一起讨论啊

参考代码链接:https://github.com/gaohongkui/GlobalPointer_pytorch

Original: https://blog.csdn.net/qq_36287702/article/details/123567764
Author: 桐原因
Title: 【代码复现】NER之GlobalPointer解析



相关阅读1

Title: PWA学习笔记(二)

APP Shell:

1、应用从显示内容上可粗略划分为内容部分和外壳部分,App Shell 就是 外壳部分,即页面的基本结构

2、它不仅包括用户能看到的 页面框架部分,还包括用户看不到的 代码逻辑

3、页面能够展现所需的 最小资源集合,即支持用户界面所需的最小的 HTML、CSS 和 JS 等静态资源集合

4、采用 App Shell 的站点,每个页面都会 先加载 App Shell 的内容

再由 App Shell 根据当前页面 URL 渲染对应的主体内容

正确地使用 APP Shell:

1、使用 Service Worker 预缓存 App Shell 的静态资源

2、用户访问 Web 站点时,通过 Service Worker 拦截请求

3、Service Worker 返回缓存中的 App Shell 给浏览器

4、App Shell 根据当前的 URL 再去 请求对应的数据来渲染

骨架屏:

1、快速渲染的静态图片/样式/色块进行 占位,让用户对后续会渲染的内容有一定的预期

2、从架构上来说分为 前端渲染(CSR)和后端渲染(SSR)两种,骨架屏适用于前端渲染的页面

3、骨架屏的 HTML 和 CSS 内联,从而使骨架屏能更快地展示出来

4、外链样式文件会阻塞骨架屏渲染所需的样式和 HTML 的渲染,可以将外联样式的 rel 设置为 preload

5、由于使用预加载来加载样式文件,所以我们无法保证浏览器会先应用样式再运行 JavaScript 渲染内容。

所以,在样式文件加载完成前,即使 JavaScript 已渲染好了内容,也先不要替换掉骨架屏, 等待样式文件

加载完成后,再触发 JavaScript 进行挂载。如果样式文件在 JavaScript 文件之前加载,则mount 函数

还没有声明,执行会出错,导致主体内容没有渲染到页面上, 需增加标记告诉 JavaScript 样式是否加载完成

Data URL:

1、允许我们使用内联(inline-code)的方式在网页中包含数据,目的是将一些小的数据,

直接嵌入到网页中,从而不用再从外部文件载入

2、格式规范

3、制作 base64 的 Data URL:

(1)使用二进制方式(rb)打开图片文件

(2)用 Python 的 base64 库来给比特流图片进行编码

4、适用场合:

(1)当访问外部资源很麻烦或受限时

(2)当图片是在服务器端用程序动态生成,每个访问用户显示的都不同时

(3)当图片的体积太小,占用一个HTTP会话不是很值得时

5、不适用场合:

(1)编码后可能会增加数据的体积

(2)Data URL形式的图片不会被浏览器缓存,这意味着每次访问这样页面时都被下载一次,但可优化

页面从加载到展现的大致顺序如下:

1、加载 HTML 文件

2、解析 DOM

3、 并行加载 CSS/JS 资源

4、如果 head 中存在外链的样式,则 阻塞渲染等待样式文件加载并解析完成

5、如果 head 中存在外链的 script,则 阻塞渲染等待 script 文件加载并执行完成

像素:

1、图像中 最小的单位,一个不可再分割的点,在计算机屏幕上一般指屏幕上的一个 光点

2、设备像素对应屏幕上 光点的数量

3、 虚拟像素,也可以叫 CSS 像素(CSS Pixels)或逻辑像素,在 Android 开发中可以叫设备无关像素

注:在 iPhone X 上,设备像素是 1125x2436,而 CSS 像素是 375x812,则一个 CSS 像素对应长和宽各 3 个设备像素

比值 3 就是我们平时所说的设备像素比(Device Pixel Ratio),简称 DPR ,它并不是一个单位,而是一个 比值

EM 和 REM:

1、EM 是相对单位,相对于元素自身的 font-size,它会从 继承父元素的字体大小,若父元素没有设置字体大小,会一直找到根元素

2、REM = Root EM,顾名思义就是相对于 根元素的 EM,是根据根元素来计算出CSS 像素点的大小,默认为 16px

3、EM 对于 模块化的页面元素比较好,而 方便是 REM 最大的好处(只需要知道 html 标签的字体大小即可)

vw、vh 和百分比:

1、vw:viewport width, 视口宽度,1vw = 1% 视口宽度

2、vh:viewport height, 视口高度,1vh = 1% 视口高度

3、百分比:根据 父元素来决定,如父元素高 100px,则 50% == 50px

弹性框:

1、不是单位,而是一种 布局方式,里面的元素可 弹性伸缩、定义 排版方向、指定 flex 元素的顺序

2、容器样式属性(display: flex 的元素):

(1)flex-direction:定义主轴方向,即子元素的排列方向,取值为

row、row-reverse、column 和 column-reverse,默认 row

(2)flex-wrap:默认情况下,弹性布局会将所有元素都压缩到一行,可以通过设置 flex-wrap 告诉

浏览器在适当时候换行取值为 nowrap、wrap、wrap-reverse

(3)flex-flow: 这个属性值是 flex-direction 和 flex-wrap 的简写

(4)justify-content: 定义子元素在主轴上对齐方式,

取值为flex -start、flex-end、center、space-between 和 space-around

(5)align-items:定义子元素在垂直于主轴的交叉轴的排列方式,

取值为flex -start、flex -end、center、baseline 和stretch

(6)align-content:定义了子元素在多条轴线上的对齐方式,如果只使用了一条轴线,那该属性不起作用,

取值为flex -start、flex -end、center、space -between、space -around 和 stretch

Original: https://www.cnblogs.com/lemonyam/p/11931506.html
Author: 贵志
Title: PWA学习笔记(二)

相关阅读2

Title: 【聚类算法】带你轻松搞懂K-means聚类(含代码以及详细解释)

文章目录

一:K-means聚类算法

聚类是一个将数据集中 在某些方面相似的数据成员进行分类组织的过程,聚类就是一种发现这种内在结构的技术,聚类技术经常被称为 无监督学习
k均值聚类是最著名的划分聚类算法,由于简洁和效率使得他成为所有聚类算法中最广泛使用的。给定一个数据点集合和需要的聚类数目k,k由用户指定,k均值算法根据某个距离函数反复把数据分入k个聚类中。

二:实例分析

现有50个二维数据点如下图,使用K-Means算法将以下数据实现聚类。
【代码复现】NER之GlobalPointer解析
结果展示:
【代码复现】NER之GlobalPointer解析

; 三:原理与步骤

K-means算法是典型的基于距离(欧式距离、曼哈顿距离)的聚类算法,采用距离作为相似性的评价指标,即认为两个对象的距离越近,其相似度就越大。该算法认为簇是由距离靠近的对象组成的,因此把得到紧凑且独立的簇作为最终目标。
K-mean算法步骤如下:

  1. 先定义总共有多少个簇类,随机选取K个样本为簇中⼼。
  2. 分别计算所有样本到随机选取的K个簇中⼼的距离。
  3. 样本离哪个中⼼近就被分到哪个簇中⼼。
  4. 计算各个中⼼样本的均值(最简单的⽅法就是求样本每个点的平均值)作为新的簇心。
  5. 重复2、3、4直到新的中⼼和原来的中⼼基本不变化的时候,算法结束。

算法结束条件:

  1. 当每个簇的质心,不再改变时就可以停止k-menas。
  2. 当循环次数达到事先规定的次数时,停止k-means

原理示意图:
【代码复现】NER之GlobalPointer解析
简单小实例:
有以下6个点,初始随机选取两个点作为两个簇的簇中心(这里假设选取的是A3,A4),求最后的簇所属情况。
【代码复现】NER之GlobalPointer解析
1️⃣:计算每个点到簇心的距离,将距离近的归为一类。
【代码复现】NER之GlobalPointer解析
2️⃣:将红色对应的点和绿色对应的每个点分别求X,Y平均值,最为新的簇心。
【代码复现】NER之GlobalPointer解析
3️⃣:计算每个点到新簇心的距离,继续将对应距离近的点归为一类。
【代码复现】NER之GlobalPointer解析
4️⃣:由于关联点没有发生变化,所以之后的结果不会发生变化。停止计算
5️⃣:得结果红色簇:A1,A3,A5,紫色簇:A2,A4,A6。

四:Matlab代码以及详解

clc;clear;close all;
data(:,1)=[90,35,52,83,64,24,49,92,99,45,19,38,1,71,56,97,63,...

    32,3,34,33,55,75,84,53,15,88,66,41,51,39,78,67,65,25,40,77,...

    13,69,29,14,54,87,47,44,58,8,68,81,31];
data(:,2)=[33,71,62,34,49,48,46,69,56,59,28,14,55,41,39,...

    78,23,99,68,30,87,85,43,88,2,47,50,77,22,76,94,11,80,...

    51,6,7,72,36,90,96,44,61,70,60,75,74,63,40,81,4];
%50 * 1
figure(1)

scatter(data(:,1),data(:,2),'MarkerEdgeColor','r','LineWidth',2)
%% 原理推导K均值
[m,n]=size(data);%m = 50,n = 1;
cluster_num=4;%4个初始中心
cluster=data(randperm(m,cluster_num),:);%randperm(m,cluster_num)在前m中随机选取cluster_num个  %随机选取中心
%data函数  取数据用
epoch_max=1000;%最大次数
therad_lim=0.001;%中心变化阈值
epoch_num=0;
while(epoch_num<epoch_max)
    epoch_num=epoch_num+1;
    for i=1:cluster_num
    distance=(data-repmat(cluster(i,:),m,1)).^2;% 50 * 2  repmat扩展矩阵
    %.^2是矩阵中的每个元素都求平方,^2是求矩阵的平方或两个相同的矩阵相乘,因此要求矩阵为方阵
    distance1(:,i)=sqrt(sum((distance),2));%求行和
    %distance1(:,i)=sqrt(sum(distance'));% 默认求列和  1表示每一列进行求和,2表示每一行进行求和;
    %sqrt(sum(distance')) == 1 * 50
    %distance1 50 * 4 表示每个点距离第i个点的距离
    end
    [~,index_cluster]=min(distance1');%distance1' = 4 * 50,min 求列最值  index_cluster = 最小值所在行号  index_cluster = 1 * 50
    for j=1:cluster_num
    cluster_new(j,:)=mean(data(find(index_cluster==j),:));% 4 * 2  找到距离对应中心最近的点 横纵坐标各取平均值
    end
    if (sqrt(sum((cluster_new-cluster).^2))>therad_lim)
        cluster=cluster_new;
    else
        break;
    end
end
%% 画出聚类效果
figure(2)
%subplot(2,1,1)
a=unique(index_cluster); %找出分类出的个数
C=cell(1,length(a));%1 * 4的元胞
for i=1:length(a)
   C(1,i)={find(index_cluster==a(i))};
end
for j=1:cluster_num
    data_get=data(C{1,j},:);%从data中取每个类的点
    scatter(data_get(:,1),data_get(:,2),80,'filled','MarkerFaceAlpha',.6,'MarkerEdgeAlpha',.9);
    hold on
end
plot(cluster(:,1),cluster(:,2),'kp','LineWidth',2);%画出4个聚类中心
hold on
sc_t=mean(silhouette(data,index_cluster'));
title_str=['原理推导K均值聚类','  聚类数为:',num2str(cluster_num),'  SC轮廓系数:',num2str(sc_t)];
title(title_str)

Original: https://blog.csdn.net/qq_43727529/article/details/126813321
Author: Rookiep
Title: 【聚类算法】带你轻松搞懂K-means聚类(含代码以及详细解释)

相关阅读3

Title: AirSim(五)---理解篇: Airsim世界坐标系、NED坐标系、机体坐标系以及控制相关API接口函数

目录

1. 坐标系 coordinate system

本部分源自:Using AirSim>>Core APIs>> AirSim APIs 官网

(1) AirSim API的坐标系:NED 坐标系 with SI unit

该坐标系也是世界坐标系,world frame。

  1. 所有AirSim API使用NED坐标系统,即+X是北North,+Y是东East,+Z是下Down。这意味着Z值越高越负:如果原点在地面上,z值是零,地面以上是负的!
  2. 所有单位都是国际单位制。
  3. 请注意,不同于Unreal Engine (UE)内部使用的坐标系统。在Unreal Engine中,+Z是向上Up而不是向下Down,长度单位是厘米cm而不是米m。
  4. AirSim API来完成从Unreal Engine的NEU坐标系到AirSim的NED坐标系的适当转换。在AirSim NED系统中,车辆的起始点总是坐标(0,0,0)。因此,当从UE坐标系转换到AirSim NED坐标系时,我们首先减去起始点偏移量,然后缩放100实现cm到m的转换。
  5. 在UE环境中,车辆由Player Start component放置的地方生成或衍生而来,在 Settings.json_配置文件中有一个设置项元素为 _OriginGeopoint,可以将地理经度、纬度和海拔分配给Player Start Component

OriginGeoPoint这指定了放置在Unreal环境中的Player Start Component的纬度、经度和高度。车辆的原点(home point)是用这个变换计算出来的。请注意,所有通过API采用的坐标都使用了以SI单位表示的NED坐标系统,这意味着每辆车在NED系统中都从(0,0,0)开始启动。
—《OriginGeopoint》官网资料

(2) Unreal Engine的坐标系

  1. AirSim不同于Unreal Engine (UE)内部使用的坐标系统。在Unreal Engine中,+Z是向上Up而不是向下Down,长度单位是厘米cm而不是米m。
  2. 用户不必特别关系UE坐标系,因为AirSim已经处理好了这个问题,即只需要按照AirSim坐标系设置即可,包括Settings.json中的OriginGeopoint元素,AirSim会帮用户自动转换。

(3)AirSim全局坐标系=NED坐标系=世界坐标系

(4) AirSim的机体坐标系:Body Frame

本部分参考:airsim document

根据Airsim文档提供的说明, 机体坐标系body frame遵循Front Left Up (FLU),右手法则。
【代码复现】NER之GlobalPointer解析

【代码复现】NER之GlobalPointer解析
Note:图片来源:GAAS

Note2: 上述FLU坐标系为右手坐标系。判断绕某个旋转轴i 的旋转正方向的方法为:右手坐标系:
右手握拳,大拇指伸直指向旋转轴i的正方向(箭头方向),其余四指指尖所朝向的方向即为正方向(等效于从旋转轴i正方向箭头端逆向看过去,或者旋转轴i正方向箭头射进你的眼睛,这时看到的逆时针旋转方向为绕i轴的正方向)。

【代码复现】NER之GlobalPointer解析

【代码复现】NER之GlobalPointer解析

Note:也有说法是认为FLU坐标系不对,应该是FRD坐标系。即机体坐标系的原点在机体的重心位置,x,y,z三个坐标轴的方向分别指向机体的前方、右方、下方. 点击此

; 2. AirSim APIs for 车辆和无人机的状态

本部分内容:Using AirSim>>Core APIs: APIs for Car

Airsim提供了两个API函数来进行车辆的控制、获取车辆状态

设置车辆控制参数setCarControlsset throttle, steering, handbrake and auto or manual gear.获取车辆状态参数getCarState获取如下的状态信息: speed, current gear,6 kinematics quantities: position, orientation, linear and angular velocity, linear and angular acceleration.设置无人机控制参数move* APIs1 Multirotor can be controlled by specifying angles, velocity vector, destination position or some combination of these.

2 当进行position control时, 需要path following算法,其中AirSim 默认采用carrot following algorithm,这部分通常被认为是 "high level control" ,因为只需要指定 high level goal,the firmware会自动完成其余功能。

3 当前AirSim提供的底层控制 lowest level control 是API函数: moveByAngleThrottleAsync API.获取无人机状态参数getMultirotorState1 这个API在一个调用中返回车辆的状态。

2 状态包括:collision、estimated kinematics(即通过融合传感器计算的运动学)和timestamp(自每个epoch以来的纳秒时间)。3. 这里的kinematics运动学指的是6个量:position, orientation, linear and angular velocity, linear and angular acceleration。

4请注意,simple_slight目前不支持state estimator,这意味着simple_flight模式下估计运动学值和地面真实运动学值是相同的获取真实vehicle和环境状态simGetGroundTruthEnvironment

(vehicle_name=''

一般是Ground Truth值)该函数返回class airsim.types.EnvironmentState,包含环境状态:

-大气密度air_density;

-大气压air_pressure;

-PlayerStart的起始地理信息geo_point;

-重力gravity;

-无人机位置in NED position;

-温度temperature.

Note

:gravity是环境的重力,NED坐标系中为z轴正方向一致,因此可得到类似9.8的结果。获取vehicle估计的运动学状态simGetGroundTruthKinematics

(vehicle_name='')

指的是kinematics估计值

该函数返回airsim.types.KinematicsState,包含信息:

  1. 角加速度:angular_acceleration

  2. 角速度:angular_velocity

  3. 加速度:linear_acceleration

  4. 速度:linear_velocity

  5. 姿态四元数:orientation

  6. 位置:position

Note:1-2: body frame; 3-6: NED frame

Note:

  1. 上述状态量中,只有角速度和加速度的值(angular velocity and accelerations )是定义在Body frame中,其余状态量是在NED坐标系中,并且采用SI单位制。
  2. kinematics输出的位置信息和simGetGroundTruthEnvironment()接口的位置信息不一定完全相同,kinematics的结果是通过动力学物理引擎计算得到的,具体可以参考AirSim论文

Original: https://blog.csdn.net/kuvinxu/article/details/124467529
Author: 致知在格物,物格而后知至
Title: AirSim(五)---理解篇: Airsim世界坐标系、NED坐标系、机体坐标系以及控制相关API接口函数