多轴机械臂运动控制:4x4坐标变换矩阵该用C语言的二维数组还是一维数组?

        做多轴机械臂的运动控制,免不了要对4x4的坐标变换矩阵进行乘法,C语言中可以用二维数组或者一维数组来实现矩阵,下面来比较一下二维数组和一维数组的性能差异。
        开发环境:Visual Studio 2022,分别在Debug和Release模式下测试函数Multi4x4和Multi16,Release模式下开了最大优化(优选速度)(/O2),代码速度优先(/Ot),每种情况重复测3次。(注:最后算出的sum值是2488321446960073699907076096.00)

#include<stdio.h>
#include<stdint.h>
#include<time.h>void Multi4x4(float A[4][4], float B[4][4], float C[4][4]) {C[0][0] = A[0][0] * B[0][0] + A[0][1] * B[1][0] + A[0][2] * B[2][0] + A[0][3] * B[3][0];C[0][1] = A[0][0] * B[0][1] + A[0][1] * B[1][1] + A[0][2] * B[2][1] + A[0][3] * B[3][1];C[0][2] = A[0][0] * B[0][2] + A[0][1] * B[1][2] + A[0][2] * B[2][2] + A[0][3] * B[3][2];C[0][3] = A[0][0] * B[0][3] + A[0][1] * B[1][3] + A[0][2] * B[2][3] + A[0][3] * B[3][3];C[1][0] = A[1][0] * B[0][0] + A[1][1] * B[1][0] + A[1][2] * B[2][0] + A[1][3] * B[3][0];C[1][1] = A[1][0] * B[0][1] + A[1][1] * B[1][1] + A[1][2] * B[2][1] + A[1][3] * B[3][1];C[1][2] = A[1][0] * B[0][2] + A[1][1] * B[1][2] + A[1][2] * B[2][2] + A[1][3] * B[3][2];C[1][3] = A[1][0] * B[0][3] + A[1][1] * B[1][3] + A[1][2] * B[2][3] + A[1][3] * B[3][3];C[2][0] = A[2][0] * B[0][0] + A[2][1] * B[1][0] + A[2][2] * B[2][0] + A[2][3] * B[3][0];C[2][1] = A[2][0] * B[0][1] + A[2][1] * B[1][1] + A[2][2] * B[2][1] + A[2][3] * B[3][1];C[2][2] = A[2][0] * B[0][2] + A[2][1] * B[1][2] + A[2][2] * B[2][2] + A[2][3] * B[3][2];C[2][3] = A[2][0] * B[0][3] + A[2][1] * B[1][3] + A[2][2] * B[2][3] + A[2][3] * B[3][3];C[3][0] = A[3][0] * B[0][0] + A[3][1] * B[1][0] + A[3][2] * B[2][0] + A[3][3] * B[3][0];C[3][1] = A[3][0] * B[0][1] + A[3][1] * B[1][1] + A[3][2] * B[2][1] + A[3][3] * B[3][1];C[3][2] = A[3][0] * B[0][2] + A[3][1] * B[1][2] + A[3][2] * B[2][2] + A[3][3] * B[3][2];C[3][3] = A[3][0] * B[0][3] + A[3][1] * B[1][3] + A[3][2] * B[2][3] + A[3][3] * B[3][3];
}void Multi16(float A[16], float B[16], float C[16]) {C[0] = A[0] * B[0] + A[1] * B[4] + A[2] * B[8] + A[3] * B[12];C[1] = A[0] * B[1] + A[1] * B[5] + A[2] * B[9] + A[3] * B[13];C[2] = A[0] * B[2] + A[1] * B[6] + A[2] * B[10] + A[3] * B[14];C[3] = A[0] * B[3] + A[1] * B[7] + A[2] * B[11] + A[3] * B[15];C[4] = A[4] * B[0] + A[5] * B[4] + A[6] * B[8] + A[7] * B[12];C[5] = A[4] * B[1] + A[5] * B[5] + A[6] * B[9] + A[7] * B[13];C[6] = A[4] * B[2] + A[5] * B[6] + A[6] * B[10] + A[7] * B[14];C[7] = A[4] * B[3] + A[5] * B[7] + A[6] * B[11] + A[7] * B[15];C[8] = A[8] * B[0] + A[9] * B[4] + A[10] * B[8] + A[11] * B[12];C[9] = A[8] * B[1] + A[9] * B[5] + A[10] * B[9] + A[11] * B[13];C[10] = A[8] * B[2] + A[9] * B[6] + A[10] * B[10] + A[11] * B[14];C[11] = A[8] * B[3] + A[9] * B[7] + A[10] * B[11] + A[11] * B[15];C[12] = A[12] * B[0] + A[13] * B[4] + A[14] * B[8] + A[15] * B[12];C[13] = A[12] * B[1] + A[13] * B[5] + A[14] * B[9] + A[15] * B[13];C[14] = A[12] * B[2] + A[13] * B[6] + A[14] * B[10] + A[15] * B[14];C[15] = A[12] * B[3] + A[13] * B[7] + A[14] * B[11] + A[15] * B[15];
}int main() {clock_t start;double sum = 0.0;start = clock();//float a[4][4], b[4][4], c[4][4], d[4][4];//for (long i = 0; i < 6e7; i++) {//	for (uint8_t j = 0; j < 16; j++) {//		*(a[0] + j) = i * 0.03 + j * 0.02f;//		*(b[0] + j) = i * 0.02 + j * 0.03f;//	}//	Multi4x4(a, b, c);//	Multi4x4(b, c, d);//	sum += d[0][0] + d[1][1] + d[2][2] + d[3][3];//}float a[16], b[16], c[16], d[16];for (long i = 0; i < 6e7; i++) {for (uint8_t j = 0; j < 16; j++) {*(a + j) = i * 0.03 + j * 0.02f;*(b + j) = i * 0.02 + j * 0.03f;}Multi16(a, b, c);Multi16(b, c, d);sum += d[0] + d[5] + d[10] + d[15];}printf("sum=%f, time = %f s\n", sum, (double)(clock() - start) / CLOCKS_PER_SEC);
}

