Skip to content

Commit 4f3a222

Browse files
committed
read max_steering_angle from the config file and add it to the calibration process
1 parent 34e9f29 commit 4f3a222

3 files changed

Lines changed: 104 additions & 9 deletions

File tree

config/vesc.lua

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ max_deceleration = 6.0; -- m/s^2
2424
joystick_normal_speed = 1.0; -- m/s
2525
joystick_turbo_speed = 2.0; -- m/s
2626

27+
-- Maximum steering angle in radians for joystick control
28+
-- Formula: max_steering_angle = atan(wheelbase / desired_min_turning_radius)
29+
-- Example: 0.286 rad (16.4°) gives 1.1m turning radius with 0.324m wheelbase
30+
max_steering_angle = 0.286; -- radians
31+
2732
-- IMU fusion parameters
2833
fuse_imu = true; -- Set to true to fuse IMU data with odometry using EKF
2934
i2c_bus_number = 7; -- I2C bus number for MPU6050 sensor

scripts/calibrate.py

Lines changed: 93 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,27 @@ def __init__(self):
7272
self.steering_corrections = [] # Store steering corrections during calibration
7373
self.recording_corrections = False
7474

75+
# Load max_steering_angle from config for joystick corrections
76+
try:
77+
import re
78+
config_path = "/home/orin/roboracer_ws/src/ut_automata/config/car.lua"
79+
with open(config_path, 'r') as f:
80+
content = f.read()
81+
match = re.search(r'max_steering_angle\s*=\s*([-0-9.]+)', content)
82+
if match:
83+
self.max_steering_angle = float(match.group(1))
84+
else:
85+
self.max_steering_angle = 0.286 # default
86+
except:
87+
self.max_steering_angle = 0.286 # default
88+
7589
# Calibration results
7690
self.results = {
7791
'steering_offset': None,
7892
'steering_gain': None,
7993
'speed_offset': None,
80-
'speed_gain': None
94+
'speed_gain': None,
95+
'max_steering_angle': None
8196
}
8297

8398
self.get_logger().info('VESC Calibrator initialized')
@@ -96,8 +111,9 @@ def joy_callback(self, msg):
96111
# Use left stick horizontal (axes[0]) for steering, same as vesc_driver
97112
# Negative because that's how vesc_driver does it
98113
steer_input = -msg.axes[0] if len(msg.axes) > 0 else 0.0
99-
# Scale by max turn rate (0.25 rad from vesc_driver.cpp)
100-
steering_correction = steer_input * 0.25
114+
# Scale by max steering angle from config (default 0.286 rad)
115+
max_steering_angle = getattr(self, 'max_steering_angle', 0.286)
116+
steering_correction = steer_input * max_steering_angle
101117
self.steering_corrections.append(steering_correction)
102118

