Apollo2.5 CANBUS調試筆記(測試版)

前言:CANBUS是Apollo須要根據你的底盤寫代碼的地方,感受也是Apollo最難調試的部分。這部分首先要選好CAN卡,由於不是Apollo推薦的CAN卡,驅動程序和對應接口,可能都須要本身調整,Apollo推薦的是ESDCAN,我買了一個單口的ESDCAN卡,4000多,超級貴。建議有錢買個4口的CAN卡,由於Apollo推薦的RADAR也要接在CAN總線上,但個人項目是Demo,RADAR就忽略了。 node

相關的下載請到本人的百度網盤。連接: https://pan.baidu.com/s/1z86gFcDqRyUpnzAc7q5K3g 提取碼: b8i2python

注意: linux

(1)購買IPC時須要注意主板上是否有PIC-E X1插槽, ESDCAN卡是要插在這個槽上的。同時注意若是在你的CAN網絡中存在120歐姆的匹配電阻,若是沒有,ESDCAN上的JX300上的跳線拿下來插到JX400上,若是有,不用動。 git

(2) Apollo給的文檔中能夠找到安裝ESD CAN的安裝方式(https://github.com/ApolloAuto/apollo-kernel/blob/master/linux/ESDCAN-README.md),提供了兩種方式:第一種方式在編譯Apollo的時候同時編譯,第二種方式在build Apollo以後安裝。本文采用第二種方式,Apollo也推薦採用第二種方式。 github

(3) Apollo的消息傳遞所有是由google的Protocol Buffers來實現,建議搭建先看一下相關的教程:https://developers.google.com/protocol-buffers/http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/.proto生成的文件都是寫保護的,不能夠修改。docker

第一部分 bootstrap

這部份內容主要是調試CANBUS以前的配置CANBUS、生成必要的文件、代碼等。數組

1. ESD CAN卡安裝 bash

參考注意(1)。 網絡

 

2. ESD CAN驅動安裝

網絡上有許多關於ESDCAN的安裝與調試,本人親測過,好用的很少。因此列出本人的調試過程。

(1) 請在個人百度網盤中下載 canpci402安裝腳本.zip。默認下載到Downloads目錄,執行:

$ cd ~/Downloads

$ unzip canpci402安裝腳本.zip

$ cd canpci402安裝腳本

(2) 在拷貝canpci402setup_v1.sh與98-esdcanpcie402.rules文件到~目錄,並將canpci402setup_v1.sh變爲可執行文件。

$ cp canpci402setup_v1.sh 98-esdcanpcie402.rules ~/

$ sudo chmod a+x ~/canpci402setup_v1.sh

(3) 拷貝 esdcan-pcie402-linux-2.6.x-x86_64-3.10.4.tgz 到 ~目錄,並解壓。

$ cp esdcan-pcie402-linux-2.6.x-x86_64-3.10.4.tgz ~/

$ cd ~/

$ tar -xv -f esdcan-pcie402-linux-2.6.x-x86_64-3.10.4.tgz

(4) 執行canpci402setup_v1.sh,執行過程當中須要輸入password。

$ ./canpci402setup_v1.sh

(5) 測試驅動是否安裝完成

$ cantest

若是正常安裝成功,會出現:

Net 0: ID=CAN_PCIE402 (1 ports) Serial no.: HA002424

Versions (hex): Lib=4.0.01 Drv=3.A.04 HW=1.0.16 FW=0.0.42 (0.0.00)

Baudrate=7fffffff (Not set) Status=0000 Features=00000ffa

Ctrl=esd Advanced CAN Core @ 80 MHz (Error Active / REC:0 / TEC:0)

Transceiver=TI SN65HVD265

TimestampFreq=80.000000 MHz Timestamp=000000007E95031A

若是沒有安裝成功(大機率會安裝成功),請檢查如下文件夾是否有相關文件(大機率會有,若是沒有我也給出了操做生成):

確認/usr/local/include中是否有ntcan.h;若是沒有,請從~/esdcan-pcie402-linux-2.6.x-x86_64-3.10.4/include中拷貝:

$ sudo install -v -g root -o root -m u=rw,g=r,o=r ~/esdcan-pcie402-linux-2.6.x-x86_64-3.10.4/include/ntcan.h /usr/local/include/

 

確認/usr/local/lib中是否有libntcan.so、libntcan.so.4以及libntcan.so.4.0.1;若是沒有所有或部分文件,請按照下述步驟補全沒有的文件,並創建動態連接庫。

請從~/esdcan-pcie402-linux-2.6.x-x86_64-3.10.4/lib64中拷貝libntcan.so.4.0.1

$ sudo install -v -g root -o root -m u=rwx,g=rx,o=rx ~/esdcan-pcie402-linux-2.6.x-x86_64-3.10.4/lib64/libntcan.so.4.0.1 /usr/local/lib/

$ cd /usr/local/lib

生成libntcan.so.4

$ sudo ldconfig -v -n /usr/local/lib

確認libntcan.so.4以後,執行

$ sudo ln -sfv libntcan.so.4 libntcan.so

這時會成成libntcan.so。創建動態鏈接庫:

$ ldconfig -p | grep ntcan

$ cat /etc/ld.so.conf | grep /usr/local/lib

$ sudo sh -c "echo /usr/local/lib >> /etc/ld.so.conf"

$ sudo ldconfig

動態鏈接庫創建完成

 

確認/etc/udev/rules.d中是否有98-esdcanpcie402.rules,若是沒有從~目錄拷貝。

$ sudo install -v -g root -o root -m u=rw,g=rw,o=r ~/98-esdcanpcie402.rules /etc/udev/rules.d/

$ sudo service udev restart

 

確認/lib/modules/$(uname -r)/kernel/drivers/pci/中是否有esdcan-pcie402.ko,若是沒有,確認~/esdcan-pcie402-linux-2.6.x-x86_64-3.10.4/src中是否有esdcan-pcie402.ko,若是沒有

$ cd ~/esdcan-pcie402-linux-2.6.x-x86_64-3.10.4

$ sudo make

$ sudo install -p -v -g root -o root -m u=rw,g=r,o=r ~/esdcan-pcie402-linux-2.6.x-x86_64-3.10.4/src/esdcan-pcie402.ko /lib/modules/$(uname -r)/kernel/drivers/pci/

 

(6) 測試CAN卡的工做。請接入您的CAN分析儀,若是沒有,也能夠接入一個能發送CAN幀設備(好比單片機、ARM等),設置波特率爲500k,並能發送數據到ESD CAN。

$ cantest 3 0 0x00 0xffff 1 10 100 1000 5000 2 -1

每一個參數的含義爲:

3 -- canRead

0 -- net0

0x00 -- first-id 0x00

0xffff -- last-id 0xffff

1 -- count of CMSG-packets

10 -- txbuf (useless here)

100 -- rxbuf

1000 -- tx timeout every 1 second (useless here)

5000 -- rx timeout every 5 seconds

2 -- baud rate 500k bit/s

-1 -- count of ntcan-API-Calls, -1 is forever canRead the bus.

若是ESD CAN接收到了數據,那麼CAN卡驅動調試完畢。

 

3. 在Apollo中安裝ESD CAN卡

(1) 根據注意(2)中提到的文檔,採用第二種方式(Build & Install Out-of-Tree ESD Kernel Driver)構建Apollo中的ESD CAN卡。在這裏,我要強調一下,按照Apollo文檔的步驟,雖然生成了esdcan-pcie402.ko,但不能通信,後來是百度美研團隊幫助生成了esdcan-pcie402.ko。因此在這裏,在百度網盤中直接提供了extra文件夾(默認下載到Downloads),而後拷貝到指定文件夾

$ sudo cp –rf ~/Downloads/extra /lib/modules/$(uname -r)/

(2) 將生成的文件拷貝到Apollo指定的文件夾。

$ cp -rf ~/esdcan-pcie402-linux-2.6.x-x86_64-3.10.4/include/ ~/apollo/third_party/can_card_library/

$ cp -rf ~/esdcan-pcie402-linux-2.6.x-x86_64-3.10.4/lib64/ ~/apollo/third_party/can_card_library/

$ mv ~/apollo/third_party/can_card_library/lib64 ~/apollo/third_party/can_card_library/lib

這時,若是打開dreamviewer,選擇module controller,在Hardware CAN部分顯示OK。

 

4. 因爲咱們的車輛底盤是自定義底盤,因此須要生成本身底盤的DBC文件,DBC文件編寫教程我的推薦:https://blog.csdn.net/king110108/article/details/79010272以及網絡上的CANOE的視頻教程(只看CANdb++編寫部分)。CANdb++的軟件在百度網盤中(CANdb-PP_Admin.J1939_30SP27.zip)。有幾點注意事項:

(1) 編寫的DBC文件只有一個Network nodes,即底盤nodes;

(2) 若干的messages,即底盤上各個模塊的收發的信息(以底盤爲基準),且每一個messages要對應TX message仍是RX message(針對底盤信息流動方向);

(3) 若干的signals,即每一個信息包含的數據意義,signals要與messages對應;

(4) 收發方向(TX,RX)以底盤的實際方向爲準;每一個信息的ID爲地盤上各個模塊的地址;

 

5. 將編輯好的DBC文件生成自定義(本身車輛)的底層CAN代碼:

(1) 將dbc文件放置apollo/moudles/tools/gen_vehicle_protocol文件下,默認生成的DBC放在Downloads文件夾下。

$ cp ~/Downloads/hnacar.dbc ~/apollo/moudles/tools/gen_vehicle_protocol/

(2) 仿照lincoln_conf.yml,在gen_vehicle_protocol 文件夾下生成hnacar_conf.yml文件(按照本身的車輛命名,咱們的車輛叫hnacar),內容以下紅色字的部分:

dbc_file: hnacar.dbc

protocol_conf: hnacar.yml

car_type: hnacar

sender_list: []

sender: MAB

black_list: []

 

output_dir: output/

(3) 安裝pyyaml

$ sudo pip install pyyaml

(4) 生成自定義車輛的CAN底層代碼:

$ cd ~/apollo/moudles/tools/gen_vehicle_protocol/

$ python gen.py hnacar_conf.yml

這時會在當前文件夾下生成output文件夾,裏面包含proto和vehicle文件夾,proto下的文件夾爲hnacar.proto,vehicle下是hnacar文件夾,包含protocol文件夾, hnacar_controller.cc, hnacar_controller.h, hnacar_message_manager.cc, hnacar_message_manager.h, hnacar_vehicle_factory.cc, hnacar_vehicle_factory.h。hnacar是本身車輛的名字。

 

6. 配置相關文件

(1) 將生成的hnacar文件夾拷貝到apollo/modules/canbus/vehicle

$ cp -rf ~/apollo/moudles/tools/gen_vehicle_protocol/output/vehicle/hnacar/ ~/apollo/modules/canbus/vehicle/

(2) 修改apollo/modules/canbus/vehicle目錄下vehicle_factory.cc,

(在21行)添加頭文件#include "modules/canbus/vehicle/hnacar/hnacar_vehicle_factory.h";

void VehicleFactory::RegisterVehicleFactory() 函數下添加(27行):

Register(VehicleParameter::HNACAR, []() -> AbstractVehicleFactory * {

return new HnacarVehicleFactory();

});

全部的hnacar中都添加本身的名字。

(3) 修改apollo/modules/canbus/vehicle目錄下BUILD文件,在cc_library的deps中添加(48行):

"//modules/canbus/vehicle/hnacar:hnacar_vehicle_factory",

(4) 修改apollo/modules/canbus/proto目錄下vehicle_parameter.proto文件,在enum VehicleBrand下添加:

HNACAR = 0;

LINCOLN_MKZ = 1;

GEM = 2;

(5) 修改apollo/modules/canbus/conf目錄下,仿照canbus_conf_gem.pb.txt文件,添加canbus_conf_hnacar.pb.txt文件:

vehicle_parameter {

brand: HNACAR

max_steer_angle: 630.0

max_steer_angle_spd: 229.0

min_steer_angle_spd: 229.0

max_acc: 1.18

min_acc: -4.5

max_brake_pressure: 7.0

max_enable_fail_attempt: 5

driving_mode: COMPLETE_AUTO_DRIVE

}

 

can_card_parameter {

brand: ESD_CAN

type: PCI_CARD

channel_id: CHANNEL_ID_ZERO

}

 

enable_debug_mode: false

enable_receiver_log: false

enable_sender_log: false

 

並修改apollo/modules/canbus/common/canbus_gflags.cc的(27行)改成:

DEFINE_string(canbus_conf_file, "modules/canbus/conf/canbus_conf_hnacar.pb.txt",

"Default canbus conf file");

 

(6) 修改apollo/modules/canbus/proto/chassis_detail.proto文件,

在message ChassisDetail的optional Gem gem = 18;下面(30行)添加:optional Hnacar hnacar = 19;

將生成的hnacar.proto文件除了第一行外的所有內容,直接拷貝到chassis_detail.proto的最下面。

 

(7) 在apollo/modules/common/data/文件夾中仿照mkz_config.pb.txt,添加hnacar_config.pb.txt,內容是與車輛相關的信息:

vehicle_param {

front_edge_to_center: 1.315

back_edge_to_center: 1.315

left_edge_to_center: 0.686

right_edge_to_center: 0.686

 

length: 2.630

width: 1.372

height: 1.9

min_turn_radius: 2.56

max_acceleration: 2.0

max_deceleration: -6.0

max_steer_angle: 0.5236

max_steer_angle_rate: 1

steer_ratio: 1

wheel_base: 1.280

wheel_rolling_radius: 0.2

}

 

7. 在dreamviewer的vehicle選項中添加hnacar選項

(1) 在apollo/modules/common/configs/config_gflags.cc中,將(37行)改成:

DEFINE_string(vehicle_config_path, "modules/common/data/hnacar_config.pb.txt",

"the file path of vehicle config file");

這樣修改時將hnacar做爲默認的車型(以前默認的車型爲mkz)。

 

8. 在docker內編譯:

$ cd ~/

$ bash Apollo.sh build

編譯完成後,打開dreamviewer會產生相應,鏈接上CAN分析儀,vehicle選項選擇hnacar,mode選navigation,點擊module controller,點擊CANBUS選項,會看到,你定義的ID號所有在發送data爲0的數據。

 

第二部分

這部份內容正式開始調試CANBUS,我會講的儘可能詳細,但若是不是本身親自實踐,恐怕很難消化這部份內容。首先,我先介紹一下CANBUS模塊中主要的函數和文件,以及他們的關係。見下圖。

首先解釋一下上面的圖(根據本身的理解畫的若是有人發現有任何問題能夠給我留言,我及時改正)。CANBUS模塊主要是未來自control等其餘模塊發出來的message轉換成底盤能夠執行的數據,經過ESDCAN發給底層,同時也將底盤的返回的數據轉換成Apollo須要的message返回給其餘模塊。上圖是具體的執行過程。首先我先介紹一下主要的功能函數。

1. 相關函數:

a) apollo/modules/canbus/proto/chassis_detail.proto:這個文件的做用是保存你車輛的自定義參數。這個文件前面兩部分的內容分別是Lincoln車和gem車的參數。在第一部分6(6)中,會根據底盤的DBC生成的hnacar.proto文件(你自定車輛的底盤參數)拷貝到chassis_detail.proto的下面。在這裏不要糾結本身車輛生成文件是否能夠被Apollo識別,由於Apollo要識別的參數並非chassis_detail.proto,而是apollo/modules/canbus/proto/chassis.proto。你須要在hnacar_controller.cc中將chassis_detail.proto轉換爲chassis.proto。

