Skip to content

Commit 7ac617f

Browse files
committed
Merge branch 'master' of github.com:UBC-Thunderbots/Software into fix_wifi_driver_task_setup_pi_ansible
2 parents 903d374 + 0f64a4e commit 7ac617f

9 files changed

Lines changed: 145 additions & 99 deletions

File tree

.github/workflows/release.yml

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -43,30 +43,53 @@ jobs:
4343
- name: Check for recent commits
4444
id: check
4545
run: |
46+
git fetch --tags
47+
48+
LATEST_TAG=$(git describe --tags --match "v[0-9]*" --abbrev=0 2>/dev/null || echo "v0.0.0")
49+
echo "Latest tag: $LATEST_TAG"
50+
51+
# Prevent double release on the same day (unless manual dispatch)
52+
if [ "${{ github.event_name }}" != "workflow_dispatch" ] && [ "$LATEST_TAG" != "v0.0.0" ]; then
53+
TAG_DATE=$(git log -1 --format=%as "$LATEST_TAG")
54+
TODAY=$(date +%Y-%m-%d)
55+
echo "Tag Date: $TAG_DATE, Today: $TODAY"
56+
57+
if [ "$TAG_DATE" = "$TODAY" ]; then
58+
echo "Already released $LATEST_TAG today. Skipping."
59+
echo "count=0" >> $GITHUB_OUTPUT
60+
exit 0
61+
fi
62+
fi
63+
4664
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
4765
echo "count=1" >> $GITHUB_OUTPUT
4866
else
49-
COUNT=$(git log --since="1 week ago" --oneline | wc -l)
67+
if [ "$LATEST_TAG" = "v0.0.0" ]; then
68+
COUNT=$(git log --oneline | wc -l)
69+
else
70+
COUNT=$(git log "$LATEST_TAG"..HEAD --oneline | wc -l)
71+
fi
5072
echo "count=$COUNT" >> $GITHUB_OUTPUT
5173
fi
5274
5375
- name: Determine Version Bump
5476
if: steps.check.outputs.count > 0
5577
id: bump_logic
5678
run: |
57-
if [ "${{ github.event.inputs.release_tag }}" = "" ]; then
79+
LATEST_TAG=$(git describe --tags --match "v[0-9]*" --abbrev=0 2>/dev/null || echo "v1.0.0")
80+
BASE_VERSION=${LATEST_TAG#v}
81+
82+
IFS='.' read -r major minor patch <<< "$BASE_VERSION"
83+
84+
if [ "${{ github.event.inputs.release_tag }}" != "" ]; then
85+
NEW_TAG="${{ github.event.inputs.release_tag }}"
86+
else
5887
BUMP="patch"
5988
if [ "$(date +%d)" = "01" ]; then BUMP="minor"; fi
6089
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
6190
BUMP="${{ github.event.inputs.version_type }}"
6291
fi
6392
64-
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v1.0.0")
65-
# Strip the 'v' prefix
66-
BASE_VERSION=${LATEST_TAG#v}
67-
68-
IFS='.' read -r major minor patch <<< "$BASE_VERSION"
69-
7093
if [ "$BUMP" = "major" ]; then
7194
major=$((major + 1)); minor=0; patch=0
7295
elif [ "$BUMP" = "minor" ]; then
@@ -76,8 +99,6 @@ jobs:
7699
fi
77100
78101
NEW_TAG="v$major.$minor.$patch"
79-
else
80-
NEW_TAG="${{ github.event.inputs.release_tag }}"
81102
fi
82103
echo "tag=$NEW_TAG" >> $GITHUB_OUTPUT
83104
echo "Using version: $NEW_TAG"
@@ -90,16 +111,6 @@ jobs:
90111
version: ${{ steps.bump_logic.outputs.tag }}
91112
tag: ${{ steps.bump_logic.outputs.tag }}
92113

93-
- name: Create GitHub Release
94-
if: steps.check.outputs.count > 0
95-
env:
96-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
97-
run: |
98-
gh release create "${{ steps.bump_logic.outputs.tag }}" \
99-
--title "${{ steps.bump_logic.outputs.tag }}" \
100-
--generate-notes \
101-
--draft
102-
103114
upload_assets:
104115
needs: prepare_release
105116
if: needs.prepare_release.outputs.should_release == 'true'
@@ -128,10 +139,13 @@ jobs:
128139
run: |
129140
cd src
130141
TAG="${{ needs.prepare_release.outputs.tag }}"
142+
131143
bazel build --show_timestamps --copt=-O3 --verbose_failures \
132144
-- //software:unix_full_system_tar_gen
133-
mv bazel-bin/software/unix_full_system_tar_gen.tar.gz "${{ runner.temp }}/unix_full_system_${{ needs.prepare_release.outputs.tag }}_${{ matrix.platform }}.tar.gz"
134-
gh release upload "$TAG" "${{ runner.temp }}/unix_full_system_${{ needs.prepare_release.outputs.tag }}_${{ matrix.platform }}.tar.gz"
145+
146+
ARTIFACT_NAME="unix_full_system_${TAG}_${{ matrix.platform }}.tar.gz"
147+
mv bazel-bin/software/unix_full_system_tar_gen.tar.gz "${{ runner.temp }}/$ARTIFACT_NAME"
148+
gh release upload "$TAG" "${{ runner.temp }}/$ARTIFACT_NAME"
135149
136150
publish_release:
137151
needs: [prepare_release, upload_assets]

src/cli/cli_params.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,5 @@ class Platform(str, Enum):
8989
EnableThunderscopeOption = Annotated[bool, Option("-t", "--enable_thunderscope")]
9090
EnableVisualizerOption = Annotated[bool, Option("-v", "--enable_visualizer")]
9191
StopAIOnStartOption = Annotated[bool, Option("-t", "--stop_ai_on_start")]
92+
93+
JobsOption = Annotated[str, Option("-j", "--jobs")]

src/software/ai/hl/stp/tactic/move_primitive.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,6 @@ MovePrimitive::generatePrimitiveProtoMessage(
123123

124124
if (!traj_path.has_value())
125125
{
126-
LOG(WARNING) << "Could not find trajectory path for robot " << robot.id()
127-
<< " to move to " << destination;
128126
return std::make_pair(std::nullopt, std::move(createStopPrimitiveProto()));
129127
}
130128

src/software/ai/navigator/trajectory/collision_evaluator.cpp

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,20 @@ CollisionEvaluator::CollisionEvaluator(const std::vector<ObstaclePtr> &obstacles
88
TrajectoryPathWithCost CollisionEvaluator::evaluate(
99
const TrajectoryPath &trajectory,
1010
const std::optional<TrajectoryPathWithCost> &sub_traj_with_cost,
11-
const std::optional<double> sub_traj_duration_s)
11+
const std::optional<double> sub_traj_duration_s, const double max_cost)
1212
{
1313
TrajectoryPathWithCost traj_with_cost(trajectory);
1414

15+
const double traj_time = trajectory.getTotalTime();
1516
const double search_end_time_s =
1617
std::min(trajectory.getTotalTime(), MAX_FUTURE_COLLISION_CHECK_SEC);
18+
const Point destination = traj_with_cost.traj_path.getDestination();
1719

18-
/**
19-
* Find the start duration before the trajectory leaves all obstacles
20-
*/
20+
// Initialize total cost as trajectory time
21+
double total_cost = traj_time;
22+
23+
24+
// Find the start duration before the trajectory leaves all obstacles
2125
double first_non_collision_time;
2226
// Avoid finding the first non-collision time if the cache sub-trajectory
2327
// has a collision time.
@@ -33,18 +37,35 @@ TrajectoryPathWithCost CollisionEvaluator::evaluate(
3337
}
3438
traj_with_cost.collision_duration_front_s = first_non_collision_time;
3539

36-
/**
37-
* Find the duration we're within an obstacle before search_end_time_s
38-
*/
40+
// Add first front collision to total cost
41+
total_cost += FRONT_COLLISION_COST_CONST * first_non_collision_time;
42+
43+
// Return early if current cost already higher than max cost
44+
if (total_cost >= max_cost)
45+
{
46+
traj_with_cost.cost = total_cost;
47+
return traj_with_cost;
48+
}
49+
50+
// Find the duration we're within an obstacle before search_end_time_s
3951
double last_non_collision_time =
4052
getLastNonCollisionTime(trajectory, search_end_time_s);
4153
traj_with_cost.collision_duration_back_s =
4254
search_end_time_s - last_non_collision_time;
4355

44-
/**
45-
* Get the first collision time, excluding the time at the start and end of path
46-
* that we may be in an obstacle for.
47-
*/
56+
// Add back collision to total cost
57+
total_cost += BACK_COLLISION_COST_CONST * traj_with_cost.collision_duration_back_s;
58+
59+
// Return early if current cost already higher than max cost
60+
if (total_cost >= max_cost)
61+
{
62+
traj_with_cost.cost = total_cost;
63+
return traj_with_cost;
64+
}
65+
66+
67+
// Get the first collision time, excluding the time at the start and end of path
68+
// that we may be in an obstacle for.
4869
if (sub_traj_with_cost.has_value() &&
4970
sub_traj_with_cost->first_collision_time_s < sub_traj_duration_s)
5071
{
@@ -60,6 +81,22 @@ TrajectoryPathWithCost CollisionEvaluator::evaluate(
6081
traj_with_cost.colliding_obstacle = collision.second;
6182
}
6283

84+
// Add penalty if mid-trajectory collision exists
85+
if (traj_with_cost.colliding_obstacle != nullptr)
86+
{
87+
total_cost += MID_TRAJ_COST_CONST;
88+
}
89+
90+
// Add distance from first collision to destination
91+
Point first_collision_position =
92+
trajectory.getPosition(traj_with_cost.first_collision_time_s);
93+
total_cost += (first_collision_position - destination).length();
94+
95+
// Add early collision penalty
96+
total_cost += std::max(
97+
0.0, MAX_FUTURE_COLLISION_CHECK_SEC - traj_with_cost.first_collision_time_s);
98+
99+
traj_with_cost.cost = total_cost;
63100
return traj_with_cost;
64101
}
65102

src/software/ai/navigator/trajectory/collision_evaluator.h

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
**/
1313
class CollisionEvaluator
1414
{
15+
static constexpr double COLLISION_CHECK_STEP_INTERVAL_SEC = 0.1;
16+
static constexpr double FORWARD_COLLISION_CHECK_STEP_INTERVAL_SEC = 0.05;
17+
static constexpr double MAX_FUTURE_COLLISION_CHECK_SEC = 2.0;
18+
static constexpr double FRONT_COLLISION_COST_CONST = 3.0;
19+
static constexpr double BACK_COLLISION_COST_CONST = 1.0;
20+
static constexpr double MID_TRAJ_COST_CONST = 6.0;
21+
1522
public:
1623
/**
1724
* Constructor
@@ -27,22 +34,20 @@ class CollisionEvaluator
2734
* If a prefix trajectory is provided, collision information that occurs entirely
2835
* before the given prefix duration may be reused to avoid redundant collision
2936
* checking. Collision checks beyond the prefix duration are computed normally.
30-
* @param trajectory
31-
* The trajectory to evaluate for collisions and cost.
32-
* @param sub_traj_with_cost
33-
* An optional previously evaluated prefix trajectory whose collision
34-
* information may be reused if the prefix fully covers the relevant
35-
* collision interval.
36-
* @param sub_traj_duration_s
37-
* The duration (in seconds) of the prefix trajectory within trajectory.
38-
* @return
39-
* A TrajectoryPathWithCost< containing the trajectory along with
37+
* @param trajectory The trajectory to evaluate for collisions and cost.
38+
* @param sub_traj_with_cost An optional previously evaluated prefix trajectory whose
39+
* collision information may be reused if the prefix fully covers the relevant
40+
* collision interval.
41+
* @param sub_traj_duration_s The duration (in seconds) of the prefix trajectory
42+
* within trajectory.
43+
* @param max_cost Current maximum cost of best path
44+
* @return A TrajectoryPathWithCost< containing the trajectory along with
4045
* computed collision timing information and total cost.
4146
*/
4247
TrajectoryPathWithCost evaluate(
4348
const TrajectoryPath &trajectory,
4449
const std::optional<TrajectoryPathWithCost> &sub_traj_with_cost,
45-
std::optional<double> sub_traj_duration_s);
50+
std::optional<double> sub_traj_duration_s, double max_cost);
4651

4752
private:
4853
std::vector<ObstaclePtr> obstacles;
@@ -89,7 +94,3 @@ class CollisionEvaluator
8994
double getLastNonCollisionTime(const TrajectoryPath &traj_path,
9095
const double search_end_time_s) const;
9196
};
92-
93-
const double COLLISION_CHECK_STEP_INTERVAL_SEC = 0.1;
94-
const double FORWARD_COLLISION_CHECK_STEP_INTERVAL_SEC = 0.05;
95-
const double MAX_FUTURE_COLLISION_CHECK_SEC = 2.0;

src/software/ai/navigator/trajectory/trajectory_path_with_cost.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ class TrajectoryPathWithCost
3232
// 0 if the trajectory does not end within an obstacle.
3333
double collision_duration_back_s = 0.0;
3434

35-
// The time and obstacle at which the trajectory first collides with an obstacle
36-
// first_collision_time_s is set to infinity and colliding_obstacle is nullptr
37-
// if the trajectory does not collide.
35+
// The time and obstacle at which the trajectory first collides with an
36+
// obstacle first_collision_time_s is set to infinity and colliding_obstacle is
37+
// nullopt if the trajectory does not collide.
3838
// Note that collisions that the trajectory starts or ends in are not considered as
3939
// those are unavoidable + they are handled by collision_duration_front_s and
4040
// collision_duration_back_s.

src/software/ai/navigator/trajectory/trajectory_planner.cpp

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "software/geom/algorithms/contains.h"
55
#include "software/geom/algorithms/distance.h"
66

7+
78
TrajectoryPlanner::TrajectoryPlanner()
89
: relative_sub_destinations(getRelativeSubDestinations())
910
{
@@ -109,8 +110,9 @@ std::optional<TrajectoryPath> TrajectoryPlanner::findTrajectory(
109110
break;
110111
}
111112

112-
TrajectoryPathWithCost full_traj_with_cost = getTrajectoryWithCost(
113-
traj_path_to_dest, obstacles, sub_trajectory, connection_time);
113+
TrajectoryPathWithCost full_traj_with_cost =
114+
getTrajectoryWithCost(traj_path_to_dest, obstacles, sub_trajectory,
115+
connection_time, best_traj_with_cost.cost);
114116
full_traj_with_cost.cost += cost_offset;
115117
if (full_traj_with_cost.cost < best_traj_with_cost.cost)
116118
{
@@ -133,58 +135,47 @@ std::optional<TrajectoryPath> TrajectoryPlanner::findTrajectory(
133135
}
134136
}
135137

136-
return best_traj_with_cost.traj_path;
138+
// In move primitive, a stop primitive is created when trajectory path is null.
139+
// Check if there is an unavoidable collision, and return a null opt if such
140+
// collision exist on best path
141+
double collision_velocity =
142+
best_traj_with_cost.traj_path
143+
.getVelocity(best_traj_with_cost.first_collision_time_s)
144+
.length();
145+
if (best_traj_with_cost.collides() &&
146+
best_traj_with_cost.first_collision_time_s <
147+
UNAVOIDABLE_COLLISION_TIME_THRESHOLD_S &&
148+
collision_velocity > UNAVOIDABLE_COLLISION_VELOCITY_THRESHOLD_M_S)
149+
{
150+
return std::nullopt;
151+
}
152+
else
153+
{
154+
return best_traj_with_cost.traj_path;
155+
}
137156
}
138157

139158
TrajectoryPathWithCost TrajectoryPlanner::getDirectTrajectoryWithCost(
140159
const Point &start, const Point &destination, const Vector &initial_velocity,
141160
const KinematicConstraints &constraints, const std::vector<ObstaclePtr> &obstacles)
142161
{
162+
// Calculate full new cost regardless by passing in maximum max cost
143163
return getTrajectoryWithCost(
144164
TrajectoryPath(std::make_shared<BangBangTrajectory2D>(
145165
start, destination, initial_velocity, constraints),
146166
BangBangTrajectory2D::generator),
147-
obstacles, std::nullopt, std::nullopt);
167+
obstacles, std::nullopt, std::nullopt, std::numeric_limits<double>::max());
148168
}
149169

150170
TrajectoryPathWithCost TrajectoryPlanner::getTrajectoryWithCost(
151171
const TrajectoryPath &trajectory, const std::vector<ObstaclePtr> &obstacles,
152172
const std::optional<TrajectoryPathWithCost> &sub_traj_with_cost,
153-
const std::optional<double> sub_traj_duration_s)
173+
const std::optional<double> sub_traj_duration_s, double max_cost)
154174
{
155175
CollisionEvaluator evaluator(obstacles);
156-
TrajectoryPathWithCost traj_with_cost(
157-
evaluator.evaluate(trajectory, sub_traj_with_cost, sub_traj_duration_s));
158-
traj_with_cost.cost = calculateCost(traj_with_cost);
176+
TrajectoryPathWithCost traj_with_cost(evaluator.evaluate(
177+
trajectory, sub_traj_with_cost, sub_traj_duration_s, max_cost));
159178

160-
return traj_with_cost;
161-
}
162179

163-
double TrajectoryPlanner::calculateCost(
164-
const TrajectoryPathWithCost &traj_with_cost) const
165-
{
166-
double total_cost = traj_with_cost.traj_path.getTotalTime();
167-
168-
// Add a large cost if the trajectory collides with an obstacle
169-
// Note that this ignores collisions that may be in at the
170-
// start of the trajectory as those are unavoidable by all trajectories.
171-
if (traj_with_cost.colliding_obstacle != nullptr)
172-
{
173-
total_cost += 6.0;
174-
}
175-
176-
// The closer the collision is to the destination, the lower its cost will be
177-
Point first_collision_position =
178-
traj_with_cost.traj_path.getPosition(traj_with_cost.first_collision_time_s);
179-
Point destination = traj_with_cost.traj_path.getDestination();
180-
total_cost += (first_collision_position - destination).length();
181-
182-
total_cost += std::max(
183-
0.0, (MAX_FUTURE_COLLISION_CHECK_SEC - traj_with_cost.first_collision_time_s));
184-
185-
total_cost += 3 * traj_with_cost.collision_duration_front_s;
186-
187-
total_cost += 1 * traj_with_cost.collision_duration_back_s;
188-
189-
return total_cost;
180+
return traj_with_cost;
190181
}

0 commit comments

Comments
 (0)