103119
def send_drive_command(self, velocity, curvature):
@@ -322,6 +338,38 @@ def write_speed_gain(self, gain, config_path="/home/orin/roboracer_ws/src/ut_aut
322338
except Exception as e:
323339
self.get_logger().error(f"Error writing config file: {e}")
324340
return False
341+
342+
def write_max_steering_angle(self, angle, config_path="/home/orin/roboracer_ws/src/ut_automata/config/car.lua"):
343+
"""Write max_steering_angle to car.lua config file"""
344+
import re
345+
346+
try:
347+
# Read existing content or create new file
348+
try:
349+
with open(config_path, 'r') as f:
350+
content = f.read()
351+
except FileNotFoundError:
352+
# Create new file with car_name if it doesn't exist
353+
content = 'car_name = "orin12";\n\n'
354+
355+
# Check if max_steering_angle already exists in the file
356+
pattern = r'(max_steering_angle\s*=\s*)([-0-9.]+)([;]?[^\n]*)'
357+
if re.search(pattern, content):
358+
# Replace existing value (preserve semicolon and comments)
359+
replacement = f'\\g<1>{angle:.4f}\\g<3>'
360+
new_content = re.sub(pattern, replacement, content)
361+
else:
362+
# Append to file if it doesn't exist
363+
new_content = content.rstrip() + f'\nmax_steering_angle = {angle:.4f}; -- radians, calibrated from turn radius\n'
364+
365+
with open(config_path, 'w') as f:
366+
f.write(new_content)
367+
368+
self.get_logger().info(f"Updated max_steering_angle to {angle:.4f} rad ({math.degrees(angle):.2f} deg) in {config_path}")
369+
return True
370+
except Exception as e:
371+
self.get_logger().error(f"Error writing config file: {e}")
372+
return False
325373

326374
def calibrate_steering_offset(self):
327375
"""
@@ -376,8 +424,8 @@ def calibrate_steering_offset(self):
376424
if self.current_joy is not None and len(self.current_joy.axes) > 0:
377425
# Use left stick horizontal (axes[0]) for steering
378426
steer_input = -self.current_joy.axes[0]
379-
# Scale by max turn rate (0.25 rad)
380-
steering_correction = steer_input * 0.25
427+
# Scale by max steering angle from config
428+
steering_correction = steer_input * self.max_steering_angle
381429

382430
# Command forward with user's steering correction
383431
# curvature = 1/turn_radius, for small angles: curvature ≈ steering_angle / wheelbase
@@ -503,7 +551,22 @@ def calibrate_steering_gain(self):
503551
return
504552

505553
wheelbase = 0.324 # meters (from vesc.lua)
506-
max_steering_angle = 0.25 # radians (from vesc_driver.cpp)
554+
555+
# Read current max_steering_angle from config (or use default)
556+
try:
557+
import re
558+
config_path = "/home/orin/roboracer_ws/src/ut_automata/config/car.lua"
559+
with open(config_path, 'r') as f:
560+
content = f.read()
561+
match = re.search(r'max_steering_angle\s*=\s*([-0-9.]+)', content)
562+
if match:
563+
max_steering_angle = float(match.group(1))
564+
print(f"Current max_steering_angle from config: {max_steering_angle:.4f} rad ({math.degrees(max_steering_angle):.2f}°)")
565+
else:
566+
max_steering_angle = 0.286 # default for ~1.1m turn radius
567+
print(f"No max_steering_angle in config, using default: {max_steering_angle:.4f} rad")
568+
except:
569+
max_steering_angle = 0.286 # default
507570

508571
# Calculate maximum curvature from max steering angle
509572
# From vesc_driver: steering_angle = atan(wheelbase / turn_radius)
@@ -565,12 +628,19 @@ def calibrate_steering_gain(self):
565628

566629
# Calculate average turn radius
567630
avg_radius = np.mean(turn_radii)
568-
print(f"\nAverage turn radius: {avg_radius:.3f} m")
631+
print(f"\n📊 Turn Radius Measurements:")
632+
print(f" Average turn radius: {avg_radius:.3f} m")
633+
if len(turn_radii) > 1:
634+
print(f" Individual radii: {', '.join([f'{r:.3f}m' for r in turn_radii])}")
635+
print(f" Variation: ±{np.std(turn_radii):.3f} m")
569636

570637
# Calculate actual steering angle from measured radius
571638
# steering_angle = atan(wheelbase / turn_radius)
572639
actual_steering_angle = math.atan(wheelbase / avg_radius)
573640

641+
# This is the max_steering_angle the vehicle can physically achieve
642+
measured_max_steering_angle = actual_steering_angle
643+
574644
# Calculate gain: commanded_angle / actual_angle gives us the gain
575645
# But we command curvature, so we need to work backwards
576646
# curvature = 1/turn_radius, steering_angle = atan(wheelbase * curvature)
@@ -606,6 +676,7 @@ def calibrate_steering_gain(self):
606676
new_gain = current_gain * correction_factor
607677

608678
self.results['steering_gain'] = new_gain
679+
self.results['max_steering_angle'] = measured_max_steering_angle
609680

610681
# Write the gain to car.lua config file
611682
if self.write_steering_gain(new_gain):
@@ -614,8 +685,18 @@ def calibrate_steering_gain(self):
614685
print(f"\n⚠️ Failed to update car.lua. Please update it manually.")
615686
print(f" Set steering_angle_to_servo_gain = {new_gain:.4f};")
616687

688+
# Write max_steering_angle to car.lua config file
689+
if self.write_max_steering_angle(measured_max_steering_angle):
690+
print(f"✅ Updated car.lua with max_steering_angle: {measured_max_steering_angle:.4f} rad ({math.degrees(measured_max_steering_angle):.2f}°)")
691+
print(f" This corresponds to minimum turning radius: {avg_radius:.3f} m")
692+
else:
693+
print(f"\n⚠️ Failed to update car.lua. Please update it manually.")
694+
print(f" Set max_steering_angle = {measured_max_steering_angle:.4f};")
695+
617696
print(f"\n✓ Steering gain calibrated: {new_gain:.4f}")
618697
print(f" (Correction factor: {correction_factor:.3f})")
698+
print(f"✓ Max steering angle calibrated: {measured_max_steering_angle:.4f} rad ({math.degrees(measured_max_steering_angle):.2f}°)")
699+
print(f" Minimum turning radius: {avg_radius:.3f} m")
619700
print("⚠️ Remember to restart the VESC driver node for the change to take effect!")
620701

621702
def calibrate_speed_offset(self):
@@ -870,6 +951,11 @@ def print_results(self):
870951
if self.results['speed_gain'] is not None:
871952
print(f"speed_to_erpm_gain = {self.results['speed_gain']:.1f};")
872953

954+
if self.results['max_steering_angle'] is not None:
955+
print(f"max_steering_angle = {self.results['max_steering_angle']:.4f}; -- {math.degrees(self.results['max_steering_angle']):.2f} degrees")
956+
min_turn_radius = 0.324 / math.tan(self.results['max_steering_angle'])
957+
print(f" --> Minimum turning radius: {min_turn_radius:.3f} m")
958+
873959
print("-"*60)
874960

875961
print("\n⚠️ IMPORTANT: You must restart the VESC driver node for changes to take effect!")

src/vesc_driver/vesc_driver.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ CONFIG_FLOAT(max_accel_, "max_acceleration");
4343
CONFIG_FLOAT(max_decel_, "max_deceleration");
4444
CONFIG_FLOAT(turbo_speed_, "joystick_turbo_speed");
4545
CONFIG_FLOAT(normal_speed_, "joystick_normal_speed");
46+
CONFIG_FLOAT(max_steering_angle_, "max_steering_angle");
4647
CONFIG_STRING(joystick_mode_, "joystick_mode");
4748
CONFIG_STRING(serial_port_, "serial_port");
4849
CONFIG_BOOL(fuse_imu_, "fuse_imu");
@@ -153,6 +154,10 @@ VescDriver::VescDriver(rclcpp::Node::SharedPtr nh,
153154
RCLCPP_INFO(nh_->get_logger(), " joystick_normal_speed = %.2f", normal_speed_);
154155
RCLCPP_INFO(nh_->get_logger(), " joystick_turbo_speed = %.2f", turbo_speed_);
155156
RCLCPP_INFO(nh_->get_logger(), " joystick_mode = %s", joystick_mode_.c_str());
157+
RCLCPP_INFO(nh_->get_logger(), " max_steering_angle = %.3f rad (%.1f deg)",
158+
max_steering_angle_, max_steering_angle_ * 180.0 / M_PI);
159+
RCLCPP_INFO(nh_->get_logger(), " calculated min_turning_radius = %.3f m",
160+
wheelbase_ / tan(max_steering_angle_));
156161
RCLCPP_INFO(nh_->get_logger(), "===================================");
157162

158163
state_msg_.header.frame_id = "base_link";
@@ -291,7 +296,6 @@ geometry_msgs::msg::TwistStamped CalculateDriveCmd(float speed, float steering_a
291296

292297
void VescDriver::joystickCallback(const sensor_msgs::msg::Joy::SharedPtr msg) {
293298
static const bool kDebug = false;
294-
static const float kMaxTurnRate = 0.25;
295299
static const float kAxesEps = 0.2;
296300
static const size_t kManualDriveButton = 4;
297301
static const size_t kAutonomousDriveButton = 5;
@@ -382,7 +386,7 @@ void VescDriver::joystickCallback(const sensor_msgs::msg::Joy::SharedPtr msg) {
382386
const bool turbo_mode = (msg->axes[2] >= 0.9);
383387
const float max_speed = (turbo_mode ? turbo_speed_ : normal_speed_);
384388
float speed = drive_joystick * max_speed;
385-
float steering_angle = steer_joystick * kMaxTurnRate;
389+
float steering_angle = steer_joystick * max_steering_angle_;
386390
mux_drive_speed_ = speed;
387391
mux_steering_angle_ = steering_angle;
388392
if (kDebug) printf("Mode: %s, Speed: %7.2f, Steering: %.1f\u00b0\n",

0 commit comments

Comments
 (0)