7-imageProjection_原理分析_空间域image projection-程序员宅基地

技术标签: 自动驾驶  机器学习  人工智能  LVI-SAM分析  

2021SC@SDUSC

ImageProjection_原理分析

1. IMU-物体姿态计算

​ 物体姿态计算是由cloudHandle()中的deskewInfo()中的imuDeskewInfo()完成的。首先,由上一篇blog,我们可以得知,imu和姿态有关的数据是方向(用四元数(Quaternion)表示),角速度来计算。下面开始用这2个数据,来计算当前物体的姿态。

(1) lidar帧初始姿态角计算

​ lidar帧初始姿态角是由imu数据中的方向(用四元数(Quaternion)表示)计算得出。虽然,在代码中,用了以下几行代码就可以轻易计算得出,但其中的数学原理,我们也可以了解下。

template<typename T>
void imuRPY2rosRPY(sensor_msgs::Imu *thisImuMsg, T *rosRoll, T *rosPitch, T *rosYaw)
{
    
    double imuRoll, imuPitch, imuYaw;
    tf::Quaternion orientation;
    tf::quaternionMsgToTF(thisImuMsg->orientation, orientation);
    tf::Matrix3x3(orientation).getRPY(imuRoll, imuPitch, imuYaw);

    *rosRoll = imuRoll;
    *rosPitch = imuPitch;
    *rosYaw = imuYaw;
}	
欧拉角(RPY角)

​ 这里要接触2个概念,四元数和欧拉角。四元数在上一篇blog已经介绍了,下面介绍一下欧拉角。

​ 莱昂哈德·欧拉用欧拉角来描述刚体在三维欧几里得空间的取向。对于任何参考系,一个刚体的取向,是依照顺序,从这参考系,做三个欧拉角的旋转而设定的。所以,刚体的取向可以用三个基本旋转矩阵来决定。换句话说,任何关于刚体旋转的旋转矩阵是由三个基本旋转矩阵复合而成的。

​ 维基百科定义:

​ 对于在三维空间里的一个参考系,任何坐标系的取向,都可以用三个欧拉角来表现。参考系(固定系)又称为实验室参考系,是静止不动的。而坐标系(固连系)则固定于刚体,随着刚体的旋转而旋转。

参阅下图。设定xyz-轴为参考系的参考轴。称xy-平面与XY-平面的相交为交点线,用英文字母(N)代表。zxz顺规的欧拉角可以静态地这样定义:

  • α \alpha α(进动角)是x-轴与交点线的夹角,
  • β \beta β(章动角)是z-轴与Z-轴的夹角,
  • γ \gamma γ(自旋角)是交点线与X-轴的夹角。

对于夹角的顺序和标记,夹角的两个轴的指定,并没有任何常规。科学家对此从未达成共识。每当用到欧拉角时,我们必须明确的表示出夹角的顺序,指定其参考轴。

实际上,有许多方法可以设定两个坐标系的相对取向。欧拉角方法只是其中的一种。此外,不同的作者会用不同组合的欧拉角来描述,或用不同的名字表示同样的欧拉角。因此,使用欧拉角前,必须先做好明确的定义。

7-1

​ 通俗解释一下:描述坐标系{B}相对于参考坐标系{A}的姿态有两种方式。第一种是绕固定(参考)坐标轴旋转:假设开始两个坐标系重合,先将{B}绕{A}的X轴旋转 γ \gamma γ,然后绕{A}的Y轴旋转 β \beta β,最后绕{A}的Z轴旋转 α \alpha α,就能旋转到当前姿态。可以称其为X-Y-Z fixed angles或RPY角(Roll, Pitch, Yaw)。如下图演示:

Roll:横滚

7-2

Pitch: 俯仰

7-3

Yaw: 偏航(航向)

7-4

旋转矩阵