b) apollo/modules/canbus/proto/chassis.proto:Apollo中的一個message。主要用來保存底盤的通用函數(即,底盤的一些通用參數,或者說Apollo用來實現無人駕駛用到的底盤參數)。這些參數經過轉換車輛自定義的chassis_detail.proto獲得。轉換函數在hancar_controller.cc中實現。

c) 在第一部分,6小節,生成了hnacar文件夾,在這個文件夾中,hancar_controller.cc就是用來將其餘模塊發出來的message轉換成底盤能夠執行的數據,同時也將底盤的返回的數據轉換成Apollo須要的message返回給其餘模塊。hnacar_message_manager.cc決定了哪些幀用來發送到CAN總線上。hnacar_vehicle_factory.cc將hnacar添加進入Apollo。下面講解了一些用到的或者須要修改的函數。

hancar_controller.cc中的Init()函數:初始化要發送到CAN總線上的幀,即,初始化hnacar/proto文件夾中的相關幀,以及將幀的ID及函數指針添加到can_sender_(請仿照Lincoln_controller.cc的Init()代碼)。

hancar_controller.cc中的chassis()函數:將chassis_detail.proto轉換爲chassis.proto。具體操做後面介紹。

hancar_controller.cc中的EnableAutoMode()函數:底盤實現無人駕駛須要打開哪些量,好比,使能剎車、油門、轉向等等。根據你生成的proto中的函數來。