耗时对比:

                            Multi4x4        Multi16

           Debug        16.7s            12.1s

        Release          2.7s              2.7s


Debug模式下,Multi4x4中C[0][0]=A[0][0]*B[0][0]+A[0][1]*B[1][0]+A[0][2]*B[2][0]+A[0][3]*B[3][0]对应的汇编指令:

mov         eax,10h
imul        rax,rax,0
mov         rcx,qword ptr [A]
add         rcx,rax
mov         rax,rcx
mov         ecx,4
imul        rcx,rcx,0
mov         edx,10h
imul        rdx,rdx,0
mov         r8,qword ptr [B]
add         r8,rdx
mov         rdx,r8
mov         r8d,4
imul        r8,r8,0
movss       xmm0,dword ptr [rax+rcx]
mulss       xmm0,dword ptr [rdx+r8]
mov         eax,10h
imul        rax,rax,0
mov         rcx,qword ptr [A]
add         rcx,rax
mov         rax,rcx
mov         ecx,4
imul        rcx,rcx,1
mov         edx,10h
imul        rdx,rdx,1
mov         r8,qword ptr [B]
add         r8,rdx
mov         rdx,r8
mov         r8d,4
imul        r8,r8,0
movss       xmm1,dword ptr [rax+rcx]
mulss       xmm1,dword ptr [rdx+r8]
addss       xmm0,xmm1
mov         eax,10h
imul        rax,rax,0
mov         rcx,qword ptr [A]
add         rcx,rax
mov         rax,rcx
mov         ecx,4
imul        rcx,rcx,2
mov         edx,10h
imul        rdx,rdx,2
mov         r8,qword ptr [B]
add         r8,rdx
mov         rdx,r8
mov         r8d,4
imul        r8,r8,0
movss       xmm1,dword ptr [rax+rcx]
mulss       xmm1,dword ptr [rdx+r8]
addss       xmm0,xmm1
mov         eax,10h
imul        rax,rax,0
mov         rcx,qword ptr [A]
add         rcx,rax
mov         rax,rcx
mov         ecx,4
imul        rcx,rcx,3
mov         edx,10h
imul        rdx,rdx,3
mov         r8,qword ptr [B]
add         r8,rdx
mov         rdx,r8
mov         r8d,4
imul        r8,r8,0
movss       xmm1,dword ptr [rax+rcx]
mulss       xmm1,dword ptr [rdx+r8]
addss       xmm0,xmm1
mov         eax,10h
imul        rax,rax,0
mov         rcx,qword ptr [C]
add         rcx,rax
mov         rax,rcx
mov         ecx,4
imul        rcx,rcx,0
movss       dword ptr [rax+rcx],xmm0