前面提到,设定刚体取向的旋转矩阵 [ R ] [\mathbf {R}] [R]是由三个基本旋转矩阵合成的:
[ R ] = [ cos ⁡ γ sin ⁡ γ 0 − sin ⁡ γ cos ⁡ γ 0 0 0 1 ] [ 1 0 0 0 cos ⁡ β sin ⁡ β 0 − sin ⁡ β cos ⁡ β ] [ cos ⁡ α sin ⁡ α 0 − sin ⁡ α cos ⁡ α 0 0 0 1 ] [\mathbf{R}] = \begin{bmatrix} \cos \gamma & \sin \gamma & 0 \\ -\sin \gamma & \cos \gamma & 0 \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 \\ 0 & \cos \beta & \sin \beta \\ 0 & -\sin \beta & \cos \beta \end{bmatrix} \begin{bmatrix} \cos \alpha & \sin \alpha & 0 \\ -\sin \alpha & \cos \alpha & 0 \\ 0 & 0 & 1 \end{bmatrix} [R]=cosγsinγ0sinγcosγ00011000cosβsinβ0sinβcosβcosαsinα0sinαcosα0001
从左到右依次代表绕着z轴的旋转、绕着交点线的旋转、绕着Z轴的旋转。

经过一番运算,
[ R ] = [ cos ⁡ α cos ⁡ γ − cos ⁡ β sin ⁡ α sin ⁡ γ sin ⁡ α cos ⁡ γ + cos ⁡ β cos ⁡ α sin ⁡ γ sin ⁡ β sin ⁡ γ − cos ⁡ α sin ⁡ γ − cos ⁡ β sin ⁡ α cos ⁡ γ − sin ⁡ α sin ⁡ γ + cos ⁡ β cos ⁡ α cos ⁡ γ sin ⁡ β cos ⁡ γ sin ⁡ β sin ⁡ α − sin ⁡ β cos ⁡ α cos ⁡ β ] [\mathbf{R}] = \begin{bmatrix} \cos\alpha\cos\gamma-\cos\beta\sin\alpha\sin\gamma & \sin\alpha\cos\gamma+\cos\beta\cos\alpha\sin\gamma & \sin\beta\sin\gamma \\-\cos\alpha\sin\gamma-\cos\beta\sin\alpha\cos\gamma & -\sin\alpha\sin\gamma+\cos\beta\cos\alpha\cos\gamma & \sin\beta\cos\gamma \\ \sin\beta\sin\alpha & -\sin\beta\cos\alpha & \cos\beta \end{bmatrix} [R]=cosαcosγcosβsinαsinγcosαsinγcosβsinαcosγsinβsinαsinαcosγ+cosβcosαsinγsinαsinγ+cosβcosαcosγsinβcosαsinβsinγsinβcosγcosβ
[ R ] [\mathbf {R}] [R]的逆矩阵是:
[ R ] − 1 = [ cos ⁡ α cos ⁡ γ − cos ⁡ β sin ⁡ α sin ⁡ γ − cos ⁡ α sin ⁡ γ − cos ⁡ β sin ⁡ α cos ⁡ γ sin ⁡ β sin ⁡ α sin ⁡ α cos ⁡ γ + cos ⁡ β cos ⁡ α sin ⁡ γ − sin ⁡ α sin ⁡ γ + cos ⁡ β cos ⁡ α cos ⁡ γ − sin ⁡ β cos ⁡ α sin ⁡ β sin ⁡ γ sin ⁡ β cos ⁡ γ cos ⁡ β ] [\mathbf{R}]^{-1}= \begin{bmatrix} \cos\alpha\cos\gamma-\cos\beta\sin\alpha\sin\gamma & -\cos\alpha\sin\gamma-\cos\beta\sin\alpha\cos\gamma & \sin\beta\sin\alpha \\ \sin\alpha\cos\gamma+\cos\beta\cos\alpha\sin\gamma & -\sin\alpha\sin\gamma+\cos\beta\cos\alpha\cos\gamma & -\sin\beta\cos\alpha \\ \sin\beta\sin\gamma & \sin\beta\cos\gamma & \cos\beta \end{bmatrix} [R]1=cosαcosγcosβsinαsinγsinαcosγ+cosβcosαsinγsinβsinγcosαsinγcosβsinαcosγsinαsinγ+cosβcosαcosγsinβcosγsinβsinαsinβcosαcosβ