hancar_controller.cc中的Brake()、Throttle()、Steer()函數:這三個函數是用來的獲得controller模塊發出的controlcommand中的剎車、轉向、油門量。這三個函數是hancar_controller.h中override函數(必定要實現的函數)。因爲個人車輛還須要獲得速度controlcommand中的速度值,因此在hancar_controller.h中添加了void Speed(double vehicle_speed) override;函數,同時我在hancar_controller.cc中添加了該函數。

d) 在hnacar/proto文件夾中,裏面的.cc與.h是你在創建DBC文件的時候,message的名稱及ID號(16進制)表示的(注意:每一個message都有獨立的.cc與.h文件),這些函數的做用是將要發送給底盤的數據轉換成底盤能夠識別的數據,或將底盤返回的數據轉換成Apollo須要的數據。這些函數都須要修改。底盤發送到IPC的message生成.cc與.h文件特色是函數會有void Parse(const std::uint8_t* bytes, int32_t length, ChassisDetail* chassis) 函數。IPC發送給底盤的message生成的.cc與.h文件有virtual void UpdateData(uint8_t *data)函數

2. Apollo調試ESDCAN接收

首先說一下,若是Apollo默認發送bitrate爲500k,若是想要修改,請到apollo/modules/drivers/canbus/can_client/esd/esd_can_client.cc中的ret = canSetBaudrate(dev_handler_, NTCAN_BAUD_500);(94行)函數,我這裏是250K,因此將NTCAN_BAUD_500改成NTCAN_BAUD_250。其餘位置,將apollo_base.sh中的sudo ip link set can0 type can bitrate 500000(182行)改成250000。

