Skip to content

Commit 25335c3

Browse files
author
Luca Toniolo
committed
Replace C++ Ruckig with pure C cruckig to fix RTAI builds
The C++ Ruckig library cannot compile in RTAI kernel space (no C++ in kernel modules). Replace it with cruckig, a pure C99 port that produces identical trajectories (validated to 5.9e-12 position error). - Vendor cruckig source (MIT license) from github.com/mika4128/cruckig - Rewrite ruckig_wrapper.c to use cruckig API (same ruckig_wrapper.h interface, zero changes needed in tp.c, tc.c, sp_scurve.c) - Add cruckig_compat.h for RTAPI kernel/userspace portability (rtapi_slab.h for malloc/free, rtapi_math.h for math functions) - Update Makefile and motion-logger Submakefile for C-only build - Remove C++ Ruckig source tree and ruckig_wrapper.cc Fixes: #3875
1 parent 4025bf9 commit 25335c3

File tree

121 files changed

+7437
-52612
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

121 files changed

+7437
-52612
lines changed

debian/copyright

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,15 @@ Copyright: 2004-2025 The LinuxCNC Developers and notably
151151
Please inspect the logs of the git repository to learn about the >230 (11/2021) committers.
152152
License: GPL-2+
153153

154-
Files: src/emc/tp/ruckig/*
154+
Files: src/emc/tp/cruckig/*
155155
Copyright:
156+
2025 Yang Yang <mika-net@outlook.com>
156157
2021-2024 Lars Berscheid
157158
License: Expat
158159

159160
Files: src/emc/tp/ruckig_wrapper.*
160161
Copyright:
161-
2024 杨阳 <mika-net@outlook.com>
162+
2024-2026 杨阳 <mika-net@outlook.com>
162163
License: GPL-2
163164

164165
Files: docs/*

src/Makefile

Lines changed: 49 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -279,37 +279,25 @@ $(sort $(CUSEROBJS)) : objects/%.o: %.c
279279
-MP -MD -MF "${@:.o=.d}" -MT "$@" \
280280
$< -o $@
281281

282-
# Ruckig files need C++20
283-
RUCKIG_CXX_FILES := $(filter emc/tp/ruckig%,$(CXXUSERSRCS))
284-
NON_RUCKIG_CXX_FILES := $(filter-out emc/tp/ruckig%,$(CXXUSERSRCS))
285-
286-
$(sort $(patsubst %.cpp,objects/%.o,$(filter %.cpp,$(RUCKIG_CXX_FILES)))) : objects/%.o: %.cpp
282+
# Cruckig (pure C) source files — compiled as C, no C++ needed
283+
# Headers are local ("" includes relative to cruckig/), no -I needed
284+
objects/emc/tp/cruckig/%.o : emc/tp/cruckig/%.c
287285
$(ECHO) Compiling $<
288286
@mkdir -p $(dir $@)
289287
@rm -f $@
290-
$(Q)$(CXX) -c $(CXXFLAGS) $(EXTRAFLAGS) -std=c++20 \
291-
-I$(BASEPWD)/emc/tp/ruckig/include \
292-
-MP -MD -MF "${@:.o=.d}" -MT "$@" \
293-
$< -o $@
294-
295-
$(sort $(patsubst %.cc,objects/%.o,$(filter %.cc,$(RUCKIG_CXX_FILES)))) : objects/%.o: %.cc
296-
$(ECHO) Compiling $<
297-
@mkdir -p $(dir $@)
298-
@rm -f $@
299-
$(Q)$(CXX) -c $(CXXFLAGS) $(EXTRAFLAGS) -std=c++20 \
300-
-I$(BASEPWD)/emc/tp/ruckig/include \
288+
$(Q)$(CC) -c $(CFLAGS) $(EXTRAFLAGS) \
301289
-MP -MD -MF "${@:.o=.d}" -MT "$@" \
302290
$< -o $@
303291

304-
$(sort $(patsubst %.cpp,objects/%.o,$(filter %.cpp,$(NON_RUCKIG_CXX_FILES)))) : objects/%.o: %.cpp
292+
$(sort $(patsubst %.cpp,objects/%.o,$(filter %.cpp,$(CXXUSERSRCS)))) : objects/%.o: %.cpp
305293
$(ECHO) Compiling $<
306294
@mkdir -p $(dir $@)
307295
@rm -f $@
308296
$(Q)$(CXX) -c $(CXXFLAGS) $(EXTRAFLAGS) \
309297
-MP -MD -MF "${@:.o=.d}" -MT "$@" \
310298
$< -o $@
311299

312-
$(sort $(patsubst %.cc,objects/%.o,$(filter %.cc,$(NON_RUCKIG_CXX_FILES)))) : objects/%.o: %.cc
300+
$(sort $(patsubst %.cc,objects/%.o,$(filter %.cc,$(CXXUSERSRCS)))) : objects/%.o: %.cc
313301
$(ECHO) Compiling $<
314302
@mkdir -p $(dir $@)
315303
@rm -f $@
@@ -1198,17 +1186,25 @@ motmod-objs += emc/motion/stashf.o
11981186
motmod-objs += emc/motion/dbuf.o
11991187
motmod-objs += emc/tp/sp_scurve.o
12001188
motmod-objs += emc/tp/ruckig_wrapper.o
1201-
motmod-objs += emc/tp/ruckig/src/ruckig/brake.o
1202-
motmod-objs += emc/tp/ruckig/src/ruckig/position_first_step1.o
1203-
motmod-objs += emc/tp/ruckig/src/ruckig/position_first_step2.o
1204-
motmod-objs += emc/tp/ruckig/src/ruckig/position_second_step1.o
1205-
motmod-objs += emc/tp/ruckig/src/ruckig/position_second_step2.o
1206-
motmod-objs += emc/tp/ruckig/src/ruckig/position_third_step1.o
1207-
motmod-objs += emc/tp/ruckig/src/ruckig/position_third_step2.o
1208-
motmod-objs += emc/tp/ruckig/src/ruckig/velocity_second_step1.o
1209-
motmod-objs += emc/tp/ruckig/src/ruckig/velocity_second_step2.o
1210-
motmod-objs += emc/tp/ruckig/src/ruckig/velocity_third_step1.o
1211-
motmod-objs += emc/tp/ruckig/src/ruckig/velocity_third_step2.o
1189+
motmod-objs += emc/tp/cruckig/block.o
1190+
motmod-objs += emc/tp/cruckig/brake.o
1191+
motmod-objs += emc/tp/cruckig/calculator.o
1192+
motmod-objs += emc/tp/cruckig/cruckig.o
1193+
motmod-objs += emc/tp/cruckig/input_parameter.o
1194+
motmod-objs += emc/tp/cruckig/output_parameter.o
1195+
motmod-objs += emc/tp/cruckig/profile.o
1196+
motmod-objs += emc/tp/cruckig/roots.o
1197+
motmod-objs += emc/tp/cruckig/trajectory.o
1198+
motmod-objs += emc/tp/cruckig/position_first_step1.o
1199+
motmod-objs += emc/tp/cruckig/position_first_step2.o
1200+
motmod-objs += emc/tp/cruckig/position_second_step1.o
1201+
motmod-objs += emc/tp/cruckig/position_second_step2.o
1202+
motmod-objs += emc/tp/cruckig/position_third_step1.o
1203+
motmod-objs += emc/tp/cruckig/position_third_step2.o
1204+
motmod-objs += emc/tp/cruckig/velocity_second_step1.o
1205+
motmod-objs += emc/tp/cruckig/velocity_second_step2.o
1206+
motmod-objs += emc/tp/cruckig/velocity_third_step1.o
1207+
motmod-objs += emc/tp/cruckig/velocity_third_step2.o
12121208

12131209
obj-m += homemod.o
12141210
homemod-objs := emc/motion/homemod.o
@@ -1225,17 +1221,25 @@ tpmod-objs += emc/nml_intf/emcpose.o
12251221
tpmod-objs += libnml/posemath/_posemath.o
12261222
tpmod-objs += emc/tp/sp_scurve.o
12271223
tpmod-objs += emc/tp/ruckig_wrapper.o
1228-
tpmod-objs += emc/tp/ruckig/src/ruckig/brake.o
1229-
tpmod-objs += emc/tp/ruckig/src/ruckig/position_first_step1.o
1230-
tpmod-objs += emc/tp/ruckig/src/ruckig/position_first_step2.o
1231-
tpmod-objs += emc/tp/ruckig/src/ruckig/position_second_step1.o
1232-
tpmod-objs += emc/tp/ruckig/src/ruckig/position_second_step2.o
1233-
tpmod-objs += emc/tp/ruckig/src/ruckig/position_third_step1.o
1234-
tpmod-objs += emc/tp/ruckig/src/ruckig/position_third_step2.o
1235-
tpmod-objs += emc/tp/ruckig/src/ruckig/velocity_second_step1.o
1236-
tpmod-objs += emc/tp/ruckig/src/ruckig/velocity_second_step2.o
1237-
tpmod-objs += emc/tp/ruckig/src/ruckig/velocity_third_step1.o
1238-
tpmod-objs += emc/tp/ruckig/src/ruckig/velocity_third_step2.o
1224+
tpmod-objs += emc/tp/cruckig/block.o
1225+
tpmod-objs += emc/tp/cruckig/brake.o
1226+
tpmod-objs += emc/tp/cruckig/calculator.o
1227+
tpmod-objs += emc/tp/cruckig/cruckig.o
1228+
tpmod-objs += emc/tp/cruckig/input_parameter.o
1229+
tpmod-objs += emc/tp/cruckig/output_parameter.o
1230+
tpmod-objs += emc/tp/cruckig/profile.o
1231+
tpmod-objs += emc/tp/cruckig/roots.o
1232+
tpmod-objs += emc/tp/cruckig/trajectory.o
1233+
tpmod-objs += emc/tp/cruckig/position_first_step1.o
1234+
tpmod-objs += emc/tp/cruckig/position_first_step2.o
1235+
tpmod-objs += emc/tp/cruckig/position_second_step1.o
1236+
tpmod-objs += emc/tp/cruckig/position_second_step2.o
1237+
tpmod-objs += emc/tp/cruckig/position_third_step1.o
1238+
tpmod-objs += emc/tp/cruckig/position_third_step2.o
1239+
tpmod-objs += emc/tp/cruckig/velocity_second_step1.o
1240+
tpmod-objs += emc/tp/cruckig/velocity_second_step2.o
1241+
tpmod-objs += emc/tp/cruckig/velocity_third_step1.o
1242+
tpmod-objs += emc/tp/cruckig/velocity_third_step2.o
12391243
tpmod-objs += libnml/posemath/sincos.o $(MATHSTUB)
12401244

12411245
TORTOBJS = $(foreach file,$($(patsubst %.o,%,$(1))-objs), objects/rt$(file))
@@ -1257,7 +1261,7 @@ modules: $(patsubst %.o,../rtlib/%.so,$(obj-m))
12571261
RTFLAGS += -fno-strict-aliasing -fwrapv
12581262

12591263
# Rules to make .o (object) files for C files
1260-
$(sort $(filter-out objects/rtemc/tp/ruckig_wrapper.o objects/rtemc/tp/ruckig/src/ruckig/%,$(RTOBJS))) : objects/rt%.o : %.c
1264+
$(sort $(filter-out objects/rtemc/tp/cruckig/%,$(RTOBJS))) : objects/rt%.o : %.c
12611265
$(ECHO) Compiling realtime $<
12621266
@rm -f $@
12631267
@mkdir -p $(dir $@)
@@ -1266,30 +1270,13 @@ $(sort $(filter-out objects/rtemc/tp/ruckig_wrapper.o objects/rtemc/tp/ruckig/sr
12661270
-MP -MD -MF "${@:.o=.d}" -MT "$@" \
12671271
$< -o $@
12681272

1269-
# C++ flags for Ruckig: drop C-only -Wno-declaration-after-statement, and suppress
1270-
# false-positive -Warray-bounds and -Wunused-parameter in Ruckig library
1271-
RUCKIG_CXXFLAGS := $(filter-out -Wno-declaration-after-statement,$(EXTRA_CFLAGS)) \
1272-
-Wno-array-bounds -Wno-unused-parameter
1273-
1274-
# Rules for C++ .cc files in realtime modules
1275-
objects/rtemc/tp/ruckig_wrapper.o : emc/tp/ruckig_wrapper.cc
1273+
# Rules for cruckig C files in realtime modules
1274+
objects/rtemc/tp/cruckig/%.o : emc/tp/cruckig/%.c
12761275
$(ECHO) Compiling realtime $<
12771276
@rm -f $@
12781277
@mkdir -p $(dir $@)
1279-
$(Q)$(CXX) -c $(OPT) $(DEBUG) $(EXTRA_DEBUG) -DRTAPI -std=c++20 \
1280-
-I$(BASEPWD)/emc/tp/ruckig/include \
1281-
$(RUCKIG_CXXFLAGS) \
1282-
-MP -MD -MF "${@:.o=.d}" -MT "$@" \
1283-
$< -o $@
1284-
1285-
# Rules for C++ .cpp files in realtime modules
1286-
objects/rtemc/tp/ruckig/src/ruckig/%.o : emc/tp/ruckig/src/ruckig/%.cpp
1287-
$(ECHO) Compiling realtime $<
1288-
@rm -f $@
1289-
@mkdir -p $(dir $@)
1290-
$(Q)$(CXX) -c $(OPT) $(DEBUG) $(EXTRA_DEBUG) -DRTAPI -std=c++20 \
1291-
-I$(BASEPWD)/emc/tp/ruckig/include \
1292-
$(RUCKIG_CXXFLAGS) \
1278+
$(Q)$(CC) -c $(OPT) $(DEBUG) $(EXTRA_DEBUG) -DRTAPI \
1279+
$(EXTRA_CFLAGS) \
12931280
-MP -MD -MF "${@:.o=.d}" -MT "$@" \
12941281
$< -o $@
12951282
endif

src/emc/motion-logger/Submakefile

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,31 @@ MOTION_LOGGER_SRCS := \
55
emc/motion/axis.c \
66
emc/motion/simple_tp.c \
77
emc/tp/sp_scurve.c \
8-
emc/tp/ruckig_wrapper.cc
8+
emc/tp/ruckig_wrapper.c
99

10-
RUCKIG_SRCS := \
11-
emc/tp/ruckig/src/ruckig/brake.cpp \
12-
emc/tp/ruckig/src/ruckig/position_first_step1.cpp \
13-
emc/tp/ruckig/src/ruckig/position_first_step2.cpp \
14-
emc/tp/ruckig/src/ruckig/position_second_step1.cpp \
15-
emc/tp/ruckig/src/ruckig/position_second_step2.cpp \
16-
emc/tp/ruckig/src/ruckig/position_third_step1.cpp \
17-
emc/tp/ruckig/src/ruckig/position_third_step2.cpp \
18-
emc/tp/ruckig/src/ruckig/velocity_second_step1.cpp \
19-
emc/tp/ruckig/src/ruckig/velocity_second_step2.cpp \
20-
emc/tp/ruckig/src/ruckig/velocity_third_step1.cpp \
21-
emc/tp/ruckig/src/ruckig/velocity_third_step2.cpp
10+
CRUCKIG_SRCS := \
11+
emc/tp/cruckig/block.c \
12+
emc/tp/cruckig/brake.c \
13+
emc/tp/cruckig/calculator.c \
14+
emc/tp/cruckig/cruckig.c \
15+
emc/tp/cruckig/input_parameter.c \
16+
emc/tp/cruckig/output_parameter.c \
17+
emc/tp/cruckig/profile.c \
18+
emc/tp/cruckig/roots.c \
19+
emc/tp/cruckig/trajectory.c \
20+
emc/tp/cruckig/position_first_step1.c \
21+
emc/tp/cruckig/position_first_step2.c \
22+
emc/tp/cruckig/position_second_step1.c \
23+
emc/tp/cruckig/position_second_step2.c \
24+
emc/tp/cruckig/position_third_step1.c \
25+
emc/tp/cruckig/position_third_step2.c \
26+
emc/tp/cruckig/velocity_second_step1.c \
27+
emc/tp/cruckig/velocity_second_step2.c \
28+
emc/tp/cruckig/velocity_third_step1.c \
29+
emc/tp/cruckig/velocity_third_step2.c
2230

23-
USERSRCS += $(MOTION_LOGGER_SRCS) $(RUCKIG_SRCS)
31+
USERSRCS += $(MOTION_LOGGER_SRCS) $(CRUCKIG_SRCS)
2432

25-
../bin/motion-logger: $(call TOOBJS, $(MOTION_LOGGER_SRCS) $(RUCKIG_SRCS)) ../lib/libnml.so.0 ../lib/liblinuxcnchal.so.0
33+
../bin/motion-logger: $(call TOOBJS, $(MOTION_LOGGER_SRCS) $(CRUCKIG_SRCS)) ../lib/libnml.so.0 ../lib/liblinuxcnchal.so.0
2634
$(ECHO) Linking $(notdir $@)
27-
$(Q)$(CXX) $(LDFLAGS) -o $@ $^ -lm
35+
$(Q)$(CC) $(LDFLAGS) -o $@ $^ -lm
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
MIT License
22

3+
cruckig - Pure C99 port of the Ruckig trajectory generation library
4+
Copyright (c) 2025 Yang Yang (mika-net@outlook.com)
5+
6+
Based on Ruckig (https://github.com/pantor/ruckig)
37
Copyright (c) 2021 Lars Berscheid
48

59
Permission is hereby granted, free of charge, to any person obtaining a copy

src/emc/tp/cruckig/block.c

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* cruckig - Pure C99 port of the Ruckig trajectory generation library
3+
* Copyright (c) 2025 Yang Yang <mika-net@outlook.com>
4+
* Copyright (c) 2021 Lars Berscheid (original C++ Ruckig)
5+
*
6+
* License: MIT, see the LICENSE file in this directory.
7+
*/
8+
9+
#include "block.h"
10+
11+
static inline double cruckig_profile_total_duration(const CRuckigProfile *p) {
12+
return p->t_sum[6] + p->brake.duration + p->accel.duration;
13+
}
14+
15+
static void remove_profile(CRuckigProfile *valid_profiles, size_t *valid_profile_counter, size_t index) {
16+
for (size_t i = index; i < *valid_profile_counter - 1; ++i) {
17+
valid_profiles[i] = valid_profiles[i + 1];
18+
}
19+
*valid_profile_counter -= 1;
20+
}
21+
22+
static void interval_from_profiles(CRuckigInterval *iv, const CRuckigProfile *profile_left, const CRuckigProfile *profile_right) {
23+
const double left_duration = cruckig_profile_total_duration(profile_left);
24+
const double right_duration = cruckig_profile_total_duration(profile_right);
25+
if (left_duration < right_duration) {
26+
iv->left = left_duration;
27+
iv->right = right_duration;
28+
iv->profile = *profile_right;
29+
} else {
30+
iv->left = right_duration;
31+
iv->right = left_duration;
32+
iv->profile = *profile_left;
33+
}
34+
iv->valid = true;
35+
}
36+
37+
void cruckig_block_init(CRuckigBlock *block) {
38+
cruckig_profile_init(&block->p_min);
39+
block->t_min = 0.0;
40+
block->a.valid = false;
41+
block->b.valid = false;
42+
}
43+
44+
void cruckig_block_set_min_profile(CRuckigBlock *block, const CRuckigProfile *profile) {
45+
block->p_min = *profile;
46+
block->t_min = cruckig_profile_total_duration(profile);
47+
block->a.valid = false;
48+
block->b.valid = false;
49+
}
50+
51+
bool cruckig_block_calculate(CRuckigBlock *block, CRuckigProfile *valid_profiles,
52+
size_t valid_profile_counter, size_t max_profiles) {
53+
(void)max_profiles;
54+
55+
if (valid_profile_counter == 1) {
56+
cruckig_block_set_min_profile(block, &valid_profiles[0]);
57+
return true;
58+
59+
} else if (valid_profile_counter == 2) {
60+
if (fabs(valid_profiles[0].t_sum[6] - valid_profiles[1].t_sum[6]) < 8 * DBL_EPSILON) {
61+
cruckig_block_set_min_profile(block, &valid_profiles[0]);
62+
return true;
63+
}
64+
65+
/* numerical_robust = true */
66+
{
67+
const size_t idx_min = (valid_profiles[0].t_sum[6] < valid_profiles[1].t_sum[6]) ? 0 : 1;
68+
const size_t idx_else_1 = (idx_min + 1) % 2;
69+
70+
cruckig_block_set_min_profile(block, &valid_profiles[idx_min]);
71+
interval_from_profiles(&block->a, &valid_profiles[idx_min], &valid_profiles[idx_else_1]);
72+
return true;
73+
}
74+
75+
/* Only happens due to numerical issues */
76+
} else if (valid_profile_counter == 4) {
77+
/* Find "identical" profiles */
78+
if (fabs(valid_profiles[0].t_sum[6] - valid_profiles[1].t_sum[6]) < 32 * DBL_EPSILON && valid_profiles[0].direction != valid_profiles[1].direction) {
79+
remove_profile(valid_profiles, &valid_profile_counter, 1);
80+
} else if (fabs(valid_profiles[2].t_sum[6] - valid_profiles[3].t_sum[6]) < 256 * DBL_EPSILON && valid_profiles[2].direction != valid_profiles[3].direction) {
81+
remove_profile(valid_profiles, &valid_profile_counter, 3);
82+
} else if (fabs(valid_profiles[0].t_sum[6] - valid_profiles[3].t_sum[6]) < 256 * DBL_EPSILON && valid_profiles[0].direction != valid_profiles[3].direction) {
83+
remove_profile(valid_profiles, &valid_profile_counter, 3);
84+
} else {
85+
return false;
86+
}
87+
88+
} else if (valid_profile_counter % 2 == 0) {
89+
return false;
90+
}
91+
92+
/* Find index of fastest profile */
93+
size_t idx_min = 0;
94+
for (size_t i = 1; i < valid_profile_counter; ++i) {
95+
if (valid_profiles[i].t_sum[6] < valid_profiles[idx_min].t_sum[6]) {
96+
idx_min = i;
97+
}
98+
}
99+
100+
cruckig_block_set_min_profile(block, &valid_profiles[idx_min]);
101+
102+
if (valid_profile_counter == 3) {
103+
const size_t idx_else_1 = (idx_min + 1) % 3;
104+
const size_t idx_else_2 = (idx_min + 2) % 3;
105+
106+
interval_from_profiles(&block->a, &valid_profiles[idx_else_1], &valid_profiles[idx_else_2]);
107+
return true;
108+
109+
} else if (valid_profile_counter == 5) {
110+
const size_t idx_else_1 = (idx_min + 1) % 5;
111+
const size_t idx_else_2 = (idx_min + 2) % 5;
112+
const size_t idx_else_3 = (idx_min + 3) % 5;
113+
const size_t idx_else_4 = (idx_min + 4) % 5;
114+
115+
if (valid_profiles[idx_else_1].direction == valid_profiles[idx_else_2].direction) {
116+
interval_from_profiles(&block->a, &valid_profiles[idx_else_1], &valid_profiles[idx_else_2]);
117+
interval_from_profiles(&block->b, &valid_profiles[idx_else_3], &valid_profiles[idx_else_4]);
118+
} else {
119+
interval_from_profiles(&block->a, &valid_profiles[idx_else_1], &valid_profiles[idx_else_4]);
120+
interval_from_profiles(&block->b, &valid_profiles[idx_else_2], &valid_profiles[idx_else_3]);
121+
}
122+
return true;
123+
}
124+
125+
return false;
126+
}
127+
128+
/* cruckig_block_is_blocked is now inlined in block.h */
129+
130+
const CRuckigProfile* cruckig_block_get_profile(const CRuckigBlock *block, double t) {
131+
if (block->b.valid && t >= block->b.right) {
132+
return &block->b.profile;
133+
}
134+
if (block->a.valid && t >= block->a.right) {
135+
return &block->a.profile;
136+
}
137+
return &block->p_min;
138+
}

0 commit comments

Comments
 (0)