Skip to content

Commit b4fe7e1

Browse files
Merge pull request autowarefoundation#220 from atanasko/main
Add GT visualization and fix decoding of steering angle from canbus
2 parents e960ccb + 7750b9e commit b4fe7e1

File tree

6 files changed

+119
-42
lines changed

6 files changed

+119
-42
lines changed
14.9 KB
Loading

VisionPilot/Production_Releases/0.5/include/drivers/can_interface.hpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,22 @@
88
#include <cmath>
99
#include <limits>
1010
#include <chrono>
11+
#include <memory>
1112

1213
namespace autoware_pov::drivers {
1314

1415
struct CanVehicleState {
1516
double speed_kmph = std::numeric_limits<double>::quiet_NaN(); // Speed, CAN ID 0xA1
1617
double steering_angle_deg = std::numeric_limits<double>::quiet_NaN(); // Steering, CAN ID 0xA4
1718
bool is_valid = false;
19+
bool is_steering_angle = false;
20+
21+
void clear()
22+
{
23+
speed_kmph = std::numeric_limits<double>::quiet_NaN();
24+
is_steering_angle = false;
25+
steering_angle_deg = std::numeric_limits<double>::quiet_NaN();
26+
}
1827
};
1928

2029
/**
@@ -31,7 +40,7 @@ class CanInterface {
3140
* @param interface_name "can0", "vcan0", or path to .asc file (e.g. "./assets/test.asc")
3241
*/
3342
explicit CanInterface(const std::string& interface_name);
34-
43+
3544
~CanInterface();
3645

3746
/**
@@ -74,7 +83,7 @@ class CanInterface {
7483
static constexpr int ID_STEERING = 0xA4; // Or A4, 164
7584

7685
void parseFrame(
77-
int can_id,
86+
int can_id,
7887
const std::vector<uint8_t>& data
7988
);
8089

@@ -84,6 +93,9 @@ class CanInterface {
8493

8594
};
8695

96+
// extern declaration
97+
extern std::unique_ptr<CanInterface> can_interface;
98+
8799
} // namespace autoware_pov::drivers
88100

89101
#endif // AUTOWARE_POV_DRIVERS_CAN_INTERFACE_HPP_

VisionPilot/Production_Releases/0.5/include/visualization/draw_lanes.hpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "../inference/lane_segmentation.hpp"
55
#include "../lane_tracking/lane_tracking.hpp"
66
#include <opencv2/opencv.hpp>
7+
#include <optional>
78

89
namespace autoware_pov::vision::egolanes
910
{
@@ -88,7 +89,13 @@ void drawMetricVerification(
8889
);
8990

9091
cv::Mat rotateSteeringWheel(const cv::Mat& img, float steering_angle_deg);
91-
void visualizeSteering(cv::Mat& img, float steering_angle, const cv::Mat& steeringWheelImage);
92+
void visualizeWheel(const cv::Mat& img, const cv::Mat& wheelImg, const int x, const int y);
93+
void visualizeSteering(
94+
cv::Mat& img,
95+
float steering_angle,
96+
const cv::Mat& rotatedPredSteeringWheelImg,
97+
std::optional<float> gtSteeringAngle,
98+
const cv::Mat& rotatedGtSteeringWheelImg);
9299

93100
} // namespace autoware_pov::vision::egolanes
94101

VisionPilot/Production_Releases/0.5/main.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -556,8 +556,11 @@ void displayThread(
556556
std::cerr << "Error: could not open " << csv_log_path << " for writing" << std::endl;
557557
}
558558

559-
std::string image_path = "images/wheel_green.png";
560-
cv::Mat steeringWheelImg = cv::imread(image_path, cv::IMREAD_UNCHANGED);
559+
// Load steering wheel images
560+
std::string predSteeringImagePath = "images/wheel_green.png";
561+
cv::Mat predSteeringWheelImg = cv::imread(predSteeringImagePath, cv::IMREAD_UNCHANGED);
562+
std::string gtSteeringImagePath = "images/wheel_white.png";
563+
cv::Mat gtSteeringWheelImg = cv::imread(gtSteeringImagePath, cv::IMREAD_UNCHANGED);
561564

562565
while (running.load()) {
563566
InferenceResult result;
@@ -588,11 +591,25 @@ void displayThread(
588591
// cv::Scalar(0,0,0)
589592
// );
590593

594+
// Display wheels over frame image
591595
float steering_angle = result.steering_angle;
592-
cv::Mat rotatedSteeringWheelImg = rotateSteeringWheel(steeringWheelImg, steering_angle);
593-
visualizeSteering(view_debug, steering_angle, rotatedSteeringWheelImg);
596+
cv::Mat rotatedPredSteeringWheelImg = rotateSteeringWheel(predSteeringWheelImg, steering_angle);
597+
598+
// Read GT steering from CAN frame
599+
std::optional<float> gtSteeringAngle;
600+
cv::Mat rotatedGtSteeringWheelImg;
601+
if (can_interface) {
602+
// float gtSteeringSpeed = can_interface->getState().speed_kmph;
603+
if (can_interface->getState().is_valid && can_interface->getState().is_steering_angle) {
604+
gtSteeringAngle = can_interface->getState().steering_angle_deg;
605+
if (gtSteeringAngle.has_value()) {
606+
rotatedGtSteeringWheelImg = rotateSteeringWheel(gtSteeringWheelImg, gtSteeringAngle.value());
607+
}
608+
}
609+
}
594610

595-
// std::cout<< "************ " << result.steering_angle << std::endl;
611+
// rotatedGtSteeringWheelImg = rotateSteeringWheel(gtSteeringWheelImg, gtSteeringAngle.value());
612+
visualizeSteering(view_debug, steering_angle, rotatedPredSteeringWheelImg, gtSteeringAngle, rotatedGtSteeringWheelImg);
596613

597614
// 2. Draw 3 views
598615
drawRawMasksInPlace(
@@ -1151,7 +1168,7 @@ int main(int argc, char** argv)
11511168
}
11521169

11531170
// Initialize CAN Interface (optional - ground truth)
1154-
std::unique_ptr<CanInterface> can_interface;
1171+
// std::unique_ptr<CanInterface> can_interface;
11551172
if (!can_interface_name.empty()) {
11561173
try {
11571174
can_interface = std::make_unique<CanInterface>(can_interface_name);

VisionPilot/Production_Releases/0.5/src/drivers/can_interface.cpp

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ CanInterface::~CanInterface() {
5656

5757
// Main update loop
5858
bool CanInterface::update() {
59+
// clear because data get from state!
60+
current_state_.clear();
5961

6062
if (is_file_mode_) {
6163
return readFileLine();
@@ -89,6 +91,7 @@ void CanInterface::parseFrame(
8991
double val = decodeSteering(data);
9092
current_state_.steering_angle_deg = val;
9193
current_state_.is_valid = true;
94+
current_state_.is_steering_angle = true;
9295
}
9396
}
9497

@@ -109,31 +112,34 @@ double CanInterface::decodeSpeed(const std::vector<uint8_t>& data) {
109112
return static_cast<double>(raw) * 0.01;
110113
}
111114

112-
// SSA (Steering angle) : Start Bit 46 | Length 15 | Signed | Factor 0.1
113-
// Format: Motorola (Big Endian: https://en.wikipedia.org/wiki/Endianness)
114-
// Bit 46 is in byte 5 (bit 6).
115-
// This is complex. We verify strictly against provided logic if possible.
116-
// Lacking exact bit-matrix, we assume standard Big Endian alignment crossing byte 5 and 6.
115+
// Steering angle
116+
// SSA (Measured steering angle) : 46|15@0- (0.1,0) [0|0] "-"
117+
// SSAZ (Steering zero point) : 29|15@0- (0.1,0) [0|0] "-"
117118
double CanInterface::decodeSteering(const std::vector<uint8_t>& data) {
118-
119-
if (data.size() < 8) return 0.0;
120119

121-
// Masking logic based on 15-bit signed integer
122-
// Assuming MSB is in byte 5, LSB in byte 6
123-
uint16_t raw_high = data[5];
124-
uint16_t raw_low = data[6];
125-
126-
// Combine
127-
uint16_t raw = (raw_high << 8) | raw_low;
128-
129-
// If it's 15 bits, we might need to shift or mask.
130-
// Assuming the 15 bits are right-aligned in the extraction:
131-
// (This is a best-guess implementation until A4 is actually observed in logs)
132-
133-
// Convert to signed 16-bit to handle negative values (Two's complement)
134-
int16_t signed_val = static_cast<int16_t>(raw);
135-
136-
return static_cast<double>(signed_val) * 0.1;
120+
// Sanity: need at least 8 bytes
121+
if (data.size() < 8) return std::numeric_limits<double>::quiet_NaN();
122+
123+
// SSAZ
124+
uint16_t b6 = (data[5] & 0xC0) >> 7; // Bit 7 from byte 6 BigEndian
125+
uint16_t b5 = data[4]; // Byte 5
126+
bool ssazSign = (data[3] & 0x10) != 0; // bit 4 → sign
127+
int16_t b4 = data[3] & 0x0F; // bits 3..0 → magnitude
128+
129+
uint16_t ssaz = (b4 << 10) | (b5 << 2) | b6;
130+
int16_t ssazSigned = ssazSign ? -ssaz : ssaz;
131+
132+
// SSA
133+
uint16_t ssa_b7 = data[6];
134+
bool ssaSign = (data[5] & 0x40) != 0; // bit 6 = sign
135+
uint16_t ssa_b6 = data[5] & 0x3F; // bits 5..0 = magnitude
136+
137+
int16_t ssa = (ssa_b6 << 7) | ssa_b7;
138+
int16_t ssaSigned = ssaSign ? -ssa : ssa;
139+
140+
double steering_angle = ssaSigned - ssazSigned;
141+
142+
return steering_angle;
137143
}
138144

139145
// ============================== REAL-TIME INFERENCE (SocketCAN) ============================== //
@@ -318,4 +324,7 @@ bool CanInterface::readFileLine() {
318324
return false; // End of file
319325
}
320326

327+
// definition of global pointer
328+
std::unique_ptr<CanInterface> can_interface=nullptr;
329+
321330
} // namespace autoware_pov::drivers

VisionPilot/Production_Releases/0.5/src/visualization/draw_lanes.cpp

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -872,24 +872,21 @@ cv::Mat rotateSteeringWheel(const cv::Mat& img, float steering_angle_deg)
872872
return rotated;
873873
}
874874

875-
void visualizeSteering(cv::Mat& img, const float steering_angle, const cv::Mat& overlay)
875+
void visualizeWheel(const cv::Mat& img, const cv::Mat& wheelImg, const int x, const int y)
876876
{
877-
int x = 10;
878-
int y = 10;
877+
if (wheelImg.empty() || img.empty()) return;
879878

880-
if (overlay.empty() || img.empty()) return;
881-
882-
int w = overlay.cols;
883-
int h = overlay.rows;
879+
int w = wheelImg.cols;
880+
int h = wheelImg.rows;
884881

885882
if (x < 0 || y < 0 || x + w > img.cols || y + h > img.rows) return;
886883

887884
cv::Mat roi = img(cv::Rect(x, y, w, h));
888885

889-
if (overlay.channels() == 4) { // has alpha
886+
if (wheelImg.channels() == 4) { // has alpha
890887
cv::Mat overlay_rgb, alpha;
891888
std::vector<cv::Mat> channels(4);
892-
cv::split(overlay, channels);
889+
cv::split(wheelImg, channels);
893890
alpha = channels[3]; // alpha channel
894891
cv::merge(std::vector<cv::Mat>{channels[0], channels[1], channels[2]}, overlay_rgb);
895892

@@ -908,9 +905,26 @@ void visualizeSteering(cv::Mat& img, const float steering_angle, const cv::Mat&
908905
// Write back to base image
909906
blended.convertTo(img(cv::Rect(x, y, w, h)), CV_8UC3, 255.0);
910907
} else {
911-
overlay.copyTo(roi); // no alpha, hard paste
908+
wheelImg.copyTo(roi); // no alpha, hard paste
912909
}
910+
}
911+
912+
void visualizeSteering(
913+
cv::Mat& img,
914+
const float steering_angle,
915+
const cv::Mat& rotatedPredSteeringWheelImg,
916+
std::optional<float> gtSteeringAngle,
917+
const cv::Mat& rotatedGtSteeringWheelImg)
918+
{
919+
int w = img.cols;
920+
int h = img.rows;
913921

922+
visualizeWheel(img, rotatedPredSteeringWheelImg, 10, 10);
923+
if (!rotatedGtSteeringWheelImg.empty()) {
924+
visualizeWheel(img, rotatedGtSteeringWheelImg, w - 80, 10);
925+
}
926+
927+
// Prediction
914928
std::ostringstream oss;
915929
oss << std::fixed << std::setprecision(2) << steering_angle;
916930
std::string steeringAngleText = "Predicted angle: " + oss.str();
@@ -924,6 +938,24 @@ void visualizeSteering(cv::Mat& img, const float steering_angle, const cv::Mat&
924938
cv::Scalar(62, 202, 130),
925939
2
926940
);
941+
942+
if (gtSteeringAngle.has_value() && !std::isnan(gtSteeringAngle.value())) {
943+
// GT
944+
std::ostringstream oss1;
945+
oss1 << std::fixed << std::setprecision(2) << gtSteeringAngle.value();
946+
std::string gtAngleText = "GT angle: " + oss1.str();
947+
948+
cv::putText(
949+
img,
950+
gtAngleText,
951+
cv::Point(w - 180, 100),
952+
cv::FONT_HERSHEY_SIMPLEX,
953+
0.6,
954+
cv::Scalar(255, 255, 255),
955+
2
956+
);
957+
}
958+
927959
}
928960

929961
} // namespace autoware_pov::vision::egolanes

0 commit comments

Comments
 (0)