1 簡(jiǎn)介
rs_driver是RoboSense雷達(dá)的基本驅(qū)動(dòng)时甚。
這次的工作是對(duì)rs_driver v1.3.2的重構(gòu)寥袭,重構(gòu)后的代碼是v1.5.7锨能。
2 做了哪些變更?
2.1 從大類中拆分出小類
classes_from_decoder.png
- 從Decoder中分拆出一系列小類离熏。這些類方便作單元測(cè)試佳谦。
- Decoder計(jì)算三角函數(shù)值時(shí),使用查表方式滋戳,以便減少計(jì)算量钻蔑。Trigon類包裝了這部分邏輯。
- 對(duì)于機(jī)械式式雷達(dá)奸鸯,Decoder計(jì)算每個(gè)通道的時(shí)間和角度時(shí)咪笑,需要先計(jì)算MSOP Packet中每個(gè)Block的時(shí)間偏移和角度偏移。BlockDiff定義了這個(gè)接口府喳,SingleReturnBlockDiff蒲肋、DualReturnBlockDiff分別實(shí)現(xiàn)了單回波模式蘑拯、雙回波模式下的時(shí)間角度偏移的計(jì)算方式钝满。
- Decoder需要將連續(xù)的MSOP包中的點(diǎn)分幀。SplitStrategy定義了這個(gè)接口申窘,SplitStrategyByAngle和SplitStrategyByNum分別實(shí)現(xiàn)了兩種分幀模式弯蚜,也就是機(jī)械式雷達(dá)按角度分幀和按照MSOP包數(shù)量分幀。SplitStrategySeq則實(shí)現(xiàn)了MEMS雷達(dá)按MSOP包序列號(hào)分幀剃法。
- 對(duì)于機(jī)械式雷達(dá)碎捺,Decoder需要加載和保存垂直角和水平角標(biāo)定數(shù)據(jù)。ChanAngles類包裝了這部分邏輯。
- 對(duì)于機(jī)械式雷達(dá)收厨,AzmuthSection對(duì)點(diǎn)的角度作校驗(yàn)晋柱。DistanceSection分別對(duì)點(diǎn)的距離作校驗(yàn)。雖然這兩部分邏輯比較簡(jiǎn)單诵叁,但把它們獨(dú)立出來雁竞,也會(huì)讓Decoder更容易理解。
2.2 將一個(gè)大類拆分成一組基類+派生類
classes_input.png
- Input類是一個(gè)龐大的類拧额,解析PCAP文件的邏輯與從Socket接收的邏輯混在一起碑诉。將它拆分成一個(gè)基類+派生類的組合,是合適的侥锦。
- 新的Input類定義數(shù)據(jù)源的接口进栽。InputSock類實(shí)現(xiàn)從Socket接收的邏輯,InputPcap實(shí)現(xiàn)從PCAP解析的邏輯恭垦,新增的InputRaw從用戶調(diào)用得到數(shù)據(jù)并解析快毛。
- InputFactory用于創(chuàng)建Input類的實(shí)例。
2.3 改變庫的接口設(shè)計(jì) - 有沒有更平滑的實(shí)現(xiàn)方式番挺?
packet_replay.png
- 原來MSOP/DIFOP Packet的錄制與回放的設(shè)計(jì)祸泪,回放有單獨(dú)的函數(shù)接口和實(shí)現(xiàn)。實(shí)際上這個(gè)實(shí)現(xiàn)與Socket和PCAP數(shù)據(jù)源的后端處理是一樣的建芙,所以把它當(dāng)做一個(gè)新的數(shù)據(jù)源InputRaw没隘,也是順理成章的。
2.4 將代碼轉(zhuǎn)移到更合適的位置上
decoder_get_const_param.png
- 機(jī)械式雷達(dá)有很多公共的邏輯禁荸,如從DIFOP包解析參數(shù)右蒲,分幀策略等,所以從Decoder創(chuàng)建一個(gè)公共的派生類DecoderMech赶熟。
- 計(jì)算每個(gè)通道的時(shí)間和角度偏移所依賴的發(fā)射時(shí)序瑰妄,是特定于雷達(dá)的,將這個(gè)計(jì)算放到單獨(dú)的函數(shù)getConstParam()中映砖。
- 同樣地间坐,判斷當(dāng)前回波模式的代碼,也放到單獨(dú)的函數(shù)getEchoMode()中邑退。
2.5 讓類之間的依賴關(guān)系變得更簡(jiǎn)單
decoder_get_timestamp.png
- 原來的設(shè)計(jì)竹宋,使用了函數(shù)指針等比較復(fù)雜的方式,來確定"使用雷達(dá)時(shí)間還是主機(jī)時(shí)間作為點(diǎn)云的時(shí)間"地技。這其實(shí)搞得有點(diǎn)復(fù)雜了蜈七,改成簡(jiǎn)單的if/else邏輯,還更容易理解莫矗。
2.6 考慮限制條件飒硅,去掉不必要的考慮砂缩,簡(jiǎn)化代碼
inline void InputSock::recvPacket()
{
...
std::shared_ptr<Buffer> pkt = cb_get_(MAX_PKT_LEN);
ssize_t ret = recvfrom(fds_[0], pkt->buf(), pkt->bufSize(), 0, NULL, NULL);
...
}
- 以太網(wǎng)包的長(zhǎng)度是有限制的,為它分配1400個(gè)字節(jié)三娩,還是1500個(gè)字節(jié)庵芭,其實(shí)對(duì)內(nèi)存占用的影響并不大,不值得為了長(zhǎng)度保存一個(gè)變量并把它計(jì)算得精確雀监。就用一個(gè)夠用的估計(jì)值15xx就好喳挑,可以讓代碼簡(jiǎn)單很多。
2.7 刪除不需要的功能 - 有更簡(jiǎn)單的替代方法嗎滔悉?
template <typename T_Point>
inline void LidarDriverImpl<T_Point>::initPointCloudTransFunc()
{
if (driver_param_.saved_by_rows)
{
point_cloud_transform_func_ = [](const typename PointCloudMsg<T_Point>::PointCloudPtr input_ptr,
const size_t& height) -> typename PointCloudMsg<T_Point>::PointCloudPtr
{
typename PointCloudMsg<T_Point>::PointCloudPtr row_major_ptr =
std::make_shared<typename PointCloudMsg<T_Point>::PointCloud>();
row_major_ptr->resize(input_ptr->size());
size_t width = input_ptr->size() / height;
for (int i = 0; i < static_cast<int>(height); i++)
{
for (int j = 0; j < static_cast<int>(width); j++)
{
row_major_ptr->at(i * width + j) = input_ptr->at(j * height + i);
}
}
return row_major_ptr;
};
}
}
- 點(diǎn)云中的點(diǎn)伊诵,默認(rèn)是按照掃描順序保存的,也就是按列保存回官。其實(shí)按行保存的目的曹宴,無非是按行訪問。由于列的點(diǎn)數(shù)是一定的歉提,所以按列保存的點(diǎn)云笛坦,按行訪問也是方便的,跳過列的點(diǎn)數(shù)訪問就可以了苔巨。
- 按行保存的實(shí)現(xiàn)版扩,實(shí)際上將點(diǎn)云復(fù)制了一遍,這個(gè)對(duì)CPU占用影響較大侄泽,是客戶不希望的礁芦。所以不如干脆去除這個(gè)特性。
3 重構(gòu)的設(shè)計(jì)原則
- 費(fèi)腦子的計(jì)算工作悼尾,讓單元測(cè)試幫忙做
- 不在同一維度的邏輯柿扣,切分成不同小塊
- 去除代碼的壞氣味(比如不好理解的邏輯),讓它有層次闺魏、有條理未状。
- 讓生活輕松一點(diǎn),再輕松一點(diǎn)析桥。
相關(guān)鏈接
測(cè)試驅(qū)動(dòng)開發(fā)與設(shè)計(jì)模式 - 為什么使用測(cè)試驅(qū)動(dòng)開發(fā)
測(cè)試驅(qū)動(dòng)開發(fā)與設(shè)計(jì)模式 - 從入門到精通
測(cè)試驅(qū)動(dòng)開發(fā)與設(shè)計(jì)模式 - C++書籍及網(wǎng)站
測(cè)試驅(qū)動(dòng)開發(fā)與設(shè)計(jì)模式 - 適應(yīng)并改進(jìn)軟件設(shè)計(jì)過程
測(cè)試驅(qū)動(dòng)開發(fā)與設(shè)計(jì)模式 - 讓“理想結(jié)構(gòu)”與“快速變更”并行
測(cè)試驅(qū)動(dòng)開發(fā)與設(shè)計(jì)模式 - 提速 — 在紙上做細(xì)節(jié)設(shè)計(jì)
測(cè)試驅(qū)動(dòng)開發(fā)與設(shè)計(jì)模式 - 開發(fā)實(shí)例一 DVR-POS庫
測(cè)試驅(qū)動(dòng)開發(fā)與設(shè)計(jì)模式 - 開發(fā)實(shí)例二 JSON過濾庫
測(cè)試驅(qū)動(dòng)開發(fā)與設(shè)計(jì)模式 - 開發(fā)實(shí)例三 DVR-POS庫