四元数与RPY角的关系

​ 绕坐标轴的多次旋转可以等效为绕某一转轴旋转一定的角度。假设等效旋转轴方向向量为 K ⃗ = [ k x , k y , k z ] T \vec{K} =[k_x,k_y,k_z]^T K =[kx,ky,kz]T,等效旋转角为 θ θ θ,则四元数 q = ( x , y , z , w ) q=(x,y,z,w) q=(x,y,z,w),其中:
x = k x ⋅ sin ⁡ ( θ 2 ) y = k y ⋅ sin ⁡ ( θ 2 ) z = k z ⋅ sin ⁡ ( θ 2 ) w = cos ⁡ ( θ 2 ) x=k_x·\sin(\frac{\theta}{2})\\ y=k_y·\sin(\frac{\theta}{2})\\ z=k_z·\sin(\frac{\theta}{2})\\ w=\cos(\frac{\theta}{2}) x=kxsin(2θ)y=kysin(2θ)z=kzsin(2θ)w=cos(2θ)
且有 x 2 + y 2 + z 2 + w 2 = 1 x^2+y^2+z^2+w^2=1 x2+y2+z2+w2=1

7-5

即四元数存储了旋转轴和旋转角的信息,它能方便的描述刚体绕任意轴的旋转。

四元数转换为旋转矩阵:
R = [ 1 − 2 y 2 − 2 z 2 2 ( x y − z w ) 2 ( x z + y w ) 2 ( x y + z w ) 1 − 2 x 2 − 2 z 2 2 ( y z − x w ) 2 ( x z − y w ) 2 ( y z + x w ) 1 − 2 x 2 − 2 y 2 ] \mathbf{R} = \begin{bmatrix} 1-2y^2-2z^2 & 2(xy-zw) & 2(xz+yw) \\2(xy+zw) & 1-2x^2-2z^2 & 2(yz-xw) \\ 2(xz-yw) & 2(yz+xw) & 1-2x^2-2y^2 \end{bmatrix} R=12y22z22(xy+zw)2(xzyw)2(xyzw)12x22z22(yz+xw)2(xz+yw)2(yzxw)12x22y2

已知旋转矩阵为:

7-6

则对应的四元数为:

在这里插入图片描述

(2) 当前时刻旋转角的计算

​ 旋转角的的计算较为简单,因为imu的信息里面有关角速度的数据。因此,当前时刻的旋转角度可以通过角速度×时间计算得出。角速度的信息提取如下:

template<typename T>
void imuAngular2rosAngular(sensor_msgs::Imu *thisImuMsg, T *angular_x, T *angular_y, T *angular_z)
{
    
    *angular_x = thisImuMsg->angular_velocity.x;
    *angular_y = thisImuMsg->angular_velocity.y;
    *angular_z = thisImuMsg->angular_velocity.z;
}

​ 我们设一开始的旋转角度为0。然后,建立一个缓存的地方存放每个帧的旋转角。那么,当前时刻的旋转角就等于,前一时刻旋转角 + 角速度 * 时差。时间差也是有已经缓存的时间信息。然后再把计算得到的旋转角度放入缓存中,等待之后的计算。代码如下:

// imu帧间时差
double timeDiff = currentImuTime - imuTime[imuPointerCur-1];
// 当前时刻旋转角 = 前一时刻旋转角 + 角速度 * 时差
imuRotX[imuPointerCur] = imuRotX[imuPointerCur-1] + angular_x * timeDiff;
imuRotY[imuPointerCur] = imuRotY[imuPointerCur-1] + angular_y * timeDiff;
imuRotZ[imuPointerCur] = imuRotZ[imuPointerCur-1] + angular_z * timeDiff;
imuTime[imuPointerCur] = currentImuTime;
++imuPointerCur;

