单应矩阵定义及应用

1. 单应矩阵介绍

在张正友标定法的时候,可以使用单应矩阵来计算标定板平面和像素平面之间的变换关系,其本身包含了相机的内参和标定板与相机的外参矩阵。而在射影几何中,单应矩阵更多地用来表征两个平面之间的变换关系。对于标定时

s \begin{bmatrix} u\\v\\1\end{bmatrix}=A\begin{bmatrix} R&t\end{bmatrix} \begin{bmatrix} X_W\\Y_W\\Z_W\\1\end{bmatrix}

令世界点的Z_W=0,并将中间部分协作M,则有

s \begin{bmatrix} u\\v\\1\end{bmatrix}=M\begin{bmatrix} X_W\\Y_W\\1\end{bmatrix}

如果存在两个不同的相机,或者相同相机在不同位置派到同一个平面,则有

\begin{bmatrix} u_2\\v_2\\1\end{bmatrix}=M_2\begin{bmatrix} X_W\\Y_W\\1\end{bmatrix}=M_2M_1^{-1}\begin{bmatrix} u_1\\v_1\\1\end{bmatrix}=H\begin{bmatrix} u_1\\v_1\\1\end{bmatrix}

这表明,存在一组关系,可以实现两个像素平面的互相变换,具体的计算和标定时类似。

因为最后一行的1的缘故,H矩阵仍然只有8个自由度,所以只需要四组对应点即可计算出。

2. 单应矩阵在计算机视觉中的作用

1. 图像校正

使用四组对应点即可实现

2. 视角变换

 可以方便将普通视图转换为鸟瞰图

https://img-blog.csdnimg.cn/20181229155213634

3.图像拼接

因为单应矩阵可以进行视角转换,则可以将不同角度拍摄的图像转换到相同的视角,从而实现图像的拼接

 https://img-blog.csdnimg.cn/20181229155213680

 https://img-blog.csdnimg.cn/20181229155213726

4. 增强现实(AR)

平面二维标记图案长用来做AR展示,根据marker不同视角下的图像可以方便地得到虚拟物体的位置姿态并进行显示 

https://img-blog.csdnimg.cn/20181229155213765

 3.单应矩阵求解

\begin{bmatrix} u_2\\v_2\\1\end{bmatrix}=H\begin{bmatrix} u_1\\v_1\\1\end{bmatrix}=\begin{bmatrix} h_1&h_2&h_3\\h_4&h_5&h_6\\h_7&h_8&h_9\end{bmatrix}\begin{bmatrix} u_1\\v_1\\1\end{bmatrix}

h_9=1 

u_2=\frac{h_1u_1+h_2u_2+h_3}{h_7u_1+h_8v_1+1}

v_2=\frac{h_4u_1+h_5u_2+h_6}{h_7u_1+h_8v_1+1}

整理可得

u_2(h_7u_1+h_8v_1+1)=h_1u_1+h_2u_2+h_3

v_2(h_7u_1+h_8v_1+1)=h_4u_1+h_5u_2+h_6

整理成矩阵形式

\begin{bmatrix} u_1&u_2&1&0&0&0&-u_1u_2&-v_1u_2\\0&0&0&u_1&u_2&1&-u_1v_2&-v_1v_2 \end{} \begin{bmatrix}h_1\\h_2\\h_3\\h_4\\h_5\\h_6\\h_7\\h_8\end{bmatrix}=\begin{bmatrix}u_2\\v_2 \end{}

所以只需要四组点即可求解出单应矩阵

但是再实际中,我们计算的关键点对会包含噪声,甚至出现误匹配的现象,所以只使用4组点来计算单应矩阵会出现很大的误差。因此一般会使用多于四组点来计算。另外直接法求解很难得到最优解,所以实际中会使用其他优化方法进行求解,比如SVD、LM等算法。

在opencv中有现成的函数可以进行调用

Mat findHomography(InputArray srcPoints, InputArray dstPoints, int method=0, double ransacReprojThreshold=3, OutputArray mask=noArray() )

从函数中看,只需要输入对应的匹配点,指定具体计算方法即可输出结果。

4. 图像拼接代码示例

输入:两张图片

​​​​​​

 输出:将图二拼接到图一

 代码:virtual_billboard.cpp

#include <opencv/cv.h>
#include <opencv2/imgproc/imgproc_c.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <vector>
#include <iostream>

class ImageMosic{
public:
    ImageMosic(cv::Mat img_src,cv::Mat img_target){
        img_src_ = img_src.clone();
        img_target_ = img_target.clone();
        SetSrcPts();
        SetTargetPts();
        CalcHAndPerspective();
    }

private:
    struct UserData{
        cv::Mat img;
        std::vector<cv::Point2f> pts;
    };
    void SetSrcPts()
    {
        cv::Size src_size = img_src_.size();
        src_pts_.push_back(cv::Point2f(0, 0));
        src_pts_.push_back(cv::Point2f(src_size.width-1, 0));
        src_pts_.push_back(cv::Point2f(src_size.width-1, src_size.height-1));
        src_pts_.push_back(cv::Point2f(0, src_size.height-1));
    }

    static void mouseHandler(int event, int x, int y, int flags,void* data_ptr)
    {
        if (event ==cv::EVENT_LBUTTONDOWN)
        {
            UserData *data = (UserData *)data_ptr;
            cv::circle( data->img, cv::Point(x, y), 3, cv::Scalar(0, 255, 255), 5);
            cv::imshow("Image_target", data->img);
            if (data->pts.size() < 4)
            {
                data->pts.push_back(cv::Point2f(x,y));
            }
        }
    }

    void SetTargetPts(){
        //show the image
        cv::imshow("Image_target", img_target_);
        std::cout << "Click on four corners of a billboard and then press ENTER" << std::endl;
        //set the callback function for any mouse event
        UserData user_data;
        user_data.img = img_target_;
        cv::setMouseCallback("Image_target", mouseHandler, &user_data);
        cv::waitKey(0);
        target_pts_ = user_data.pts;
    }
    void CalcHAndPerspective(){
        assert(target_pts_.size() == 4);
        cv::Mat H = cv::findHomography(src_pts_, target_pts_, 0);                   //计算单应矩阵
        cv::warpPerspective(img_src_, img_src_perspective_, H, img_target_.size()); //透视变换
        cv::Point pts_dst[4] = {target_pts_[0], target_pts_[1], target_pts_[2], target_pts_[3]};
        cv::fillConvexPoly(img_target_, pts_dst, 4, cv::Scalar(0));
        img_target_ = img_target_ + img_src_perspective_;

        cv::imshow("Image_target", img_target_);
        cv::waitKey(0);
    }
private:
    cv::Mat img_src_;
    cv::Mat img_src_perspective_;
    cv::Mat img_target_;
    std::vector<cv::Point2f> src_pts_;
    std::vector<cv::Point2f> target_pts_;
};

