karto探秘之open_karto 第一章 --- 数据结构与类的初始化
目录
1 雷达硬件与雷达数据管理
2 扫描匹配相关
3 地图相关
4 图优化的图结构相关
5 初始化
REFERENCES
本节将简要介绍open_karto中重要的类及其成员变量。
1 雷达硬件与雷达数据管理
1.1 抽象类 Sensor
这个类为抽象类,无具体实现,只有一个成员变量
Parameter<Pose2>* m_pOffsetPose; // 这个变量定义了雷达坐标系与base_link间的偏差
1.2 class LaserRangeFinder : public Sensor
这个类继承Sensor,定义了激光雷达数据的各个参数,如 最大最小距离,最大最小角度,角度分辨率,一帧雷达的点的个数,以及Sensor中定义的雷达坐标系与base_link间的偏差
1.3 class SensorData : public Object
SensorData是所有传感器数据的基类,定义了如下变量
/*** ID unique to individual sensor*/kt_int32s m_StateId;/*** ID unique across all sensor data*/kt_int32s m_UniqueId;/*** Sensor that created this sensor data*/Name m_SensorName;/*** Time the sensor data was created*/kt_double m_Time;CustomDataVector m_CustomData;
1.4 class LaserRangeScan : public SensorData
存储了最原始的扫描深度数据
kt_double* m_pRangeReadings; // 一帧scan的所有距离值,指向数组的指针kt_int32u m_NumberOfRangeReadings; // 一帧scan的点数,也就是数组的个数
1.5 class LocalizedRangeScan : public LaserRangeScan
LocalizedRangeScan包含来自激光测距传感器传感器的单次扫描的二维空间和位置信息中的范围数据。 里程表位置是记录范围数据时机器人报告的位置。 校正后的位置是由映射器(或定位器)计算出的位置
/*** Average of all the point readings* 所有点读数的平均值*/Pose2 m_BarycenterPose;/*** Vector of point readings * 将laser的扫描数据转换为 在世界坐标系中的二维坐标结果,在Update()函数中实现*/PointVectorDouble m_PointReadings;/*** Vector of unfiltered point readings* 去掉雷达数据中nan数值后前的集合*/PointVectorDouble m_UnfilteredPointReadings;/*** Bounding box of localized range scan* 这帧雷达数据的bounding box*/BoundingBox2 m_BoundingBox;/*** Internal flag used to update point readings, barycenter and bounding box* */kt_bool m_IsDirty;
1.6 class LocalizedRangeScanWithPoints : public LocalizedRangeScan
LocalizedRangeScanWithPoints是LocalizedRangeScan的扩展,带有预先计算的点。
1.7 class ScanManager
管理设备的扫描数据
m_RunningScans 实时维护的局部激光数据链,首末两帧距离在一定距离范围内,且满足一定数据规模,否则需要删除末端数据帧。维护当前局部数据链
LocalizedRangeScanVector m_Scans;// 短时间内存储的一系列雷达点// 存储依据为:1 最初的雷达数据与最新的雷达数据 的frame_link 不超过一定距离,2 数量不超过一定数量LocalizedRangeScanVector m_RunningScans; LocalizedRangeScan* m_pLastScan;kt_int32u m_RunningBufferMaximumSize;kt_double m_RunningBufferMaximumDistance;
1.8 class MapperSensorManager
以不同的名字来管理不同的雷达设备,可以返回指定名字的雷达设备的上一帧的雷达数据
typedef std::map<Name, ScanManager*> ScanManagerMap;
2 扫描匹配相关
2.1 class ScanMatcher
/*** Scan matcher*/class KARTO_EXPORT ScanMatcher{private:Mapper* m_pMapper;// 更多用来存储栅格,同时提供world2grid这个功能,在其内部有 GridIndex 方法似乎和 Grid<T>::GridIndex 一样CorrelationGrid* m_pCorrelationGrid; Grid<kt_double>* m_pSearchSpaceProbs;// 用来存储经过不同旋转之后的nPoints个扫描点应该落在的位置上面GridIndexLookup<kt_int8u>* m_pGridLookup; }; // ScanMatcher
2.2 class CorrelationGrid : public Grid<kt_int8u>
用于扫描匹配的相关网格的实现
/*** Implementation of a correlation grid used for scan matching*/class CorrelationGrid : public Grid<kt_int8u>{/*** The point readings are smeared by this value in X and Y to create a smoother response.* Default value is 0.03 meters.*/kt_double m_SmearDeviation;// Size of one side of the kernelkt_int32s m_KernelSize;// Cached kernel for smearingkt_int8u* m_pKernel;// region of interestRectangle2<kt_int32s> m_Roi;}; // CorrelationGrid
2.3 Grid
/*** Defines a grid class*/template<typename T>class Grid{public:/*** Creates a grid of given size and resolution* @param width* @param height* @param resolution* @return grid pointer*/static Grid* CreateGrid(kt_int32s width, kt_int32s height, kt_double resolution){Grid* pGrid = new Grid(width, height);pGrid->GetCoordinateConverter()->SetScale(1.0 / resolution);return pGrid;}private:kt_int32s m_Width; // width of gridkt_int32s m_Height; // height of gridkt_int32s m_WidthStep; // 8 bit aligned width of gridT* m_pData; // grid data// coordinate converter to convert between world coordinates and grid coordinatesCoordinateConverter* m_pCoordinateConverter;}; // Grid
2.4 查找表 class GridIndexLookup
/ ***创建查找表以在网格中以不同角度读取点。*对于每个角度,将为每个范围读数计算网格索引。*这是为了加快寻找最佳角度/位置以进行局部范围扫描的速度**在Mapper和Localizer中大量使用。**在定位器中,这极大地提高了计算可能的位置的速度。 对于每个粒子,计算概率。 范围扫描是相同的,但是计算了所有可能角度的所有栅格索引。 因此,在特定角度计算粒子概率时,将使用索引表在概率网格中查找概率!** //*** Create lookup tables for point readings at varying angles in grid.* For each angle, grid indexes are calculated for each range reading.* This is to speed up finding best angle/position for a localized range scan** Used heavily in mapper and localizer.** In the localizer, this is a huge speed up for calculating possible position. For each particle,* a probability is calculated. The range scan is the same, but all grid indexes at all possible angles are* calculated. So when calculating the particle probability at a specific angle, the index table is used* to look up probability in probability grid!**/template<typename T>class GridIndexLookup{private:Grid<T>* m_pGrid;kt_int32u m_Capacity;kt_int32u m_Size;LookupArray **m_ppLookupArray;// for sanity checkstd::vector<kt_double> m_Angles;}; // class GridIndexLookup/*** An array that can be resized as long as the size* does not exceed the initial capacity*/class LookupArray{private:kt_int32s* m_pArray;kt_int32u m_Capacity;kt_int32u m_Size;}; // LookupArray
3 地图相关
3.1 Mapper
/*** Graph SLAM mapper. Creates a map given a set of LocalizedRangeScans* The current Karto implementation is a proprietary, high-performance scan-matching algorithm that constructs a map from individual range scans and corrects for errors in the range and odometry data.*/class KARTO_EXPORT Mapper : public Module{friend class MapperGraph;friend class ScanMatcher;protected:kt_bool m_Initialized;ScanMatcher* m_pSequentialScanMatcher;MapperSensorManager* m_pMapperSensorManager;MapperGraph* m_pGraph;ScanSolver* m_pScanOptimizer;LocalizationScanVertices m_LocalizationScanVertices;std::vector<MapperListener*> m_Listeners;// 以及各个参数};struct LocalizationScanVertex{LocalizationScanVertex(){return;};LocalizationScanVertex(const LocalizationScanVertex& obj){scan = obj.scan; vertex = obj.vertex;};LocalizedRangeScan* scan;Vertex<LocalizedRangeScan>* vertex;};typedef std::queue<LocalizationScanVertex> LocalizationScanVertices;
3.2 class MapperGraph : public Graph
/*** Graph*/template<typename T>class Graph{protected:/*** Map of names to vector of vertices*/VertexMap m_Vertices;/*** Edges of this graph*/std::vector<Edge<T>*> m_Edges;}; // Graph<T>/*** Graph for graph SLAM algorithm*/class KARTO_EXPORT MapperGraph : public Graph<LocalizedRangeScan>{private:/*** Mapper of this graph*/Mapper* m_pMapper;/*** Scan matcher for loop closures*/ScanMatcher* m_pLoopScanMatcher;/*** Traversal algorithm to find near linked scans*/GraphTraversal<LocalizedRangeScan>* m_pTraversal;}; // MapperGraph/*** Graph traversal algorithm*/template<typename T>class GraphTraversal{Graph<T>* m_pGraph;}; // GraphTraversal<T>// 深度优先(DFS)广度优先(BFS)算法可以参考这篇文章(http://www.cnblogs.com/skywang12345/p/3711483.html)template<typename T> class BreadthFirstTraversal : public GraphTraversal<T>{/*** Traverse the graph starting with the given vertex; applies the visitor to visited nodes* @param pStartVertex* @param pVisitor* @return visited vertices*/virtual std::vector<T*> Traverse(Vertex<T>* pStartVertex, Visitor<T>* pVisitor)}
3.3 占用网格
/*** Occupancy grid definition. See GridStates for possible grid values.*/class OccupancyGrid : public Grid<kt_int8u>{friend class CellUpdater;friend class IncrementalOccupancyGrid;private:CellUpdater* m_pCellUpdater;// NOTE: These two values are dependent on the resolution. If the resolution is too small,// then not many beams will hit the cell!// Number of beams that must pass through a cell before it will be considered to be occupied// or unoccupied. This prevents stray beams from messing up the map.Parameter<kt_int32u>* m_pMinPassThrough;// Minimum ratio of beams hitting cell to beams passing through cell for cell to be marked as occupiedParameter<kt_double>* m_pOccupancyThreshold;}; // OccupancyGridclass KARTO_EXPORT CellUpdater : public Functor{public:CellUpdater(OccupancyGrid* pGrid): m_pOccupancyGrid(pGrid){}/*** Updates the cell at the given index based on the grid's hits and pass counters* @param index*/virtual void operator() (kt_int32u index);private:OccupancyGrid* m_pOccupancyGrid;}; // CellUpdater
4 图优化的图结构相关
4.1 Vertex
/*** Represents an object in a graph*/template<typename T>class Vertex{friend class Edge<T>;public:/*** Constructs a vertex representing the given object* @param pObject*/Vertex(): m_pObject(NULL), m_Score(1.0){}Vertex(T* pObject): m_pObject(pObject), m_Score(1.0){}// .../*** Adds the given edge to this vertex's edge list* @param pEdge edge to add*/inline void AddEdge(Edge<T>* pEdge){m_Edges.push_back(pEdge);}T* m_pObject;std::vector<Edge<T>*> m_Edges;kt_double m_Score;friend class boost::serialization::access;template<class Archive>void serialize(Archive &ar, const unsigned int version){ar & BOOST_SERIALIZATION_NVP(m_pObject);ar & BOOST_SERIALIZATION_NVP(m_Edges);ar & BOOST_SERIALIZATION_NVP(m_Score);}}; // Vertex<T>
4.2 Edge
/*** Represents an edge in a graph*/template<typename T>class Edge{private:Vertex<T>* m_pSource;Vertex<T>* m_pTarget;EdgeLabel* m_pLabel;}; // class Edge<T>/*** Class for edge labels*/class EdgeLabel{public:/*** Default constructor*/EdgeLabel(){}/*** Destructor*/virtual ~EdgeLabel(){}}; // EdgeLabel// A LinkInfo object contains the requisite information for the "spring"// that links two scans together--the pose difference and the uncertainty// (represented by a covariance matrix).class LinkInfo : public EdgeLabel{private:Pose2 m_Pose1;Pose2 m_Pose2;Pose2 m_PoseDifference;Matrix3 m_Covariance;}; // LinkInfo
4.3 SPA优化方法
/*** Graph optimization algorithm*/class ScanSolver{public:/*** Vector of id-pose pairs*/typedef std::vector<std::pair<kt_int32s, Pose2> > IdPoseVector;}class SpaSolver : public karto::ScanSolver
{private:karto::ScanSolver::IdPoseVector corrections;sba::SysSPA2d m_Spa;
};
其他
/*** Represents a vector (x, y) in 2-dimensional real space.*/template<typename T>class Vector2{private:T m_Values[2];}; // Vector2<T>/*** Type declaration of Vector2<kt_double> vector*/typedef std::vector< Vector2<kt_double> > PointVectorDouble;/*** Defines a position (x, y) in 2-dimensional space and heading.*/class Pose2{private:Vector2<kt_double> m_Position;kt_double m_Heading;}; // Pose2/*** Type declaration of Pose2 vector*/typedef std::vector< Pose2 > Pose2Vector;
5 初始化
// slam_karto.cpp
int
main(int argc, char** argv)
{ros::init(argc, argv, "slam_karto");SlamKarto kn;ros::spin();return 0;
}class SlamKarto
{// Karto bookkeepingkarto::Mapper* mapper_;karto::Dataset* dataset_;SpaSolver* solver_;
};SlamKarto::SlamKarto() :got_map_(false),laser_count_(0),transform_thread_(NULL),marker_count_(0)
{// Initialize Karto structuresmapper_ = new karto::Mapper();dataset_ = new karto::Dataset();solver_ = new SpaSolver();mapper_->SetScanSolver(solver_);
}
5.1 Mapper的初始化
/*** Default constructor*/Mapper::Mapper(): Module("Mapper"), m_Initialized(false), m_pSequentialScanMatcher(NULL), m_pMapperSensorManager(NULL), m_pGraph(NULL), m_pScanOptimizer(NULL){InitializeParameters();}void Mapper::Initialize(kt_double rangeThreshold){if (m_Initialized == false){// create sequential scan and loop matcherm_pSequentialScanMatcher = ScanMatcher::Create(this,m_pCorrelationSearchSpaceDimension->GetValue(), // 0.3m_pCorrelationSearchSpaceResolution->GetValue(), // 0.01m_pCorrelationSearchSpaceSmearDeviation->GetValue(), // 0.03rangeThreshold);assert(m_pSequentialScanMatcher);//m_pScanBufferSize running_scan 数量长度,单位:个//m_pScanBufferMaximumScanDistance running_scan 地图上的长度,单位:mm_pMapperSensorManager = new MapperSensorManager(m_pScanBufferSize->GetValue(),m_pScanBufferMaximumScanDistance->GetValue());m_pGraph = new MapperGraph(this, rangeThreshold);m_Initialized = true;}}
5.2 scan_match初始化
ScanMatcher* ScanMatcher::Create(Mapper* pMapper, kt_double searchSize, kt_double resolution,kt_double smearDeviation, kt_double rangeThreshold){// invalid parametersif (resolution <= 0){return NULL;}if (searchSize <= 0){return NULL;}if (smearDeviation < 0){return NULL;}if (rangeThreshold <= 0){return NULL;}assert(math::DoubleEqual(math::Round(searchSize / resolution), (searchSize / resolution)));// calculate search space in grid coordinates //searchSize大小是0.3,既然和rangeThreshold有相同单位,就是说匹配范围扩大0.3m,类似于卷积核大小是0.3m// 31kt_int32u searchSpaceSideSize = static_cast<kt_int32u>(math::Round(searchSize / resolution) + 1);// compute requisite size of correlation grid (pad grid so that scan points can't fall off the grid// if a scan is on the border of the search space) // 1200kt_int32u pointReadingMargin = static_cast<kt_int32u>(ceil(rangeThreshold / resolution));//这里写出了应该搜索的范围的grid = 2431kt_int32s gridSize = searchSpaceSideSize + 2 * pointReadingMargin;// create correlation gridassert(gridSize % 2 == 1);//CorrelationGrid 和 Grid<kt_double> 都是 Grid<T>的形式,但是这里面有borderSize//作为x,y的坐标,实在是不能理解CorrelationGrid* pCorrelationGrid = CorrelationGrid::CreateGrid(gridSize, gridSize, resolution, smearDeviation);// create search space probabilitiesGrid<kt_double>* pSearchSpaceProbs = Grid<kt_double>::CreateGrid(searchSpaceSideSize,searchSpaceSideSize, resolution);ScanMatcher* pScanMatcher = new ScanMatcher(pMapper);pScanMatcher->m_pCorrelationGrid = pCorrelationGrid;pScanMatcher->m_pSearchSpaceProbs = pSearchSpaceProbs;pScanMatcher->m_pGridLookup = new GridIndexLookup<kt_int8u>(pCorrelationGrid);return pScanMatcher;}
5.2.1 CorrelationGrid初始化
/*** Create a correlation grid of given size and parameters* @param width* @param height* @param resolution* @param smearDeviation* @return correlation grid*/static CorrelationGrid* CreateGrid(kt_int32s width,kt_int32s height,kt_double resolution,kt_double smearDeviation){assert(resolution != 0.0);// +1 in case of roundoff// borderSize 的值是7,作用是什么呢kt_int32u borderSize = GetHalfKernelSize(smearDeviation, resolution) + 1; CorrelationGrid* pGrid = new CorrelationGrid(width, height, borderSize, resolution, smearDeviation);return pGrid;}/*** Computes the kernel half-size based on the smear distance and the grid resolution.* Computes to two standard deviations to get 95% region and to reduce aliasing.* @param smearDeviation* @param resolution* @return kernel half-size based on the parameters*/static kt_int32s GetHalfKernelSize(kt_double smearDeviation, kt_double resolution){assert(resolution != 0.0);return static_cast<kt_int32s>(math::Round(2.0 * smearDeviation / resolution));}/*** Constructs a correlation grid of given size and parameters* @param width* @param height* @param borderSize* @param resolution* @param smearDeviation*/CorrelationGrid(kt_int32u width, kt_int32u height, kt_int32u borderSize,kt_double resolution, kt_double smearDeviation): Grid<kt_int8u>(width + borderSize * 2, height + borderSize * 2), m_SmearDeviation(smearDeviation), m_pKernel(NULL){GetCoordinateConverter()->SetScale(1.0 / resolution);// setup region of interestm_Roi = Rectangle2<kt_int32s>(borderSize, borderSize, width, height);// calculate kernelCalculateKernel();}/*** Constructor initializing rectangle parameters* @param x x-coordinate of left edge of rectangle* @param y y-coordinate of bottom edge of rectangle* @param width width of rectangle* @param height height of rectangle*/Rectangle2(T x, T y, T width, T height): m_Position(x, y), m_Size(width, height){}/*** Sets up the kernel for grid smearing.*/virtual void CalculateKernel(){kt_double resolution = GetResolution();assert(resolution != 0.0);assert(m_SmearDeviation != 0.0);// min and max distance deviation for smearing;// will smear for two standard deviations, so deviation must be at least 1/2 of the resolutionconst kt_double MIN_SMEAR_DISTANCE_DEVIATION = 0.5 * resolution;const kt_double MAX_SMEAR_DISTANCE_DEVIATION = 10 * resolution;// check if given value too small or too bigif (!math::InRange(m_SmearDeviation, MIN_SMEAR_DISTANCE_DEVIATION, MAX_SMEAR_DISTANCE_DEVIATION)){std::stringstream error;error << "Mapper Error: Smear deviation too small: Must be between "<< MIN_SMEAR_DISTANCE_DEVIATION<< " and "<< MAX_SMEAR_DISTANCE_DEVIATION;throw std::runtime_error(error.str());}// NOTE: Currently assumes a two-dimensional kernel// +1 for center // = 13m_KernelSize = 2 * GetHalfKernelSize(m_SmearDeviation, resolution) + 1;// allocate kernelm_pKernel = new kt_int8u[m_KernelSize * m_KernelSize];if (m_pKernel == NULL){throw std::runtime_error("Unable to allocate memory for kernel!");}// calculate kernelkt_int32s halfKernel = m_KernelSize / 2;for (kt_int32s i = -halfKernel; i <= halfKernel; i++){for (kt_int32s j = -halfKernel; j <= halfKernel; j++){#ifdef WIN32kt_double distanceFromMean = _hypot(i * resolution, j * resolution);#elsekt_double distanceFromMean = hypot(i * resolution, j * resolution);#endifkt_double z = exp(-0.5 * pow(distanceFromMean / m_SmearDeviation, 2));kt_int32u kernelValue = static_cast<kt_int32u>(math::Round(z * GridStates_Occupied));assert(math::IsUpTo(kernelValue, static_cast<kt_int32u>(255)));int kernelArrayIndex = (i + halfKernel) + m_KernelSize * (j + halfKernel);m_pKernel[kernelArrayIndex] = static_cast<kt_int8u>(kernelValue);}}}
5.2.2 Grid初始化
/*** Creates a grid of given size and resolution* @param width* @param height* @param resolution* @return grid pointer*/static Grid* CreateGrid(kt_int32s width, kt_int32s height, kt_double resolution){Grid* pGrid = new Grid(width, height);pGrid->GetCoordinateConverter()->SetScale(1.0 / resolution);return pGrid;}
5.3 MapperSensorManager初始化
//集中了所有的device的scans,以名字为分隔/*** Manages the devices for the mapper*/MapperSensorManager(kt_int32u runningBufferMaximumSize, kt_double runningBufferMaximumDistance): m_RunningBufferMaximumSize(runningBufferMaximumSize), m_RunningBufferMaximumDistance(runningBufferMaximumDistance), m_NextScanId(0){}
5.4 MapperGraph初始化
MapperGraph::MapperGraph(Mapper* pMapper, kt_double rangeThreshold): m_pMapper(pMapper){m_pLoopScanMatcher = ScanMatcher::Create(pMapper, m_pMapper->m_pLoopSearchSpaceDimension->GetValue(), // 8.0m_pMapper->m_pLoopSearchSpaceResolution->GetValue(), // 0.05m_pMapper->m_pLoopSearchSpaceSmearDeviation->GetValue(), rangeThreshold); // 0.03, 12assert(m_pLoopScanMatcher);m_pTraversal = new BreadthFirstTraversal<LocalizedRangeScan>(this);}template<typename T> class BreadthFirstTraversal : public GraphTraversal<T>{public:/*** Constructs a breadth-first traverser for the given graph*/BreadthFirstTraversal(Graph<T>* pGraph): GraphTraversal<T>(pGraph){}}
REFERENCES
Karto_slam框架与代码解析 https://blog.csdn.net/qq_24893115/article/details/52965410
Karto SLAM之open_karto代码学习笔记(一) https://blog.csdn.net/wphkadn/article/details/85144186
karto探秘之open_karto 第一章 --- 数据结构与类的初始化相关推荐
- 第一章 数据结构与算法-前言
Hello,I'm 郭永峰,一名IT从业者,也是一名Java Sharer.Teacher.欢迎添加微信号或者QQ号,一起学习交流,微信与QQ同号(1030103135).目前在腾讯课堂也出了一些教程 ...
- 大话数据结构 摘录 第一章 数据结构绪论
文章目录 启示:数据结构 学习数据机构的重要性 数据结构引发的案例 数据结构的起源 程序设计=数据结构+算法 基础概念与术语 数据 数据元素 数据项 数据对象 数据结构 数据结构:是相互之间存在一种或 ...
- 第一章——数据结构之绪论
1.1 什么是数据结构 1.2 基本概念和术语 1.3 抽象数据类型的表示与实现 1.4 算法和算法分析 1.4.1 算法 1.4.2 算法设计的要求 1.4.3 算法效率的度量 1.4.4 算法的存 ...
- 数据结构 第一章 数据结构绪论
数据结构是一门研究非数值计算的程序设计问题中的操作对象,以及它们之间的关系和操作等相关问题的学科. 数据:所有能被输入到计算机中,且能被计算机处理的符号的集合.是计算机操作的对象的总称. 数据元素:数 ...
- 数据结构与算法(第一章 数据结构的基本概念 )
说起数据结构与算法,他们之间的关系是怎么样的呢?某位dalao曾经这样说过: The relationship between the data structures and algorithms l ...
- 读书笔记-《大话数据结构》第一章数据结构绪论
1.3数据结构的起源 数据结构:是一门研究非数值计算的程序设计问题中的操作对象,以及它们之间的关系和操作等相关问题的学科. 程序设计=数据结构+算法 1.4基本概念和术语 1.4.1数据:描述客观事物 ...
- 数据结构—绪论(基本知识点第一章)
第一章数据结构绪论 目录 第一章数据结构绪论 什么是数据结构? 1.3数据结构起源 1.4基本概念和术语 1.4.1数据 1.4.2数据元素 1.4.3数据项 1.4.4数据对象 1.4.5数据结构 ...
- 全国计算机二级第一套ppt,全国计算机二级共基础第一章.ppt
全国计算机二级共基础第一章 全国计算机等级考试 二级公共基础知识辅导 复习 计算机的工作原理是什么? 存储程序和原始数据,然后逐条执行. 什么是计算机程序? 很多指令的有机组合. 计算机程序 计算机算 ...
- 使用java实现面向对象编程第十章嗖嗖移动业务大厅项目_ACCP6.0使用Java实现面向对象编程-第一章.ppt...
ACCP6.0使用Java实现面向对象编程-第一章 练习--用类图设计Dog和Penguin类 需求说明: 运用面向对象思想抽象出Dog类和Penguin类,画出对应类图 根据类图编写Dog类和Pen ...
- Python3-Cookbook总结 - 第一章:数据结构和算法
第一章:数据结构和算法 Python 提供了大量的内置数据结构,包括列表,集合以及字典.大多数情况下使用这些数据结构是很简单的. 但是,我们也会经常碰到到诸如查询,排序和过滤等等这些普遍存在的问题. ...
最新文章
- Lua学习笔记(2)
- python全栈开发基础【第二十三篇】线程
- 计算机网络中各层的协议图表(TCP/IP)
- 用C语言实现控制台模拟显示时
- Spring Cloud Gateway 原生支持接口限流该怎么玩
- 吐血整理!12种通用知识图谱项目简介
- javascript 编译与执行过程
- pandas将series所有值转变为字符串类型
- About UserAccountControl
- 自学python能干些什么副业-她把摄影当副业:月薪3000,副业收入上万
- 2021-09-0884. 柱状图中最大的矩形 栈
- PostgreSQL与MySQL语法对比总结
- Win10 关闭屏幕旋转(转向)
- cookie二级域名和三级域名跨域实践
- 【软考:网工】协议篇(非常重要)
- python数据表盘_构建一个简单地分析表盘
- 在outlook客户端设置阿里企业邮箱
- hive--解决使用not in之后返回数据为空的问题
- 线程池七个参数的含义
- 横空出世IDEA画图神器来了,比Visio快10倍