2. 里程计计算

​ 物体的里程计计算是由cloudHandle()中的deskewInfo()中的odomDeskewInfo()完成的。这个函数的主要功能为遍历当前激光帧起止时刻之间的imu里程计数据,初始时刻对应imu里程计设为当前帧的初始位姿,并用起始、终止时刻对应imu里程计,计算相对位姿变换,保存平移增量。下面开始讲解技术要点。

(1)位姿计算

​ 位姿的计算和上面的四元数与欧拉角的相互转换相同。参考上面的理论介绍即可。

// 提取imu里程计姿态角
tf::Quaternion orientation;
tf::quaternionMsgToTF(startOdomMsg.pose.pose.orientation, orientation);

double roll, pitch, yaw;
tf::Matrix3x3(orientation).getRPY(roll, pitch, yaw);

// 用当前激光帧起始时刻的odom,初始化lidar位姿,后面用于mapOptmization
cloudInfo.odomX = startOdomMsg.pose.pose.position.x;
cloudInfo.odomY = startOdomMsg.pose.pose.position.y;
cloudInfo.odomZ = startOdomMsg.pose.pose.position.z;
cloudInfo.odomRoll  = roll;
cloudInfo.odomPitch = pitch;
cloudInfo.odomYaw   = yaw;
cloudInfo.odomResetId = (int)round(startOdomMsg.pose.covariance[0]);

(2)坐标系之间的转换 - 仿射变换

​ 在用起始、终止时刻对应imu里程计,计算相对位姿变换,保存平移增量的时候,就涉及到了几何学的知识,需要在2个不同的坐标系之间进行变换。

Eigen::Affine3f transBegin = pcl::getTransformation(startOdomMsg.pose.pose.position.x, startOdomMsg.pose.pose.position.y, startOdomMsg.pose.pose.position.z, roll, pitch, yaw);

tf::quaternionMsgToTF(endOdomMsg.pose.pose.orientation, orientation);
tf::Matrix3x3(orientation).getRPY(roll, pitch, yaw);
Eigen::Affine3f transEnd = pcl::getTransformation(endOdomMsg.pose.pose.position.x, endOdomMsg.pose.pose.position.y, endOdomMsg.pose.pose.position.z, roll, pitch, yaw);

Eigen::Affine3f transBt = transBegin.inverse() * transEnd;

float rollIncre, pitchIncre, yawIncre;
pcl::getTranslationAndEulerAngles(transBt, odomIncreX, odomIncreY, odomIncreZ, rollIncre, pitchIncre, yawIncre);

​ 这里其实是计算起止时刻imu里程计的相对变换,并且提取增量平移,旋转(欧拉角)。这里的相对变换其实是用了仿射变换。因此,我们需要了解仿射变换的相关知识。

仿射变换

​ 仿射变换(Affine transformation),又称仿射映射,是指在几何中,对一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间。一个对向量 x ⃗ \displaystyle {\vec {x}} x 平移 b ⃗ \displaystyle {\vec {b}} b ,与旋转放大缩小 A ⃗ \displaystyle{\vec A} A 的仿射映射为
y ⃗ = A x ⃗ + b ⃗ \vec y = A\vec x+\vec b y =Ax +b
​ 上式在其次坐标上,等价于下面的式子
[ y ⃗ 1 ] = [ A b ⃗ 0 , . . . , 0 1 ] [ x ⃗ 1 ] \begin{bmatrix} \vec y \\ 1 \end{bmatrix} = \begin{bmatrix} A & \vec b \\ 0,...,0 & 1\end{bmatrix} \begin{bmatrix} \vec x \\ 1 \end{bmatrix} [y 1]=[A0,...,0b 1][x 1]
​ 在分形的研究里,收缩平移仿射映射可以制作具有自相似性的分形。

