Skip to content

Commit 184b497

Browse files
committed
support gear change on real vehicle and mpc controller / adjust pedal active range / unify mpc interface
1 parent 45bef18 commit 184b497

7 files changed

Lines changed: 354 additions & 293 deletions

File tree

GEMstack/knowledge/defaults/current.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ control:
3030
mpc:
3131
dt: 0.2 # Time step for the MPC controller (seconds)
3232
horizon: 30 # Prediction horizon for the MPC controller (number of time steps)
33+
desired_speed: trajectory # Speed reference source: can be "trajectory", "path", or a constant value
3334

3435
# Shared longitudinal control parameters
3536
longitudinal_control:

GEMstack/knowledge/vehicle/dynamics.py

Lines changed: 165 additions & 160 deletions
Large diffs are not rendered by default.

GEMstack/knowledge/vehicle/gem_e4_dynamics.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ max_accelerator_power: #Watts. Power at max accelerator pedal, by gear
1313
max_accelerator_power_reverse: 10000.0 #Watts. Power (backwards) in reverse gear
1414

1515
acceleration_model : kris_v1
16-
accelerator_active_range : [0.2, 1.0] #range of accelerator pedal where output acceleration is not flat
16+
accelerator_active_range : [0.32, 1.0] #range of accelerator pedal where output acceleration is not flat
1717
brake_active_range : [0,1] #range of brake pedal where output deceleration is not flat
1818

1919
internal_dry_deceleration: 0.2 #m/s^2: deceleration due to internal dry friction (non-speed dependent)

GEMstack/onboard/interface/gem_hardware.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from tf.transformations import euler_from_quaternion, quaternion_from_euler
2121

2222
# GEM PACMod Headers
23-
from pacmod_msgs.msg import PositionWithSpeed, PacmodCmd, SystemRptFloat, VehicleSpeedRpt, GlobalRpt
23+
from pacmod_msgs.msg import PositionWithSpeed, PacmodCmd, SystemRptFloat, VehicleSpeedRpt, GlobalRpt, SystemRptInt
2424

2525
# OpenCV and cv2 bridge
2626
import cv2
@@ -49,6 +49,7 @@ def __init__(self):
4949
self.speed_sub = rospy.Subscriber("/pacmod/parsed_tx/vehicle_speed_rpt", VehicleSpeedRpt, self.speed_callback)
5050
self.steer_sub = rospy.Subscriber("/pacmod/parsed_tx/steer_rpt", SystemRptFloat, self.steer_callback)
5151
self.global_sub = rospy.Subscriber("/pacmod/parsed_tx/global_rpt", GlobalRpt, self.global_callback)
52+
self.gear_sub = rospy.Subscriber("/pacmod/parsed_tx/shift_rpt", SystemRptInt, self.geer_callback)
5253
self.gnss_sub = None
5354
self.imu_sub = None
5455
self.front_radar_sub = None
@@ -125,6 +126,18 @@ def speed_callback(self,msg : VehicleSpeedRpt):
125126

126127
def steer_callback(self, msg):
127128
self.last_reading.steering_wheel_angle = msg.output
129+
130+
def geer_callback(self, msg):
131+
# map pacmod gear to gear in vehicle state
132+
if msg.output == 2:
133+
# Neutral
134+
self.last_reading.gear = 0
135+
elif msg.output == 1:
136+
# Reverse
137+
self.last_reading.gear = -1
138+
else:
139+
#Forward
140+
self.last_reading.gear = 1
128141

129142
def global_callback(self, msg):
130143
self.faults = []
@@ -317,7 +330,14 @@ def send_command(self, command : GEMVehicleCommand):
317330
self.accel_cmd.clear = False
318331
self.accel_cmd.ignore = False
319332