CANBUS接收調試從驗證ESDCAN是否能夠在Apollo內接收信息。因此個人調試步驟爲:

(1) 將CAN分析儀借到ESDCAN上,將CAN分析儀的bitrate調爲500k,在CAN分析儀軟件幀ID的位置默認爲00 00 00 01不用變,數據位置由低到高寫入31 32 33 34 35 36 37 38,這時由於Apollo的AINFO函數默認接收ASCII碼。

(2) 在apollo/modules/drivers/canbus/can_comm/can_receiver.h文件aRecvThreadFunc()函數中(直接接收的信息的位置,169行)添加以下代碼:

  1. AINFO << "received message is " << std::hex << buf[0].data[0] << " " \
  2. << std::hex << buf[0].data[1] << " " \
  3. << std::hex << buf[0].data[2] << " " \
  4. << std::hex << buf[0].data[3] << " " \
  5. << std::hex << buf[0].data[4] << " " \
  6. << std::hex << buf[0].data[5] << " " \
  7. << std::hex << buf[0].data[6] << " " \
  8. << std::hex << buf[0].data[7];

can_client_->Receive(&buf, &frame_num)(144行)函數是在CANBUS接收線程中接收數據的函數,buf的長度爲1。因此只查看buf[0]的內容就好。

(3) 在docker內編譯,並打開dreamviewer