​ 简单来讲,“仿射变换”就是:“线性变换”+“平移”。仿射变换从几何直观只有两个要点:

  • 变换前是直线,变换后也应该是直线

  • 直线比例保持不变

​ 那么什么是线性变换呢?

线性变换

​ 在数学中,线性映射(有的书上将“线性变换”作为其同义词,有的则不然)是在两个向量空间(包括由函数构成的抽象的向量空间)之间的一种保持向量加法和标量乘法的特殊映射。线性映射从抽象代数角度看是向量空间的同态,从范畴论角度看是在给定的域上的向量空间所构成的范畴中的态射。

​ 简单来讲,线性变换就是只含有向量的加法或标量的乘法的一种运算。线性变换从几何直观有三个要点:

  • 变换前是直线的,变换后依然是直线
  • 直线比例保持不变
  • 变换前是原点的,变换后依然是原点

比如说旋转:

7-8

7-9

比如说推移:

7-10

7-11

这两个叠加也是线性变换:

7-12

在上面通俗地理解了线性变换之后,可以看看它的数学定义:

​ 设 V V V W W W是在相同域* K K K上的向量空间。法则 f : V → W f: V \rightarrow W f:VW被称为是线性映射,如果对于 V V V中任何两个向量 x x x y y y K K K*中任何标量 a a a,满足下列两个条件:

可加性: f ( x + y ) = f ( x ) + f ( y ) f(x+y)=f(x)+f(y) f(x+y)=f(x)+f(y)
齐次性: f ( a x ) = a f ( x ) f(ax)=af(x) f(ax)=af(x)

这等价于要求对于任何向量 x 1 , … , x m \displaystyle x_{1},\ldots ,x_{m} x1,,xm和标量 a 1 , … , a m \displaystyle a_{1},\ldots ,a_{m} a1,,am,方程

f ( a 1 x 1 + ⋯ + a m x m ) = a 1 f ( x 1 ) + ⋯ + a m f ( x m ) f(a_1x_1+\dots+a_mx_m)=a_1f(x_1)+\dots+a_mf(x_m) f(a1x1++amxm)=a1f(x1)++amf(xm)
成立。

通过线性变换来完成仿射变换

7-13

什么意思?继续举例子:

7-14

7-15

这样我就可以在三维空间下通过 [ A b ⃗ 0 1 ] \begin{bmatrix}A & \vec b \\ 0 & 1\end{bmatrix} [A0b 1] 这个线性变换来操作 z = 1 z=1 z=1 平面上的二维正方形,完成仿射变换:

7-16

自己动手操作一下:

7-17

我们平移到需要的位置的时候:

7-18

7-19

如果还有没有清楚的地方,可以结合之前的描述,看一下维基百科“仿射变换”词条里的一个gif动图,非常生动的表明了这一过程:

7-20

仿射变换在代码中的使用

​ 在代码中,我找到不到pcl::getTransformation (float x, float y, float z, float roll, float pitch, float yaw)的源代码,也找不到头文件中找不到实际的源码,只有对这个函数的描述:

Create a transformation from the given translation and Euler angles (XYZ-convention)

从给定的平移和欧拉角创建一个变换(XYZ-公约)。

​ 但是按照我个人的理解,这个函数pcl::getTransformation (float x, float y, float z, float roll, float pitch, float yaw),最后得到的是描述这个平面的一个变换矩阵。然后用这个方法得到两个这样的矩阵 A A A B B B,矩阵之间能用这么一个式子来转换。
A ⋅ T = B A·T = B AT=B
​ 然后,只需要求出T就能求得它们的2个平面的点的转换关系。因此,可得下面的式子:
T = A T B T = A^TB T=ATB
​ 在上面的代码中,变量transBt就是这么一个T。

3. 降采样