320-
self.gear_cmd.ui16_cmd = PacmodCmd.SHIFT_FORWARD
333+
#switch gear
334+
if command.gear == -1:
335+
self.gear_cmd.ui16_cmd = PacmodCmd.SHIFT_REVERSE
336+
elif command.gear == 1:
337+
self.gear_cmd.ui16_cmd = PacmodCmd.SHIFT_FORWARD
338+
else:
339+
self.gear_cmd.ui16_cmd = PacmodCmd.SHIFT_NEUTRAL
340+
321341
self.gear_cmd.enable = True
322342
self.gear_pub.publish(self.gear_cmd)
323343
self.accel_pub.publish(self.accel_cmd)
Lines changed: 97 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,98 @@
1-
from dataclasses import replace
2-
import math
3-
from typing import List
4-
from ...utils import settings
5-
from ...mathutils import transforms
6-
from ...state.vehicle import VehicleState,VehicleGearEnum
7-
from ...state.physical_object import ObjectFrameEnum,ObjectPose,convert_xyhead
8-
from ...knowledge.vehicle.geometry import front2steer,steer2front
9-
from ...mathutils.signal import OnlineLowPassFilter
10-
from ..interface.gem import GEMInterface
11-
from ..component import Component
12-
from ..interface.gem import GNSSReading
13-
14-
class GNSSStateEstimator(Component):
15-
"""Just looks at the GNSS reading to estimate the vehicle state"""
16-
def __init__(self, vehicle_interface : GEMInterface):
17-
self.vehicle_interface = vehicle_interface
18-
if 'gnss' not in vehicle_interface.sensors():
19-
raise RuntimeError("GNSS sensor not available")
20-
vehicle_interface.subscribe_sensor('gnss',self.gnss_callback,GNSSReading)
21-
self.gnss_pose = None
22-
self.location = settings.get('vehicle.calibration.gnss_location')[:2]
23-
self.yaw_offset = settings.get('vehicle.calibration.gnss_yaw')
24-
self.speed_filter = OnlineLowPassFilter(1.2, 30, 4)
25-
self.status = None
26-
27-
# Get GNSS information
28-
def gnss_callback(self, reading : GNSSReading):
29-
self.gnss_pose = reading.pose
30-
self.gnss_speed = reading.speed
31-
self.status = reading.status
32-
33-
def rate(self):
34-
return 10.0
35-
36-
def state_outputs(self) -> List[str]:
37-
return ['vehicle']
38-
39-
def healthy(self):
40-
return self.gnss_pose is not None
41-
42-
def update(self) -> VehicleState:
43-
if self.gnss_pose is None:
44-
return
45-
#TODO: figure out what this status means
46-
#print("INS status",self.status)
47-
48-
# vehicle gnss heading (yaw) in radians
49-
# vehicle x, y position in fixed local frame, in meters
50-
# reference point is located at the center of GNSS antennas
51-
localxy = transforms.rotate2d(self.location,-self.yaw_offset)
52-
gnss_xyhead_inv = (-localxy[0],-localxy[1],-self.yaw_offset)
53-
center_xyhead = self.gnss_pose.apply_xyhead(gnss_xyhead_inv)
54-
vehicle_pose_global = replace(self.gnss_pose,
55-
t=self.vehicle_interface.time(),
56-
x=center_xyhead[0],
57-
y=center_xyhead[1],
58-
yaw=center_xyhead[2])
59-
60-
readings = self.vehicle_interface.get_reading()
61-
raw = readings.to_state(vehicle_pose_global)
62-
63-
#filtering speed
64-
raw.v = self.gnss_speed
65-
#filt_vel = self.speed_filter(raw.v)
66-
#raw.v = filt_vel
67-
return raw
68-
69-
70-
71-
class OmniscientStateEstimator(Component):
72-
def __init__(self, vehicle_interface : GEMInterface):
73-
self.vehicle_interface = vehicle_interface
74-
if 'gnss' not in vehicle_interface.sensors():
75-
raise RuntimeError("GNSS sensor not available")
76-
vehicle_interface.subscribe_sensor('gnss',self.fake_gnss_callback)
77-
self.vehicle_state = None
78-
79-
# Get GNSS information
80-
def fake_gnss_callback(self, vehicle_state):
81-
self.vehicle_state = vehicle_state
82-
83-
def rate(self):
84-
return 50.0
85-
86-
def state_outputs(self) -> List[str]:
87-
return ['vehicle']
88-
89-
def healthy(self):
90-
return self.vehicle_state is not None
91-
92-
def update(self) -> VehicleState:
1+
from dataclasses import replace
2+
import math
3+
from typing import List
4+
from ...utils import settings
5+
from ...mathutils import transforms
6+
from ...state.vehicle import VehicleState,VehicleGearEnum
7+
from ...state.physical_object import ObjectFrameEnum,ObjectPose,convert_xyhead
8+
from ...knowledge.vehicle.geometry import front2steer,steer2front
9+
from ...mathutils.signal import OnlineLowPassFilter
10+
from ..interface.gem import GEMInterface
11+
from ..component import Component
12+
from ..interface.gem import GNSSReading
13+
14+
class GNSSStateEstimator(Component):
15+
"""Just looks at the GNSS reading to estimate the vehicle state"""
16+
def __init__(self, vehicle_interface : GEMInterface):
17+
self.vehicle_interface = vehicle_interface
18+
if 'gnss' not in vehicle_interface.sensors():
19+
raise RuntimeError("GNSS sensor not available")
20+
vehicle_interface.subscribe_sensor('gnss',self.gnss_callback,GNSSReading)
21+
self.gnss_pose = None
22+
self.location = settings.get('vehicle.calibration.gnss_location')[:2]
23+
self.yaw_offset = settings.get('vehicle.calibration.gnss_yaw')
24+
self.speed_filter = OnlineLowPassFilter(1.2, 30, 4)
25+
self.status = None
26+
27+
# Get GNSS information
28+
def gnss_callback(self, reading : GNSSReading):
29+
self.gnss_pose = reading.pose
30+
self.gnss_speed = reading.speed
31+
self.status = reading.status
32+
33+
def rate(self):
34+
return 10.0
35+
36+
def state_outputs(self) -> List[str]:
37+
return ['vehicle']
38+
39+
def healthy(self):
40+
return self.gnss_pose is not None
41+
42+
def update(self) -> VehicleState:
43+
if self.gnss_pose is None:
44+
return
45+
#TODO: figure out what this status means
46+
#print("INS status",self.status)
47+
48+
# vehicle gnss heading (yaw) in radians
49+
# vehicle x, y position in fixed local frame, in meters
50+
# reference point is located at the center of GNSS antennas
51+
localxy = transforms.rotate2d(self.location,-self.yaw_offset)
52+
gnss_xyhead_inv = (-localxy[0],-localxy[1],-self.yaw_offset)
53+
center_xyhead = self.gnss_pose.apply_xyhead(gnss_xyhead_inv)
54+
vehicle_pose_global = replace(self.gnss_pose,
55+
t=self.vehicle_interface.time(),
56+
x=center_xyhead[0],
57+
y=center_xyhead[1],
58+
yaw=center_xyhead[2])
59+
60+
readings = self.vehicle_interface.get_reading()
61+
raw = readings.to_state(vehicle_pose_global)
62+
63+
#filtering speed
64+
raw.v = self.gnss_speed
65+
66+
# Assume no backward slide, use gear to decide velocity sign
67+
if raw.gear == -1:
68+
raw.v *= -1
69+
70+
#filt_vel = self.speed_filter(raw.v)
71+
#raw.v = filt_vel
72+
return raw
73+
74+
75+
76+
class OmniscientStateEstimator(Component):
77+
def __init__(self, vehicle_interface : GEMInterface):
78+
self.vehicle_interface = vehicle_interface
79+
if 'gnss' not in vehicle_interface.sensors():
80+
raise RuntimeError("GNSS sensor not available")
81+
vehicle_interface.subscribe_sensor('gnss',self.fake_gnss_callback)
82+
self.vehicle_state = None
83+
84+
# Get GNSS information
85+
def fake_gnss_callback(self, vehicle_state):
86+
self.vehicle_state = vehicle_state
87+
88+
def rate(self):
89+
return 50.0
90+
91+
def state_outputs(self) -> List[str]:
92+
return ['vehicle']
93+
94+
def healthy(self):
95+
return self.vehicle_state is not None
96+
97+
def update(self) -> VehicleState:
9398
return self.vehicle_state

0 commit comments

Comments
 (0)