Debug模式下,Multi16中C[0]=A[0]*B[0]+A[1]*B[4]+A[2]*B[8]+A[3]*B[12]对应的汇编指令:

mov         eax,4
imul        rax,rax,0
mov         ecx,4
imul        rcx,rcx,0
mov         rdx,qword ptr [A]
mov         r8,qword ptr [B]
movss       xmm0,dword ptr [rdx+rax]
mulss       xmm0,dword ptr [r8+rcx]
mov         eax,4
imul        rax,rax,1
mov         ecx,4
imul        rcx,rcx,4
mov         rdx,qword ptr [A]
mov         r8,qword ptr [B]
movss       xmm1,dword ptr [rdx+rax]
mulss       xmm1,dword ptr [r8+rcx]
addss       xmm0,xmm1
mov         eax,4
imul        rax,rax,2
mov         ecx,4
imul        rcx,rcx,8
mov         rdx,qword ptr [A]
mov         r8,qword ptr [B]
movss       xmm1,dword ptr [rdx+rax]
mulss       xmm1,dword ptr [r8+rcx]
addss       xmm0,xmm1
mov         eax,4
imul        rax,rax,3
mov         ecx,4
imul        rcx,rcx,0Ch
mov         rdx,qword ptr [A]
mov         r8,qword ptr [B]
movss       xmm1,dword ptr [rdx+rax]
mulss       xmm1,dword ptr [r8+rcx]
addss       xmm0,xmm1
mov         eax,4
imul        rax,rax,0
mov         rcx,qword ptr [C]
movss       dword ptr [rcx+rax],xmm0

 Release模式下,Multi4x4和Multi16的汇编指令一样:

movss       xmm5,dword ptr [rcx+4]
movss       xmm4,dword ptr [rcx+8]
movss       xmm3,dword ptr [rcx]
movaps      xmm1,xmm4
mulss       xmm1,dword ptr [rdx+20h]
movss       xmm2,dword ptr [rdx]
movss       xmm0,dword ptr [rdx+10h]
mulss       xmm2,xmm3
mulss       xmm0,xmm5
movaps      xmmword ptr [rax-18h],xmm6
movss       xmm6,dword ptr [rcx+0Ch]
addss       xmm2,xmm0
movaps      xmmword ptr [rax-28h],xmm7

        结论:在Debug模式下,一维数组的汇编指令(39行)比二维数组(75行)几乎少一半,速度更快。因为访问二维数组的元素需要先找到其所在的行,再找到其所在的列,相当于进行了两次数组访问。在Release模式下,两者的汇编指令一样,都用了SSE指令集进行优化加速,速度一样。但SSE指令是intel和AMD等电脑CPU才有的(Arm的Neon类似于SSE),单片机可没法用。因此,如果想在单片机上获得极致的速度,那还是使用一维数组吧。

        按照D-H表示法建立的4x4矩阵,第4行始终为[0, 0, 0, 1],做乘法以后也不会改变,因此,专门做此类4x4矩阵乘法的函数可以进一步优化,即只取前12个元素参与运算。

void Multi16(float A[12], float B[12], float C[12]) {C[0] = A[0] * B[0] + A[1] * B[4] + A[2] * B[8] ;C[1] = A[0] * B[1] + A[1] * B[5] + A[2] * B[9] ;C[2] = A[0] * B[2] + A[1] * B[6] + A[2] * B[10] ;C[3] = A[0] * B[3] + A[1] * B[7] + A[2] * B[11] + A[3] ;C[4] = A[4] * B[0] + A[5] * B[4] + A[6] * B[8] ;C[5] = A[4] * B[1] + A[5] * B[5] + A[6] * B[9] ;C[6] = A[4] * B[2] + A[5] * B[6] + A[6] * B[10] ;C[7] = A[4] * B[3] + A[5] * B[7] + A[6] * B[11] + A[7] ;C[8] = A[8] * B[0] + A[9] * B[4] + A[10] * B[8] ;C[9] = A[8] * B[1] + A[9] * B[5] + A[10] * B[9] ;C[10] = A[8] * B[2] + A[9] * B[6] + A[10] * B[10] ;C[11] = A[8] * B[3] + A[9] * B[7] + A[10] * B[11] + A[11] ;
}

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

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