​ 在代码中,检测激光雷达数据的时候有一个检测降采样的判断。而降采样对我来说,也是一个完全陌生的东西,因此,打算了解一下什么是降采样。下面是维基百科对降采样的描述。

数位信号处理领域中,降采样,又作减采集,是一种多速率数字信号处理的技术或是降低信号采样率的过程,通常用于降低数据传输速率或者数据大小。

​ 不过,这个降采样应该是其他部分处理得,等遇到了我们再深入研究。

4. 激光运动矫正

​ 激光运动矫正也是用上面提到的仿射变换来利用当前帧起止时刻之间的imu数据计算旋转增量,imu里程计数据计算平移增量,进而将每一时刻激光点位置变换到第一个激光点坐标系下,进行运动补偿。

​ 首先计算当前帧相对于起始帧的旋转增量(因为初始帧的旋转设为0,所以增量就是当前的旋转的数值)。这个计算较为简单,要分2种情况讨论。

  1. 要计算的激光雷达帧的时间 >= 最新的IMU数据的时间。这时候激光雷达帧的旋转增量就是最近的IMU的旋转的增量。

  2. 要计算的激光雷达帧的时间 < 最新的IMU数据的时间。这时候假设2个IMU数据帧之间的旋转增量是线性变化的,激光雷达帧的旋转增量就是利用激光雷达帧时间的最近的前后2个IMU数据帧,通过计算时间加权得出。

    int imuPointerBack = imuPointerFront - 1;
    double ratioFront = (pointTime - imuTime[imuPointerBack]) / (imuTime[imuPointerFront] - imuTime[imuPointerBack]);
    double ratioBack = (imuTime[imuPointerFront] - pointTime) / (imuTime[imuPointerFront] - imuTime[imuPointerBack]);
    *rotXCur = imuRotX[imuPointerFront] * ratioFront + imuRotX[imuPointerBack] * ratioBack;
    *rotYCur = imuRotY[imuPointerFront] * ratioFront + imuRotY[imuPointerBack] * ratioBack;
    *rotZCur = imuRotZ[imuPointerFront] * ratioFront + imuRotZ[imuPointerBack] * ratioBack;
    

至于后面利用得到的旋转增量和平移增量进行仿射变换,把当前的激光雷达数据帧变换到初始位置的数据帧则使用到前面的仿射变换的知识。

引用

四元数与欧拉角(RPY角)的相互转换 - XXX已失联 - 博客园

欧拉角 - 维基百科,自由的百科全书

(50 封私信 / 80 条消息) 如何通俗地讲解「仿射变换」这个概念? - 知乎

仿射变换 - 维基百科,自由的百科全书

降采样 - 维基百科,自由的百科全书

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/loyer_kong/article/details/120996485

智能推荐

技术前沿(解读)-程序员宅基地

文章浏览阅读2.3k次。1)量子技术方面2)人工智能方面3)生物技术方面4)3D打印方面5)能源方面

电脑安装hexo执行npm install 报错ional SKIPPING OPTIONAL DEPENDENCY: [email protected] (node_modules\fsevents):-程序员宅基地

文章浏览阅读3.7k次。windows 不是hexo执行npm install 报错:然后当时电脑安装npm好像换成国内的cnpm,所以改为执行命令cpnm install成功;现在电脑node环境很久之前弄得,很乱,不过能执行成功…[email protected] (node_modules\fsevents):

Python3 对列表中的字典按[key]排序_python 数组里面字典按key排序-程序员宅基地

文章浏览阅读1.5k次,点赞3次,收藏6次。列表中的字典按照key进行排序,可以使用 Python 内置的 sorted() 函数,其中的 key 参数可以指定排序的依据。_python 数组里面字典按key排序

字符16进制加密/解密在线工具-程序员宅基地

文章浏览阅读3.4w次,点赞3次,收藏5次。字符16进制加密/解密在线工具:http://www.atoolbox.net/Tool.php?Id=816将文本加密成16进制形式。_16进制加密

