图形学
光线与球体的相交测试:
可以通过判断这个一元二次方程的delta判别式判断光线是否击中了球体
结构体:默认的成员访问权限是 public
。
类:默认的成员访问权限是 private
。
视口裁剪(Viewport Clipping)
视口裁剪是指在渲染过程中,图形系统根据当前的视口(viewport)大小和位置,裁剪出只在视口内的部分。这通常发生在将三维场景转换为二维图像时。
- 视口定义:视口是指在窗口或屏幕上的一个矩形区域,图形渲染的结果只显示在这个区域内。
- 用途:视口裁剪确保只绘制视口范围内的图形,避免无效的计算和渲染,提高效率。
透视裁剪(Perspective Clipping)
透视裁剪是指在透视投影过程中,决定哪些对象在视锥体内并且可见,从而只渲染可见部分。透视投影会产生一个视锥体,位于观察者与场景之间。
- 视锥体:在透视投影中,视锥体是一个从观察点(摄像机位置)向外扩展的锥形区域。只有位于这个区域内的对象才会被渲染。
- 裁剪:透视裁剪会去除视锥体外的对象,避免不必要的计算和渲染,并处理对象的深度关系。
总结
- 视口裁剪:关注的是最终图像在屏幕上的显示区域,只显示视口内的内容。
- 透视裁剪:关注的是三维场景中哪些对象在视锥体内,以决定哪些对象是可见的。
屏幕空间和 NDC(Normalized Device Coordinates)空间之间的关系:
坐标变换流程
在图形渲染过程中,顶点坐标经历多个变换,从世界空间到最终的屏幕空间,这个过程大致包括:
- 模型变换:将顶点从局部模型坐标转换到世界坐标。
- 视图变换:将世界坐标转换到相机坐标(视图空间)。
- 投影变换:将相机坐标转换到裁剪空间。
- 裁剪:将不在视野范围内的顶点剔除。
- 透视除法:将裁剪空间的坐标转换到 NDC 空间。这个步骤涉及将每个坐标的 x、y 和 z 分别除以 w(齐次坐标),使得坐标范围归一化到 [-1, 1]。
NDC 到屏幕空间的转换
一旦顶点处于 NDC 空间,它们需要被转换到屏幕空间:
- 视口变换:将 NDC 坐标映射到实际的屏幕像素坐标。视口变换使用屏幕的分辨率来进行坐标的线性变换。具体步骤是:
- 将 NDC 的 x 和 y 坐标从 [-1, 1] 范围映射到屏幕像素的范围。例如,对于一个宽度为 W,高度为 H 的屏幕:
screenX = (ndcX + 1) * 0.5 * (W - 1)
screenY = (1 - (ndcY + 1) * 0.5) * (H - 1)
(Y 轴可能需要翻转,具体取决于坐标系统的定义)
- 将 NDC 的 x 和 y 坐标从 [-1, 1] 范围映射到屏幕像素的范围。例如,对于一个宽度为 W,高度为 H 的屏幕:
- NDC 空间 是一个归一化的坐标系统,主要用于在渲染管线中的处理,使得顶点坐标能够统一处理,不论目标显示设备的分辨率如何。
- 屏幕空间 是实际显示的坐标系统,与屏幕的物理尺寸和分辨率相关。
- 转换:通过视口变换,NDC 空间的坐标被转换为屏幕空间的像素坐标,从而最终呈现在用户的屏幕上。
NDC 空间可以看作是从三维世界到二维屏幕的中间步骤,而屏幕空间则是最终的输出结果。
虚函数(Virtual Function)
- 定义:虚函数是在基类中声明为
virtual
的成员函数,可以在派生类中重写(override)。 - 实现:虚函数可以有具体的实现。基类中的虚函数可以提供默认的实现,派生类可以选择重写它。
- 对象创建:可以创建基类的对象,也可以创建派生类的对象。
纯虚函数(Pure Virtual Function)
- 定义:纯虚函数是在基类中声明为
virtual
并且等于0的函数。语法是virtual void functionName() = 0;
。 - 实现:纯虚函数没有实现,基类通常不可以实例化。
- 对象创建:不能直接创建类的对象(即抽象类),只能创建派生类的对象。
- 用途:用于定义接口,强制派生类实现特定的函数
输入流操作符 (>>
) 在处理流时,会自动跳过空格和其他空白字符(如换行符和制表符),直到遇到下一个有意义的值为止。因此,空格在这一过程中并不会被显式处理。
line.compare(0, 2, "v ")
的含义:
- **
0
**:表示从line
字符串的第一个字符开始进行比较。 - **
2
**:表示比较的长度为 2,也就是说,只比较line
字符串的前两个字符。 - **
"v "
**:表示要将line
字符串的前两个字符与字符串"v "
进行比较。 - 如果
line
的前两个字符与"v "
完全匹配,compare
方法返回 0。 - 如果不匹配,返回一个非 0 的值(具体的值取决于比较的结果:如果
line
字符串小于"v "
,返回一个负数;如果line
字符串大于"v "
,返回一个正数)。
平面和场景
场景:管理世界空间下所有的形状(Shape)
平面的数学定义:
修改是为了改进多线程环境中的 线程安全性 和 竞态条件 的问题。我们来详细分析一下原始代码和修改后的代码之间的差异,以及为什么要这样修改。
原始代码:
count++;
if (count % film.getWidth() == 0) {
std::cout << static_cast<float>(count) / (film.getWidth() * film.getHeight()) << std::endl;
}
修改后的代码:
int n = ++count;
if (n % film.getWidth() == 0) {
std::cout << static_cast<float>(n) / (film.getWidth() * film.getHeight()) << std::endl;
}
问题分析:
1. count++
是非原子操作
count++
实际上是由 两个操作 组成的:读取count
的值,然后 **增加count
**。在多线程环境中,如果多个线程同时执行count++
,就会发生 竞态条件(race condition),可能导致count
的值增加不正确或者丢失。- 例如,如果线程 A 和线程 B 同时读取到相同的
count
值,然后都加 1 写回,这样就会丢失一个递增的结果,导致count
的值不准确。
2. ++count
是原子操作
++count
是 自增并返回自增后的值,它在执行过程中是原子的,不会有并发冲突(前提是count
本身是原子变量或操作)。这是因为它在自增的时候直接对count
的值进行更新并返回,而不需要先读取再写入,避免了多个线程同时读取和写入的情况。
3. 存储递增结果到 n
- 修改后的代码
int n = ++count;
将自增后的结果保存在n
中。这样做的好处是:- 保证了
count
更新后的值在后续代码中是确定的。如果我们直接在if (count % film.getWidth() == 0)
中访问count
,其他线程可能会在我们检查count
时修改它,导致判断条件不稳定。而n
是在更新后的值保存时就固定了,因此后续的判断和输出使用n
可以确保一致性。 - 避免了
count
被其他线程修改时的影响。虽然count
本身是全局共享的,但通过把递增结果保存在n
中,我们保证了n
的值不会在后续代码执行时被其他线程改动,确保了输出的正确性。
- 保证了
线程安全与性能考虑:
- 使用
int n = ++count;
的修改,确保了 每个线程对count
的更新是安全的。同时,虽然++count
在某些情况下可能是原子操作,但若count
是一个普通变量,并且没有显式的线程同步机制,那么可能仍然存在隐性的问题。将更新后的count
值保存到n
可以减少这种不确定性。 - 在多线程环境下,避免直接在条件判断中使用共享变量(如
count
)是一个常见的做法,尤其是当这个变量在多个线程中共享且没有其他同步机制时。通过中间变量n
来持有更新后的值,避免了在count
被其他线程修改时产生的竞态条件。
glm::translate(glm::mat4(1.f), pos) *
glm::rotate(glm::mat4(1.f), glm::radians(rotate.z), { 0, 0, 1 }) *
glm::rotate(glm::mat4(1.f), glm::radians(rotate.y), { 0, 1, 0 }) *
glm::rotate(glm::mat4(1.f), glm::radians(rotate.x), { 1, 0, 0 }) *
glm::scale(glm::mat4(1.f), scale)
glm::mat4(1.f)
:创建一个单位矩阵,表示没有任何变换。glm::translate(..., pos)
:将矩阵平移到pos
指定的位置,pos
是一个glm::vec3
向量,表示物体在3D空间中的平移偏移量(x
,y
,z
)。glm::radians(rotate.z)
:将角度rotate.z
转换为弧度,因为GLM的rotate
函数期望的旋转角度单位是弧度。{ 0, 0, 1 }
:指定旋转轴为Z轴。glm::scale(glm::mat4(1.f), scale)
:执行一个缩放变换,其中scale
是一个glm::vec3
向量,表示沿着X、Y和Z轴的缩放比例。例如,scale = { 2.f, 3.f, 1.f }
表示在X轴上放大2倍,在Y轴上放大3倍,而Z轴保持不变。
在GLM中,矩阵的乘法是从 右到左 进行的
frame坐标系不用储存坐标系的原点,只用存储坐标轴的方向
镜面反射:x,z取反
漫反射:采样
for (size_t i = 0; i < shapeInstances.size(); i++) {
auto shapeInstance = shapeInstances[i];
auto ray_object = ray.rayObjectFromWorld(shapeInstance.object_from_world);
hitInfo = shapeInstance.shape.intersect(ray_object, t_min, t_max); // 需要把世界空间下的光线转换成对象空间里相交测试
if (hitInfo.has_value()) {
t_max = hitInfo->distance;
closest_hitInfo = hitInfo;
closest_instance = &shapeInstance;
}
}
与:
for (size_t i = 0; i < shapeInstances.size(); i++) {
auto ray_object = ray.rayObjectFromWorld(shapeInstances[i].object_from_world);
hitInfo = shapeInstances[i].shape.intersect(ray_object, t_min, t_max); //需要把世界空间下的光线转换成对象空间里相交测试
if (hitInfo.has_value()) {
t_max = hitInfo->distance;
closest_hitInfo = hitInfo;
closest_instance = &shapeInstances[i];
}
}
看似一样,实则不一样
第一个是拷贝,
shapeInstance
就是一个独立的对象,它与原始 shapeInstances[i]
没有直接关系
减小光追的噪点:
motion_planning
地图处理:
Octree Map存储效率高,占用内存小,适合三维空间
缺点:构建和查询比较复杂,需要递归操作
对于平面,可以使用四叉树地图
拓扑地图
不能生成具体的路径
欧式距离场地图
每一个像素点的值是到目标点的距离
高精地图+激光雷达:
基于结构化地图(已经使用数据结构/储存方式存储好了环境信息)的搜索:
基于采样的路径搜索算法(不太适用于移动机器人)
没有地图的算法:概率路图
没有地图,先构造地图:
建完图后路径规划:
PRM的升级:RRT(快速搜索随机数)
(一边构建地图,一边搜索路径)
基于图搜索的路径搜索算法
1.朴素的搜索思想——BFS,DFS
栅格地图可以很容易的转换成graph,欧式距离场可以很容易的转换成栅格地图,拓扑地图本身就是graph
图搜索的核心问题:
std::reverse
是 C++ 标准库中的一个算法,用于反转给定范围内的元素顺序。它定义在 <algorithm>
头文件中。
std::reverse
接受两个迭代器作为参数,表示要反转的范围。其基本语法如下:
#include <algorithm> // 需要包含这个头文件
#include <vector>
#include <iostream>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// 反转 vec 中的元素
std::reverse(vec.begin(), vec.end());
// 输出反转后的结果
for (int v : vec) {
std::cout << v << " "; // 输出: 5 4 3 2 1
}
return 0;
}
2.(DFS,BFS)搜索算法的进化————Dijkstra,A*
Dijkstra(使用八联通图,可以走斜线)
为什么: 如果distance._current大于distance[current]: 继续下一次循环
优先队列会按照节点的最小距离进行排序。可能在多个阶段,一个节点被加入队列,但这些加入可能是基于旧的距离值。
当我们从队列中弹出一个节点时,这个节点的距离可能并不是它的最短距离(因为它可能已经被更短的路径更新过)。
如果当前弹出的节点的距离大于我们在
distance
数组中记录的最短距离,这意味着我们已经找到了一条更短的路径到达这个节点,因此可以跳过对这个节点的处理。
A*(优化搜索速度)
A*的核心思想:不但考虑七点到当前节点的代价值,还要考虑当前节点到目标点的代价值
这里的估算函数叫启发式函数
常用的启发式函数
注意:曼哈顿距离>真实距离(有可能无法保证路径最优),但就因为它比较大,所以有时候它的效果最好
A*算法的最优性保证:
只有满足条件,A*所得的路径才是最短路径
启发函数的选择:
A*的平衡性问题:
虽然A* 算法的结果和理想结果的路径都是最短的,但是理想结果的转折更少,更适合使用,而且A* 算法搜索的节点还是有点多
怎么解决呢?
从Dijkstra到A *增加了一个指标,效果大大提升,那再加一个指标
在实际工程上,DFS会用递归方法实现
一种有趣的搜索思路:JPS
JPS是一种跳跃式搜索算法:关注障碍物边缘的关键性节点(最短路径是起点+障碍物边缘节点+终点)
解决A *算法的平衡性问题
强迫邻居是必须要探索的点(强迫邻居有可能就是绕过障碍物的关键节点)
跳点和强迫邻居都需要继续拓展,所以都要加到openlist里面
这就是向前看规则:
跳跃规则:
起点的八个邻居都需要加入到openlist里,都需要拓展
八邻域探索方向可分为正向和对角线方向
A*会八个方向一圈一圈的探索,JPS是只沿一个方向探索:
对比:
蓝色是被拓展过并添加到openlist里的,绿色是没有拓展过但是添加到openlist里的,右图里灰色是访问过的点
A*的openlist里的节点更多
从容应对动态障碍物–D*(动态A星算法)
D* 又称Dynamic A*
初始生成一条最优路径,在跟踪过程中,根据障碍物的变化,实时调整受影响的局部局部路径
算法流程:
1.方向构建:
h(X):用来存储路径代价,指从X到达终点G的路径({X,……G},简记为{X})代价,不一定是全局最优,第一次搜索到起点时时,所有点的h会被更新,计算方式同Dijkstra算法,是用相邻两点的代价+上一个点的代价累加得到
k(X):用来记录自X节点被加入到OPEN_LIST中后的最小h(X)值(具体计算方式由Insert函数决定),也是优先队列OPEN_LIST的排序依据,k将会保持到最小,它表示了本点在全图环境中到G点的最小代价(k(x)没有受到障碍物增加的影响)
3,代价修改:
4,状态处理
考虑动力学和运动学的路径搜索
优化了节点的扩展方式,节点不一定是栅格的中心(弧形路径)
优化了启发式函数
搜索到的路径还要进行轨迹优化
轨迹优化——Min-Jerk(jerk是加加速度)
贝塞尔曲线把对连续曲线的规划,转变为对控制点的规划
贝塞尔曲线还有一个巨大的优点:导数还是贝塞尔曲线,仍然有控制点
缺点:贝塞尔曲线的形状完全依赖于控制点的数量和位置
移动一个控制点可能会对曲线的整体形状产生意想不到的影响,这使得局部控制变得困难
B样条曲线解决这两个问题
2.
Min-Jerk是舒适性,Min-Snap是节省燃料
lattice planner
轨迹跟踪
Navigation2组成模块
运行流程:
功能包:
Nav2中的地图
代价地图
分层代价地图:(直更新有变化的区域)
论文里的分层:
Nav2分层:
(速度过滤器:限速区)
重启蓝牙服务:
sudo systemctl restart bluetooth
代价地图局部更新的过程:
更新边界,更新值
Nav2的核心特点是使用插件机制,插件机制的实现过程是每个层去继承一个基类
膨胀层具体实现:
限速区具体实现:
配置参数
机器人坐标系:
(TF树里只能有一个parent)
代价地图有两张:
全局路径搜索(静态),局部轨迹优化(动态)
全局代价地图参数:
局部代价地图参数:
footprint
是机器人在环境中占据的空间的几何形状。这个形状通常用一组坐标点来表示,形成一个多边形。
resolution=0.05
表示地图的分辨率。指每个栅格(grid cell)的大小,以米为单位
全局路径规划服务器
Planner_Server的功能
1.对行为树接口:
(navigation2里面使用了大量的action通信机制)
2,开启全局代价地图
3.加载规划算法
插件机制:
planning_server的源码实现:
planner_server参数配置
Controller_Server
1.接口回调函数(给行为树调用)
2.控制器插件
重要插件–进度检查器
重要插件–目标检查器
控制流程:
3.开启局部代价地图
和Planner_Server类似
DWB算法详解
C++STL
STL 即标准模板库(Standard Template Library),是 C++ 标准库的一部分,里面包含了一些模板化的通用的数据结构和算法。由于其模板化的特点,它能够兼容自定义的数据类型,避免大量的造轮子工作。NOI 和 ICPC 赛事都支持 STL 库的使用,因此合理利用 STL 可以避免编写无用算法,并且充分利用编译器对模板库优化提高效率。
Boost 是除了标准库外,另一个久副盛名的开源 C++ 工具库,其代码具有可移植、高质量、高性能、高可靠性等特点。Boost 中的模块数量非常之大,功能全面,并且拥有完备的跨平台支持,因此被看作 C++ 的准标准库。C++ 标准中的不少特性也都来自于 Boost,如智能指针、元编程、日期和时间等。尽管在 OI 中无法使用 Boost,但是 Boost 中有不少轮子可以用来验证算法或者对拍,如 Boost.Geometry 有 R 树的实现,Boost.Graph 有图的相关算法,Boost.Intrusive 则提供了一套与 STL 容器用法相似的侵入式容器。
STL 容器
分类:
1. ==序列式==容器
- vector(向量)后端可高效增加元素的顺序表.
- array(数组),定长的顺序表。
- deque(双端队列)双端可高效增加元素的顺序表。
- list(列表)可以沿双向遍历的链表。
- forward_list(单向列表)只能沿一个方向遍历的链表。
2,==关联式==容器
- set(集合)有序储存互异元素的容器
- 不允许重复元素。存储唯一的元素,且自动按升序排序。
- 元素的插入、删除和查找操作的时间复杂度为O(log n)。
- multiset(多重集合)允许存储重复的元素,且也会自动按升序排序
- 允许重复元素。
- 与
set
类似,插入、删除和查找操作的时间复杂度同样为O(log n)。
- map(映射)由{建,值}对组成的集合(也是集合)
- 唯一性:每个键只能出现一次;如果插入一个已存在的键,则会更新其对应的值。
- 有序性:
map
中的元素会根据键自动排序,默认情况下是升序排列。 - 时间复杂度:插入、删除和查找操作的平均时间复杂度为O(log n)。
3,无序(并联式)容器
- 无序(多重)集合(
unordered_set
/unordered_multiset
)C++11,与set
/multiset
的区别在于元素无序,只关心「元素是否存在」,使用哈希实现。- 唯一性:
unordered_set
中的每个元素都是唯一的,不能重复。 - 无序性:元素的排列是基于哈希值的,不按照任何特定顺序存储。
- 性能:插入、查找和删除操作的平均时间复杂度为 O(1),但在最坏情况下可能会退化为 O(n)
- 唯一性:
- 无序(多重)映射(
unordered_map
/unordered_multimap
)C++11,与map
/multimap
的区别在于键 (key) 无序,只关心 “键与值的对应关系”,使用哈希实现。
容器适配器
容器适配器其实并不是容器。它们不具有容器的某些特点(如:有迭代器、有 clear()
函数……)。
「适配器是使一种事物的行为类似于另外一种事物行为的一种机制」,适配器对容 器进行包装,使其表现出另外一种行为。
- 栈(
stack
) 后进先出 (LIFO) 的容器,默认是对双端队列(deque
)的包装。 - 队列(
queue
) 先进先出 (FIFO) 的容器,默认是对双端队列(deque
)的包装。 - 优先队列(
priority_queue
) 元素的次序是由作用于所存储的值对上的某种谓词决定的的一种队列,默认是对向量(vector
)的包装。
容器声明
都是 containerName<typeName,...> name
的形式,但模板参数(<>
内的参数)的个数、形式会根据具体容器而变。
本质原因:STL 就是「标准模板库」,所以容器都是模板类。
容器共有函数
= :赋值运算符,赋值构造函数
begain() :返回指向开头元素的迭代器
end():返回指向末尾的下一个元素的迭代器。end()
不指向某个元素.end() 迭代器指向的是一个“哨兵”位置,表示容器的结束。可以用来判断迭代是否完成。
size():返回容器内的元素个数
max_size()
:返回容器 理论上 能存储的最大元素个数。依容器类型和所存储变量的类型而变。
empty()
:返回容器是否为空。
swap()
:交换两个容器。
clear()
:清空容器。
==
/!=
/<
/>
/<=
/>=
:按 字典序 比较两个容器的大小.无序容器不支持 <
/>
/<=
/>=
。
Webots
WARNING: To drag this element, first rotate the view so that the horizontal plane is clearly visible.
旋转到水平面上再shift+左键
右键平移
保存:Ctrl shift s
向导:
1.新建项目目录向导(新建场景)
2.新建机器人控制器向导(新建控制器)
只有机器人节点才能导出urdf文件
转换完后的文件放到protos目录下
在 Webots 中,Solid
节点用于定义具有物理属性的物体,主要用于仿真中的动态交互。它包含了物理模拟所需的各种参数,如质量、摩擦力和弹性等。
在 Webots 中,Shape
节点用于定义物体的外观和几何形状。
Solid
节点通常与 Shape
节点结合使用,以创建既有外观又具物理特性的对象
- **形状 (Geometry)**:指的是物体的几何结构和形式,比如它是一个立方体、球体、圆柱体等。形状决定了物体在空间中的占用区域,以及与其他物体的交互和碰撞。
- **外观 (Appearance)**:指的是物体的视觉特征,如颜色、材质、纹理等。外观影响的是物体的视觉效果,而不是其物理结构。
ubuntu-nvidia
写于2024-10-3-00:46
记录一次把ubnutu20.04de nvidia 驱动搞坏又修复,加上对ubuntu显卡驱动和独显/混显,lightdm/gdm3桌面管理器的理解。
由于webots仿真卡死崩溃,就找到一篇博客说:
glxinfo | grep OpenGL
确实opengl输出的是intel集显,使用
sudo prime-select nvidia
再次查看输出:
zgh@zgh-Legion-Y7000P-IAH7:~$ glxinfo | grep OpenGL
OpenGL vendor string: Mesa/X.org
OpenGL renderer string: llvmpipe (LLVM 12.0.0, 256 bits)
虽然不是nvidia,但好歹不是intel了,而且仿真不会卡死崩溃了
sudo systemctl restart gdm3
/etc/X11/xorg.conf
xrandr
glxinfo | grep OpenGL
nvidia-settings
####sudo nvidia-xconfig 不要用这个命令,它会生成什么Xorg的配置文件,把你之前能用的显示配置覆盖掉
/etc/X11/xorg.conf
内容:
# nvidia-xconfig: X configuration file generated by nvidia-xconfig
# nvidia-xconfig: version 535.154.05
Section "ServerLayout"
Identifier "Layout0"
Screen 0 "Screen0" 0 0
InputDevice "Keyboard0" "CoreKeyboard"
InputDevice "Mouse0" "CorePointer"
EndSection
Section "Files"
EndSection
Section "InputDevice"
# generated from default
Identifier "Mouse0"
Driver "mouse"
Option "Protocol" "auto"
Option "Device" "/dev/psaux"
Option "Emulate3Buttons" "no"
Option "ZAxisMapping" "4 5"
EndSection
Section "InputDevice"
# generated from default
Identifier "Keyboard0"
Driver "kbd"
EndSection
Section "Monitor"
Identifier "Monitor0"
VendorName "Unknown"
ModelName "Unknown"
Option "DPMS"
EndSection
Section "Device"
Identifier "Device0"
Driver "nvidia"
VendorName "NVIDIA Corporation"
BusID "PCI:1:0:0"
EndSection
Section "Screen"
Identifier "Screen0"
Device "Device0" #就是要用独立显卡启动
Monitor "Monitor0"
DefaultDepth 24
SubSection "Display"
Depth 24
EndSubSection
EndSection
如果这样写:
Section "Device"
Identifier "Device0"
Driver "nvidia"
VendorName "NVIDIA Corporation"
BusID "PCI:1:0:0"
EndSection
Section "Screen"
Identifier "Screen0"
Device "Device1"
Monitor "Monitor0"
DefaultDepth 24
SubSection "Display"
Depth 24
EndSubSection
EndSection
Section "Device"
Identifier "Device1"
Driver "intel"
VendorName "Intel Corporation"
Option "TripleBuffer" "true"
Option "TearFree" "true"
Option "DRI" "false" #这三条都是为了防止集显显示桌面的时候画面割裂
BusID "PCI:0:2:0"
EndSection
是以Intel集显启动,确实有用
但是此时bios里设置的混合显示,只会使用集显,nvidia-settings内容错误,只能显示笔记本屏幕,无法连接外接显示器。
然后又换lightdm试一试,发现还是只能显示一个,甚至只能显示外接屏幕,拔去外接屏幕,笔记本屏幕也不会亮。
最后又换回了dgm3
##总结:
#只有bios独立显卡 + / etc/X11/xorg.conf使用独立显卡 +dgm3才能正常使用。
2024-10-3-14:44
这样确实可以用,但是独显功耗太大,而且不知道为什么webots非常卡,看来还是不对
bios混显模式+删除xorg.conf
先修改xorg.conf,把GPU,Intel标清,然后用Intel显示,进入桌面,看到nvidia-settings能选择这三种模式了(这是还不是很对,左面只有一个PRIME Profiles)
重启
在tty里把xorg.conf文件rm掉,
直接reboot
就一切正常了,看一下:
nvidia-smi也正常了
2024-10-4-10:29
zgh@zgh-Legion-Y7000P-IAH7:~$ __NV_PRIME_RENDER_OFFLOAD=1 vkcube
zgh@zgh-Legion-Y7000P-IAH7:~$ __NV_PRIME_RENDER_OFFLOAD=1 zed
这个是nvidia gpu的详细官方文档primerenderoffload
对于on-demand模式无法正常切换的,可以通过命令手动切换,上面的zed就是例子,zed需要用nvidia gpu的vulkan,
这里面有vulkan.opengl等等
###这个文档真的很有用
ROS
legged_control
基于OCS2和ros-controls的非线性MPC 1与WBC框架
- docs - 包含项目文档和说明。
- legged_common - 共享的数据结构和函数库。
- legged_control - 主控制堆栈的源码,实现了NMPC和WBC的核心算法。
- legged_controllers - 控制器的具体实现,如腿部运动模式控制器。
- legged_estimation - 估计相关的组件,可能涉及状态估计或传感器数据处理。
- legged_examples - 示例代码,可能含有特定机器人的示例配置或仿真设置。
- legged_gazebo - Gazebo仿真相关文件,用于虚拟环境中测试机器人。
- legged_hw - 硬件交互层,用于实际机器人的控制。
- legged_interface - 提供与机器人交互的API。
- legged_wbc - Whole Body Control的相关代码。
- qpoases_catkin - 可能是作为依赖项的QPOASES求解器的ROS集成。
Ubuntu 20.04
ROS noetic
catkin
OCS2 是一个针对切换系统优化控制的 C++ 工具箱,适用于机器人任务,包括路径约束处理和 URDF 模型支持。它提供了高效算法如 SLQ、iLQR 和 SQP,并具有 ROS 接口。工具箱包含自动微分工具,确保在有限计算能力的机器人应用中实现数值稳定和高效控制。
OCS2是一个大型单库项目;请不要尝试编译整个仓库。
URDF文件和YAML文件通常在 ROS 启动文件中被调用。具体步骤如下:
URDF 文件:
- 在启动文件(
.launch
)中使用<param>
标签将 URDF 文件加载到参数服务器:<param name="robot_description" command="$(find xacro)/xacro $(find your_package)/urdf/your_robot.urdf.xacro"/>
- 在启动文件(
YAML 文件:
- 也可以在启动文件中使用
<rosparam>
标签加载 YAML 文件:<rosparam file="$(find your_package)/config/your_config.yaml" command="load"/>
- 也可以在启动文件中使用
节点中访问:
- 在你的 ROS 节点代码中,可以通过
ros::param::get()
或ros::NodeHandle
来访问这些参数。
- 在你的 ROS 节点代码中,可以通过
这样,URDF 和 YAML 文件就能在你的 ROS 项目中被有效地调用和使用。
<geometry> <!--几何形状-->
<inertial> <!--惯性属性-->
<joint name="world_to_slideBar" type="fixed"> <!--这个关节用于将 slideBar 固定在世界坐标系中的特定位置,使得它在仿真中保持稳定,提供支撑作用。-->
<parent link="world"/>
<child link="slideBar"/>
<origin xyz="0.0 0.0 2.0"/>
</joint>
<gazebo reference="slideBar">
- Gazebo特定属性: 用于指定该链接在 Gazebo 中的特性。
<mass value="0.1"/>
- 质量: 指定了链接的质量为 0.1 千克
<origin>
位置
自由度通俗的讲就是为了唯一确定一个机构的运动状态所必须的独立变量的个数
c12=cos(seta1+seta2 )
<joint name="base_to_plat" type="prismatic">
<axis xyz="1 0 0"/>
<origin xyz="0.0 0.0 0.0"/>
<parent link="slideBar"/>
<child link="cart"/>
<limit effort="100000.0" lower="-15" upper="15" velocity="100"/>
<dynamics damping="0.0" friction="0.0"/>
</joint>
是什么意思
这段代码定义了一个名为 base_to_plat
的滑动关节(prismatic joint),其主要功能是让 cart
在 slideBar
上进行线性滑动。以下是对每个部分的详细解释:
1. <joint name="base_to_plat" type="prismatic">
- 名称和类型:
name="base_to_plat"
: 该关节的名称是base_to_plat
。type="prismatic"
: 关节类型为滑动关节,允许连接的部分沿一个轴线性移动。
2. <axis xyz="1 0 0"/>
- 运动轴:
xyz="1 0 0"
: 表示滑动关节的运动轴为 X 轴。1
表示在 X 方向上有运动,而 Y 和 Z 方向上没有运动。
3. <origin xyz="0.0 0.0 0.0"/>
- 原点位置:
xyz="0.0 0.0 0.0"
: 该关节的原点位置设置在 (0, 0, 0),通常是关节的中心点。
4. <parent link="slideBar"/>
- 父链接:
link="slideBar"
: 指定slideBar
是该关节的父链接。也就是说,cart
相对于slideBar
移动。
5. <child link="cart"/>
- 子链接:
link="cart"
: 指定cart
是该关节的子链接。表示cart
将通过该关节的运动影响到。
6. <limit effort="100000.0" lower="-15" upper="15" velocity="100"/>
- 运动限制:
effort="100000.0"
: 该关节能够承受的最大力为 100,000(单位通常为牛顿)。lower="-15"
: 关节的最小位置限制为 -15(单位通常为米),表示cart
可以向后滑动的最大距离。upper="15"
: 关节的最大位置限制为 15(单位通常为米),表示cart
可以向前滑动的最大距离。velocity="100"
: 关节的最大速度为 100(单位通常为米/秒),表示cart
的移动速度限制。
7. <dynamics damping="0.0" friction="0.0"/>
- 动力学属性:
damping="0.0"
: 阻尼系数为 0,表示没有额外的阻力影响滑动。friction="0.0"
: 摩擦系数为 0,表示在滑动过程中没有摩擦力。
总结
这段代码的主要目的是定义一个滑动关节,使得 cart
可以在 X 轴上滑动,受到的限制和动力学特性被明确设定。这在机器人和机械模拟中是非常重要的,可以精确控制其运动行为。
<transmission name="tran2">
<type>transmission_interface/SimpleTransmission</type>
<joint name="base_to_plat">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
</joint>
<actuator name="planar_motor_2">
<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
<mechanicalReduction>1</mechanicalReduction>
</actuator>
</transmission>
是什么意思
这段代码定义了一个名为 tran2
的传动系统,主要用于连接一个滑动关节和一个执行器。以下是各部分的解释:
1. <transmission name="tran2">
- 传动系统名称: 这个传动系统被命名为
tran2
。
2. <type>transmission_interface/SimpleTransmission</type>
- 传动类型: 使用简单传动接口,表明传动的实现相对直接。
3. <joint name="base_to_plat">
- 关节连接: 连接到先前定义的滑动关节
base_to_plat
。
4. <hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
- 硬件接口: 该关节使用的是努力关节接口,意味着通过施加力来控制关节运动。
5. <actuator name="planar_motor_2">
- 执行器名称: 该传动系统连接的执行器名为
planar_motor_2
。
6. <mechanicalReduction>1</mechanicalReduction>
- 机械减速比: 设置为 1,表示执行器的输出与输入之间没有减速,直接传递力。
总结
整体上,这段代码用于定义一个简单的传动系统,将滑动关节和电机连接起来,以便通过施加力来控制滑动运动。
JointState
消息内容
JointState
消息通常包含以下字段:
name
: 存储关节名称的字符串数组。position
: 存储每个关节当前角度或位置的浮点数数组。velocity
: 存储每个关节当前速度的浮点数数组。effort
: 存储每个关节当前力矩或用力程度的浮点数数组。
LQR(线性二次调节器,Linear Quadratic Regulator)是一种用于控制系统的优化方法。它旨在通过最小化某个代价函数来设计控制器,通常用于线性动态系统。
LQR 的基本公式
假设有一个线性系统描述为:
$ \dot{x} = Ax + Bu $
其中:
- ( x ) 是状态向量
- ( u ) 是控制输入
- ( A ) 和 ( B ) 是系统矩阵
LQR 通过最小化以下代价函数来设计控制器:
$ J = \int_0^{\infty} (x^T Q x + u^T R u) , dt $
其中:
- ( Q ) 是状态权重矩阵,通常是半正定的$(Q \geq 0 )$
- ( R ) 是控制输入权重矩阵,通常是正定的($ R > 0 $)
最优控制律
通过求解代价函数的最小值,可以得到最优控制律:
$ u = -Kx $
其中 $K $是增益矩阵,通过以下公式计算:
$K = R^{-1} B^T P $
而$ P $ 是 Riccati 方程的解:
$A^T P + PA - PBR^{-1}B^T P + Q = 0 $
总结
LQR 方法通过设计一个控制器,最小化系统状态和控制输入的加权平方和,从而实现对线性系统的有效控制。它广泛应用于工程、自动控制、机器人等领域。
代码实现过程
在你提供的代码中,LQR控制器的设计过程主要包含以下几个步骤:
逆矩阵计算:
Eigen::Matrix<double, 1, 1> R_inv = R_.inverse();
这里计算了控制输入权重矩阵 (R) 的逆。
计算 BRB 项:
Eigen::Matrix<double, 4, 4> BRB = B_ * R_inv(0, 0) * B_.transpose();
这一步计算了 $BR^{-1}B^T$项,用于构建哈密尔顿矩阵。
构造哈密尔顿矩阵:
Eigen::Matrix<do uble, 8, 8> H; H.topLeftCorner(4, 4) = A_; H.topRightCorner(4, 4) = -BRB; H.bottomLeftCorner(4, 4) = -Q_; H.bottomRightCorner(4, 4) = -A_.transpose();
哈密尔顿矩阵 (H) 是 LQR 解的核心部分,它结合了系统的动态矩阵 (A)、控制输入矩阵 (B) 和权重矩阵 (Q) 和 (R)。
特征值和特征向量计算:
Eigen::ComplexEigenSolver<Eigen::Matrix<double, 8, 8>> ces; ces.compute(H);
通过计算哈密尔顿矩阵的特征值和特征向量来获取系统的稳定性信息。
选择稳定特征向量:
for (int i = 0; i < 8; ++i) { if (eigenvalues(i).real() < 0 && index < 4) { Vs.col(index) = eigenvectors.col(i); ++index; } }
选择具有负实部的特征值对应的特征向量,以确保系统的稳定性。
解Ricatti方程:
Eigen::MatrixXcd P_c = Vs2 * Vs1.inverse(); Eigen::Matrix4d P = P_c.real(); // Take the real part
通过特征向量计算 Riccati 方程的解 (P),这是 LQR 控制中的关键步骤。
计算 LQR 增益矩阵:
Eigen::Matrix<double, 1, 4> K_temp = R_inv(0, 0) * B_.transpose() * P; return K_temp.transpose();
最后计算控制增益矩阵 (K),该矩阵用于生成控制输入。
总结
LQR 控制器的实现通过构造哈密尔顿矩阵,计算其特征值和特征向量,求解 Riccati 方程,最终得到一个最优控制增益 (K)。通过这种方式,LQR 能够有效地平衡系统的性能和控制输入,以保证系统的稳定性和响应速度。
Eigen::MatrixXd
和 Eigen::Matrix4d
是 Eigen 库中用于表示矩阵的两种不同类型。它们的主要区别如下:
1. 尺寸
Eigen::MatrixXd
:- 这是一个动态大小的矩阵,表示一个任意大小的矩阵。
- 在运行时可以根据需要动态调整其行数和列数。
Eigen::Matrix4d
:- 这是一个固定大小的矩阵,专门表示一个 4x4 的矩阵。
- 行数和列数在编译时就已经确定,不能被更改。
2. 内存管理
Eigen::MatrixXd
:- 由于是动态大小,内存分配通常是在堆上进行管理,可能会有额外的开销。
Eigen::Matrix4d
:- 由于大小固定,内存分配是静态的,通常在栈上分配,性能更高,尤其在小矩阵操作时。
3. 使用场景
Eigen::MatrixXd
:- 适合处理不确定大小的矩阵,特别是在算法中需要处理各种尺寸的情况时。
Eigen::Matrix4d
:- 适合处理明确为 4x4 矩阵的场景,如图形变换、旋转矩阵等。
###ubuntu上ros与anaconda冲突的最好解决办法:
catkin_make -DPYTHON_EXECUTABLE=/usr/bin/python3
下次再安装ros时,在anaconda环境里安装,要不然python混乱太搞了
现在有更好的方法:
export PATH="/usr/bin:$PATH"
zgh@zgh-Legion-Y7000P-IAH7:~/ROS_Projects/dog_gazebo$ which python
/usr/local/anaconda3/bin/python
zgh@zgh-Legion-Y7000P-IAH7:~/ROS_Projects/dog_gazebo$ which python3
/usr/local/anaconda3/bin/python3
zgh@zgh-Legion-Y7000P-IAH7:~/ROS_Projects/dog_gazebo$ sudo vim ~/.bashrc
zgh@zgh-Legion-Y7000P-IAH7:~/ROS_Projects/dog_gazebo$ source ~/.bashrc
zgh@zgh-Legion-Y7000P-IAH7:~/ROS_Projects/dog_gazebo$ which python3
/usr/bin/python3
zgh@zgh-Legion-Y7000P-IAH7:~/ROS_Projects/dog_gazebo$ which python
/usr/bin/python
zgh@zgh-Legion-Y7000P-IAH7:~/ROS_Projects/dog_gazebo$ python -V
Python 3.8.10
完美解決
[ERROR] [1728964010.917743989]: [registerPublisher] Failed to contact master at [localhost:11311]. Retrying…
只是roscore没开
在 MoveIt! 的 moveit_setup_assistant
中,配置规划组时的 Base Link 和 Tip Link 是指机器人模型中关节链的起始和结束部分。具体来说:
Base Link
- 定义: Base Link 是指关节链的起始位置,通常是机器人的“基础”或“根”部分。这通常是固定的、与机器人底座相连的链接。
- 作用: 它作为整个机器人运动的参考点,所有的运动都是相对于这个基准链接进行的。在机器人操作时,Base Link 通常是地面上的固定部分,负责提供稳定性。
Tip Link
- 定义: Tip Link 是指关节链的结束位置,它是规划组的最后一个链接。通常,这个链接表示执行器的末端,如机械手爪、工具或传感器等。
- 作用: Tip Link 是你希望 MoveIt! 进行运动规划的目标位置。通过指定 Tip Link,MoveIt! 可以计算从 Base Link 到 Tip Link 的运动轨迹。
举例
以一个机械臂为例:
- Base Link: 机械臂的底座部分,可能是与地面固定的框架。
- Tip Link: 如果机械臂的末端是一个夹具,那么这个夹具的位置就是 Tip Link。
在 MoveIt! 的设置中,您提到的参数具有特定的含义和功能。以下是每个参数的解释:
1. Group Name: arm
- 定义: 这是规划组指定的名称。在这个例子中,
arm
表示该规划组控制的是机器人的手臂。 - 作用: 通过命名规划组,可以在后续的运动规划和控制中方便地引用这一组链接和关节。
2. Kinematic Solver: kdl_kinematics_plugin/KDLKinematicsPlugin
- 定义: 这是所使用的运动学求解器。
KDLKinematicsPlugin
是基于 Kinematics and Dynamics Library (KDL) 的运动学求解插件。 - 作用: 它负责计算机器人的正向运动学和逆向运动学。正向运动学用于从关节角度计算末端执行器的位置,而逆向运动学则用于根据目标位置计算所需的关节角度。
3. Kin. Search Resolution: 0.005
- 定义: 这个参数表示在进行运动规划时的搜索分辨率,单位是米(m)。
- 作用: 它定义了在搜索空间中步进的大小。较小的值会导致更精细的搜索,这可能增加计算时间,但能提供更高精度的路径。
4. Kin. Search Timeout (sec): 0.05
- 定义: 这是运动规划过程中允许的最大搜索时间,单位是秒。
- 作用: 如果在 0.05 秒内未找到有效的运动规划,求解器将停止搜索。这可以帮助避免长时间的计算延迟,尤其是在实时应用中,但也可能导致未找到可行的解决方案。
在配置机械臂的末端执行器(End Effector)时,需要填写一些参数
- End Effector Name(末端执行器名称):
- 这是给末端执行器(例如夹爪、工具等)起的名称。
- End Effector Group(末端执行器组):
- 这是将末端执行器归类到特定组中的选项。此组通常用于定义哪些关节或链接与该末端执行器相关。在您的例子中,组名是
gripper
。
- 这是将末端执行器归类到特定组中的选项。此组通常用于定义哪些关节或链接与该末端执行器相关。在您的例子中,组名是
- Parent Link(父链接):
- 这是末端执行器连接到的父链接。通常,这是机械臂的一部分,负责控制末端执行器的位置和姿态。在例子中,父链接为
grasping_frame
,这可能是机械臂的一个特定链接,用于抓取物体。
- 这是末端执行器连接到的父链接。通常,这是机械臂的一部分,负责控制末端执行器的位置和姿态。在例子中,父链接为
- Parent Group(父组, 可选项):
- 这是一个可选字段,如果希望将末端执行器与某个特定的组关联,可以在这里填写。若不需要特别设置,此项可以留空
roslaunch moveit_setup_assistant setup_assistant.launch
要在source ./devel/setup.bash的终端使用
学长您好,我看您的【ROS-Moveit!】机械臂控制探索学机械臂联合仿真,但是您开源的代码为啥在运行:
roslaunch pigot_moveit_config moveit_planning_execution.launch
后,rviz显示出错呀,
woc,noetic版本的压根就不用改,新建其他功能包,直接用demo_gazebo.launch,要不然你猜为什么moveit的配置助手会生成关于gazebo的文件,为什么生成gazebo的urdf,就是让你用的,怪不得显示不了xacro模型
1. <visual>
定义机器人的可视化外观,主要包括几何形状和材质。
<geometry>
: 指定连接的几何形状。在这个示例中,使用了一个网格文件(STL格式)来定义link_1
的形状。该文件的路径是:Copy Codepackage://lebai_lm3_support/meshesc/lm3/visual/link1.stl
<material>
: 定义连接的材质和颜色。在此示例中,材质名为white
,并且设置了RGBA颜色值(红、绿、蓝、透明度)为(0.86, 0.85, 0.81, 1.0)
。这意味着连接的颜色为一种淡灰色,完全不透明。
2. <collision>
这一部分描述连接的碰撞模型,它与可视化模型通常是不同的。碰撞模型用于物理仿真,确保机器人在模拟环境中与其他物体进行碰撞检测。
<geometry>
: 这里也使用了一个网格文件,但它的路径是:
Copy Codepackage://lebai_lm3_support/meshes/lm3/collision/link1.stl
通常,碰撞模型会比可视化模型简单,以提高仿真性能。
3. <inertial>
这一部分定义了连接的惯性属性,包括质量和转动惯量。惯性属性在机器人运动学和动力学计算中至关重要。
<origin>
: 定义了惯性参考点的位置和方向。rpy
指的是绕X、Y、Z轴的滚转(roll)、俯仰(pitch)、偏航(yaw)角度,这里均为0.0
,表示没有旋转。xyz
指的是惯性参考点相对于链接坐标系的位置,这里是(0.0, -0.011, -0.015)
。<mass>
: 定义连接的质量,这里为2.147 kg
。<inertia>
: 定义连接的转动惯量矩阵。ixx
、iyy
和izz
分别表示绕X、Y、Z轴的转动惯量,而ixy
、ixz
和iyz
是关于这些轴的耦合项
下次淘宝下单不要付款,先放弃付款,然后再付款的时候有几率跳出“付款再减XX元”,应该是后台店铺给的,香!!!
ODriver
共有PWM模块、ADC电流采集、定时器编码器配置、SVPWM模块、FOC核心、PID模块、电压限幅模块,其实有了PWM与SVPWM以及一些必要的数学变换,我们就可以开环使电机转起来了,加入电角度与电流采集作为反馈后,我们就能做到电流闭环,再加入速度PID就可以做到速度闭环,其他的模块只是这些目的的辅助手段罢了
一、编码器的分类
按工作原理的不同可分为增量型和绝对值型。
(1) 增量型 (增量式可以理解为信号即位移的增加,需要参考量)
增量式编码器的原理是将位移转换成周期性的电信号,再把这个电信号转变成计数脉冲,用脉冲的个数表示位移的大小。增量式编码器转轴旋转时,有相应的脉冲输出,其旋转方向的判别和脉冲数量的增减借助后部的判向电路和计数器来实现。其计数起点任意设定,可实现多圈无限累加和测量。还可以把每转发出一个脉冲的Z信号,作为参考机械零位。编码器轴转一圈会输出固定的脉冲,脉冲数由编码器光栅的线数决定。需要提高分辨率时,可利用 90 度相位差的 A、B两路信号对原脉冲数进行倍频,或者更换高分辨率编码器。
简单来说,增量型编码器总共有三种相线输出,A相、B相、Z相。其中电机每转过一定的角度,A相和B相就输出一个脉冲,且A相和B相相互延迟1/4周期,电机的正反转就是根据A相和B相的延迟关系判断的。Z相为单圈脉冲,即每转一圈输出一个脉冲,可以作为参考机械零位。
(2) 绝对型(直接输出数字量的传感器,不需要参考量)
绝对编码器光码盘上有许多道光通道刻线,每道刻线依次以2线、4线、8线、16线编排,这样,在编码器的每一个位置,通过读取每道刻线的通、暗,获得一组从2的零次方到2的n-1次方的唯一的2进制编码(格雷码),这就称为n位绝对编码器。这样的编码器是由光电码盘进行记忆的。
简单来说,就是对应一圈,电机的每个角度都有一个与该角度对应二进制的数值,且这个数值不会改变,所以称为绝对型编码器。
二、不同编码器的优缺点及其对应应用范围
增量型:
增量式编码器十分合适测速度,可无限累加丈量
是存在零点累计差错,抗干扰较差,接纳设备的停机需断电回忆
增量式编码器的一般应用测速,测转动方向,测移动角度、距离(相对)。
绝对型:
发生电源故障也不丢失轴位置
绝对式编码器十分适合测量位置,可直接输出
绝对编码器一般能够以 8 到 12位输出 360 °更精确,但也更昂贵
0、系统配置
1、电机有力了!(PWM模块)
高级定时器主要用于产生6路互补的PWM来驱动MOS管,加入死区防止电源导通,本文未使用刹车引脚。高级定时器1通道1、2、3用于产生PWM,通道4用于触发ADC电流采样,根据扇区的位置,灵活设置PWM占空比,进而选择合理的触发点,避免在噪声点采样。引脚配置与PWM极性请根据自己的硬件合理配置,如IR2101是高电平有效,而IR2103则是低端低有效,高端高有效。
定时器从0开始向上计数 当0-t1段,定时器计数器TIMx_CNT值小于CCRx值,输出低电平 t1-t2段,定时器计数器TIMx_CNT值大于CCRx值,输出高电平 当TIMx_CNT值达到ARR时,定时器溢出,重新向上计数…循环此过程 至此一个PWM周期完成
TIMx_ARR寄存器确定PWM频率,
TIMx_CCRx寄存器确定占空比
LED等推挽输出
使用opencv进行颜色识别时受光照影响很大怎么办?
1.白平衡算法
2.对于图像数据受到光照度不足或者曝光引起的对比度太小的情况,在图像处理中一般是图像直方图均衡化的方法来处理。直方图均衡化是通过拉伸像素强度分布范围来增强图像对比度的一种方法。例如,更亮的图像将所有像素限制在高值。但是一个好的图像会有来自图像所有区域的像素。因此,我们需要将这个直方图扩展到两端,而这就是直方图均衡化所做的事情(用简单的话来说)。这通常会改善图像的对比度。
全局直方图均衡化,自适应局部直方图均衡化
自适应阈值?
-