$ cd /apollo

$ bash apollo.sh build_gpu

$ ./scripts/bootstrap.sh

(4) 查看相應的log文件

$ tail -f data/log/canbus.INFO

點擊CAN分析儀軟件發送按鈕,會在終端上看到:

I0804 15:24:10.373136 30229 can_receiver.h:169] received message is 1 2 3 4 5 6 7 8

3. 調試Apollo接收底層數據

將底盤的發送的數據轉換爲Apollo的chassis.proto數據,發送到chassis的message中。(建議:在開始調試以前,調試一下apollo/modules/drivers/canbus/common/byte.cc與byte.h文件,試試這個byte的功能)。在第一部分第5步,經過Apollo用我自定義的DBC文件生成了本身的hnacar相關函數,但實際上,我生成的相關函數是有問題的,我生成的函數全是默認是底盤發送的函數,由於我生成的.cc與.h文件都是由Parse()函數(根據Apollo美研團隊給個人反饋,是因爲我在編寫DBC文件的時候沒有區分Tx Messages與Rx Messages,但我重寫了DBC文件,從新生成了hnacar/proto中的函數仍是所有爲由Parse()函數。不過沒有關係,由於生成的hnacar/proto中的函數全是要修改的)。

首先說明一下你在執行完第一部分的操做,以及第8步build以後生成了哪些函數,若是你瞭解了Protocol Buffers,那麼在第一部分第6步(6)中chassis_detail.proto中添加的optional Hnacar hnacar = 19會在chassis_detail.pb.h(apollo/bazel-out/local-dbg/genfiles/modules/canbus/proto/)中生成Hnacar類,在Hnacar類下面"組合"了各個DBC文件生成的message的類,各個message類中又包含了對message中對signal的操做。因此在IPC讀取CANBUS的1幀後,都是經過chassis_detail訪問Hnacar而後訪問message最後訪問操做。其中每一個signal基本包含5個操做:

  1. bool has_r_b_turn_motor_high_byte() const; //是否有r_b_turn_motor_high_byte這個信號,若是_has_bits_[0]的對應位爲1
  2. void set_has_r_b_turn_motor_high_byte() const; //r_b_turn_motor_high_byte信號在_has_bits_[0]的對應位設置爲1
  3. void clear_r_b_turn_motor_high_byte(); //r_b_turn_motor_high_byte信號在_has_bits_[0]的對應位清除爲1
  4.  
  5. ::google::protobuf::int32 r_b_turn_motor_high_byte() const; //返回r_b_turn_motor_high_byte信號的值。
  6. void set_r_b_turn_motor_high_byte(::google::protobuf::int32 value); //r_b_turn_motor_high_byte信號賦值。

