文章目录
前言
Apollo星火计划课程链接如下
星火计划2.0基础课:https://apollo.baidu.com/community/online-course/2
星火计划2.0专项课:https://apollo.baidu.com/community/online-course/12
Apollo中对路径规划解耦,分为路径规划与速度规划两部分。并将规划分为决策与优化两个部分。
• 路径规划 —— 静态环境(道路,静止/低速障碍物)
• 速度规划 —— 动态环境(中/高速障碍物)

## 1.1 速度规划的坐标系




ps:蓝色四边形为障碍车在ST图下的投影。长边的斜率代表车速,短边代表障碍车在主车规划出的路径中占据的长度。


路径规划的配置文件在lane_follow_config.pb.txt中
// /home/yuan/apollo-edu/modules/planning/conf/scenario/lane_follow_config.pb.txt
scenario_type: LANE_FOLLOW
stage_type: LANE_FOLLOW_DEFAULT_STAGE
stage_config: {
//路径规划
stage_type: LANE_FOLLOW_DEFAULT_STAGE
enabled: true
task_type: LANE_CHANGE_DECIDER
task_type: PATH_REUSE_DECIDER
task_type: PATH_LANE_BORROW_DECIDER
task_type: PATH_BOUNDS_DECIDER
task_type: PIECEWISE_JERK_PATH_OPTIMIZER
//速度规划
task_type: PATH_ASSESSMENT_DECIDER
task_type: PATH_DECIDER
task_type: RULE_BASED_STOP_DECIDER
task_type: SPEED_BOUNDS_PRIORI_DECIDER
task_type: SPEED_HEURISTIC_OPTIMIZER
task_type: SPEED_DECIDER
task_type: SPEED_BOUNDS_FINAL_DECIDER
task_type: PIECEWISE_JERK_SPEED_OPTIMIZER
# task_type: PIECEWISE_JERK_NONLINEAR_SPEED_OPTIMIZER
task_type: RSS_DECIDER
_DECIDER结尾的为决策部分 _OPTIMIZER结尾的为优化部分。
产生速度可行驶边界

所形成的区域是非凸的,不能用之前凸优化的方法去做,需要用动态规划的方法去做。
动态规划规划目标
产生粗糙速度规划曲线

产生速度决策
根据粗规划出的速度曲线,依据曲线在障碍物的上方还是下方,采取不同的决策。
产生速度规划边界

在障碍物的上方或下方确定可行使区域。
产生平滑速度规划曲线

根据ST图的可行驶区域,优化出一条平滑的速度曲线。满足一阶导、二阶导平滑(速度加速度平滑);满足道路限速;满足车辆动力学约束。
PIECEWISE_JERK_SPEED_OPTIMIZER 基于二次规划的速度规划
PIECEWISE_JERK_NONLINEAR_SPEED_OPTIMIZER 基于非线性规划的速度规划
两者二选一即可
将SL曲线、ST曲线合成为完整轨迹,之后作为Planning的输出。

动态规划——通过把原问题分解为相对简单的子问题,再根据子问题的解来求解出原问题解的方法

状态转移方程
f
(
P
)
=
min
{
f
(
R
)
+
w
R
→
P
}
f(P) = \min \{ f(R) + {w_{R \to P}}\}
f(P)=min{f(R)+wR→P}
基于动态规划的速度规划的流程如下:
1.对路程和时间进行采样
2.搜索出粗略的可行路线
3.选出代价最小的一条


速度规划在ST图进行采样,在
t
t
t的方向上以固定的间隔进行采样,在
s
s
s方向上以先密后疏的方式进行采样(离主车越近,所需规划的精度就需更高;离主车越远,牺牲采样精度,提升采样效率)
// 时间采样的一般参数设置
unit_t: 1.0 //采样时间
dense_dimension_s: 101 // 采样密集区域的点数
dense_unit_s: 0.1 //采样密集区域的间隔
sparse_unit_s: 1.0 //采样系数区域的间隔

S_safe_overtake超车的安全距离
S_safe_follow跟车的安全距离
在设计状态转移方程时,要求不能与障碍物发生碰撞以及和障碍物不发生碰撞。于是可以得到以下方程:
目的是更快的到达目的地
距离cost计算方式如下
C
s
p
a
t
i
a
l
=
w
s
p
a
t
i
a
l
(
s
t
o
t
a
l
−
s
(
j
)
)
{C_{spatial}} = {w_{spatial}}({s_{total}} - s(j))
Cspatial=wspatial(stotal−s(j))
w
s
p
a
t
i
a
l
{w_{spatial}}
wspatial为损失权值
(
s
t
o
t
a
l
−
s
(
j
)
)
({s_{total}} - s(j))
(stotal−s(j))当前点到目标点的差值。
状态转移cost计算分为三个部分:
C
e
d
g
e
=
C
s
p
e
e
d
+
C
a
c
c
+
C
j
e
r
k
{C_{edge}} = {C_{speed}} + {C_{acc}} + {C_{jerk}}
Cedge=Cspeed+Cacc+Cjerk
C
s
p
e
e
d
{C_{speed}}
Cspeed——速度代价
C
a
c
c
{C_{acc}}
Cacc——加速度代价
C
j
e
r
k
{C_{jerk}}
Cjerk——加加速度代价
节点间速度为:
v
=
s
(
j
+
k
)
−
s
(
j
)
Δ
t
v = \frac{{s(j + k) - s(j)}}{{\Delta t}}
v=Δts(j+k)−s(j)
限速比率:
v
det
=
v
−
v
l
i
m
i
t
v
l
i
m
i
t
{v_{\det }} = \frac{{v - {v_{limit}}}}{{{v_{limit}}}}
vdet=vlimitv−vlimit
C
s
p
e
e
d
{C_{speed}}
Cspeed速度代价的计算如下:
若速度<0,则是倒车的状况,轨迹不可行,代价值设为无穷大;若速度>0,且高于限速,则会有超速的惩罚;若速度<0,且低于限速,则会有低速的惩罚。在Apollo中,超速的惩罚值(1000)远大于低速的惩罚值(10)。

加速度的计算如下:
a
(
i
+
1
,
j
+
k
)
=
s
(
k
+
j
)
−
s
(
j
)
Δ
t
−
s
(
j
)
−
s
(
l
)
Δ
t
Δ
t
a(i + 1,j + k) = \frac{{\frac{{s(k + j) - s(j)}}{{\Delta t}} - \frac{{s(j) - s(l)}}{{\Delta t}}}}{{\Delta t}}
a(i+1,j+k)=ΔtΔts(k+j)−s(j)−Δts(j)−s(l)
C
a
c
c
{C_{acc}}
Cacc加速度代价的计算如下:
若超过最大加速度或小于最小加速度,则代价值设为无穷大,若在之间,Apollo设计了这样的代价函数进行计算:
y
=
x
2
+
x
2
1
+
e
x
+
4
+
x
2
1
+
e
x
+
2
y = {x^2} + \frac{{{x^2}}}{{1 + {e^{x + 4}}}} + \frac{{{x^2}}}{{1 + {e^{x + 2}}}}
y=x2+1+ex+4x2+1+ex+2x2 其函数图像如下:
越靠近0,代价值越小;越靠近目标值,代价值越大,满足舒适性与平滑性。
加加速度的计算方式如下:
j
e
r
k
=
s
4
−
3
s
3
+
3
s
2
−
s
1
Δ
t
3
jerk = \frac{{{s_4} - 3{s_3} + 3{s_2} - {s_1}}}{{\Delta {t^3}}}
jerk=Δt3s4−3s3+3s2−s1
加加速度超过设定边界,设为无穷;若在之间,则按二次方的方式进行计算。加加速度越小越好。
最后是总的代价:
迭代范围:
在每次迭代时会将总的代价与当前节点的代价进行比较,取最小的一个,进行更新。
从
s
(
i
,
j
)
s(i,j)
s(i,j)到
s
(
i
+
1
,
j
+
k
)
s(i+1,j+k)
s(i+1,j+k)可以拓展到速度范围内的节点,按代价值的大小进行更新,最后按最后一列代价值最小的点进行求解,再进行回溯,得到ST曲线。
动态规划得到的轨迹还比较粗糙,需要用优化的方法对轨迹进行进一步的平滑。基于二次规划的速度规划的方法与路径规划基本一致。

优化变量
x
x
x,
x
x
x有三个部分组成:从
s
0
s_0
s0,
s
1
s_1
s1,
s
2
s_2
s2到
s
n
−
1
s_{n-1}
sn−1,从
s
˙
0
\dot s_0
s˙0,
s
˙
1
\dot s_1
s˙1,
s
˙
2
\dot s_2
s˙2到
s
˙
n
−
1
\dot s_{n-1}
s˙n−1,从
s
¨
0
\ddot s_0
s¨0,
s
¨
1
\ddot s_1
s¨1,
s
¨
2
\ddot s_2
s¨2到
s
¨
n
−
1
\ddot s_{n-1}
s¨n−1.
ps:三阶导的求解方式为:
s
′
′
i
+
1
−
s
′
′
i
Δ
t
\frac{{{{s''}_{i + 1}} - {{s''}_i}}}{{\Delta t}}
Δts′′i+1−s′′i
对于目标函数的设计,我们需要明确以下目标:
最后会得到以下目标函数:
w
s
w_s
ws——位置的权重
w
v
w_v
wv——速度的权重
p
i
p_i
pi——曲率的权重
w
a
w_a
wa——加速度的权重
w
j
w_j
wj——加加速度的权重
接下来谈谈约束的设计。
要满足的约束条件:
• 主车必须在道路边界内,同时不能和障碍物有碰撞
s
i
∈
(
s
min
i
,
s
max
i
)
{s_i} \in (s_{\min }^i,s_{\max }^i)
si∈(smini,smaxi)• 根据当前状态,主车的横向速度/加速度/加加速度有特定运动学限制:
•必须满足基本的物理原理:

•起始点约束:;
s
0
=
s
i
n
i
t
s_0=s_{init}
s0=sinit,
s
˙
0
=
s
i
n
i
t
\dot s_0=s_{init}
s˙0=sinit,
s
¨
0
=
s
i
n
i
t
\ddot s_0=s_{init}
s¨0=sinit满足的是起点的约束,即为实际车辆规划起点的状态。
代入OSQP求解器进行求解,输出一条平稳、舒适、能安全避开障碍物并且尽快到达目的地的速度分配曲线。
为了使得限速更加精细,Apollo提出了一种基于非线性规划的速度规划方法。

基于二次规划的速度规划中,
p
i
p_i
pi是曲率关于时间
t
t
t的函数,但实际上路径的曲率是与
s
s
s相关的。二次规划在原先动态规划出来的粗糙ST曲线上将关于
s
s
s的曲率惩罚转化为关于
t
t
t的曲率惩罚,如此,当二次规划曲线与动态规划曲线差别不大,规划出来基本一致;若规划差别大,则会差别很大。就如图所示,规划出来的区间差别较大。限速/曲率的函数是关于
s
s
s的函数,而
s
s
s是我们要求的优化量,只能通过动态规划进行转化,如此就会使得二次规划的速度约束不精确。
基于非线性规划的速度规划步骤与之前规划步骤基本一致。
采样方式:等间隔的时间采样。
s
l
o
w
e
r
s_{lower}
slower与
s
u
p
p
e
r
s_{upper}
supper为松弛变量,防止求解失败。
目标函数与二次规划的目标函数差不多,增加了横向加速度的代价值以及松弛变量
w
s
o
f
t
s
l
o
w
e
r
w_{soft}s_{lower}
wsoftslower与
w
s
o
f
t
s
u
p
p
e
r
w_{soft}s_{upper}
wsoftsupper。
横向加速度的计算方式:
曲率是关于
s
s
s的关系式,所以要进行平滑,对于非线性规划的求解器,无论是目标函数还是约束函数,都需要满足二阶可导:
κ
′
=
f
′
′
(
s
)
\kappa ' = f''(s)
κ′=f′′(s)
曲率的平滑也是用到了二次规划的方法,用曲率的一阶导、二阶导、三阶导作为损失函数.
最后得到一条平滑曲率的曲线。
接下来是约束条件:
限速的函数并非直接可以得到,接下来看看限速函数是怎么来的。
限速的来源如下图所示:
将所有的限速函数相加,得到下图的限速函数,很明显,该函数既不连续也不可导,所以需要对其进行平滑处理。
对于限速曲线的平滑,Apollo采样分段多项式进行平滑,之后采样二次规划的方式进行求解。限速曲线的目标函数如下:
如此,我们就有了连续且可导的限速曲线。
再回到约束中,为了避免求解的失败,二次规划中对位置的硬约束,在非线性规划中转为了对位置的软约束。提升求解的精度。
同时还需满足基本的物理学原理
最后代入Ipopt中进行非线性规划的求解。
Ipopt(Interior Point Optimizer)是一个用于大规模非线性优化的开源软件包。它可用于解决如下形式的非线性规划问题:
g
L
{g^L}
gL和
g
U
{g^U}
gU是约束函数的上界和下界,
x
L
{x^L}
xL和
x
U
{x^U}
xU是优化变量的上界和下界。
Ipopt的求解由以下几个函数构成:
1.get_nlp_info()定义问题规模
/** Method to return some info about the nlp */
bool get_nlp_info(int &n, int &m, int &nnz_jac_g, int &nnz_h_lag,
IndexStyleEnum &index_style) override;
• 优化变量数量:n
• 约束函数数量:m
• 雅可比矩阵非0项数量:nnz_jac_g
• 黑塞矩阵非0项数量:nnz_h_lag
2.get_bounds_info()定义约束边界约束
/** Method to return the bounds for my problem */
bool get_bounds_info(int n, double *x_l, double *x_u, int m, double *g_l,
double *g_u) override;
• 自变量的下边界:x_l
• 自变量的上边界: x_u
• 约束函数下边界:g_l
• 约束函数的上边界:g_u
3.get_starting_point()定义初值
/** Method to return the starting point for the algorithm */
bool get_starting_point(int n, bool init_x, double *x, bool init_z,
double *z_L, double *z_U, int m, bool init_lambda,
double *lambda) override;
• 定义优化变量的初始值x
对于速度规划问题,如何计算初始解?
Apollo同样用分段多项式二次规划的求解方式,得到符号约束的速度平滑曲线,作为非线性规划的初值。
4.eval_f()求解目标函数
/** Method to return the objective value */
bool eval_f(int n, const double *x, bool new_x, double &obj_value) override;
• 变量值:x
• 目标函数值:obj_val
5.eval_grad_f()求解梯度
/** Method to return the gradient of the objective */
bool eval_grad_f(int n, const double *x, bool new_x, double *grad_f) override;
• 变量值:x
• 梯度值:grad_f
梯度的定义:
目标函数:
偏导数:



6.eval_g()求解约束函数
/** Method to return the constraint residuals */
bool eval_g(int n, const double *x, bool new_x, int m, double *g) override;
• 变量值:x
• 约束函数值:g
7.eval_jac_g()求解约束雅可比矩阵
/** Method to return:
* 1) The structure of the jacobian (if "values" is nullptr)
* 2) The values of the jacobian (if "values" is not nullptr)
*/
bool eval_jac_g(int n, const double *x, bool new_x, int m, int nele_jac,
int *iRow, int *jCol, double *values) override;
• 变量值:x
• 雅可比矩阵非0元素数量:nele_jac
• 雅可比矩阵值:values
雅可比矩阵:
求解器通过稀疏矩阵来保存值。

求解雅可比矩阵需要对约束函数进行求偏导:

微分关系等式约束:



8.eval_h()求解黑塞矩阵
/** Method to return:
* 1) The structure of the hessian of the lagrangian (if "values" is
* nullptr) 2) The values of the hessian of the lagrangian (if "values" is not
* nullptr)
*/
bool eval_h(int n, const double *x, bool new_x, double obj_factor, int m,
const double *lambda, bool new_lambda, int nele_hess, int *iRow,
int *jCol, double *values) override;
• 变量值:·x·
• 拉格朗日乘数:·lambda·
• 黑塞矩阵值:·values·
• 目标函数因数:·obj_factor·
黑塞矩阵:
拉格朗日函数
Ipopt的拉格朗日黑塞矩阵:
目标函数的二阶偏导数:

约束函数的二阶偏导数:

9. finalize_solution()
/** @name Solution Methods */
/** This method is called when the algorithm is complete so the TNLP can
* store/write the solution */
void finalize_solution(Ipopt::SolverReturn status, int n, const double *x,
const double *z_L, const double *z_U, int m,
const double *g, const double *lambda,
double obj_value, const Ipopt::IpoptData *ip_data,
Ipopt::IpoptCalculatedQuantities *ip_cq) override;
目标函数取得最小值时的优化量:x
目标函数最小值:obj_value
云实验地址——Apollo规划之速度规划仿真调试
1.启动DreamView
bash scripts/bootstrap.sh

模式选择Mkz Standard Debug,地图选择Apollo Virutal Map,打开Sim Control模式,打开PNC Monitor,等待屏幕中间区域出现Mkz车模型和地图后即表示成功进入仿真模式。
点击左侧Tab栏Module Controller,启动Planning,Prediction, Routing模块, 如果需要录制数据则打开Recorder模块。
模块启动完成后,点击左侧Tab栏Profile, 选择Scenario Profiles里的course场景集,右上角选择场景场景开始仿真,点击减速让行场景和加速超车场景,观察PNC Monitor st曲线区别.
PNC Monitor中上方的st图是动态规划生成的st曲线,下方的st图是优化算法生成的st曲线
减速让行的场景,可以看到规划出的曲线在ST图中位于障碍物的下方。

加速超车的场景,可以看到规划出的曲线在ST图中位于障碍物的上方。

打开Data Recorder,将场景切换为掉头场景,接近弯道时点击Updata Time记录时间,场景运行结束后关闭planning模块
在云实验界面,点击Notebook打开jupyter
jupyter notebook

创建新的notebook,并输入%matplotlib notebook激活matplotlib
%matplotlib notebook
在jupyter notebook中运行以下命令打开对应时间的非线性规划的中间运行结果/apollo/modules/planning/tools/plot_st_nlp.py为绘图脚本文件的路径,planning.INFO为planning日志文件的路径,23:29:03为update更新的时间
run /apollo/modules/planning/tools/plot_st_nlp.py -f planning.INFO -t 23:29:03

有可能会出现这种状况
刷新几次,或将日志文件用其他方式打开(例如vim),当里面出现日志内容时,就可以了


default_task_config: {
task_type: PIECEWISE_JERK_NONLINEAR_SPEED_OPTIMIZER
piecewise_jerk_nonlinear_speed_optimizer_config {
acc_weight: 2.0
jerk_weight: 3.0
lat_acc_weight: 10.0
s_potential_weight: 0.05
ref_v_weight: 5.0
ref_s_weight: 100.0
soft_s_bound_weight: 1e6
use_warm_start: true
}
}





lat_acc_weight: 1000.0
打开planning模块,切换场景到减速带场景进行仿真,接近减速带时记录下时间,并通过plot st nlp.py脚本观察接近减速带时非线性规划算法的速度规划

在lane_ follow config.pb.txt文件中, 修改速度优化算法为QP算法,重新打开planning模块, 重新运行减速带场景,并记录接近减速带时的时间
stage_config: {
stage_type: LANE_FOLLOW_DEFAULT_STAGE
enabled: true
task_type: LANE_CHANGE_DECIDER
task_type: PATH_REUSE_DECIDER
task_type: PATH_LANE_BORROW_DECIDER
task_type: PATH_BOUNDS_DECIDER
task_type: PIECEWISE_JERK_PATH_OPTIMIZER
task_type: PATH_ASSESSMENT_DECIDER
task_type: PATH_DECIDER
task_type: RULE_BASED_STOP_DECIDER
task_type: SPEED_BOUNDS_PRIORI_DECIDER
task_type: SPEED_HEURISTIC_OPTIMIZER
task_type: SPEED_DECIDER
task_type: SPEED_BOUNDS_FINAL_DECIDER
task_type: PIECEWISE_JERK_SPEED_OPTIMIZER
#task_type: PIECEWISE_JERK_NONLINEAR_SPEED_OPTIMIZER
task_type: RSS_DECIDER
运行plot_ st qp.py,观察二次规划算法的速度 规划曲线在减速带区域速度规划和非线性规划算法有何区别。




非线性规划




二次规划
二次规划求解效率高,但不精确;非线性规划求解效率低,但精度高。
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵
目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal
我认为我的问题最好用一个例子来描述。假设我有一个名为“Thing”的简单模型,它有一些简单数据类型的属性。像...Thing-foo:string-goo:string-bar:int这并不难。数据库表将包含具有这三个属性的三列,我可以使用@thing.foo或@thing.bar之类的东西访问它们。但我要解决的问题是当“foo”或“goo”不再包含在简单数据类型中时会发生什么?假设foo和goo代表相同类型的对象。也就是说,它们都是“Whazit”的实例,只是数据不同。所以现在事情可能看起来像这样......Thing-bar:int但是现在有一个新的模型叫做“Whazit”,看起来
我有一个要在我的Rails3项目中使用的数组扩展方法。它应该住在哪里?我有一个应用程序/类,我最初把它放在(array_extensions.rb)中,在我的config/application.rb中我加载路径:config.autoload_paths+=%W(#{Rails.root}/应用程序/类)。但是,当我转到railsconsole时,未加载扩展。是否有一个预定义的位置可以放置我的Rails3扩展方法?或者,一种预先定义的方式来添加它们?我知道Rails有自己的数组扩展方法。我应该将我的添加到active_support/core_ext/array/conversion
我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or
如何学习ruby的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/