LCD1.axf: Error: L6218E: Undefined symbol Image$$RO$$Base (referred from 2440.0)解决_ads l6218e-程序员宅基地

文章浏览阅读5.3k次。1.如果直接将 ADS 下的启动代码copy 到 MDK 下,编译会提示出错,一般错误是:.\OutPutFiles\LCD1.axf: Error: L6238E:2440_init.o(Init_2440) contains invalid call from '~PRES8 (The user did notrequire code to preserve 8-byte aligment_ads l6218e

毕设项目:爱心之家——流浪动物救助平台的设计与实现(JSP+java+springmvc+mysql+MyBatis)_的流浪动物救助平台项目开发-程序员宅基地

文章浏览阅读238次。随着城市化的不断推进,流浪动物的数量逐渐增多,而传统的救助方式存在着效率低下、资源浪费等问题。该平台通过互联网技术,实现了对流浪动物信息的实时更新和管理,同时也提供了在线领养、志愿者招募等服务功能。+ Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。JSP技术生成动态的Web页面,将页面展示给用户。用户发送请求:用户通过浏览器或其他客户端向系统发送请求,请求访问特定的页面或执行特定的操作。后端程序接收到请求后,根据请求的类型和参数进行相应的处理。_的流浪动物救助平台项目开发

随便推点

【C++】菱形继承及菱形虚拟继承_什么是菱形继承-程序员宅基地

文章浏览阅读242次。什么是菱形继承?首先,我们看一下单继承,以及多继承是什么?单继承:一个子类只有一个直接父类时称这个继承关系为单继承多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承菱形继承:菱形继承是多继承的一种特殊情况菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。在Assistant的对象中Person成员会有两份。虚拟继承..._什么是菱形继承

c#按键键盘事件监控上下左右和其它按键_c# 监听键盘按键-程序员宅基地

文章浏览阅读1.4k次。重写:protected override bool ProcessDialogKey(Keys keyData)就可以实现了。_c# 监听键盘按键

SQL Server 2008 定时自动备份和自动删除方法_数据库备份自动删除早期文件-程序员宅基地

文章浏览阅读1.3k次。SQL Server 2008 数据定时自动备份和自动删除方法,同一个计划兼备数据备份数数据删除的操作方法。_数据库备份自动删除早期文件

华为OJ——图片整理_oj平台已完成的照片-程序员宅基地

文章浏览阅读429次。图片整理题目描述Lily上课时使用字母数字图片教小朋友们学习英语单词,每次都需要把这些图片按照大小(ASCII码值从小到大)排列收好。请大家给Lily帮忙,通过C语言解决。输入描述:Lily使用的图片包括"A"到"Z"、"a"到"z"、"0"到"9"。输入字母或数字个数不超过1024。输出描述:Lily的所有图片按照从小到大的顺序输出输入例子:Ihave1nose2_oj平台已完成的照片

pdfbox java,在使用PDFBox的java中,如何使用文本创建可见的数字签名-程序员宅基地

文章浏览阅读616次。Digital text with text and background imageI am trying to digitally sign pdf file using PDFBox in Java with visible text to appear on page similar to one that gets created when manually created in Ac..._pdrectangle setupperrightx

玩转移远SC60 Android开发板------(4)USB和otg切换_sc60linux-程序员宅基地

文章浏览阅读1k次。某客户要求实现usb device和otg的软切换,即通过软件设置实现usb device和otg的切换。原理图上可以设计一个GPIO来控制USB ID和数据线的切换。VBUS的供电方面,客户的otg是外供电,可以不用考虑;作device时vbus用来检测插入即可。参考原理图如下:图中使用GPIO_96作为控制切换的管脚,它输出0时,USB_ID脚为高,s脚为高,usb切换开关选择的是HSD1:USB_DP_EXT和USB_DM_EXT,此时模块作为device;GPIO_96输出1时,USB_I_sc60linux