在這裏我舉一個我本身的例子說明調試過程,我會盡量詳盡的說明。

(1) 在個人車輛須要返回車輛的速度,這個message在個人DBC文件中ID爲3,因此在個人proto中有這樣一個類Returnturn3。我跟據個人車實際額須要,改寫了Returnturn3類下的Parse()函數:

  1. void Returnturn3::Parse(const std::uint8_t* bytes, int32_t length,
  2. ChassisDetail* chassis) const {
  3. chassis->mutable_hnacar()->mutable_return_turn_3()->set_l_b_turn_motor_low_byte(return_current_speed(bytes, length));
  4. chassis->mutable_hnacar()->mutable_return_turn_3()->set_l_b_turn_motor_high_byte(return_current_steer(bytes, length));
  5. }

其中,chassis爲ChassisDetail的指針,mutable_hnacar()是chassis_detail.proto生成的返回Hnacar指針的函數,mutable_return_turn_3()是chassis_detail.proto生成的返回Returnturn3指針的函數,set_l_b_turn_motor_low_byte是我在定義個人DBC文件中Return_turn的message中的一個名爲l_b_turn_motor_low_byte的signal,set_l_b_turn_motor_low_byte函數是對這個信號賦值。return_current_speed(bytes, length)函數是Returnturn3類的函數,做用是將Return_turn的message的8個data字節組拼裝成想要的數據。

