gwordal

Lesson 1 of 5 · 20 min

Octave basics and matrices

A robot is mostly arrays of numbers in motion: a row of sensor readings, a rotation stored as a 3 by 3 table, a list of motor commands over time. GNU Octave is a free language built for exactly that kind of data, and its syntax is deliberately close to MATLAB. A one-line Octave expression often replaces a ten-line loop in C, which is why engineers reach for it when they want to try an idea fast.

Setting it up

Start the program by typing octave in a terminal. You land in a REPL (read, evaluate, print, loop): you type an expression, Octave evaluates it and prints the result. A few habits that save time:

  • A trailing semicolon suppresses printing. Without it, every line echoes its result, which is handy for exploring and noisy in scripts.
  • The result of an expression you did not assign goes into the variable ans.
  • help linspace explains any function, and who lists your variables.

Everything is a matrix

A single number is a 1 by 1 matrix, a row vector is 1 by n, and a column vector is n by 1. You build them with square brackets: spaces or commas separate columns, semicolons separate rows. This design is why Octave code is short. The same line works for one sensor or for a hundred, because you never write the loop over elements.

Indexing starts at 1

Here is a 3 by 3 matrix and four ways to pick pieces of it.

A = [1 2 3; 4 5 6; 7 8 10];
A(2,3)
A(1,:)
A(:,1)'
A(end,end)
disp(mat2str(A(:)'))
disp(mat2str(A(A > 5)'))
ans = 6
ans =

   1   2   3

ans =

   1   4   7

ans = 10
[1 4 7 2 5 8 3 6 10]
[7 8 6 10]

A(2,3) is row 2, column 3, exactly as you would write a23 on paper. Octave counts from 1 because it follows mathematical notation. C, Python and NumPy count from 0 because an index there is an offset from the start of the memory block. Neither is wrong, but bugs appear when you port code between them, so keep this table nearby.

TaskOctaveNumPy
first element of vv(1)v[0]
last elementv(end)v[-1]
row 2 of AA(2,:)A[1, :]
column 3 of AA(:,3)A[:, 2]
elements 2 to 4v(2:4)v[1:4]
matrix productA * BA @ B
element-wise productA .* BA * B
transposeA'A.T

Look at the last two lines of the output. A colon alone means "everything", and A(:) flattens the matrix column by column, a convention inherited from Fortran. NumPy flattens row by row by default, so A(A > 5) returns 7, 8, 6, 10 in Octave but 6, 7, 8, 10 for A[A > 5]. The data is the same, the order is not. In the NumPy version below you will see order="F" used to reproduce Octave's order.

Element-wise versus matrix operators

In Octave * is the true matrix product: row times column, the operation you use to chain rotations or apply a transform to a vector. To multiply entry by entry you add a dot: .*, ./, .^. The reason for two families is that both operations are common in robotics. Scaling every sample in a recording is element-wise; composing two coordinate frames is a matrix product.

B = [1 2; 3 4];
C = [5 6; 7 8];
B * C
B .* C
B .^ 2
B'
ans =

   19   22
   43   50

ans =

    5   12
   21   32

ans =

    1    4
    9   16

ans =

   1   3
   2   4

Check the first entry by hand: row 1 of B times column 1 of C is 15 + 27 = 19, while the element-wise product is just 1*5 = 5.

Colon ranges

start:step:stop builds a row vector and includes the stop value if the steps land on it. linspace(a, b, n) instead fixes the number of points. Both are how you make time axes.

disp(mat2str(0:0.25:1))
disp(mat2str(10:-3:1))
isequal(0:0.25:1, linspace(0, 1, 5))
[0 0.25 0.5 0.75 1]
[10 7 4 1]
ans = 1

NumPy's np.arange(0, 1, 0.25) would stop before 1, because Python ranges exclude the end. To match Octave you write np.arange(0, 1.25, 0.25).

The same maths in NumPy

numpy_version.py

Run it and compare with the Octave output above. A[1, 2] prints 6, the same element as A(2,3). The product B @ C gives 19, 22, 43, 50. Try changing A and predict what each line prints before you press Run.

Check yourself

In Octave, v = [10 20 30 40]. Which NumPy expression returns the same element as v(3)?

Check yourself

A = [1 2; 3 4]. What does A .* A return?