相关文章

Linux ~ 查看日志的常用命令总结

1.tail -n <行数>&#xff0c;显示文件的尾部n行内容。 -f 循环读取&#xff0c;常用于查阅正在改变的日志文件。 ① tail -f test.log 实时显示test.log文件里的最尾部的内容&#xff0c;只要test.log更新就可以看到最新的文件内容。 ② tail -100f test.log 实时监控…

IOS面试题编程机制 41-45

41. lldb(gdb)常用的控制台调试命令?1). p 输出基本类型。是打印命令,需要指定类型。是print的简写 p (int)[[[self view] subviews] count] 2). po 打印对象,会调用对象description方法。是print-object的简写 po [self view] 3). expr 可以在调试时动态执行指定表达式,…

Vue3+echarts绘制世界地图

先放效果图 之前所查找的资料都没有讲清楚如何引入地图文件并绘制地图&#xff0c;下面做一个记录。 首先下载对应的地图json文件&#xff0c;这里可以参考我的这篇文章&#xff0c;提供了下载地址&#xff1a;记录echarts各种地图json文件下载地址-CSDN博客 第二步&#xff…

笔记本和台式机主板内部结构分析

笔记本和态势机主板内存接口以及配件安装位置 笔记本主板 1 以thinkpad L-490为例,使用拆机小工具拆机&#xff0c;打开后面板&#xff0c;内部结构示意图如下 台式机主板 以技嘉-B660M-AORUS-PRO-AX型号主板为例 笔记本电脑和台式机电脑的相同之处 CPU&#xff1a;笔记本…

IOS面试题编程机制 36-40

36. 阐述IOS ViewController生命周期?1. initWithCoder:通过nib文件初始化时触发。 2. awakeFromNib:nib文件被加载的时候,会发生一个awakeFromNib的消息到nib文件中的每个对象。 3. loadView:开始加载视图控制器自带的view。 4. viewDidLoad:视图控制器的view被加载完成…

【boost_search搜索引擎】1.获取数据源

boost搜索引擎 1、项目介绍2、获取数据源 1、项目介绍 boost_search项目和百度那种不一样&#xff0c;百度是全站搜索&#xff0c;而boost_search是一个站内搜索。而项目的宏观上实现思路就如同图上的思路。 2、获取数据源 我们要实现一个站内搜索&#xff0c;我们就要有这…

【No.15】蓝桥杯动态规划上|最少硬币问题|0/1背包问题|小明的背包1|空间优化滚动数组(C++)

DP初步&#xff1a;状态转移与递推 最少硬币问题 有多个不同面值的硬币(任意面值)数量不限输入金额S&#xff0c;输出最少硬币组合。 回顾用贪心求解硬币问题 硬币面值1、2、5。支付13元&#xff0c;要求硬币数量最少 贪心: (1)5元硬币&#xff0c;2个 (2)2元硬币&#xff0c…

【微服务】设计弹性微服务架构模式

目录 模式#1 — 超时模式#2 — 重试模式#3— 隔离模式#4— 断路器模式#5 — 冗余推荐超级课程: Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战在微服务架构中,服务通常相互协作以提供业务用例。这些服务可能在可用性、可伸缩性、弹性等方面具有…

LeetCode-热题100:3. 无重复字符的最长子串

题目描述 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 "abc"&#xff0c;所以其长度为 3。 示例 2: 输入: s “bbbbb” 输出: 1 解释: 因为无重复字…

数据分析-Pandas分类数据的比较如何避坑