int main(){
    cv::Mat img_src = cv::imread("../images/cvlife.jpg");
    cv::Mat img_target = cv::imread("../images/ad.jpg");
    ImageMosic img_mosaic(img_src, img_target);
   
    return 0;
}

 CMakelists.txt

cmake_minimum_required(VERSION 1.0)
project(virtual_billboard)

find_package(OpenCV)

add_executable(virtual-billboard src/virtual_billboard.cpp)
target_link_libraries(virtual-billboard ${OpenCV_LIBS})

 操作步骤:在图片上从左上角顺时针选4个点,按enter结束

 结果:

5. 旋转平移恢复

        与本质矩阵类似,单应矩阵需要进行分解,才能得到旋转平移矩阵。分解的方法包含数值法[2][3]和解析法[4]。与本质矩阵类似,单应矩阵的分解也会返回4组旋转平移矩阵,并且同时可以解算出他们分别对应的场景点所在平面的法向量。如果已知成像的地图点的深度全为正值(即在相机前方),则可以排除两组。最后剩余两组解,需要更多的先验信息进行判断。通常我们可以通过假设已知场景平面的法向量来解决,如场景平面与相机平面平行,那么法向量的理论值为\textbf{1}^T

        单应性在SLAM中有重要意义。当特征点共面或相机发生纯旋转时,基础矩阵自由度下降,出现退化。现实中数据总会包含一些噪声,这是继续使用八点法求解基础矩阵,其多余出来的自由度将会主要由噪声决定。为了避免退化现象造成影响,通常我们会同时估计基础矩阵F和单应矩阵H,选择重投影误差较小的那个作为最终的运动估计矩阵。

6.参考

[1]单应性矩阵的理解及求解_机器视觉001的博客-CSDN博客_单应性矩阵

[2]Faugeras O D, Lustman F. Motion and structure from motion in a piecewise planar environment[J]. International Journal of Pattern Recognition and Artificial Intelligence, 1988, 2(03): 485-508.

[3]Zhang Z, Hanson A R. 3D reconstruction based on homography mapping[J]. Proc. ARPA96, 1996: 1007-1012.