其餘的類也請根據本身的需求改寫。實際上,改寫的最終目的就是爲了將本身的chassis_details.proto,變成Apollo須要的chassis.proto,而後最爲message發送到其餘模塊。

(2) 上面提到的Parse函數在apollo/modules/drivers/canbus/can_comm/message_manager.h中的void MessageManager<SensorType>::Parse(const uint32_t message_id, const uint8_t *data, int32_t length)函數中調用,具體細節你們本身看。這個Parse函數在apollo/modules/drivers/canbus/can_comm/can_receiver.h文件aRecvThreadFunc()線程中被調用。這個線程調用後,底盤的速度和轉角會賦值到chassis_detail.proto的l_b_turn_motor_low_byte_與l_b_turn_motor_high_byte_中。

(3) 這時,hnacar_controller.cc中的chassis()函數會調用chassis.proto生成的函數對chassis.proto對應的速度與轉角賦值。以下:

  1. if (chassis_detail.hnacar().return_turn_3().has_l_b_turn_motor_low_byte()) {
  2. chassis_.set_speed_mps((float)(chassis_detail.hnacar().return_turn_3().l_b_turn_motor_low_byte() / 1000));
  3. //AINFO << "The speed is set";
  4. } else {
  5. chassis_.set_speed_mps(0);
  6. //AINFO << "The speed is unset";
  7. }

if (chassis_detail.hnacar().return_turn_3().has_l_b_turn_motor_low_byte())這一串就是爲了判斷chassis_detail中是否有l_b_turn_motor_low_byte信號。chassis_.set_speed_mps()函數是用於對chassis中的speed_mps_賦值。chassis_detail.hnacar().return_turn_3().l_b_turn_motor_low_byte()函數是爲了返回l_b_turn_motor_low_byte_的值,也就是速度值。

到此,就是將chassis_detail.proto中的值賦給了chassis_detail.proto

4. 調試底盤發送數據

在調試Apollo底層數據接收的時候,提到了經過DBC生成的每一個message的類,都是帶Parse()函數的。而第二部分1小節d)中,全部經過DBC文件將IPC發送給底盤的message類中含有UpdateData函數。因此我將要發送到底盤的message類中的Parse()函數所有屏蔽掉,加入在.h中加入virtual void UpdateData(uint8_t *data)函數,而後在.cc中實現它(UpdateData函數的功能是將你要發送到CANBUS上的數據賦值到一個長度爲8的uint8_t的數組上)。下面具體說。

首先,在apollo/modules/canbus/canbus.cc裏的回調函數OnControlCommand中,調用Update函數,Update函數將control_command中的值經過HnaController類中實現的Gear、Brake、Throttle、Steer等函數 (Gear、Brake、Throttle、Steer函數是VehicleController中的虛函數,HnaController繼承了VehicleController並實現了這些函數),在經過這些函數中調用的DBC生成的各個message類中的函數,賦值到各個signal。在由SenderMessage類中的Update()函數(apollo/modules/drivers/canbus/can_comm/can_sender.h的245行)調用message類中的UpdateData函數合成又給8字節的message,而後賦值給can_frame_to_send_。最後在PowerSendThreadFunc()線程中(apollo/modules/drivers/canbus/can_comm/can_sender.h的273行)調用CanFrame()函數返回can_frame_to_send_,經過SendSingleFrame()函數發送到CAN總線上。(這裏只介紹主幹,其餘代碼本身看吧)

因爲個人車子底盤是由速度控制,而在Apollo默認代碼中沒有下發速度(在VehicleController中的虛函數中沒有Speed虛函數),那麼我須要從Controller模塊中獲得速度,且下發到CANBUS上。下面是個人作法,請你們參考。

