补传一下,好久之前做的。
理想下双灯塔求结果点
函数原型
bool lighthouseGeometryGetPositionFromRayIntersection(const baseStationGeometry_t* geo1, const baseStationGeometry_t* geo2, float angles1[2], float angles2[2], vec3d position, float *position_delta);
/**
* @brief Get a normalized vector representing the direction of a ray in world reference frame, based on
* sweep angles and base station orientation.
*
* @param baseStation - Geometry data for the base statsion (position and orientation)
* @param angle1 - horizontal sweep angle
* @param angle2 - vertical sweep angle
* @param ray - (output) the resulting normalized vector
*/
提供的数据
灯塔
这个函数首先获取了两个baseStationGeometry_t
记录了两个灯塔的信息
#define vec3d_size 3
typedef float vec3d[vec3d_size];
typedef float mat3d[vec3d_size][vec3d_size];
typedef struct {
__attribute__((aligned(4))) vec3d origin;
__attribute__((aligned(4))) mat3d mat;
bool valid;
} __attribute__((packed)) baseStationGeometry_t;
vec3d
代表了灯塔的具体位置,mat3d
存储了三个向量,分别表示了前向量,上向量,右向量
角度
每个灯塔的两次扫描会得到两个角度,这两个角度表示的两个面交于一线来表示一条空间直线
两个灯塔各有两个角度,一共提供四个角度即可。
计算的结果
结果点
通过两个灯塔扫描出的点的坐标
偏差范围
两个灯塔的射线最近距离长度
计算流程
先获取向量
我们有角度a和角度b,角度a代表平面和xoz面的角度,角度b代表平面与xoy平面的角度,可得直线方程
叉乘两个平面的法向量即可获得交线的方向向量p
再对p进行单位化。此时的p是相对于灯塔,以灯塔作为坐标原点的,需要进行下一步转换。
利用灯塔的mat3d(记录了灯塔朝向)对该向量进行基变换
此时的向量d就被转化为了绝对向量,是基于地面的。
void lighthouseGeometryGetRay(const baseStationGeometry_t* baseStationGeometry, const float angleH, const float angleV, vec3d ray) {
vec3d a = {arm_sin_f32(angleH), -arm_cos_f32(angleH), 0}; // Normal vector to X plane
vec3d b = {-arm_sin_f32(angleV), 0, arm_cos_f32(angleV)}; // Normal vector to Y plane
// 获取两个面 两个面的交线就是直线
vec3d raw_ray = {};
vec_cross_product(b, a, raw_ray); // Intersection of two planes -> ray vector.
// 方向向量
float len = vec_length(raw_ray);
arm_scale_f32(raw_ray, 1 / len, raw_ray, vec3d_size); // Normalize raw ray length.
// 单位化方向向量
arm_matrix_instance_f32 source_rotation_matrix = {3, 3, (float32_t *)baseStationGeometry->mat};
arm_matrix_instance_f32 ray_vec = {3, 1, raw_ray};
arm_matrix_instance_f32 ray_rotated_vec = {3, 1, ray};
mat_mult(&source_rotation_matrix, &ray_vec, &ray_rotated_vec);
// 通过发出面的方向旋转当前向量
}
调整出发点偏移
激光发射器与灯塔坐标有一定的偏移,进行调整。
void lighthouseGeometryGetBaseStationPosition(const baseStationGeometry_t* bs, vec3d baseStationPos) {
// TODO: Make geometry adjustments within base station.
vec3d rotated_origin_delta = {};
//vec3d base_origin_delta = {-0.025f, -0.025f, 0.f}; // Rotors are slightly off center in base station.
// arm_matrix_instance_f32 origin_vec = {3, 1, base_origin_delta};
// arm_matrix_instance_f32 origin_rotated_vec = {3, 1, rotated_origin_delta};
// mat_mult(&source_rotation_matrix, &origin_vec, &origin_rotated_vec);
baseStationGeometry_t* bs_unconst = (baseStationGeometry_t*)bs;
arm_add_f32(bs_unconst->origin, rotated_origin_delta, baseStationPos, vec3d_size);
}
计算结果点
我们有两个基站的坐标以及它们发射出去的向量方向
此时产生两条射线,由于射线不一定相交,所以只能找到估计点。对于这两个射线的最短距离的线段,估计点就是这个线段的中点。
线段AB有点A在射线1上,点B在射线2上,容易理解A是在射线1上距离B最近的点,易得AB垂直于两射线。
设p1为射线1(灯塔1)的起始点,p2为射线2(灯塔2)的起始点,对于射线1上的点A有
OA = Op1 + t1 * d1
OB = Op2 + t2 * d2
上式同理(t1和t2都是待计算的值)
有以下推导
有了t1和t2,AB点的值就得出来了,
再求AB两点的中点即为答案点。
static bool intersect_lines(vec3d orig1, vec3d vec1, vec3d orig2, vec3d vec2, vec3d res, float *dist) {
vec3d w0 = {};
arm_sub_f32(orig1, orig2, w0, vec3d_size);
float a, b, c, d, e;
arm_dot_prod_f32(vec1, vec1, vec3d_size, &a);
arm_dot_prod_f32(vec1, vec2, vec3d_size, &b);
arm_dot_prod_f32(vec2, vec2, vec3d_size, &c);
arm_dot_prod_f32(vec1, w0, vec3d_size, &d);
arm_dot_prod_f32(vec2, w0, vec3d_size, &e);
float denom = a * c - b * b;
// 求|vec1 x vec2|^2的值,越小越相近
if (fabsf(denom) < 1e-5f) {
return false;
}
// 可能基站过近,两个向量几乎重合,无法计算
// Closest point to 2nd line on 1st line
float t1 = (b * e - c * d) / denom;
vec3d pt1 = {};
arm_scale_f32(vec1, t1, pt1, vec3d_size);
arm_add_f32(pt1, orig1, pt1, vec3d_size);
// Closest point to 1st line on 2nd line
float t2 = (a * e - b * d) / denom;
vec3d pt2 = {};
arm_scale_f32(vec2, t2, pt2, vec3d_size);
arm_add_f32(pt2, orig2, pt2, vec3d_size);
// Result is in the middle
vec3d tmp = {};
arm_add_f32(pt1, pt2, tmp, vec3d_size);
arm_scale_f32(tmp, 0.5f, res, vec3d_size);
// Dist is distance between pt1 and pt2
arm_sub_f32(pt1, pt2, tmp, vec3d_size);
*dist = vec_length(tmp);
return true;
}
3D-3D:ICP 推导
激光SLAM(没有点和点之间的匹配)
先验:确定匹配关系
给定了点Pi和Qi,计算刚体变换Rt使得 RPi + T≈Qi
优化问题:(使得误差最小)
设最优解为(R*, t*)
由最优化理论易得最优解在一阶导数为0
*
$ 记C_p = \frac{1}{n}\sum^{n}{i = 1}pi,C_q = \frac{1}{n}\sum^{n}{i = 1}q_i$
pi实际上就是点云A的质心
也就是点云B的质心可得:
把t*代入原优化函数
几何意义上相当于每个点去掉点云的质心,把点云移动到了原点附近,之后的pi和qi记为减去质心的点。
我们要试F®最小化,也就是让<Rpi, qi>
最大化T®即可
记矩阵
A的SVD分解有
原式
已知V^TRU依旧是一个正交矩阵,可得要使原式值最大,
trace(AB) = trace(BA)