[4]Malis E, Vargas M. Deeper understanding of the homography decomposition for vision-based control[D]. INRIA, 2007.

  • 6
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
文库帮手网 www.365xueyuan.com 免费帮下载 百度文库积分 资料 本文由pengliuhua2005贡献 doc文档可能在WAP端浏览体验不佳。建议您优先选择TXT,或下载源文件到本机查看。 51 单片机设计跑马灯的程序用(c 语言)编写 P1 口接 8 个发光二极管共阳,烧入下面程序 #include unsigned char i; unsigned char temp; unsigned char a,b; void delay(void) { unsigned char m,n,s; for(m=20;m>0;m--) for(n=20;n>0;n--) for(s=248;s>0;s--); } void main(void) { while(1) { temp=0xfe; P1=temp; delay(); for(i=1;i<8;i++) { a=temp(8-i); P1=a|b; delay(); } for(i=1;i>i; b=temp<= 4000 ){ us250 = 0; if( ++s1 >= 10 ){ s1 = 0; if( ++s10 >= 6 ) s10 = 0; if( key10 == 1 ){ //等松键 if( P3.2 == 1 ) key10=0; } //未按键 37. else{ 38. 39. 40. 41. if( P3.2 == 0 ){ key10 = 1; if( ++s10 >= 6 ) s10 = 0; break; //结束“循环 2”,修改显示 42. 43. 44. 45. 46. } } //按个位键处理 P3.3 = 1; //P3.3 作为输入,先要输出高电平 if( key1 == 1 ) //等松键 47. { if( P3.3 == 1 ) key1=0; } 48. 49. 50. 51. 52. 53. 54. 55. } } //循环 2’end }//循环 1’end } else { //未按键 if( P3.3 == 0 ){ key1 = 1; if( ++s1 >= 10 ) s1 = 0; break; //结束“循环 2”,修改显示 56. }//main’end 第三节: 第三节:十字路口交通灯 如果一个单位时间为 1 秒,这里设定的十字路口交通灯按如下方式四个步骤循环工作: 60 个单位时间,南北红,东西绿;λ 10 个单位时间,南北红,东西黄;λ 60 个单位时间,南北绿,东西红;λ 10 个单位时间,南北黄,东西红;λ 解:用 P1 端口的 6 个引脚控制交通灯,高电平灯亮,低电平灯灭。 代码 1. 2. 3. 4. 5. 6. 7. 8. 9. #include //sbit 用来定义一个符号位地址,方便编程,提高可读性,和可移植性 sbit SNRed =P1^0; //南北方向红灯 //南北方向黄灯 //南北方向绿灯 //东西方向红灯 //东西方向黄灯 //东西方向绿灯 sbit SNYellow =P1^1; sbit SNGreen =P1^2; sbit EWRed =P1^3; sbit EWYellow =P1^4; sbit EWGreen =P1^5; /* 用软件产生延时一个单位时间 */ 10. void Delay1Unit( void ) 11. { 12. 13. 14. unsigned int i, j; for( i=0; i<1000; i++ ) for( j<0; j= 8 ) i=0; 12. } 13. void Timer0IntRoute( void ) interrupt 1 14. { 15. 16. TL0 = -1000; //由于 TL0 只有 8bits,所以将(-1000)低 8 位赋给 TL0 TH0 = (-1000)>>8; //取(-1000)的高 8 位赋给 TH0,重新定时 1ms 17. 18. } DisplayBrush(); 19. void Timer0Init( void ) 20. { TMOD=(TMOD & 0xf0) | 0x01; //初始化,定时器 T0,工作方式 1 21. 22. 23. 24. 25. } 26. void Display( unsigned char index, unsigned char dataValue ){ DisBuf[ inde x ] = dataValue; } 27. void main( void ) 28. { 29. unsigned char i; 30. for( i=0; i>8; TR0 = 1; ET0 = 1; //允许 T0 开始计数 //允许 T0 计数溢出时产生中断请求 第五节:键盘驱动 第五节: 指提供一些函数给任务调用,获取按键信息,或读取按键值。 定义一个头文档 ,描述可用函数,如下: 代码 1. 2. 3. 4. 5. 6. 7. #ifndef _KEY_H_ #define _KEY_H_ //防止重复引用该文档,如果没有定义过符号 _KEY_H_,则编译下面语句 防止重复引用该文档, , 防止重复引用该文档 //只要引用过一次,即 #include ,则定义符号 _KEY_H_ 只要引用过一次, 只要引用过一次 , unsigned char keyHit( void ); //如果按键,则返回非0,否则返回0 unsigned char keyGet( void ); //读取按键值,如果没有按键则等待到按键为止 void keyPut( unsigned char ucKeyVal ); //保存按键值 ucKeyVal 到按键缓冲队列末 void keyBack( unsigned char ucKeyVal ); //退回键值 ucKeyVal 到按键缓冲队列首 #endif 定义函数体文档 KEY.C,如下: 代码 1. 2. 3. #include “key.h” #define KeyBufSize 16 //定义按键缓冲队列字节数 定义按键缓冲队列字节数 unsigned char KeyBuf[ KeyBufSize ]; //定义一个无符号字符数组作为按键缓冲队列。该队列为 先进 4. 5. 6. 7. 8. 9. 10. //先出,循环存取,下标从0到 KeyBufSize-1 unsigned char KeyBufWp=0; //作为数组下标变量,记录存入位置 unsigned char KeyBufRp=0; //作为数组下标变量,记录读出位置 //如果存入位置与读出位置相同,则表明队列中无按键数据 unsigned char keyHit( void ) { if( KeyBufWp == KeyBufRp ) return( 0 ); else return( 1 ); } 11. unsigned char keyGet( void ) 12. { unsigned char retVal; //暂存读出键值 13. while( keyHit()==0 ); //等待按键,因为函数 keyHit()的返回值为 0 表示无按键 14. retVal = KeyBuf[ KeyBufRp ]; //从数组中读出键值 15. if( ++KeyBufRp >= KeyBufSize ) KeyBufRp=0; //读位置加1, 超出队列则循环回初始位置 16. 17. } 18. 19. void keyPut( unsigned char ucKeyVal ) 20. { KeyBuf[ KeyBufWp ] = ucKeyVal; //键值存入数组 21. if( ++KeyBufWp >= KeyBufSize ) KeyBufWp=0; //存入位置加1, 超出队列则循环回初始位置 return( retVal ); 22. } 23. 由于某种原因,读出的按键,没有用,但其它任务要用该按键,但传送又不方便。此时可以退回按键队列。 就如取错了信件,有必要退回一样 24. void keyBack( unsigned char ucKeyVal ) 25. { 26. 27. 如果 KeyBufRp=0; 减 1 后则为 FFH,大于 KeyBufSize,即从数组头退回到数组尾。或者由于干扰使得 KeyBufRp 超出队列位置,也要调整回到正常位置, 28. */ 29. if( --KeyBufRp >= KeyBufSize ) KeyBufRp=KeyBufSize-1; 30. KeyBuf[ KeyBufRp ] = ucKeyVal; //回存键值 31. } 下面渐进讲解键盘物理层的驱动。 电路共同点:P2 端口接一共阴数码管,共阴极接 GND,P2.0 接 a 段、P2.1 接 b 段、…、P2.7 接 h 段。 软件共同点:code unsigned char Seg7Code[10] 是七段数码管共阴编码表。 Code unsigned char Seg7Code[16]= // 0 1 2 3 4 5 6 7 8 9 A b C d E F {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71}; 例一:P1.0 接一按键到 GND,键编号为‘6’,显示按键。 代码 1. 2. 3. 4. 5. 6. 7. 8. 9. #include #include “KEY.H” void main( void ) { P1_0 = 1; //作为输入引脚,必须先输出高电平 while( 1 ) //永远为真,即死循环 { if( P1_0 == 0 ) //如果按键,则为低电平 { keyPut( 6 ); //保存按键编号值为按键队列 while( P1_0 == 0 ); //如果一直按着键,则不停地执行该循环,实际是等待松键 } 10. if( keyHit() != 0 ) //如果队列中有按键 11. P2=Seg7Code[ keyGet() ]; //从队列中取出按键值,并显示在数码管上 12. 13. } } 例二:在例一中考虑按键 20ms 抖动问题。 代码 1. 2. 3. 4. 5. 6. 7. 8. 9. #include #include “KEY.H” void main( void ) { P1_0 = 1; //作为输入引脚,必须先输出高电平 while( 1 ) //永远为真,即死循环 { if( P1_0 == 0 ) //如果按键,则为低电平 { delay20ms(); //延时 20ms,跳过接下抖动 keyPut( 6 ); //保存按键编号值为按键队列 while( P1_0 == 0 ); //如果一直按着键,则不停地执行该循环,实际是等待松键 10. delay20ms(); //延时 20ms,跳过松开抖动 11. } 12. if( keyHit() != 0 ) //如果队列中有按键 13. P2=Seg7Code[ keyGet() ]; //从队列中取出按键值,并显示在数码管上 14. 15. } } 例三:在例二中考虑干扰问题。即小于 20ms 的负脉冲干扰。 代码 1. 2. 3. 4. 5. 6. #include #include “KEY.H” void main( void ) { P1_0 = 1; //作为输入引脚,必须先输出高电平 while( 1 ) //永远为真,即死循环 { if( P1_0 == 0 ) //如果按键,则为低电平 7. 8. 9. 10. { delay20ms(); //延时 20ms,跳过接下抖动 if( P1_0 == 1 ) continue; //假按键 keyPut( 6 ); //保存按键编号值为按键队列 while( P1_0 == 0 ); //如果一直按着键,则不停地执行该循环,实际是等待松键 11. delay20ms(); //延时 20ms,跳过松开抖动 12. } 13. if( keyHit() != 0 ) //如果队列中有按键 14. P2=Seg7Code[ keyGet() ]; //从队列中取出按键值,并显示在数码管上 15. 16. } } 例四:状态图编程法。通过 20ms 周期中断,扫描按键。 代码 采用晶体为 12KHz 时,指令周期为 1ms(即主频为 1KHz),这样 T0 工作在定时器方式 2,8 位自动重载。 计数值为 20,即可产生 20ms 的周期性中断,在中断服务程序中实现按键扫描 2. 3. 4. 5. 6. 7. 8. 9. #include #include “KEY.H” void main( void ) { TMOD = (TMOD & 0xf0 ) | 0x02; //不改变 T1 的工作方式,T0 为定时器方式 2 TH0 = -20; TL0=TH0; TR0=1; //计数周期为 20 个主频脉,即 20ms //先软加载一次计数值 //允许 T0 开始计数 //允许 T0 计数溢出时产生中断请求 //允许 CPU 响应中断请求 1. 10. ET0=1; 11. EA=1; 12. while( 1 ) //永远为真,即死循环 13. { 14. if( keyHit() != 0 ) //如果队列中有按键 15. P2=Seg7Code[ keyGet() ]; //从队列中取出按键值,并显示在数码管上 16. 17. } 18. void timer0int( void ) interrupt 1 //20ms;T0 的中断号为 1 19. { static unsigned char sts=0; 20. P1_0 = 1; //作为输入引脚,必须先输出高电平 } 21. switch( sts ) 22. 23. 24. { case 0: if( P1_0==0 ) sts=1; break; //按键则转入状态 1 case 1: //假按错,或干扰,回状态 0 25. if( P1_0==1 ) sts=0; 26. else{ sts=2; keyPut( 6 ); } //确实按键,键值入队列,并转状态 2 27. break; 28. case 2: if( P1_0==1 ) sts=3; break; //如果松键,则转状态 3 29. 30. 31. 32. 33. } } case 3: if( P1_0==0 ) sts=2; else sts=0; //假松键,回状态 2 //真松键,回状态 0,等待下一次按键过程 例五:状态图编程法。 代码 如果采用晶体为 12MHz 时,指令周期为 1us(即主频为 1MHz),要产生 20ms 左右的计时,则计数值达到 20000,T0 工作必须为定时器方式 1,16 位非自动重载,即可产生 20ms 的周期性中断,在中断服务程序中 实现按键扫描 2. 3. 4. 5. 6. 7. 8. 9. #include #include “KEY.H” void main( void ) { TMOD = (TMOD & 0xf0 ) | 0x01; //不改变 T1 的工作方式,T0 为定时器方式 1 TL0 = -20000; TH0 = (-20000)>>8; TR0=1; //计数周期为 20000 个主频脉,自动取低 8 位 //右移 8 位,实际上是取高 8 位 1. //允许 T0 开始计数 //允许 T0 计数溢出时产生中断请求 //允许 CPU 响应中断请求 10. ET0=1; 11. EA=1; 12. while( 1 ) //永远为真,即死循环 13. { 14. if( keyHit() != 0 ) //如果队列中有按键 15. P2=Seg7Code[ keyGet() ]; //从队列中取出按键值,并显示在数码管上 16. 17. } 18. void timer0int( void ) interrupt 1 //20ms;T0 的中断号为 1 19. { static unsigned char sts=0; 20. TL0 = -20000; 21. TH0 = (-20000)>>8; 22. P1_0 = 1; //方式 1 为软件重载 //右移 8 位,实际上是取高 8 位 } //作为输入引脚,必须先输出高电平 23. switch( sts ) 24. 25. 26. { case 0: if( P1_0==0 ) sts=1; break; //按键则转入状态 1 case 1: //假按错,或干扰,回状态 0 27. if( P1_0==1 ) sts=0; 28. else{ sts=2; keyPut( 6 ); } //确实按键,键值入队列,并转状态 2 29. break; 30. 31. 32. 33. case 2: if( P1_0==1 ) sts=3; break; //如果松键,则转状态 3 case 3: if( P1_0==0 ) sts=2; else sts=0; //假松键,回状态 2 //真松键,回状态 0,等待下一次按键过程 34. 35. } } 例六:4X4 按键。 代码 由 P1 端口的高 4 位和低 4 位构成 4X4 的矩阵键盘, 本程序只认为单键操作为合法, 同时按多键时无效。 这样下面的 X,Y 的合法值为 0x7, 0xb, 0xd, 0xe, 0xf,通过表 keyCode 影射变换可得按键值 1. 2. 3. 4. 5. 6. 7. 8. #include #include “KEY.H” unsigned char keyScan( void ) //返回 0 表示无按键,或无效按键,其它值为按键编码值 { code unsigned char keyCode[16]= /0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0 xF 9. { 0, }; 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 3, 4, 0 10. unsigned char x, y, retVal; 11. P1=0x0f; 12. x=P1&0x0f; 13. P1=0xf0; //低四位输入,高四位输出 0 //P1 输入后,清高四位,作为 X 值 //高四位输入,低四位输出 0 14. y=(P1 >> 4) & 0x0f; //P1 输入后移位到低四位,并清高四位,作为 Y 值 15. retVal = keyCode[x]*4 + keyCode[y]; //根据本公式倒算按键编码 16. if( retVal==0 ) return(0); else return( retVal-4 ); 17. } 18. //比如按键‘1’,得 X=0x7,Y=0x7,算得 retVal= 5,所以返回函数值 1。 19. //双如按键‘7’,得 X=0xb,Y=0xd,算得 retVal=11,所以返回函数值 7。 20. void main( void ) 21. { 22. TMOD = (TMOD & 0xf0 ) | 0x01; //不改变 T1 的工作方式,T0 为定时器方式 1 23. TL0 = -20000; 24. TH0 = (-20000)>>8; 25. TR0=1; 26. ET0=1; 27. EA=1; //计数周期为 20000 个主频脉,自动取低 8 位 //右移 8 位,实际上是取高 8 位 //允许 T0 开始计数 //允许 T0 计数溢出时产生中断请求 //允许 CPU 响应中断请求 28. while( 1 ) //永远为真,即死循环 29. { 30. if( keyHit() != 0 ) //如果队列中有按键 31. P2=Seg7Code[ keyGet() ]; //从队列中取出按键值,并显示在数码管上 32. 33. } 34. void timer0int( void ) interrupt 1 //20ms;T0 的中断号为 1 } 35. { static unsigned char sts=0; 36. TL0 = -20000; 37. TH0 = (-20000)>>8; 38. P1_0 = 1; //方式 1 为软件重载 //右移 8 位,实际上是取高 8 位 //作为输入引脚,必须先输出高电平 39. switch( sts ) 40. 41. 42. { case 0: if( keyScan()!=0 ) sts=1; break; //按键则转入状态 1 case 1: //假按错,或干扰,回状态 0 43. if( keyScan()==0 ) sts=0; 44. else{ sts=2; keyPut( keyScan() ); } //确实按键,键值入队列,并转状态 2 45. break; 46. 47. 48. 49. 50. 51. } } case 2: if(keyScan()==0 ) sts=3; break; //如果松键,则转状态 3 case 3: if( keyScan()!=0 ) sts=2; else sts=0; //假松键,回状态 2 //真松键,回状态 0,等待下一次按键过程 第六节: 第六节:低频频率计 实例目的:学时定时器、计数器、中断应用 说明:选用 24MHz 的晶体,主频可达 2MHz。用 T1 产生 100us 的时标,T0 作信号脉冲计数器。假设 晶体频率没有误差,而且稳定不变(实际上可达万分之一);被测信号是周期性矩形波(正负脉冲宽 度都不能小于 0.5us),频率小于 1MHz,大于 1Hz。要求测量时标 1S,测量精度为 0.1%。 解:从测量精度要求来看,当频率超过 1KHz 时,可采用 1S 时标内计数信号脉冲个数来测量信号频, 而信号频率低于 1KHz 时,可以通过测量信号的周期来求出信号频率。两种方法自动转换。 对于低于 1KHz 的信号,信号周期最小为 1ms,也就是说超过 1000us,而我们用的定时器计时脉冲周 期为 0.5us,如果定时多计或少计一个脉冲,误差为 1us,所以相对误差为 1us/1000us=0.1%。信号 周期越大,即信号频率越低,相对误差就越小。 从上面描述来看,当信号频率超过 1KHz 后,信号周期就少于 1000us,显然采用上面的测量方法,不 能达到测量精度要求,这时我们采用 1S 单位时间计数信号的脉冲个数,最少能计到 1000 个脉冲,由 于信号频率不超过 1MHz,而我们定时脉冲为 2MHz,最差多计或少计一个信号脉冲,这样相对误差为 1/1000,可见信号频率越高,相对误差越小。 信号除输入到 T1(P3.5)外,还输入到 INT1(P3.3)。 代码 //对 100us 时间间隔单位计数,即有多少个 100us。 1. 2. 3. 4. 5. 6. 7. unsigned int us100; unsigned char Second; unsigned int K64; unsigned char oldT0; //对 64K 单位计数,即有多少个 64K unsigned int oldus, oldK64, oldT1; unsigned long fcy; bit HighLow=1; //存放频率值,单位为 Hz //1:表示信号超过 1KHz;0:表示信号低于 1KHz。 8. 9. 10. void InitialHigh( void ) { IE=0; IP=0; HighLow=1; 11. TMOD = (TMOD & 0xf0) | 0x02; TH0=-200; TL0=TH0; PX0=1; T0=1; 12. 13. 14. 15. 16. 17. } 18. void InitialLow( void ) 19. { 20. IE=0; IP=0; HighLow=0; TMOD = (TMOD & 0x0f) | 0x50; TH1=0; TL1=0; T1=1; ET1=1; Us100=0; Second=0; K64=0; oldK64=0; oldT1=0; TCON |= 0x50; EA = 1; //同时置 TR0=1; TR1=1; 同时置 21. TMOD = (TMOD & 0xf0) | 0x02; TH0=-200; TL0=TH0; ET0=1; TR0=1; 22. 23. 24. 25. 26. } 27. void T0intr( void ) interrupt 1 28. { if( HighLow==0 ) ++us100; 29. else 30. if( ++us100 >= 10000 ) 31. { unsigned int tmp1, tmp2; INT1 = 1; IT1=1; EX1=1; Us100=0; Second=0; K64=0; oldK64=0; oldT1=0; EA = 1; 32. TR1=0; tmp1=(TH1<<8) + (TL1); tmp2=K64; TR1=1; 33. fcy=((tmp2-oldK64)<<16) + (tmp1-oldT1); 34. oldK64=tmp1; oldT1=tmp2; 35. Second++; 36. us100=0; 37. } 38. } 39. void T1intr( void ) interrupt 3 { ++K64; } 40. void X1intr( void ) interrupt 2 41. { static unsigned char sts=0; 42. switch( sts ) 43. { 44. case 0: sts = 1; break; 45. case 1: oldT0=TL0; oldus=us100; sts=2; break; 46. case 2: 47. { 48. 49. 50. 51. 52. } 53. 54. 55. Sts = 0; break; } unsigned char tmp1, tmp2; TR0=0; tmp1=TL0; tmp2=us100; TR0=1; fcy = 1000000L/( (tmp2-oldus)*100L + (256-tmp1)/2 ); Second ++; 56. } 57. void main( void ) 58. { 59. if( HighLow==1) InitialHigh(); else InitialLow(); 60. 61. While(1) { 62. if( Second != 0 ) 63. { 64. Second = 0; 65. //display fcy 引用前面的数码管驱动程序, 引用前面的数码管驱动程序,注意下面对 T0 中断服务程序的修改 66. { unsigned char i; 67. 68. } 69. if( HighLow==1 ) 70. if( fcy1000L ){ InitalHigh();} for( i=0; i= 10000 ) 83. { unsigned int tmp1, tmp2; 84. TR1=0; tmp1=(TH1<<8) + (TL1); tmp2=K64; TR1=1; 85. fcy=((tmp2-oldK64)<= 10 ){ ms=0; DisplayBrush(); } //1ms 数码管刷新 第七节: 第七节:电子表 单键可调电子表:主要学习编程方法。 外部中断应用,中断嵌 解:电子表分为工作状态和调整状态。平时为工作状态,按键不足一秒,接键为换屏‘S’。按键超过一 秒移位则进入调整状态‘C’,而且调整光标在秒个位开始。调整状态时,按键不足一秒为光标移动‘M’, 超过一秒则为调整读数,每 0.5 秒加一‘A’,直到松键;如果 10 秒无按键则自动回到工作状态‘W’。 如果有年、月、日、时、分、秒。四联数码管可分三屏显示,显示格式为“年月.”、“日.时.”、“分.秒”, 从小数点的位置来区分显示内容。(月份的十位数也可以用“-”和“-1”表示)。 代码 1. 2. 3. enum status = { Work, Change, Add, Move, Screen } //状态牧举 //计时和调整都是对下面时间数组 Time 进行修改 unsigned char Time[12]={0,4, 0,6, 1,0, 0,8, 4,5, 3,2}; //04 年 06 月 10 日 08 时 45 分 32 秒 4. 5. 6. 7. unsigned char cursor = 12; //指向秒个位,=0 时无光标 unsigned char YmDhMs = 3; //指向“分秒”显示 ,=0 时无屏显 static unsigned char sts = Work; 如果 cursor 不为 0,装入 DisBuf 的对应数位,按 0.2 秒周期闪烁,即设一个 0.1 秒计数器 S01,S01 为奇数时灭,S01 为偶数时亮。 8. 9. 小数点显示与 YmDhMs 变量相关。 */ 10. void DisScan( void ) //动态刷新显示时调用。没编完,针对共阴数码管,只给出控控制算法 11. { 12. //DisBuf 每个显示数据的高四位为标志,最高位 D7 为负号,D6 为小数点,D5 为闪烁 13. unsigned char tmp; 14. 15. 16. 17. 18. 19. } 20. void Display( void ) 21. { 22. if( cursor != 0 ){ YmDhMs=(cursor+3)/4; } //1..4=1; 5..8=2; 9..12=3 //根据状态进行显示 tmp = Seg7Code[?x & 0x1f ]; //设?x 为显示数据,高 3 位为控制位,将低 5 位变为七段码 if( ?x & 0x40 ) tmp |= 0x80; //添加小数点 if( ?x & 0x20 ){ if( S01 & 0x01 ) tmp=0; } //闪烁,S01 奇数时不亮 //这里没有处理负号位 //将 tmp 送出显示,并控制对应数码管动作显示 23. for( i=(YmDhMs-1)*4; i ‘9’) Dat=‘0’; } 二、 在上题的基础上,改为 2400bps,循环发送小写字母‘a’到‘z’,然后是大写字母‘A’到‘Z’。 代码 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. #include void main( void ) { TMOD = (TMOD & 0x0F) | 0x20; TH1 = -96; //注意不用倍频方式 PCON &= 0x7F; //SMOD = 0 TR1 = 1; SCON = 0x42; while( 1 ) { if( TI==1 ) { static unsigned char Dat=‘a’; SBUF = Dat; TI = 0; //If( ++Dat > ‘9’) Dat=‘0’; ++Dat; if( Dat == (‘z’+1) ) if( Dat == (‘Z’+1) ) } } Dat=‘A’; Dat=‘a’; 22. } 上述改变值时,也可以再设一变量表示当前的大小写状态,比如写成如下方式: 代码 1. 2. 3. 4. ++Dat; { static unsigned char Caps=1; if( Caps != 0 ) 5. 6. 7. 8. } if( Dat>‘Z’){ Dat=‘a’; Caps=0; } else if( Dat>‘z’){ Dat=‘A’; Caps=1; } 如下写法有错误:因为小 b 比大 Z 的编码值大,所以 Dat 总是‘a’ 代码 1. 2. 3. ++Dat; if( Dat>‘Z’){ Dat=‘a’} else if( Dat>‘z’){ Dat=‘A’} 三、 有 A 和 B 两台单片机,晶体频率分别为 13MHz 和 14MHz,在容易编程的条件下,以最快的速度进 行双工串行通信,A 给 B 循环发送大写字母从‘A’到‘Z’,B 给 A 循环发送小写字母从‘a’到‘z’,双方都用 中断方式进行收发。 解:由于晶体频率不同,又不成 2 倍关系,所以只有通信方式 1 和方式 3,由于方式 3 的帧比方式 1 多一位,显然方式 3 的有效数据(9/11)比方式 1(8/10)高,但要用方式 3 的第 9 位 TB8 来发送数 据,编程难度较大,这里方式 1 较容易编程。 在计算最高速率时,由于单方程,双未知数,又不知道波特率为多少,所以要综合各方面的条件,估 算出 A 和 B 的分频常数,分别为-13 和-14 时,速率不但相同,且为最大值。如下给出 A 机的程序: 代码 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. #include void main( void ) { TMOD = (TMOD & 0x0F) | 0x20; TH1 = -13; //注意用倍频方式 PCON |= 0x80; //SMOD = 1 TR1 = 1; SCON = 0x52; //REN = 1 ES = 1; EA = 1; while( 1 ); 12. } 13. void RS232_intr( void ) interrupt 4 14. { 15. 16. 17. 18. 19. 20. unsigned char rDat; if( RI == 1 ){ RI=0; rDat=SBUF; } if( TI==1 ) { static unsigned char tDat=‘a’; SBUF = tDat; //注意 RI 和 TI 任一位变为 1 都中断 21. 22. 23. 24. } } TI = 0; If( ++Dat > ‘z’) Dat=‘a’; 四、 多机通位 在方式 2 和方式 3,SM2 只对接收有影 响,当 SM2=1 时,只接收第 9 位等于 1 的帧(伪地址帧), 而 SM2=0 时,第 9 位不影响接收。λ 多机通信中,地址的确认与本机程序有关,所以可以实现点对点、点对组、以及通播方式的通信。λ 如果收发共用一总线,任何时刻只有一个发送源能占用总线发送数据,否则发生冲突。由此可构造无 竞争的令牌网;或者多主竞争总线网。λ 1
中南大学有限单元法考试试卷(岳乐-庞俊)。 有限元分析复习点点滴滴——张义涵 1402 1、 什么是平面应力问题?什么是平面应变问题?(张毅涵做) 答:平面应变问题是指薄板受平行于板面且沿厚度均度载荷用 。 在考虑有限元法表述位移时,首先是选取一组函数,他们可以用节点位移来表示有限元内任一点的位移分量。然后从外加位移场发展解法得的各个步都是,应变分量由位移的各种导数唯一确定,于是外加位移变化确定了整个单元的应变状态。这些诱导应变和介质弹性性质一起确定了单元的诱导应力。将初始应力与诱导应力叠加就得到了单元的总应力。 有限单元法的假设是,相邻有限单元边界之间的内力通过单元节点的相互作用来传递。因此必须建立节点力的表达式,节点力在静力学上等价于单元之间沿边界的作用力。该方法通过离散区域的一组节点力和位移来分析连续介质问题。为便于讲述。 什么是位移模式? 位移模式是单元范围内的位移函数。是坐标的函数。位移模式通常应当满足:1)反映刚体位移。2)反映常变形。3)单位边界上位移连续。 什么是节点力?什么是节点载荷?(陈尹依) 答:节点力是单元给节点的力,或者节点给单元的力;等于单元的弹性力,节点载荷是外界作用在弹性节点上的力。 什么是单元分析?说说单元分析的过程。(石登明) 答:单元分析就是寻求单元节点力与单元位移之间的关系。单元分析的大致过程:设定节点位移表达单元内任意一点位移、建立应变与位移之间的几何方程、建立应变与应力之间的几何关系、又虚功原理建立节点力与单元内任意一点应力之间的平衡关系,从而得到单元刚度方程。 单元刚度矩阵具有哪些特点?简述其物理意义。(课本) 答:单元刚度矩阵具有对称性、奇异性。可按节点分块对称性反映功的互等关系,奇异性说明单元在无约束情况下可以发生刚体位移。由于每个节点具有相同的自由度,因此单元矩阵可按节点分成若干个相似的子块。 功互等定理:对于线弹性体,作用在同一构件上的第一组力在第二组引起的位移上所作的功,等于第二组力在第一组力引起的位以上所作的功. 1. 诉述有限元法的定义 答:有限元法是近似求解一般连续场问题的数值方法 2. 有限元法的基本思想是什么 答:首先,将表示结构的连续离散为若干个子域,单元之间通过其边界上的节点连接成组合体。其次,用每个单元内所假设的近似函数分片地表示求解域内待求的未知厂变量。 3. 有限元法的分类和基本步骤有哪些 答:分类:位移法、力法、混合法;步骤:结构的离散化,单元分析,单元集成,引入约束条件,求解线性方程组,得出节点位移。 4. 有限元法有哪些优缺点 答:优点:有限元法可以模拟各种几何形状复杂的结构,得出其近似解;通过计算机程序,可以广泛地应用于各种场合;可以从其他CAD软件中导入建好的模型;数学处理比较方便,对复杂形状的结构也能适用;有限元法和优化设计方法相结合,以便发挥各自的优点。 缺点:有限元计算,尤其是复杂问题的分析计算,所耗费的计算时间、内存和磁盘空间等计算资源是相当惊人的。对无限求解域问题没有较好的处理办法。尽管现有的有限元软件多数使用了网络自适应技术,但在具体应用时,采用什么类型的单元、多大的网络密度等都要完全依赖适用者的经验。 5. 梁单元和平面钢架结构单元的自由度由什么确定 答:由每个节点位移分量的总和确定 6. 简述单元刚度矩阵的性质和矩阵元素的物理意义 答:单元刚度矩阵是描述单元节点力和节点位移之间关系的矩阵 单元刚度矩阵中元素aml的物理意义为单元第L个节点位移分量等于1,其他节点位移分量等于0时,对应的第m个节点力分量。 7. 有限元法基本方程中的每一项的意义是什么 P14 答:Q——整个结构的节点载荷列阵(外载荷、约束力);整个结构的节点位移列阵;结构的整体刚度矩阵,又称总刚度矩阵。 8. 位移边界条件和载荷边界条件的意义是什么 答:由于刚度矩阵的线性相关性不能得到解,引入边界条件,使整体刚度矩阵求的唯一解。 9. 简述整体刚度矩阵的性质和特点 P14 答:对称性;奇异性;稀疏性;对角线上的元素恒为正。 10 简述整体坐标的概念 P25 答:在整体结构上建立的坐标系叫做整体坐标,又叫做统一坐标系。 11. 简述平面钢架问题有限元法的基本过程 答:1)力学模型的确定,2)结构的离散化,3)计算载荷的等效节点力,4)计算各单元的刚度矩阵,5)组集整体刚度矩阵,6)施加边界约束条件,7)求解降价的有限元基本方程,8)求解单元应力,9)计算结果的输出。 12. 弹性力学的基本假设是什么。 答:连续性假定,弹性假定,均匀性和各向同性假定,小变形假定,无初应力假定。 13.弹性力学和材料力学相比,其研究方法和对象有什么不同。 答:研究对象:材料力学主要研究杆件,如柱体、梁和轴,在拉压、剪切、弯曲和扭转等作用下的应力、形变和位移。弹性力学研究各种形状的弹性体,除杆件外,还研究平面体、空间体,板和壳等。因此,弹性力学的研究对象要广泛得多。研究方法:弹性力学和材料力学既有相似之外,又有一定区别。弹性力学研究问题,在弹性体区域内必须严格考虑静力学、几何学和物理学三方面条件,在边界上严格考虑受力条件或约束条件,由此建立微分方程和边界条件进行求解,得出较精确的解答。而材料力学虽然也考 虑这几方面的条件,但不是十分严格的,材料力学只研究和适用于杆件问题。 14. 简述圣维南原理。 答;把物体一小部分上的面力变换为分布不同但静力等效的面力,但影响近处的应力分量,而不影响远处的应力。“局部影响原理” 15.平面应力问题和平面应变问题的特点和区别各是什么?试各举出一个典型平面应力和平面应变的问题的实例。 答:平面应力问题的特点:长、宽尺寸远大于厚度,沿板面受有平行板的面力,且沿厚度均匀分布,体力平行于板面且不沿厚度变化,在平板的前后表面上无外力作用平面应变问题的特点:Z向尺寸远大于x、y向尺寸,且与z轴垂直的各个横截面尺寸都相同,受有平行于横截面且不沿z向变化的外载荷,约束条件沿z向也不变,即所有内在因素的外来作用都不沿长度变化。区别:平面应力问题中z方向上应力为零,平面应变问题中z方向上应变为零、应力不为零。举例:平面应力问题等厚度薄板状弹性体,受力方向沿板面方向,荷载不沿板的厚度方向变化,且板的表面无荷载作用。 平面应变问题——水坝用于很长的等截面四柱体,其上作用的载荷均平行于横截面,且沿柱长方向不变法。 16. 三角形常应变单元的特点是什么?矩形单元的特点是什么?写出它们的位移模式。 答:三角形单元具有适应性强的优点,较容易进行网络划分和逼近边界形状,应用比较灵活。其缺点是它的位移模式是线性函数,单元应力和应变都是常数,精度不够理想。 矩形单元的位移模式是双线性函数,单元的应力、应变式线性变化的,具有精度较高,形状规整,便于实现计算机自动划分等优点,缺点是单元不能适应曲线边界和斜边界,也不能随意改变大小,适用性非常有限。 17. 写出单元刚度矩阵表达式、并说明单元刚度与哪些因素有关。 答:单元刚度矩阵与 节点力坐标变换矩阵, 局部坐标系下的单元刚度矩阵, 节点位移有关的坐标变换矩阵。 18. 如何由单元刚度矩阵组建整体刚度矩阵(叠加法)? 答:(1)把单元刚度矩阵 扩展成单元贡献矩阵 ,把单元刚度矩阵中的子块按其在整体刚度矩阵中的位置排列,空白处用零子块填充。(2)把单元的贡献矩阵 的对应列的子块相叠加,即可得出整体刚度矩阵 。 19. 整体刚度矩阵的性质。 答:(1)整体刚度矩阵 中每一列元素的物理意义为:欲使弹性体的某一节点沿坐标方形发生单位为移,而其他节点都保持为零的变形状态,在各节点上所需要施加的节点力;(2)整体刚度矩阵中的主对角元素总是正的;(3)整体刚度矩阵是一个对称阵;(4)整体刚度矩阵式一个呈带状分布的稀疏性矩阵。(5)整体刚度矩阵式一个奇异阵,在排除刚体位移后,他是正定阵。 20. 简述形函数的概念和性质。 答:形函数的性质有:(1)形函数单元节点上的值,具有“本点为一、他点为零”的性质;(2)在单元的任一节点上,三角函数之和等于1;(3)三角形单元任一一条边上的形函数,仅与该端点节点坐标有关,而与另外一个节点坐标无关;(4)型函数的值在0~1之间变换。 21. 结构的网格划分应注意哪些问题.如何对其进行节点编号。才能使半带宽最小。P50,P8相邻节点的号码差最小 答:一般首选三角形单元或等参元。对平直边界可选用矩形单元,也可以同时选用两种或两种以上的单元。一般来说,集中力,集中力偶,分布在和强度的突变点,分布载荷与自由边界的分界点,支撑点都应该取为节点,相邻节点的号码差尽可能最小才能使半带宽最小 22. 为了保证解答的收敛性,单元位数模式必须满足什么条件? 答:(1)位移模式必须包含单元刚体位移;(2)位移模式必须包含单元的常应变;(3)位移模式在单元内要连续,且唯一在相邻单元之间要协调。在有限单元法中,把能够满足条件1和条件2的单元称为完备单元,把满足条件3的单元叫做协调单元或保续单元。 23 有限元分析求得的位移解收敛于真实解得下界的条件。 答:1.位移模式必须包含单元的刚体位移,2.位移模式必须包含单元的常应变,3.位移模式在单元内要连续,且位移在相邻单元之间要协调。 24. 简述等参数单元的概念。 答:坐标变换中采用节点参数的个数等于位移模式中节点参数的个数,这种单元称为等参单元。 25. 有限元法中等参数单元的主要优点是什么? 答:1)应用范围广。在平面或空间连续体,杆系结构和板壳问题中都可应用。 2)将不规则的单元变化为规则的单元后,易于构造位移模式。 3)在原结构中可以采用不规则单元,易于适用边界的形状和改变单元的大小。 4)可以灵活的增减节点,容易构造各种过度单元。 5)推导过程具有通用性。一维,二维三维的推导过程基本相同。 26. 简述四节点四边形等参数单元的平面问题分析过程。 答:(1)通过整体坐标系和局部坐标系的映射关系得到四节点四边形等参单元的母单元,并选取单元的唯一模式;(2)通过坐标变换和等参元确定平面四节点四边形等参数单元的几何形状和位移模式;(3)将四节点四边形等参数单元的位移模式代入平面问题的几何方程,得到单元应变分量的计算式,再将单元应变代入平面问题的物理方程,得到平面四节点等参数单元的应力矩阵(4)用虚功原理球的单元刚度矩 阵,,最后用高斯积分法计算完成。
一个简单的PCB键盘,可轻松用于Arduino项目。提供了完全可定制的代码! 硬件部件: Arduino UNO × 1个 1N4148 –通用快速开关 × 64 触觉开关,顶部致动 × 64 排针× 1个 74HC595移位寄存器× 1个 软件应用程序和在线服务: Arduino IDE 手动工具和制造机: 烙铁(通用) 我目前正在开发一个带有集成键盘的项目,这带来了一个问题:如何在开发板原型中包含键盘?我不能使用USB键盘或现有的基于Arduino的键盘,因为实际项目中的键盘直接连接到处理所有其他功能的微控制器。因此,我设计了这种基于PCB的基本64键原型键盘矩阵。 该PCB不包含任何IC(集成电路)。键盘矩阵的行和列直接连接到引脚接头,以便键盘可以连接到Arduino或任何其他微控制器。它是为您的项目原型制作的完美选择,其中包括集成键盘。 我已经包含了详细的,经过注释的代码,以使其与任何具有足够I / O引脚可用的Arduino兼容开发板一起使用,需要11个引脚。键盘有64个键,包括shift,caps,ctrl,alt,fn和“ special”的修饰符。还有六个其他键可用于任何您喜欢的操作。每个单个键的功能都可以单独定义,包括激活修饰符时每个键的功能。我认为,这比现有的键盘代码明显有用,后者严重限制了您自定义按键行为的能力。 提供的代码将文本打印到串行。如果您希望文本移至其他位置,则可以轻松更改。 关于程序大小的注意事项: 我提供的代码很大,因为它不使用任何现有的库。我完全从头开始编写此代码,以实现所需的可定制性。在Arduino UNO上,这将使用9100字节(28%)的程序存储空间,而全局变量使用394字节(19%)的动态内存。 我的代码可能会更高效,并且键盘的库和草图肯定会更小,但这是我可以设计的唯一方法,可以为每个键和每个修饰符提供完全的灵活性。它还考虑了实际的键盘使用情况。例如,在启用Caps Lock的情况下,按我的代码的同时按Shift键,将产生应小写的字母。默认情况下,在按ESC的同时按住FN键不会执行任何操作。但是该行为是完全可定制的,因此您可以根据自己的喜好进行更改。
### 回答1: 单应性矩阵也被称为单应性变换矩阵,是指一个平面上的点通过一个矩阵的变换后,被映射到另一个平面上的点。在Matlab中,可以通过使用homography函数来求解单应性矩阵。 具体步骤如下: 1.准备待变换的点集。在Matlab中,可以通过使用ginput函数在图像上手动选择需要变换的点集。 2.通过使用findHomography函数来计算单应性矩阵。该函数需要输入两个点集,分别是待变换的点集和变换后的点集。 3.输出单应性矩阵。findHomography函数将会输出一个3x3的矩阵,即单应性矩阵。 需要注意的是,单应性矩阵通常通过二维点集之间的对应关系来求解。因此,在选择点集时,需要确保两个点集中的点数相同,并且在相应的位置上是一一对应的。 另外,由于单应性矩阵的求解涉及到矩阵运算,因此在实际应用中可能存在数值计算误差。为了减小误差,可以尝试使用更为精确的数值计算方法,比如SVD分解等。 ### 回答2: 在计算机视觉领域中,单应性矩阵是指两个平面上的点集之间的一种变换关系。在MATLAB中,求解单应性矩阵可以使用HOMOG函数。 使用HOMOG函数,需要输入两个平面上的点集,其中第一组点为原图像上的点,第二组点为目标图像上对应的点。例如: ```matlab points1 = [16 20; 28 46; 234 36; 222 254]; points2 = [17 22; 29 49; 236 35; 223 257]; H = homog(points1, points2) ``` 这里,我们定义了两个点集`points1`和`points2`,然后使用HOMOG函数求解单应性矩阵,并将结果存储在变量H中。 在MATLAB中,HOMOG函数可以通过“Computer Vision Toolbox”获得。如果没有安装此工具箱,可通过以下命令进行安装: ```matlab matlab.addons.toolbox.install('computer-vision') ``` 此命令将从MATLAB Add-Ons库中下载和安装此工具箱。 总之,在MATLAB中求解单应性矩阵可以使用HOMOG函数,需要提供两个平面上的对应点集。 ### 回答3: 单应性矩阵是一种用于计算图像变形的矩阵,通常用于计算摄影和计算机视觉中的问题。MATLAB是一个强大的科学计算软件,可以用来求解单应性矩阵。下面是求解单应性矩阵的步骤: 首先,需要获取两幅图像上的对应点,这些点应该具有一一对应关系。 接下来,需要使用这些对应点来计算单应性矩阵。MATLAB中可以使用“estimateGeometricTransform”函数来计算单应性矩阵。该函数的输入参数包括对应点的坐标和单应性矩阵的类型。输出参数为单应性矩阵。 最后,可以使用计算出的单应性矩阵来变换图像。在MATLAB中,可以使用“imwarp”函数来变换图像。该函数的输入参数包括需要变换的图像和计算出的单应性矩阵。输出参数为变换后的图像。 需要注意的是,在计算单应性矩阵时,对应点应该足够多且分布均匀,否则计算出的单应性矩阵可能有误。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值