首先,我須要獲得速度。Controller模塊的做用就是將速度轉換成剎車、油門量,而後下發。因此根據Apollo美研團隊的建議,讓我打通Controller模塊(即不用Controller模塊),個人修改以下:

a)在lon_controller.cc中的268行添加:cmd->set_speed(speed_controller_input_limited); 在195~207行對輸入的速度進行修正,speed_controller_input_limited是修正結果。

b)在VehicleController類中添加:virtual void Speed(double vehicle_speed) = 0; 由於在VehicleController類中沒有實現從control_command中直接接收Speed。而在control_cmd.proto(/apollo/modules/control/proto/control_cmd.proto)中是有Speed(optional double speed = 9;41行)。

c)在vehicle_controller.cc的ErrorCode VehicleController::Update(const ControlCommand &command)函數的132行添加:Speed(control_command.speed()); 由於在Update函數中只將Gear、Throttle、Brake、SetEpbBreak等量發下去,沒有發Speed。

d)在hnacar_controller.h中添加:void Speed(double vehicle_speed) override;在hnacar_controller.cc中添加void HnacarController::Speed(double vehicle_speed) {}函數,用以實現虛函數。Speed函數將control_command轉換成從canbus下發的值。

e)在canbus的vehicle中的hna_controller.cc中的chassis()函數添加:

  1. if (chassis_detail.has_vehicle_spd() &&
  2. chassis_detail.vehicle_spd().has_vehicle_spd()) {
  3. chassis_.set_speed_mps(chassis_detail.vehicle_spd().vehicle_spd());
  4. } else {
  5. chassis_.set_speed_mps(0);
  6. }

f) 修改我用DBC生成的下發類。在生成的最初,個人類帶有

  1. // void Setsteer40::Parse(const std::uint8_t* bytes, int32_t length,
  2. // ChassisDetail* chassis) const {
  3. // chassis->mutable_hnacar()->mutable_set_steer_40()->set_r_b_turn_motor_high_byte(r_b_turn_motor_high_byte(bytes, length));
  4. // chassis->mutable_hnacar()->mutable_set_steer_40()->set_r_b_turn_motor_low_byte(r_b_turn_motor_low_byte(bytes, length));
  5. // chassis->mutable_hnacar()->mutable_set_steer_40()->set_l_b_turn_motor_high_byte(l_b_turn_motor_high_byte(bytes, length));
  6. // chassis->mutable_hnacar()->mutable_set_steer_40()->set_l_b_turn_motor_low_byte(l_b_turn_motor_low_byte(bytes, length));
  7. // chassis->mutable_hnacar()->mutable_set_steer_40()->set_l_f_turn_motor_high_byte(l_f_turn_motor_high_byte(bytes, length));
  8. // chassis->mutable_hnacar()->mutable_set_steer_40()->set_l_f_turn_motor_low_byte(l_f_turn_motor_low_byte(bytes, length));
  9. // chassis->mutable_hnacar()->mutable_set_steer_40()->set_r_f_turn_motor_high_byte(r_f_turn_motor_high_byte(bytes, length));
  10. // chassis->mutable_hnacar()->mutable_set_steer_40()->set_r_f_turn_motor_low_byte(r_f_turn_motor_low_byte(bytes, length));
  11. // }

我將上述代碼屏蔽(固然還有.h的代碼),添加以下:

  1. void Setsteer40::UpdateData(uint8_t *data) {
  2. set_steering_angle_spd_p(data, steering_angle_, moving_spd_); //設置旋轉的角度與速度,若是直行,那麼轉角爲0
  3. set_drive_motor_flag_p(data, drive_motor_enable); //使能輪轂電機。
  4. set_turn_motor_flag_p(data, turn_motor_enable); //使能轉向電機。
  5. set_brake_motor_flag_p(data, brake_motor_enable); //使能剎車電機。
  6. set_brake_flag_p(data, brake_enable, brake_disable); //容許剎車。
  7. }

這樣速度數據就能夠發下來了。

(未完,待續。若是上述過程你們在調試過程當中發現有錯誤,請及時聯繫我,我在博客中及時改正。)

相關文章
相關標籤/搜索