数据分析-Pandas分类数据的比较如何避坑 数据分析和处理中&#xff0c;难免会遇到各种数据&#xff0c;那么数据呈现怎样的规律呢&#xff1f;不管金融数据&#xff0c;风控数据&#xff0c;营销数据等等&#xff0c;莫不如此。如何通过图示展示数据的规律&#xff1f; 数据表…

Lombok简单使用

1、介绍 Lombok是一个Java库&#xff0c;它通过注解的方式简化了Java代码的编写。它提供了一些注解&#xff0c;可以自动生成一些常用的代码&#xff0c;如getter和setter方法、构造函数、equals和hashCode方法等。使用Lombok可以减少冗余的代码&#xff0c;提高开发效率。 2…

Rust 程序设计语言学习——结构体

结构体和元组类似&#xff0c;它们都包含多个相关的值。和元组一样&#xff0c;结构体的每一部分可以是不同类型。但不同于元组&#xff0c;结构体需要命名各部分数据以便能清楚的表明其值的意义。由于有了这些名字&#xff0c;结构体比元组更灵活&#xff1a;不需要依赖顺序来…

医院预约挂号系统设计与实现|jsp+ Mysql+Java+ Tomcat(可运行源码+数据库+设计文档)

本项目包含可运行源码数据库LW&#xff0c;文末可获取本项目的所有资料。 推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java&#xff0c;…

【WPF应用11】如何对StackPanel中的控件进行间距设置?

在WPF中&#xff0c;堆叠面板&#xff08;StackPanel&#xff09;是一个常用的布局控件&#xff0c;它允许您将子控件垂直或水平堆叠起来。在设计用户界面时&#xff0c;合理的间距设置可以提高界面的美观性和易用性。本文将介绍如何在StackPanel控件中设置控件之间的间距&…

初识kafka-数据存储篇1

目录 背景 1 kafka总体体系结构 2 疑问解答 2.1 高吞吐低延迟 2.2 实现分布式存储和数据读取 2.3 如何保证数据不丢失 背景 最近在和产品过项目审批的时候&#xff0c;深刻感受到业务方对系统的时时响应提出了更高的要求。目前手上大部分的业务都是基础定时任务去实现的&…

nodejs+vue高校会议室预订管理系统python-flask-django-php

伴随着我国社会的发展&#xff0c;人民生活质量日益提高。于是对系统进行规范而严格是十分有必要的&#xff0c;所以许许多多的信息管理系统应运而生。此时单靠人力应对这些事务就显得有些力不从心了。所以本论文将设计一套高校会议室预订管理系统&#xff0c;帮助学校进行会议…

JDK,JRE,JVM之间的关系

他们明面上的关系是JDK包含JRE&#xff0c;JRE包含JVM。 简单理解JDK就是Java开发工具包。JRE是Java运行环境。JVM是Java虚拟机。 JDK是面向开发者的&#xff0c;JRE是面向JAVA程序的用户的。也就是说开发者开发JAVA程序是需要用到JDK&#xff0c;如果用户不去开发JAVA程序&am…

【WPF应用10】基本控件-StackPanel:布局原理与实际应用

在Windows Presentation Foundation&#xff08;WPF&#xff09;中&#xff0c;布局是用户界面设计的核心部分&#xff0c;它决定了控件如何排列和空间如何分配。WPF提供了一系列布局面板&#xff08;Panel&#xff09;&#xff0c;以便开发者可以根据需要灵活地组织控件。在这…

OpenHarmony IDL工具规格及使用说明书(仅对系统应用开放)

IDL接口描述语言简介 当客户端和服务器进行IPC通信时&#xff0c;需要定义双方都认可的接口&#xff0c;以保障双方可以成功通信&#xff0c;OpenHarmony IDL&#xff08;OpenHarmony Interface Definition Language&#xff09;则是一种定义此类接口的工具。OpenHarmony IDL先…

掌握 Unity 中的状态机:综合指南

作者简介: 高科,先后在 IBM PlatformComputing从事网格计算,淘米网,网易从事游戏服务器开发,拥有丰富的C++,go等语言开发经验,mysql,mongo,redis等数据库,设计模式和网络库开发经验,对战棋类,回合制,moba类页游,手游有丰富的架构设计和开发经验。 (谢谢你的关注…